feat: Add admin page and made a ton of UI improvements
All checks were successful
Build Docker / Build Image (push) Successful in 25s
All checks were successful
Build Docker / Build Image (push) Successful in 25s
This commit is contained in:
parent
28e11e2596
commit
5430465744
2
dist/bundle.js
vendored
2
dist/bundle.js
vendored
File diff suppressed because one or more lines are too long
186
main.py
186
main.py
@ -10,13 +10,13 @@ import nacl.encoding
|
||||
import nacl.exceptions
|
||||
import base58
|
||||
import render
|
||||
import hashlib
|
||||
import random
|
||||
|
||||
app = Flask(__name__)
|
||||
dotenv.load_dotenv()
|
||||
|
||||
CURRENT_VOTE = os.getenv('CURRENT_VOTE')
|
||||
OPTIONS = os.getenv('OPTIONS')
|
||||
OPTIONS = json.loads(OPTIONS)
|
||||
|
||||
DISCORD_WEBHOOK = os.getenv('DISCORD_WEBHOOK')
|
||||
|
||||
# If votes file doesn't exist, create it
|
||||
@ -53,10 +53,28 @@ def faviconPNG():
|
||||
@app.route('/')
|
||||
def index():
|
||||
year = datetime.datetime.now().year
|
||||
votes = render.votes()
|
||||
options = render.options(OPTIONS)
|
||||
|
||||
|
||||
info = get_vote_info()
|
||||
options = render.options(info["options"])
|
||||
|
||||
return render_template('index.html',year=year,votes=votes, current_vote=CURRENT_VOTE, options=options)
|
||||
enabled = info["enabled"]
|
||||
end = datetime.datetime.strptime(info["end"], "%Y-%m-%d")
|
||||
if end < datetime.datetime.now():
|
||||
enabled = False
|
||||
end = "Voting has closed"
|
||||
info["public"] = True
|
||||
else:
|
||||
end = f'Voting ends on {end.strftime("%B %d, %Y")}'
|
||||
|
||||
revote = "not" if not info["revote"] else ""
|
||||
if info["public"]:
|
||||
votes = render.votes()
|
||||
else:
|
||||
votes = ""
|
||||
|
||||
return render_template('index.html',year=year,votes=votes, options=options,
|
||||
current_vote=info["vote"], description=info["description"], end=end,enabled=enabled, public=info["public"], revote=revote)
|
||||
|
||||
@app.route('/<path:path>')
|
||||
def catch_all(path):
|
||||
@ -87,6 +105,24 @@ def vote():
|
||||
public_key = data["walletAddress"]
|
||||
percent = data["percent"]
|
||||
|
||||
# Check if revote is enabled
|
||||
info = get_vote_info()
|
||||
if not info['revote']:
|
||||
with open('data/votes.json') as file:
|
||||
votes = json.load(file)
|
||||
for vote in votes:
|
||||
if vote["walletAddress"] == public_key:
|
||||
return render_template('revotes.html', year=datetime.datetime.now().year)
|
||||
|
||||
# Make sure the voting is enabled and hasn't ended
|
||||
if not info['enabled']:
|
||||
return render_template('404.html', year=datetime.datetime.now().year)
|
||||
|
||||
end = datetime.datetime.strptime(info['end'], "%Y-%m-%d")
|
||||
if end < datetime.datetime.now():
|
||||
return render_template('404.html', year=datetime.datetime.now().year)
|
||||
|
||||
|
||||
# Verify signature
|
||||
try:
|
||||
# Decode base58 encoded strings
|
||||
@ -108,7 +144,17 @@ def vote():
|
||||
# Send message to discord
|
||||
send_discord_message(data)
|
||||
save_vote(data)
|
||||
return render_template('success.html', year=datetime.datetime.now().year, vote=data["message"],percent=percent,signature=signature,votes=render.votes())
|
||||
|
||||
vote_chart = ""
|
||||
if info['public']:
|
||||
vote_chart = render.votes()
|
||||
else:
|
||||
date = datetime.datetime.strptime(info['end'], "%Y-%m-%d")
|
||||
if date < datetime.datetime.now():
|
||||
vote_chart = render.votes()
|
||||
|
||||
|
||||
return render_template('success.html', year=datetime.datetime.now().year, vote=data["message"],percent=percent,signature=signature,votes=vote_chart)
|
||||
|
||||
def save_vote(data):
|
||||
# Load votes
|
||||
@ -176,11 +222,137 @@ def send_discord_message(data):
|
||||
|
||||
@app.route('/votes')
|
||||
def download():
|
||||
if 'walletAddress' in request.args:
|
||||
address = request.args['walletAddress']
|
||||
with open('data/votes.json') as file:
|
||||
votes = json.load(file)
|
||||
for vote in votes:
|
||||
if vote["walletAddress"] == address:
|
||||
return jsonify([vote])
|
||||
return jsonify([])
|
||||
|
||||
|
||||
|
||||
info = get_vote_info()
|
||||
if not info['public']:
|
||||
end = datetime.datetime.strptime(info['end'], "%Y-%m-%d")
|
||||
if end > datetime.datetime.now():
|
||||
return render_template('blocked.html', year=datetime.datetime.now().year), 404
|
||||
|
||||
|
||||
resp = make_response(send_from_directory('data', 'votes.json'))
|
||||
# Set as json
|
||||
resp.headers['Content-Type'] = 'application/json'
|
||||
return resp
|
||||
|
||||
|
||||
#region admin
|
||||
@app.route('/login', methods=['POST'])
|
||||
def login():
|
||||
# If account.json doesn't exist, create it
|
||||
if not os.path.isfile('data/account.json'):
|
||||
|
||||
user = request.form['email']
|
||||
# Hash password
|
||||
password = request.form['password']
|
||||
hashed = hashlib.sha256(password.encode()).hexdigest()
|
||||
token = random.randint(100000, 999999)
|
||||
with open('data/account.json', 'w') as file:
|
||||
json.dump({'email': user, 'password': hashed, 'token': token}, file)
|
||||
resp = make_response(redirect('/admin'))
|
||||
resp.set_cookie('token', str(token))
|
||||
return resp
|
||||
|
||||
|
||||
# Read account.json
|
||||
with open('data/account.json') as file:
|
||||
account = json.load(file)
|
||||
|
||||
|
||||
|
||||
user = request.form['email']
|
||||
# Hash password
|
||||
password = request.form['password']
|
||||
hashed = hashlib.sha256(password.encode()).hexdigest()
|
||||
|
||||
if user == account['email'] and hashed == account['password']:
|
||||
token = random.randint(100000, 999999)
|
||||
account['token'] = token
|
||||
with open('data/account.json', 'w') as file:
|
||||
json.dump(account, file)
|
||||
resp = make_response(redirect('/admin'))
|
||||
resp.set_cookie('token', str(token))
|
||||
return resp
|
||||
|
||||
return redirect('/')
|
||||
|
||||
@app.route('/admin')
|
||||
def admin():
|
||||
if not 'token' in request.cookies:
|
||||
return redirect('/login')
|
||||
with open('data/account.json') as file:
|
||||
account = json.load(file)
|
||||
if request.cookies['token'] != str(account['token']):
|
||||
return redirect('/login')
|
||||
|
||||
info = get_vote_info()
|
||||
options = ','.join(info['options'])
|
||||
|
||||
return render_template('admin.html', year=datetime.datetime.now().year, name=info['vote'], description=info['description'], end=info['end'], enabled=info['enabled'], public=info['public'], revote=info['revote'], options=options)
|
||||
|
||||
@app.route('/admin', methods=['POST'])
|
||||
def admin_post():
|
||||
if not 'token' in request.cookies:
|
||||
return redirect('/login')
|
||||
with open('data/account.json') as file:
|
||||
account = json.load(file)
|
||||
if request.cookies['token'] != str(account['token']):
|
||||
return redirect('/login')
|
||||
|
||||
info = get_vote_info()
|
||||
|
||||
info['vote'] = request.form['name']
|
||||
info['description'] = request.form['description']
|
||||
info['end'] = request.form['end']
|
||||
info['enabled'] = 'enabled' in request.form
|
||||
info['public'] = 'public' in request.form
|
||||
info['revote'] = 'revote' in request.form
|
||||
options = request.form['options']
|
||||
options = options.split(',')
|
||||
info['options'] = options
|
||||
|
||||
with open('data/info.json', 'w') as file:
|
||||
json.dump(info, file)
|
||||
|
||||
return redirect('/admin')
|
||||
|
||||
@app.route('/admin/clear')
|
||||
def clear():
|
||||
if not 'token' in request.cookies:
|
||||
return redirect('/login')
|
||||
with open('data/account.json') as file:
|
||||
account = json.load(file)
|
||||
if request.cookies['token'] != str(account['token']):
|
||||
return redirect('/login')
|
||||
with open('data/votes.json', 'w') as file:
|
||||
json.dump([], file)
|
||||
return redirect('/admin')
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
def get_vote_info():
|
||||
if not os.path.isfile('data/info.json'):
|
||||
with open('data/info.json', 'w') as file:
|
||||
end = datetime.datetime.now() + datetime.timedelta(days=7)
|
||||
end = end.strftime("%Y-%m-%d")
|
||||
|
||||
json.dump({'vote': '','description':'', 'end': end,'enabled': False, 'public': True, 'revote': True, 'options': []}, file)
|
||||
with open('data/info.json') as file:
|
||||
info = json.load(file)
|
||||
return info
|
||||
|
||||
|
||||
# 404 catch all
|
||||
@app.errorhandler(404)
|
||||
def not_found(e):
|
||||
|
45
src/index.js
45
src/index.js
@ -4,7 +4,7 @@ import base58 from 'bs58'
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
// Set testing to true if running in a local environment
|
||||
const testing = false;
|
||||
const testing = true;
|
||||
let TOKENID = "G9GQFWQmTiBzm1Hh4gM4ydQB4en3wPUxBZ1PS8DruXy8";
|
||||
let supply = 100000;
|
||||
let balance = 0;
|
||||
@ -18,7 +18,6 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
// Initialize Solana connection
|
||||
const solana = window.solana;
|
||||
if (!solana || !solana.isPhantom) {
|
||||
alert('Phantom wallet not detected. Please install Phantom and try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -89,9 +88,48 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
document.getElementById('percent').textContent = roundedPercent.toString();
|
||||
balance = output;
|
||||
});
|
||||
|
||||
// Show the user their existing vote if they have one
|
||||
// Read list of votes from the server
|
||||
const response = await fetch('/votes?walletAddress=' + solana.publicKey.toString());
|
||||
const votes = await response.json();
|
||||
|
||||
// Find the user's vote
|
||||
const userVote = votes.find(vote => vote.walletAddress === solana.publicKey.toString());
|
||||
if (userVote) {
|
||||
// Display the user's vote
|
||||
const vote = JSON.parse(userVote.message);
|
||||
const existingVote = document.getElementById('existingVote');
|
||||
existingVote.innerHTML = '<h3 class="display-4">Your existing vote:</h3>';
|
||||
let total = 0;
|
||||
for (const key in vote) {
|
||||
if (vote.hasOwnProperty(key)) {
|
||||
const value = vote[key];
|
||||
total += parseInt(value);
|
||||
const li = document.createElement('li');
|
||||
li.textContent = `${key}: ${value}%`;
|
||||
// Remove the bullet points
|
||||
li.style.listStyleType = 'none';
|
||||
if (parseInt(value) > 0) {
|
||||
existingVote.appendChild(li);
|
||||
}
|
||||
}
|
||||
}
|
||||
const sub = document.createElement('p');
|
||||
sub.textContent = `You have used ${total}% of your voting power`;
|
||||
existingVote.appendChild(sub);
|
||||
|
||||
existingVote.style.display = 'block';
|
||||
existingVote.style.marginBottom = '20px';
|
||||
existingVote.style.backgroundColor = 'black';
|
||||
existingVote.style.color = 'white';
|
||||
existingVote.style.padding = '10px';
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error connecting wallet:', error);
|
||||
alert('Error connecting wallet. Check the console for details.');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -152,7 +190,6 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error submitting vote:', error);
|
||||
alert('Error submitting vote. Check the console for details.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
112
templates/admin.html
Normal file
112
templates/admin.html
Normal file
@ -0,0 +1,112 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-bs-theme="dark" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Admin - Vote | Nathan.Woodburn/</title>
|
||||
<meta name="twitter:image" content="https://vote.woodburn.au/assets/img/favicon.png">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta property="og:title" content="Vote | Nathan.Woodburn/">
|
||||
<meta name="description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta name="twitter:title" content="Vote | Nathan.Woodburn/">
|
||||
<meta property="og:image" content="https://vote.woodburn.au/assets/img/favicon.png">
|
||||
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/android-chrome-192x192.png">
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="assets/img/android-chrome-512x512.png">
|
||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&display=swap">
|
||||
<link rel="stylesheet" href="assets/css/default.css">
|
||||
<script async src="https://umami.woodburn.au/script.js" data-website-id="6a55028e-aad3-481c-9a37-3e096ff75589"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md fixed-top navbar-shrink py-3 navbar-light" id="mainNav">
|
||||
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span>Nathan.Woodburn/</span></a><button class="navbar-toggler" data-bs-toggle="collapse"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button></div>
|
||||
</nav>
|
||||
<header class="pt-5">
|
||||
<div class="container pt-4 pt-xl-5">
|
||||
<div class="row pt-5">
|
||||
<div class="col-md-8 text-center text-md-start mx-auto">
|
||||
<div class="text-center">
|
||||
<h1 class="display-4 fw-bold mb-5">Admin</h1>
|
||||
<form method="post"><input class="form-control" type="text" name="name" value="{{name}}" placeholder="Vote Name" style="margin-bottom: 5px;"><textarea class="form-control" style="margin-bottom: 5px;" name="description" placeholder="Vote Description">{{description}}</textarea><input class="form-control" type="text" name="options" value="{{options}}" placeholder="Vote Options comma separated" style="margin-bottom: 5px;">
|
||||
<div style="margin-bottom: 5px;"><label class="form-label" for="end" style="display: inline-block;margin-right: 10px;">End Date</label><input class="form-control" type="date" name="end" value="{{end}}" style="width: auto;display: inline-block;"></div>{% if revote %}
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input id="formCheck-1" class="form-check-input" type="checkbox" name="revote" checked />
|
||||
<label class="form-check-label" for="formCheck-1">Allow revotes</label>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input id="formCheck-1" class="form-check-input" type="checkbox" name="revote" />
|
||||
<label class="form-check-label" for="formCheck-1">Allow revotes</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if public %}
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input id="formCheck-2" class="form-check-input" type="checkbox" name="public" checked />
|
||||
<label class="form-check-label" for="formCheck-2">Show results during vote</label>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input id="formCheck-2" class="form-check-input" type="checkbox" name="public" />
|
||||
<label class="form-check-label" for="formCheck-2">Show results during vote</label>
|
||||
</div>
|
||||
{% endif %}{% if enabled %}
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input id="formCheck-3" class="form-check-input" type="checkbox" name="enabled" checked />
|
||||
<label class="form-check-label" for="formCheck-3">Enabled</label>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input id="formCheck-3" class="form-check-input" type="checkbox" name="enabled" />
|
||||
<label class="form-check-label" for="formCheck-3">Enabled</label>
|
||||
</div>
|
||||
{% endif %}<input class="btn btn-primary" type="submit" style="display: block;margin: auto;margin-top: 25px;">
|
||||
</form>
|
||||
<div style="margin-top: 40px;">
|
||||
<h1>Reset Votes</h1>
|
||||
<p>Clear all votes to allow for the next vote</p><a class="btn btn-primary" role="button" href="/admin/clear">Reset Votes</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-10 mx-auto">
|
||||
<div class="text-center position-relative"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<footer>
|
||||
<div class="container py-4 py-lg-5">
|
||||
<div class="row row-cols-2 row-cols-md-4">
|
||||
<div class="col-12 col-md-3">
|
||||
<div class="fw-bold d-flex align-items-center mb-2"><span>Nathan.Woodburn/</span></div>
|
||||
<p class="text-muted">Australian developer and student.</p>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-3 text-lg-start d-flex flex-column">
|
||||
<h3 class="fs-6 fw-bold">Sites</h3>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
|
||||
<li><a href="https://hns.au" target="_blank">HNSAU</a></li>
|
||||
<li><a href="https://hnshosting.au" target="_blank">HNSHosting</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
|
||||
<p class="mb-0">Copyright © {{year}} Nathan.Woodburn/</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="assets/js/startup-modern.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
1
templates/assets/img/illustrations/login.svg
Normal file
1
templates/assets/img/illustrations/login.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300" width="406" height="306" class="illustration styles_illustrationTablet__1DWOa"><path d="M217.37,104.48s23.73,6,23.9,25.71-10.53,54.05,4.74,56.86,24.4-39.66,24.4-39.66l10.88,4.38s-11.58,72.57-51.6,60.59S217.37,104.48,217.37,104.48Z" fill="#68e1fd"></path><path d="M217.37,104.48s23.73,6,23.9,25.71-10.53,54.05,4.74,56.86,24.4-39.66,24.4-39.66l10.88,4.38s-11.58,72.57-51.6,60.59S217.37,104.48,217.37,104.48Z" fill="#fff" opacity="0.44"></path><ellipse cx="202.28" cy="230.74" rx="157.96" ry="14.45" fill="#e6e6e6" opacity="0.3"></ellipse><path d="M76.51,213.73S66.25,210.93,64,201.39c0,0,15.89-3.21,16.34,13.19Z" fill="#68e1fd" opacity="0.58"></path><path d="M77.76,212.72s-7.16-11.33-.85-21.92c0,0,12.07,7.67,6.71,21.94Z" fill="#68e1fd" opacity="0.73"></path><path d="M79.61,212.72s3.79-12,15.23-14.22c0,0,2.14,7.76-7.41,14.26Z" fill="#68e1fd"></path><polygon points="72.17 212.46 74.25 226.68 87.34 226.73 89.27 212.53 72.17 212.46" fill="#24285b"></polygon><polygon points="196.01 72.21 188.78 97.7 203.12 100.69 202.62 80.94 196.01 72.21" fill="#f4a28c"></polygon><path d="M202.92,85.25a9.57,9.57,0,0,1-4.37-3.36s-.37,4.84,4.35,10.22Z" fill="#ce8172" opacity="0.31"></path><path d="M188.78,97.71l8.5,1.76s38.9,4.19,40.48,26.15-20.39,78.06-20.39,78.06H158.43S111.2,91.9,188.78,97.71Z" fill="#68e1fd"></path><path d="M183.69,118.05s4.75,13.72-7.71,22.67-6.38,42.65,7.26,48.09-8.53,17.08-18,12.31-10.77-8-10.77-8-8.77-25.35-9.7-41.52S183.69,118.05,183.69,118.05Z" opacity="0.09"></path><path d="M158.43,203.68s-33.18-11.8-45.29-6.76-5.26,28.52,34.23,29.68,97.93,7.61,109.52-6.67S224.77,198,158.43,203.68Z" fill="#24285b"></path><path d="M183.69,118.05c0-12.07-12.23-20.32-23.71-16.55-10,3.27-22.15,9.34-29.54,20.44-14.22,21.32-46.7,83.65,35.75,86.05l2.42-9.18S119.12,185.12,141,156.16C158,133.64,183.74,135.15,183.69,118.05Z" fill="#68e1fd"></path><path d="M183.69,118.05c0-12.07-12.23-20.32-23.71-16.55-10,3.27-22.15,9.34-29.54,20.44-14.22,21.32-46.7,83.65,35.75,86.05l2.42-9.18S119.12,185.12,141,156.16C158,133.64,183.74,135.15,183.69,118.05Z" fill="#fff" opacity="0.44"></path><polygon points="166.51 209.87 207.52 209.87 217.37 165.64 160.19 165.64 166.51 209.87" fill="#ffd200"></polygon><path d="M273,148.41s.62-10.5,4.83-13.49,3.51,4.57,3.51,4.57,4.21-4.92,7.72-2.63-7.72,14.91-7.72,14.91Z" fill="#f4a28c"></path><rect x="268.45" y="97.1" width="37.48" height="48.09" transform="translate(42.89 -72.43) rotate(15.52)" fill="#e6e6e6"></rect><rect x="272.34" y="120.86" width="24.78" height="18.25" transform="translate(45.16 -71.45) rotate(15.52)" fill="#c1c1c1"></rect><rect x="295.14" y="106.58" width="7.04" height="12.28" transform="translate(41.05 -75.8) rotate(15.52)" fill="#c1c1c1" opacity="0.24"></rect><path d="M210.86,73s-.72,8.06-3,13.1a4.07,4.07,0,0,1-5.4,2c-2.52-1.18-5.59-3.51-5.72-7.87l-1.15-7.38a7.27,7.27,0,0,1,4.51-7.08C205.17,63.3,211.54,68.07,210.86,73Z" fill="#f4a28c"></path><path d="M208.83,72.93a31.48,31.48,0,0,1-7.24-1.87,6.75,6.75,0,0,1-1.3,7.29,5.48,5.48,0,0,1-6.91,1l2-10.21a8.23,8.23,0,0,1,5.39-6.5,29.4,29.4,0,0,1,3.71-1.06c3.16-.66,7.12,2.07,10.11.55a2,2,0,0,1,2.83,1.8c-.1,3.19-1.52,8-6,8.95A7.56,7.56,0,0,1,208.83,72.93Z" fill="#24285b"></path><path d="M201.19,78.27s.48-3.08-1.86-3.25-3.08,4.26,0,5.23Z" fill="#f4a28c"></path><path d="M210.14,78l1.68,3.47a1.29,1.29,0,0,1-1.16,1.85l-3.14,0Z" fill="#f4a28c"></path><circle cx="187.77" cy="184.73" r="4.08" fill="#fff"></circle><path d="M160.19,219.08s-16.73,7.25-12.32,11.66,15.71-3.47,15.71-3.47Z" fill="#68e1fd"></path><path d="M212.54,220.92s16.73,7.24,12.31,11.65-15.71-3.47-15.71-3.47Z" fill="#68e1fd"></path><circle cx="285.11" cy="108.25" r="6.32" fill="#c1c1c1"></circle><circle cx="286.69" cy="127.03" r="2.02" fill="#e6e6e6"></circle><polygon points="272.95 133.26 281.29 125.01 283.58 133.98 289.69 130.63 292.7 138.87 272.95 133.26" fill="#e6e6e6"></polygon><rect x="275.47" y="187.75" width="39.65" height="39.66" fill="#c1c1c1"></rect><rect x="298.13" y="187.75" width="39.65" height="39.66" fill="#e6e6e6"></rect></svg>
|
After Width: | Height: | Size: 4.0 KiB |
70
templates/blocked.html
Normal file
70
templates/blocked.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-bs-theme="dark" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Page Not Found - Vote | Nathan.Woodburn/</title>
|
||||
<meta name="twitter:image" content="https://vote.woodburn.au/assets/img/favicon.png">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta property="og:title" content="Vote | Nathan.Woodburn/">
|
||||
<meta name="description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta name="twitter:title" content="Vote | Nathan.Woodburn/">
|
||||
<meta property="og:image" content="https://vote.woodburn.au/assets/img/favicon.png">
|
||||
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/android-chrome-192x192.png">
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="assets/img/android-chrome-512x512.png">
|
||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&display=swap">
|
||||
<link rel="stylesheet" href="assets/css/default.css">
|
||||
<script async src="https://umami.woodburn.au/script.js" data-website-id="6a55028e-aad3-481c-9a37-3e096ff75589"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md fixed-top navbar-shrink py-3 navbar-light" id="mainNav">
|
||||
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span>Nathan.Woodburn/</span></a><button class="navbar-toggler" data-bs-toggle="collapse"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button></div>
|
||||
</nav>
|
||||
<section class="py-5 mt-5">
|
||||
<div class="container">
|
||||
<div class="row row-cols-1 d-flex justify-content-center align-items-center">
|
||||
<div class="col text-center">
|
||||
<h2 class="display-3 fw-bold mb-4">Votes are not public until after this vote.</h2>
|
||||
<p class="fs-4 text-muted">You will be able to verify the votes after the voting has finished</p><a class="btn btn-primary" role="button" href="/">Home</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="container py-4 py-lg-5">
|
||||
<div class="row row-cols-2 row-cols-md-4">
|
||||
<div class="col-12 col-md-3">
|
||||
<div class="fw-bold d-flex align-items-center mb-2"><span>Nathan.Woodburn/</span></div>
|
||||
<p class="text-muted">Australian developer and student.</p>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-3 text-lg-start d-flex flex-column">
|
||||
<h3 class="fs-6 fw-bold">Sites</h3>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
|
||||
<li><a href="https://hns.au" target="_blank">HNSAU</a></li>
|
||||
<li><a href="https://hnshosting.au" target="_blank">HNSHosting</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
|
||||
<p class="mb-0">Copyright © {{year}} Nathan.Woodburn/</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="assets/js/startup-modern.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -44,24 +44,37 @@
|
||||
<div class="col-md-8 text-center text-md-start mx-auto">
|
||||
<div class="text-center">
|
||||
<h1 class="display-4 fw-bold mb-5">Vote on projects from<br><a class="no-a-display" href="https://nathan.woodburn.au" target="_blank"><span class="underline">Nathan.Woodburn/</span></a></h1>
|
||||
<h2 style="margin-bottom: 20px;">{{current_vote}}</h2>
|
||||
<h3>You have <span id="balance">0</span> votes.</h3>
|
||||
<h4><span id="percent">0</span>% of the voting power.</h4>
|
||||
<p class="fs-5 text-muted mb-5">Vote here</p>
|
||||
<form class="d-flex justify-content-center flex-wrap" id="signMessageForm" data-bs-theme="light">
|
||||
<div class="shadow-lg mb-3">
|
||||
<p>Select your vote or split your votes between options.</p>{{options|safe}}
|
||||
</div>
|
||||
<div class="shadow-lg mb-3"><button class="btn btn-primary" type="submit" style="margin-top: 25px;">Vote</button></div>
|
||||
</form>
|
||||
</div>{{votes|safe}}
|
||||
</div>
|
||||
<div class="col-12 col-lg-10 mx-auto">
|
||||
<div class="text-center position-relative" style="padding-top: 25px;"><a class="btn btn-primary" role="button" target="_blank" href="/votes">View vote proofs</a><img class="img-fluid" src="assets/img/illustrations/meeting.svg" style="width: 800px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<section>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6 text-center">
|
||||
<h2 style="margin-bottom: 20px;">{{current_vote}}</h2>
|
||||
<p style="margin: 0px;">{{description}}</p>
|
||||
<p>{{end}}<br>You are {{revote}} allowed to redo your vote.</p>{{votes|safe}}
|
||||
</div>
|
||||
<div class="col-md-6 text-center">
|
||||
<h3>You have <span id="balance">0</span> votes.</h3>
|
||||
<h4><span id="percent">0</span>% of the voting power.</h4>
|
||||
<div id="existingVote"></div>
|
||||
<h5 class="display-5">Vote Here</h5>
|
||||
<form class="d-flex justify-content-center flex-wrap" id="signMessageForm" data-bs-theme="light">
|
||||
<div class="mb-3">
|
||||
<p>Select your vote or split your votes between options.</p>{{options|safe}}
|
||||
</div>
|
||||
<div class="mb-3"><button class="btn btn-primary" type="submit" style="margin-top: 25px;" {% if not enabled %}disabled{% endif %}>Vote</button></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-10 mx-auto">
|
||||
<div class="text-center position-relative" style="padding-top: 25px;"><a class="btn btn-primary" role="button" target="_blank" href="/votes">View vote proofs</a><img class="img-fluid" src="assets/img/illustrations/meeting.svg" style="width: 800px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="container py-4 py-lg-5">
|
||||
<div class="row row-cols-2 row-cols-md-4">
|
||||
|
75
templates/login.html
Normal file
75
templates/login.html
Normal file
@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-bs-theme="dark" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Log in - Vote | Nathan.Woodburn/</title>
|
||||
<meta name="twitter:image" content="https://vote.woodburn.au/assets/img/favicon.png">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta property="og:title" content="Vote | Nathan.Woodburn/">
|
||||
<meta name="description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta name="twitter:title" content="Vote | Nathan.Woodburn/">
|
||||
<meta property="og:image" content="https://vote.woodburn.au/assets/img/favicon.png">
|
||||
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/android-chrome-192x192.png">
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="assets/img/android-chrome-512x512.png">
|
||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&display=swap">
|
||||
<link rel="stylesheet" href="assets/css/default.css">
|
||||
<script async src="https://umami.woodburn.au/script.js" data-website-id="6a55028e-aad3-481c-9a37-3e096ff75589"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md fixed-top navbar-shrink py-3 navbar-light" id="mainNav">
|
||||
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span>Nathan.Woodburn/</span></a><button class="navbar-toggler" data-bs-toggle="collapse"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button></div>
|
||||
</nav>
|
||||
<section class="py-4 py-md-5 my-5">
|
||||
<div class="container py-md-5">
|
||||
<div class="row">
|
||||
<div class="col-md-6 text-center"><img class="img-fluid w-100" src="assets/img/illustrations/login.svg"></div>
|
||||
<div class="col-md-5 col-xl-4 text-center text-md-start">
|
||||
<h2 class="display-6 fw-bold mb-5"><span class="underline pb-1"><strong>Login</strong><br></span></h2>
|
||||
<form method="post" data-bs-theme="light">
|
||||
<div class="mb-3"><input class="shadow form-control" type="email" name="email" placeholder="Email"></div>
|
||||
<div class="mb-3"><input class="shadow form-control" type="password" name="password" placeholder="Password"></div>
|
||||
<div class="mb-5"><button class="btn btn-primary shadow" type="submit">Log in</button></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="container py-4 py-lg-5">
|
||||
<div class="row row-cols-2 row-cols-md-4">
|
||||
<div class="col-12 col-md-3">
|
||||
<div class="fw-bold d-flex align-items-center mb-2"><span>Nathan.Woodburn/</span></div>
|
||||
<p class="text-muted">Australian developer and student.</p>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-3 text-lg-start d-flex flex-column">
|
||||
<h3 class="fs-6 fw-bold">Sites</h3>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
|
||||
<li><a href="https://hns.au" target="_blank">HNSAU</a></li>
|
||||
<li><a href="https://hnshosting.au" target="_blank">HNSHosting</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
|
||||
<p class="mb-0">Copyright © {{year}} Nathan.Woodburn/</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="assets/js/startup-modern.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
70
templates/revotes.html
Normal file
70
templates/revotes.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-bs-theme="dark" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Page Not Found - Vote | Nathan.Woodburn/</title>
|
||||
<meta name="twitter:image" content="https://vote.woodburn.au/assets/img/favicon.png">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta property="og:title" content="Vote | Nathan.Woodburn/">
|
||||
<meta name="description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:description" content="Vote on Nathan.Woodburn/ projects">
|
||||
<meta name="twitter:title" content="Vote | Nathan.Woodburn/">
|
||||
<meta property="og:image" content="https://vote.woodburn.au/assets/img/favicon.png">
|
||||
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/img/android-chrome-192x192.png">
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="assets/img/android-chrome-512x512.png">
|
||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800&display=swap">
|
||||
<link rel="stylesheet" href="assets/css/default.css">
|
||||
<script async src="https://umami.woodburn.au/script.js" data-website-id="6a55028e-aad3-481c-9a37-3e096ff75589"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md fixed-top navbar-shrink py-3 navbar-light" id="mainNav">
|
||||
<div class="container"><a class="navbar-brand d-flex align-items-center" href="/"><span>Nathan.Woodburn/</span></a><button class="navbar-toggler" data-bs-toggle="collapse"><span class="visually-hidden">Toggle navigation</span><span class="navbar-toggler-icon"></span></button></div>
|
||||
</nav>
|
||||
<section class="py-5 mt-5">
|
||||
<div class="container">
|
||||
<div class="row row-cols-1 d-flex justify-content-center align-items-center">
|
||||
<div class="col text-center">
|
||||
<h2 class="display-3 fw-bold mb-4">Revotes are disabled on this vote.</h2>
|
||||
<p class="fs-4 text-muted">You are not able to redo your vote.</p><a class="btn btn-primary" role="button" href="/">Home</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="container py-4 py-lg-5">
|
||||
<div class="row row-cols-2 row-cols-md-4">
|
||||
<div class="col-12 col-md-3">
|
||||
<div class="fw-bold d-flex align-items-center mb-2"><span>Nathan.Woodburn/</span></div>
|
||||
<p class="text-muted">Australian developer and student.</p>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-3 text-lg-start d-flex flex-column">
|
||||
<h3 class="fs-6 fw-bold">Sites</h3>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="https://nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a></li>
|
||||
<li><a href="https://hns.au" target="_blank">HNSAU</a></li>
|
||||
<li><a href="https://hnshosting.au" target="_blank">HNSHosting</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="text-muted d-flex justify-content-between align-items-center pt-3">
|
||||
<p class="mb-0">Copyright © {{year}} Nathan.Woodburn/</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="assets/js/startup-modern.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -34,9 +34,9 @@
|
||||
<div class="container pt-4 pt-xl-5">
|
||||
<div class="row pt-5">
|
||||
<div class="col-md-8 text-center text-md-start mx-auto">
|
||||
<div class="text-center">
|
||||
<div class="text-center" style="margin-bottom: 25px;">
|
||||
<h1 class="display-4 fw-bold mb-5">Thank you for voting!</h1>
|
||||
<p class="fs-5 text-muted mb-5">Your voice matters!</p>
|
||||
<p class="fs-5 text-muted">Your voice matters!</p><a class="btn btn-primary" role="button" href="/">Home</a>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark">
|
||||
|
Loading…
Reference in New Issue
Block a user