add some css to the mix, add search functionality #3
15 changed files with 10897 additions and 62 deletions
23
app.py
23
app.py
|
@ -1,6 +1,25 @@
|
|||
from flask import Flask
|
||||
import os
|
||||
|
||||
from flask import Flask, send_from_directory
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
app = Flask(__name__)
|
||||
app = Flask(__name__, static_url_path='/public')
|
||||
app.config.from_object('config.Configuration')
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
|
||||
@app.route('/js/<path:path>')
|
||||
def send_js(path):
|
||||
return send_from_directory('public/js', path)
|
||||
|
||||
|
||||
@app.route('/css/<path:path>')
|
||||
def send_css(path):
|
||||
return send_from_directory('public/css', path)
|
||||
|
||||
|
||||
@app.route('/favicon.ico')
|
||||
def favicon():
|
||||
return send_from_directory(os.path.join(app.root_path, 'public', 'img'),
|
||||
'favicon.ico',
|
||||
mimetype='image/vnd.microsoft.icon')
|
||||
|
|
10599
public/css/bulma.css
vendored
Normal file
10599
public/css/bulma.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
public/css/bulma.css.map
Normal file
1
public/css/bulma.css.map
Normal file
File diff suppressed because one or more lines are too long
1
public/css/bulma.min.css
vendored
Normal file
1
public/css/bulma.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
3
public/css/index.css
Normal file
3
public/css/index.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
a {
|
||||
color: #ff9f00;
|
||||
}
|
BIN
public/img/favicon.ico
Normal file
BIN
public/img/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 361 KiB |
24
public/js/index.js
Normal file
24
public/js/index.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Get all "navbar-burger" elements
|
||||
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||
|
||||
// Check if there are any navbar burgers
|
||||
if ($navbarBurgers.length > 0) {
|
||||
|
||||
// Add a click event on each of them
|
||||
$navbarBurgers.forEach( el => {
|
||||
el.addEventListener('click', () => {
|
||||
|
||||
// Get the target from the "data-target" attribute
|
||||
const target = el.dataset.target;
|
||||
const $target = document.getElementById(target);
|
||||
|
||||
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||
el.classList.toggle('is-active');
|
||||
$target.classList.toggle('is-active');
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
27
public/js/users.js
Normal file
27
public/js/users.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
function searchUser(search) {
|
||||
matches = [];
|
||||
users.forEach(user => {
|
||||
if (user.id.toLowerCase().includes(search.toLowerCase())) {
|
||||
matches.push(user);
|
||||
}
|
||||
});
|
||||
|
||||
if (matches.length === 0) {
|
||||
document.getElementById("users-parent").innerHTML = "<p>No matches found</p>";
|
||||
} else {
|
||||
var html = "";
|
||||
matches.forEach(match => {
|
||||
html += `
|
||||
<a href="/users/${match.id}" class="tile is-child box is-4">
|
||||
<!-- The magical tile element! -->
|
||||
<p class="title">${ match.id }</p>
|
||||
<div class="content">
|
||||
${ match.achievements.length } achievement${(match.achievements.length != 1) ? "s" : "" }
|
||||
</div>
|
||||
</a>
|
||||
`
|
||||
});
|
||||
document.getElementById("users-parent").innerHTML = html;
|
||||
|
||||
}
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
FLASK_APP="endorsement.py" flask run
|
||||
FLASK_APP="endorsement.py" FLASK_DEBUG=true flask run
|
||||
|
|
|
@ -1,21 +1,36 @@
|
|||
<html>
|
||||
<body>
|
||||
<h2>{{data.name}} by <a href="{{url_for('get_user', id=data.achieved_by)}}">{{ data.achieved_by }} </a></h2>
|
||||
<ul>
|
||||
{% for endorser in data.endorsed_by -%}
|
||||
{% extends 'base.html' %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="section">
|
||||
<div class="container">
|
||||
|
||||
<h2 class="title is-2">Achievement: {{data.name}}</h2>
|
||||
<h4 class="subtitle is-4">by <a href="{{url_for('get_user', id=data.achieved_by)}}">{{ data.achieved_by }} </a></h4>
|
||||
|
||||
<div class="menu">
|
||||
<p></p>
|
||||
<ul>
|
||||
<li>
|
||||
{% if endorsed -%}
|
||||
<a href="{{url_for('unendorse_achievement', id=data.id)}}" class="has-text-danger">Unendorse</a>
|
||||
{% else -%}
|
||||
<a href="{{url_for('endorse_achievement', id=data.id)}}" class="has-text-success">Endorse</a>
|
||||
{% endif -%}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Endorsed by</p>
|
||||
<ul class="menu-list">
|
||||
{% for endorser in data.endorsed_by -%}
|
||||
<li>
|
||||
<a href="{{url_for('get_user', id=endorser.id)}}">{{ endorser.id }} </a>
|
||||
</li>
|
||||
{% endfor -%}
|
||||
{% if endorsed -%}
|
||||
<li>
|
||||
<a href="{{url_for('unendorse_achievement', id=data.id)}}">Unendorse</a>
|
||||
</li>
|
||||
{% else -%}
|
||||
<li>
|
||||
<a href="{{url_for('endorse_achievement', id=data.id)}}">Endorse</a>
|
||||
</li>
|
||||
{% endif -%}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
{% endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,12 +1,42 @@
|
|||
<html>
|
||||
<body>
|
||||
<h2>Insert cool name here</h2>
|
||||
<ul>
|
||||
{% for user in data -%}
|
||||
<li>
|
||||
<a href="{{url_for('get_user', id=user.id)}}">{{ user.id }}</a>: {{ user.achievements|length }} achievement{{ "s" if (user.achievements|length != 1) else "" }}
|
||||
</li>
|
||||
{% endfor -%}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
{% extends 'base.html' %}
|
||||
|
||||
|
||||
{% block head %}
|
||||
<script src="/js/users.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="section">
|
||||
<div class="container">
|
||||
<h1 class="title is-2">Endorsements</h1>
|
||||
|
||||
<div class="field">
|
||||
<p class="control has-icons-left has-icons-right">
|
||||
<input oninput="searchUser(this.value)" class="input" type="text" placeholder="Search">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-search"></i>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="tile is-ancestor">
|
||||
<div id="users-parent" class="tile is-parent is-vertical">
|
||||
{% for user in data -%}
|
||||
<!-- If you update this block, update it also in the js search function -->
|
||||
<a href="{{url_for('get_user', id=user.id)}}" class="tile is-child box is-4">
|
||||
<!-- The magical tile element! -->
|
||||
<p class="title">{{ user.id }}</p>
|
||||
<div class="content">
|
||||
{{ user.achievements|length }} achievement{{ "s" if (user.achievements|length != 1) else "" }}
|
||||
</div>
|
||||
</a>
|
||||
{% endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var users = {{ data|safe }};
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
62
templates/base.html
Normal file
62
templates/base.html
Normal file
|
@ -0,0 +1,62 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="/css/bulma.css" type="text/css">
|
||||
<link rel="stylesheet" href="/css/index.css" type="text/css">
|
||||
|
||||
<script src="/js/index.js"></script>
|
||||
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="https://bulma.io">
|
||||
<img src="https://bulma.io/images/bulma-logo.png" width="112" height="28">
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a href="/" class="navbar-item">
|
||||
Endorsements
|
||||
</a>
|
||||
|
||||
<a href="/graph" class="navbar-item" disabled>
|
||||
Graph
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<a class="button is-primary">
|
||||
<strong>Log in</strong>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<body>
|
||||
|
||||
{% block content %}
|
||||
No content yet.
|
||||
{% endblock %}
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,15 +1,34 @@
|
|||
<html>
|
||||
<body>
|
||||
<form action="" method="post">
|
||||
{{ form.csrf }}
|
||||
{% extends 'base.html' %}
|
||||
|
||||
<div class="input text">
|
||||
{{ form.name.label }} {{ form.name }}
|
||||
</div>
|
||||
|
||||
<div class="input submit">
|
||||
<input type="submit" value="Add" />
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
{% block content %}
|
||||
|
||||
<div class="section"><div class="container">
|
||||
|
||||
<h1 class="title">New achievement</h1>
|
||||
<form action="" method="post">
|
||||
{{ form.csrf }}
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="{{form.name.label.for}}">Name</label>
|
||||
<div class="control has-icons-left has-icons-right">
|
||||
<input id="{{form.name.id}}" name="{{form.name.name}}" type="{{form.name.type}}" value="{{form.name.value}}" required="{{form.name.required}}" class="input" placeholder="the bestest boi" >
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-user"></i>
|
||||
</span>
|
||||
<span class="icon is-small is-right">
|
||||
<i class="fas fa-check"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<button class="button is-link">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div></div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,13 +1,29 @@
|
|||
<html>
|
||||
<body>
|
||||
<h2>{{data.id}}</h2>
|
||||
<ul>
|
||||
{% for achievement in data.achievements -%}
|
||||
<li>
|
||||
<a href="{{url_for('get_achievement', id=achievement.id)}}">{{ achievement.name }}</a>: {{ achievement.endorsed_by|length }} endorsement{{ "s" if (achievement.endorsed_by|length != 1) else "" }}
|
||||
</li>
|
||||
{% endfor -%}
|
||||
<li> <a href="{{url_for('add_achievement', id=data.id)}}">Add achievement</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
{% extends 'base.html' %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="section">
|
||||
<div class="container">
|
||||
|
||||
<h1 class="title is-1">User: {{data.id}}</h1>
|
||||
<h3 class="subtitle is-3">Achievements</h3>
|
||||
<div class="tile is-ancestor">
|
||||
<div class="tile is-parent is-vertical">
|
||||
{% for achievement in data.achievements -%}
|
||||
<a href="{{url_for('get_achievement', id=achievement.id)}}" class="tile is-child box is-4">
|
||||
<!-- The magical tile element! -->
|
||||
<p class="title">{{ achievement.name }}</p>
|
||||
<div class="content">
|
||||
{{ achievement.endorsed_by|length }} endorsement{{ "s" if (achievement.endorsed_by|length != 1) else "" }}
|
||||
</div>
|
||||
</a>
|
||||
{% else %}
|
||||
<h5 class="subtitle is-5">No achievements</h5>
|
||||
{% endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
<a class="button" href="{{url_for('add_achievement', id=data.id)}}">Add achievement</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
21
test.py
21
test.py
|
@ -1,5 +1,8 @@
|
|||
from models import User, Achievement
|
||||
import random
|
||||
import string
|
||||
|
||||
from app import db
|
||||
from models import Achievement, User
|
||||
|
||||
db.drop_all()
|
||||
db.create_all()
|
||||
|
@ -13,6 +16,22 @@ achievementB = Achievement(name="BB", achieved_by=persoonB.id)
|
|||
|
||||
achievementA.endorsed_by.append(persoonC)
|
||||
|
||||
# Long generated lists
|
||||
for i in range(30):
|
||||
achievement = Achievement(name=''.join(
|
||||
random.choices(string.ascii_uppercase + string.digits, k=6)),
|
||||
achieved_by=persoonA.id)
|
||||
db.session.add(achievement)
|
||||
|
||||
people = [
|
||||
User(id=''.join(random.choices(string.ascii_uppercase +
|
||||
string.digits, k=6))) for i in range(30)
|
||||
]
|
||||
|
||||
for person in people:
|
||||
db.session.add(person)
|
||||
achievementA.endorsed_by.append(person)
|
||||
|
||||
db.session.add(persoonA)
|
||||
db.session.add(persoonB)
|
||||
db.session.add(persoonC)
|
||||
|
|
Loading…
Reference in a new issue