From b20d65ca96ee5edfd5417ae96ec82c5d4ea00bec Mon Sep 17 00:00:00 2001 From: kokofixcomputers Date: Thu, 22 May 2025 09:14:41 -0700 Subject: [PATCH] Initial Commit --- .DS_Store | Bin 0 -> 6148 bytes __pycache__/api.cpython-38.pyc | Bin 0 -> 11450 bytes __pycache__/frontend.cpython-38.pyc | Bin 0 -> 3664 bytes api.py | 469 ++++++++++++++++++ frontend.py | 151 ++++++ instance/game_save.db | Bin 0 -> 49152 bytes main.py | 16 + static/.DS_Store | Bin 0 -> 6148 bytes static/js/color-modes.js | 24 + static/js/modals.js | 33 ++ static/js/navbar.js | 42 ++ templates/developer_dashboard.html | 237 +++++++++ .../developer_dashboard_delete_game.html | 267 ++++++++++ templates/developer_dashboard_game.html | 238 +++++++++ templates/developer_registered.html | 0 templates/game_registered.html | 0 templates/index.html | 366 ++++++++++++++ templates/login_developer.html | 257 ++++++++++ templates/login_user.html | 257 ++++++++++ templates/register_developer.html | 257 ++++++++++ templates/register_game.html | 296 +++++++++++ templates/register_user.html | 266 ++++++++++ templates/scratch_modal.html | 116 +++++ templates/user_dashboard.html | 251 ++++++++++ templates/user_registered.html | 0 25 files changed, 3543 insertions(+) create mode 100644 .DS_Store create mode 100644 __pycache__/api.cpython-38.pyc create mode 100644 __pycache__/frontend.cpython-38.pyc create mode 100644 api.py create mode 100644 frontend.py create mode 100644 instance/game_save.db create mode 100644 main.py create mode 100644 static/.DS_Store create mode 100644 static/js/color-modes.js create mode 100644 static/js/modals.js create mode 100644 static/js/navbar.js create mode 100644 templates/developer_dashboard.html create mode 100644 templates/developer_dashboard_delete_game.html create mode 100644 templates/developer_dashboard_game.html create mode 100644 templates/developer_registered.html create mode 100644 templates/game_registered.html create mode 100644 templates/index.html create mode 100644 templates/login_developer.html create mode 100644 templates/login_user.html create mode 100644 templates/register_developer.html create mode 100644 templates/register_game.html create mode 100644 templates/register_user.html create mode 100644 templates/scratch_modal.html create mode 100644 templates/user_dashboard.html create mode 100644 templates/user_registered.html diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d9c959f4125bea784d289ae8928228b7d1610d4c GIT binary patch literal 6148 zcmeHK&x_MQ6n@jK+r%OiQP^WZFG9h#xQAsg*;?I$m&J%4RASPEZ7|7%CbdW@<{pwMdrcGH<@|w&6ls4nGOJu{v_xEv;e@s!mxfG zt0l(9Sy`}}?74_Yc#a`>FoY3=;4XOU4y}MzVAB*}YqyE3)rAb6zmn&J6}!?)_Dhi;GK=CENo1Rn^6QQ7inrBjzkA47Y&08> zoe`Zng;z}SzBhizp6*c^`=@QszaNEDw{__zO$#qd!!Zwtf)HIEJ&4kPPWyC{204#) z#DHlUrrX+?&31dcZL8bq&fC^(uh(f?H}-n-xoKR!a((}<`!r5c`jQESTQ~0A+%8Bw zg;&^6-bvN@>f!I>RgJ%&y#4S~XsqO@>buHE)%J+-QF1(U#%WB`W9(<#P5cr_O9i1h@>k(hB@l1%3g+ Cuib6{ literal 0 HcmV?d00001 diff --git a/__pycache__/api.cpython-38.pyc b/__pycache__/api.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..502facbdb0755c42d6fa4c8c49de7e516ac9c9ac GIT binary patch literal 11450 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�Po&0Tj(D0sE z8t>EV@H<_g24}fJ6f1+0t+zV38wK>znLbtO^2M0)n$%=()P>a~fdk6{VSvE2pX)^cwHHI` z5Zj6bN(E0A4Hp=m^7!h*y#uZ_2GZw2I3^=-B<4?bjhfYt3X-&xjWLu4K3iD7kDJEt zteD(H+s;XjHFTF5Y`-fHRDuAn^@OEYzuk zk;Rx)pPLAp2jEDMWH?&GS;lu1Kp}OJEb8SPL|rvdR}Ir|ih;aCXzMB2iO|`F7rg83Ww`4`P(RJ-~Hb}EpJLP zxtd~(aXcR4cw!jG@=q{J&yU>-t=Qn883+I?x2PNA^HhLOb$v*wv${JqkKEa5w%V&} zMTK{wXVtzBU-_pr-ry6N`-vrzyUrFX>Bc zFj)LrNtB$1*yh2t=_g6oP&=2rIgO5*L2-gS82P`SPRge=>T7mvg?^SO4V)=OMt8GE z*jmOf4J+z*iIRk_omFP_-BQxhRG$FWsK%QLNve%fQKMp=3MQW)qh`lG!RA7pf7_4%{RFdsFv6i`*6qEZk7O4XzYETq%n-6VEwpV?g( zVmZ8&*GRm#mDPumMrUU{Fwc@@Noqm^$!Scan`kZtjT(I)9yJWV=as?#ScTFv6XJ2eb#4 zEW;lXk1sZEDIry(r5hh7tzl2}wQ77C_r!X9Tk!Q^t0k1$9Co|AjZK;KHJtj~VApuG zDlVgNXUmogAV($ebgSKO3}H>5U=_k`X~xSDeu6RZFaIsP-eXsg3G z+p*HZSbkdu+@)1@6O){+K=RA6h(Ev-WHq z=%NleBq9FWbH)y&VKBAyhdkyX`bNli;Uzdhha$%JIh?D6bLL^{agMCS6Uw6~R#8xBRx;&vX-S0gUpgLk@XUREl=V$r%;?maRx;V#fvBi)(p`U zKu4ZM)ypW*zr&zS(kgQa*HsAL9CCJ<$v0qZw9t@ZtF5$T#=VWDGE5oQB;$uQe~ zK4ws}9#w7uD%Zc~@*u0+B2?~o+UFr)7(n%wzycZpVC7>o;EU6D!fk0%pUZiDPTe&D zR!$#?E62}Nw%|LOM!W9HH^J*%FWAO2u0OKyIMb^3))& z)zRuFKo5e~bW)pYYq*%K!;FLCe*xD4OF|g9C5JR4>a+wa)75G3*I|l6-c7MQ2US=_ zBrm|oXeCE71OEv)vl8SmC4=+3=fn9F^1T6Y1rrF103j3~L711Gf2j0U50&2ZU^D3C z)prgLw3ZzMI%%4t6Nm^G`8FD@PE}E6ofhHAry!J>5KQtNSU{dfaVY9J;@OOgU^Wwa zBtQ!NyXeGX+B!1Up9?$UjKqEwSD1$Vfz8=Nwg=r!oz#wYIdB)Tm)9w1ANrqj-{$Vv zx@)x=_a@a7@B&oN286x%5PM$8ec(N)EeQS08r*`5K=FFY4n)l}?G3wzG9ERnoasg@ zR0-Ju(uTxLNW(N|!dS*nTZ%Hyme6^-c{YFH`Rh*V4YAgHFmNd1at+3=z%ya0GILqk zJ^?`v;4f)M)kX$JBXe{v($H1&NWTUjnBdQjGU4DRKo}kI-@bO*+yD#CP%bEoxi^r2VIyydctZ6PckjCbYhCzJd;`Gd;Ssjl% zlS@@$k7>UH*CR+Omo8P_nDLaD9U^9XY+BLu{_$kCGoFy%b9Z7wh2Y-+bu@Ks{(X%L>Q^}H<6FfH?^5ix41!t0f1 zWA*&bi}5CMo@VY@9H8_)#1%P1cJY;sRZLB`usZAuoGL0dm&yiNQcg~awsJw0_Y zPb0d>(UlFGw+W&MLl{NIi3bByXeIs4_7+_ej3<*J^lt14bPJ=VPW?b4Gg0MGo=Mz$ vCN1d{bju4a_YywriVqPjg(?0r3*c54Iw-O7MjEz*rd`e~JAI= data['points']: + user_stats.points_earned -= data['points'] + db.session.commit() + + new_log = Log( + user_id=user_id, + game_id="game_id", + action='remove_points', + points=data['points'] + ) + db.session.add(new_log) + db.session.commit() + return jsonify({'message': 'Points removed successfully'}), 200 + else: + return jsonify({'error': 'Not enough points to remove'}), 400 + else: + return jsonify({'error': 'User stats not found'}), 404 + +@app.route('/api/logs', methods=['GET']) +def get_logs(): + session_token = request.cookies.get('session_token') + if not session_token: + return jsonify({'error': 'Session token is required'}), 401 + + session = Session.query.filter_by(session_token=session_token).first() + if not session: + return jsonify({'error': 'Invalid session'}), 401 + + logs = Log.query.filter_by(user_id=session.user_id).all() + log_data = [] + for log in logs: + log_data.append({ + 'id': log.id, + 'user_id': log.user_id, + 'game_id': log.game_id, + 'action': log.action, + 'points': log.points, + 'timestamp': log.timestamp + }) + return jsonify(log_data), 200 + +@app.route('/api/developerstats/games', methods=['POST']) +def games_count(): + data = request.json + if 'token' not in data: + return jsonify({'error': 'token is required'}), 401 + + session = Session.query.filter_by(session_token=data['token']).first() + if not session: + return jsonify({'error': 'Invalid session'}), 401 + + if session.user_type != "developer": + return jsonify({'error': 'Invalid User type. Requested developer, got user.'}) + + total_games = Game.query.filter_by(developer_id=session.user_id).all() + games_data = [ + {'name': game.name, 'ugi': game.ugi} + for game in total_games + ] + + return jsonify({"len": len(total_games), "games": games_data}), 200 + + +# Purge old tokens +#@app.before_first_request +#def purge_old_tokens(): +# old_tokens = AuthToken.query.filter(AuthToken.created_at < datetime.utcnow() - timedelta(days=10)).all() +# for token in old_tokens: +# db.session.delete(token) +# db.session.commit() +with app.app_context(): + db.create_all() +#if __name__ == '__main__': +# with app.app_context(): +# db.create_all() + #app.run(debug=True, host='0.0.0.0', port=5034) diff --git a/frontend.py b/frontend.py new file mode 100644 index 0000000..c83f077 --- /dev/null +++ b/frontend.py @@ -0,0 +1,151 @@ +from flask import Flask, render_template, request, redirect, url_for, make_response, abort, jsonify +import requests + +app = Flask(__name__) +app.config['SECRET_KEY'] = 'your_secret_key_here' + +# API URL +API_URL = 'http://localhost:5023' + +# Routes + +@app.route('/') +def index(): + return render_template('index.html') + + +@app.route('/modal') +def modal(): + id = request.args.get('id') + if id == "scratch": + return render_template('scratch_modal.html') + +@app.route('/login/user', methods=['GET', 'POST']) +def login_user(): + if request.method == 'POST': + data = request.form + response = requests.post(f'{API_URL}/api/user/login', json={'email': data['email'], 'password': data['password']}) + if response.status_code == 200: + session_token = response.json()['session_token'] + resp = make_response("Login Successful!") + resp.set_cookie('session_token', session_token, secure=True, httponly=True) + return resp + else: + abort(400) + return render_template('login_user.html') + +@app.route('/login/developer', methods=['GET', 'POST']) +def login_developer(): + if request.method == 'POST': + data = request.form + response = requests.post(f'{API_URL}/api/developer/login', json={'email': data['email'], 'password': data['password']}) + if response.status_code == 200: + session_token = response.json()['session_token'] + resp = make_response("Login Successful!") + resp.set_cookie('devsession_token', session_token, secure=True, httponly=True) + return resp + else: + return render_template('login_developer.html', error='Invalid email or password') + return render_template('login_developer.html') + +@app.route('/register/user', methods=['GET', 'POST']) +def register_user(): + if request.method == 'POST': + data = request.form + response = requests.post(f'{API_URL}/api/user/register', json={'username': data['username'], 'email': data['email'], 'password': data['password'], 'game_password': data['game_password']}) + if response.status_code == 201: + return render_template('user_registered.html', message=response.json()['message']) + else: + return render_template('register_user.html', error='Failed to register user') + return render_template('register_user.html') + +@app.route('/register/developer', methods=['GET', 'POST']) +def register_developer(): + if request.method == 'POST': + data = request.form + response = requests.post(f'{API_URL}/api/developer/register', json={'email': data['email'], 'password': data['password']}) + if response.status_code == 201: + return render_template('developer_registered.html', message=response.json()['message']) + else: + return render_template('register_developer.html', error='Failed to register developer') + return render_template('register_developer.html') + +@app.route('/user/dashboard') +def user_dashboard(): + session_token = request.cookies.get('session_token') + if not session_token: + return redirect(url_for('login_user')) + + response = requests.post(f'{API_URL}/api/user/stats', json={'token': f'{session_token}'}) + if response.status_code == 200: + user_stats = response.json() + games_played_count = len(user_stats['games_played']) + return render_template('user_dashboard.html', user_stats=user_stats, games_played_count=games_played_count) + else: + return redirect(url_for('login_user')) + +@app.route('/developer/dashboard') +def developer_dashboard(): + session_token = request.cookies.get('devsession_token') + if not session_token: + return redirect(url_for('login_developer')) + + count = requests.post(f'{API_URL}/api/developerstats/games', json={'token': session_token}) + if count.status_code == 200: + return render_template('developer_dashboard.html', session_token=session_token, count=count.json()['len']) + else: + return redirect('login_developer') + +@app.route('/developer/dashboard/games') +def developer_dashboard_games(): + session_token = request.cookies.get('devsession_token') + if not session_token: + return redirect(url_for('login_developer')) + count = requests.post(f'{API_URL}/api/developerstats/games', json={'token': session_token}) + if count.status_code == 200: + return render_template('developer_dashboard_game.html', session_token=session_token, count=count.json()['len'], games=count.json()['games']) + else: + return redirect('login_developer') + +@app.route('/developer/register/game', methods=['GET', 'POST']) +def register_game(): + session_token = request.cookies.get('devsession_token') + if not session_token: + return redirect(url_for('login_developer')) + + if request.method == 'POST': + data = request.form + response = requests.post(f'{API_URL}/api/game/register', json={'session_token': session_token, 'name': data['name']}) + if response.status_code == 201: + return "OK" + else: + return response.json()['error'], 400 + return render_template('register_game.html') + +@app.route('/developer/delete/game', methods=['GET']) +def delete_game(): + gameugi = request.args.get('ugi') + gamename = request.args.get('name') + session_token = request.cookies.get('devsession_token') + if not session_token: + return redirect(url_for('login_developer')) + if not gameugi or not gamename: + return jsonify({"error": "The ugi or the name parameter was not found"}) + return render_template('developer_dashboard_delete_game.html', name=gamename, ugi=gameugi) + +@app.route('/developer/dashboard/games/delete/confirmed', methods=['POST']) +def delete_actually_do_it(): + session_token = request.cookies.get('devsession_token') + ugi = request.args.get('ugi') + if not session_token: + return redirect(url_for('login_developer')) + if not ugi: + return jsonify({"error": "Ugi parameter must be provided."}) + resp = requests.post(f'{API_URL}/api/game/delete', json={'session_token': session_token, 'ugi': ugi}) + if resp.status_code == 200: + return "OK" + +# Other routes... + +#if __name__ == '__main__': + #app.run(debug=True, port=5001) diff --git a/instance/game_save.db b/instance/game_save.db new file mode 100644 index 0000000000000000000000000000000000000000..801bcc396874d2d35a42b77bec6d3811be39deea GIT binary patch literal 49152 zcmeI*%TwD%90zbYJdAlcLx!Ol4k*)f0+Y(oTNXVujw7%MHehT*a`8yA3<}0JmU!NP z-g?fT(L?`;-a5@SGd=XwTXO8Qy>%rU6B}?~n9e}vtC7vJS}*(iXm_zJTV7t8w_S@a zR%)BNOAFLpDiENa(lkX;Bjh$nZYN70Npzq5LVgB5Nney4p&~m!4u$`r27>RX;lIMa z4E;X*>)_vm!~Guz{tUhE`-5a-fdB*`009U<;Ccc_Lj%FF2M+?rma7{jOW$_a^KNCs zDxanX;~6cM)#z;O*}O)d=F^k?A-g~)(^+j+%h2hCES=8H&woqzhx{7ol~^X8h-D^u zmYr(ZyryqjdGgSc0@1eZ+m2Q9wx3tES+jK4D&)zNkLRRS*_=PSA$ir~*)snt%or;_xt5)o?>*OuHRwl*HgSlqlMf~y}_}GiNNuA6DaTKJHC>i z2%e$xvpacp82tWU?l5t9e>z{#U7gNqt7JO*WJ)^E>MHYs-V+=f9}gVg_XBk-$00qo z@uRmT$VO&|5E~`x6K!+%(b6>fu-rK+TWArNbZrv@> zc{v?I^ES8i>h>;gZR&QZ88g=&QB`-G-Ae6rCOlKZ?*pGNcHaIz85@2ZruwHIb$9CK zUMP9ayYD$n1^jM)WcxDB;nGapX)$VMUlGdtUv+Jt%LLBd=^fK1E*js>=I6O)vWq;; z@Q&xHA#w|Zf1=2W1p*L&00bZa0SG_<0uX=z1Rwx`n;@_h=%uK~hdmpWjmlH+jvUWN zO!FacJmmO?>0~);)fV=RT5)#W-j>svorrAf@t3>1oHoBIDMeS?TzrvqW}RwTc`5A0 zw@tpE#1#mCq{xZ|0uX=z1Rwwb2tWV=5P$##AOL|cL13ioaj5OJgZlaZw^aD;mk=Og zfB*y_009U<00Izz00bZa0SMe80Xq<+#>shprg4tH?U=oBzF(TzTrMZ)-0CuGN7gN& z5I3Ia+Yxp?ky%n|=D~~_(Gpv`xrI3~{W_Q658O33;#I}0laJgvjQ7b42j~B9QNKV_ zApijgKmY;|fB*y_009U<00Ng0xZwPM_wa92_~)<@{)!~9KmY;|fB*y_009U<00Izz z00eHlK%%>s%8iC%R#eqx%aR!*D)EfSvI?V%QI(MmSuzTIL6J?>yAmMJ@)E-e3@6a6 zFfEZ)ig1FWDA7b$y{4jCG%emRnF1@Yj3}@cW0;CgQj*Fls!-%v|C)lWH3`ZzCq`6J zloT~}htxC?it(1hD++mhQBW;Ll;t9$t0K8mI611yg38Hi^ZI#o{|{97 z!;Rm$Xdnb2009U<00Izz00bZa0SG_<0+$dN3G`5*@HZTJ^S^7=m^`^?pin>le@BJ! zJ$#oCL=gx;00Izz00bZa0SG_<0uX?}jSzT34)JsMLn5athRli#&zmMAN|wY#b=_bj zLu3`jG7XjYE&)JZ1*m`i|2-9ce%Vibh{1Rwwb2tWV=5P$##AOHafK;YI1)ZhRAFBSgx)-@dsh5!U0009U< z00Izz00bZa0SG|ga{@iy#Q=u->c{_!RCw`o55RT^KmY;|fB*y_009U<00Izz00d41 z4w*oZI{G?HzJp+YZrzwQ?FD--nL9{wX*=m8%NfaxC*|Zub#)~^r$$Jjl9ho-z>!q>xHf)H7Uj?3aiR2Z_Syzi&3ua`v20j$VWt(d>sMzA4E!8LI3~& literal 0 HcmV?d00001 diff --git a/main.py b/main.py new file mode 100644 index 0000000..f65231a --- /dev/null +++ b/main.py @@ -0,0 +1,16 @@ +import threading +from api import app as api_app +from frontend import app as frontend_app + +def run_api(): + api_app.run(debug=False, port=5023) + +def run_frontend(): + frontend_app.run(debug=False, port=5024) + +if __name__ == '__main__': + api_thread = threading.Thread(target=run_api) + frontend_thread = threading.Thread(target=run_frontend) + + api_thread.start() + frontend_thread.start() diff --git a/static/.DS_Store b/static/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..415c1a8968e6c6050c8c57f3691d3e4ec786f7a0 GIT binary patch literal 6148 zcmeHKJ5Iwu5S@a6@^uFf`jYAJ{0FF;MM{`>x#tcXl(#K1^8cNS@?* z{V-kpsCT~Ynr6A~mdL9vW|wcD?;qFOnzmaV-P=6wo_BTlcCu7}3Qz$mKn4Dv0_fRh z)oCCj6`%rC;9CLvJ`}iNo!AHZrvt%90HFQGyW!bq31Be-SSR*@U|<@hz$n!mF*M4- zm&~gZ`@kp{^_y`{-mE#HsNW90c)DmE$VdgKz`g<}v7A`{zl9%}|M!cyq5@RlPbr|| zX3@;?O4(ZnFK4~B!0+H&L$0SIcq;~aE5^cF@%c_(v1jbpiG84#gZFYEe*{by8Ws2r G1-<|@l_bXi literal 0 HcmV?d00001 diff --git a/static/js/color-modes.js b/static/js/color-modes.js new file mode 100644 index 0000000..fef931d --- /dev/null +++ b/static/js/color-modes.js @@ -0,0 +1,24 @@ +/*! + * Color mode toggler with OS change detection + */ + +(() => { + 'use strict'; + + // Function to update theme based on localStorage or system preference + const updateTheme = () => { + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } + }; + + // Initialize theme on load + updateTheme(); + + // Listen for OS color scheme changes + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + updateTheme(); + }); +})(); \ No newline at end of file diff --git a/static/js/modals.js b/static/js/modals.js new file mode 100644 index 0000000..d37125b --- /dev/null +++ b/static/js/modals.js @@ -0,0 +1,33 @@ +// Performs basic AJAX functionality to fetch and render a modal. + +function showLoadingOverlay() { + $(`#loadingOverlay`)[0].classList.remove("hidden"); +} + +function hideLoadingOverlay() { + $(`#loadingOverlay`)[0].classList.add("hidden"); +} + +function closeModal() { + $(`#dynamicModal`)[0].classList.add('modal-closing'); + setTimeout(() => { + $("#modalContainer").empty(); + }, 200); +} + +function openModal(id) { + showLoadingOverlay(); // Show loading overlay before fetching content + $("#modalContainer").load(`/modal?id=${id}`, function(responseTxt, statusTxt, xhr){ + hideLoadingOverlay(); // Hide loading overlay after content is fetched + if (statusTxt == "error") { + console.log("Error loading modal: " + xhr.status + ": " + xhr.statusText); + } + }); +} + +// Open a modal when a button is clicked +document.querySelectorAll(".open-modal-button").forEach(button => { + button.addEventListener("click", () => { + openModal(button.dataset.itemId); // Use the item ID from the button instead of the target, as it tends to be undefined + }); +}); \ No newline at end of file diff --git a/static/js/navbar.js b/static/js/navbar.js new file mode 100644 index 0000000..d53c4b4 --- /dev/null +++ b/static/js/navbar.js @@ -0,0 +1,42 @@ +const mobileMenuButton = document.getElementById('mobile-menu-button'); +const mobileMenu = document.getElementById('mobile-menu'); + +mobileMenuButton.addEventListener('click', function () { + const isExpanded = this.getAttribute('aria-expanded') === 'true'; + + this.setAttribute('aria-expanded', !isExpanded); + mobileMenu.classList.toggle('hidden'); + + // Toggle icons + const icons = this.getElementsByTagName('svg'); + icons[0].classList.toggle('hidden'); + icons[1].classList.toggle('hidden'); +}); + +const desktopDropdownButton = document.getElementById('desktop-dropdown-button'); +const desktopDropdownMenu = document.getElementById('desktop-dropdown-menu'); + +desktopDropdownButton.addEventListener('click', function (e) { + e.stopPropagation(); + desktopDropdownMenu.classList.toggle('hidden'); +}); + +const mobileDropdownButton = document.getElementById('mobile-dropdown-button'); +const mobileDropdownMenu = document.getElementById('mobile-dropdown-menu'); + +mobileDropdownButton.addEventListener('click', function () { + mobileDropdownMenu.classList.toggle('hidden'); +}); + +document.addEventListener('click', function (e) { + if (!desktopDropdownButton.contains(e.target)) { + desktopDropdownMenu.classList.add('hidden'); + } +}); + +document.addEventListener('keydown', function (e) { + if (e.key === 'Escape') { + desktopDropdownMenu.classList.add('hidden'); + mobileDropdownMenu.classList.add('hidden'); + } +}); \ No newline at end of file diff --git a/templates/developer_dashboard.html b/templates/developer_dashboard.html new file mode 100644 index 0000000..4f54453 --- /dev/null +++ b/templates/developer_dashboard.html @@ -0,0 +1,237 @@ + + + + + + + + +
+ + + + +
+ +
+

+ Overview +

+
+ + +
+
+

+ Total Games Created +

+

+ {{ count }} +

+
+
+ +
+

+ Logs +

+ + + + + + + + + + + + + + + + + + + + + + + +
+ Timestamp + + Class + + Description + + Status +
nilGame + Game ID 123 has been created! + Transaction Successful
nilgame + Game ID 123 have been deleted! + Transaction Successful
+
+
+
+ + diff --git a/templates/developer_dashboard_delete_game.html b/templates/developer_dashboard_delete_game.html new file mode 100644 index 0000000..214cf7e --- /dev/null +++ b/templates/developer_dashboard_delete_game.html @@ -0,0 +1,267 @@ + + + + + + + + + +
+ + + + +
+ +
+

You are attempting to delete the game {{ name }} with the UGI {{ ugi }}

+

Please confirm your action below.

+

Take a moment to observe the consequences and then confirm below if you really want to do this.

+

Please note that deleting this game is an inreversable action.

+

All game saves from all users will be deleted.

+
+
+ + + + + + + + Cancel + + +
+
+ + + + + + + +
+
+ + diff --git a/templates/developer_dashboard_game.html b/templates/developer_dashboard_game.html new file mode 100644 index 0000000..2c32426 --- /dev/null +++ b/templates/developer_dashboard_game.html @@ -0,0 +1,238 @@ + + + + + + + + +
+ + + + +
+ +
+

+ Games +

+
+ + +
+
+

+ Total Games Created +

+

+ {{ count }} +

+
+
+ + + +
+

+ Your Games +

+ + + + + + + + + {% for game in games %} + + + + + + {% endfor %} + +
+ Name + + UGI +
{{ game.name }}{{ game.ugi }} + + + +
+
+
+
+ + diff --git a/templates/developer_registered.html b/templates/developer_registered.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/game_registered.html b/templates/game_registered.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..d33078d --- /dev/null +++ b/templates/index.html @@ -0,0 +1,366 @@ + + + + + + + + Home Page + + + + + + + + + + + + + + + + + +