add some css to the mix, add search functionality #3
14 changed files with 10834 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
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__, static_url_path='/public')
|
||||||
app.config.from_object('config.Configuration')
|
app.config.from_object('config.Configuration')
|
||||||
db = SQLAlchemy(app)
|
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 |
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
|
#!/bin/bash
|
||||||
|
|
||||||
FLASK_APP="endorsement.py" flask run
|
FLASK_APP="endorsement.py" FLASK_DEBUG=true flask run
|
||||||
|
|
|
@ -1,21 +1,36 @@
|
||||||
<html>
|
{% extends 'base.html' %}
|
||||||
<body>
|
|
||||||
<h2>{{data.name}} by <a href="{{url_for('get_user', id=data.achieved_by)}}">{{ data.achieved_by }} </a></h2>
|
|
||||||
|
{% 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>
|
<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 -%}
|
{% for endorser in data.endorsed_by -%}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{url_for('get_user', id=endorser.id)}}">{{ endorser.id }} </a>
|
<a href="{{url_for('get_user', id=endorser.id)}}">{{ endorser.id }} </a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor -%}
|
{% 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>
|
</ul>
|
||||||
</body>
|
</div>
|
||||||
</html>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -1,12 +1,42 @@
|
||||||
<html>
|
{% extends 'base.html' %}
|
||||||
<body>
|
|
||||||
<h2>Insert cool name here</h2>
|
|
||||||
<ul>
|
{% 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 -%}
|
{% for user in data -%}
|
||||||
<li>
|
<!-- If you update this block, update it also in the js search function -->
|
||||||
<a href="{{url_for('get_user', id=user.id)}}">{{ user.id }}</a>: {{ user.achievements|length }} achievement{{ "s" if (user.achievements|length != 1) else "" }}
|
<a href="{{url_for('get_user', id=user.id)}}" class="tile is-child box is-4">
|
||||||
</li>
|
<!-- 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 -%}
|
{% endfor -%}
|
||||||
</ul>
|
</div>
|
||||||
</body>
|
</div>
|
||||||
</html>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var users = {{ data|safe }};
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
23
templates/base.html
Normal file
23
templates/base.html
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="/css/bulma.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="/css/index.css" type="text/css">
|
||||||
|
|
||||||
|
{% block head %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
No content yet.
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -1,15 +1,34 @@
|
||||||
<html>
|
{% extends 'base.html' %}
|
||||||
<body>
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="section"><div class="container">
|
||||||
|
|
||||||
|
<h1 class="title">New achievement</h1>
|
||||||
<form action="" method="post">
|
<form action="" method="post">
|
||||||
{{ form.csrf }}
|
{{ form.csrf }}
|
||||||
|
|
||||||
<div class="input text">
|
<div class="field">
|
||||||
{{ form.name.label }} {{ form.name }}
|
<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>
|
||||||
|
|
||||||
<div class="input submit">
|
<div class="field">
|
||||||
<input type="submit" value="Add" />
|
<div class="control">
|
||||||
|
<button class="button is-link">Add</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</body>
|
|
||||||
</html>
|
</div></div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -1,13 +1,29 @@
|
||||||
<html>
|
{% extends 'base.html' %}
|
||||||
<body>
|
|
||||||
<h2>{{data.id}}</h2>
|
|
||||||
<ul>
|
{% 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 -%}
|
{% for achievement in data.achievements -%}
|
||||||
<li>
|
<a href="{{url_for('get_achievement', id=achievement.id)}}" class="tile is-child box is-4">
|
||||||
<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 "" }}
|
<!-- The magical tile element! -->
|
||||||
</li>
|
<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 -%}
|
{% endfor -%}
|
||||||
<li> <a href="{{url_for('add_achievement', id=data.id)}}">Add achievement</a></li>
|
</div>
|
||||||
</ul>
|
</div>
|
||||||
</body>
|
<a class="button" href="{{url_for('add_achievement', id=data.id)}}">Add achievement</a>
|
||||||
</html>
|
</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 app import db
|
||||||
|
from models import Achievement, User
|
||||||
|
|
||||||
db.drop_all()
|
db.drop_all()
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
@ -13,6 +16,22 @@ achievementB = Achievement(name="BB", achieved_by=persoonB.id)
|
||||||
|
|
||||||
achievementA.endorsed_by.append(persoonC)
|
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(persoonA)
|
||||||
db.session.add(persoonB)
|
db.session.add(persoonB)
|
||||||
db.session.add(persoonC)
|
db.session.add(persoonC)
|
||||||
|
|
Loading…
Reference in a new issue