Searching, sorting, and updating users #36
2 changed files with 114 additions and 5 deletions
|
@ -4,6 +4,8 @@ from flask_mail import Message
|
||||||
|
|
||||||
from goathacks.models import User
|
from goathacks.models import User
|
||||||
|
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
bp = Blueprint("admin", __name__, url_prefix="/admin")
|
bp = Blueprint("admin", __name__, url_prefix="/admin")
|
||||||
|
|
||||||
from goathacks import db, mail as app_mail
|
from goathacks import db, mail as app_mail
|
||||||
|
@ -221,6 +223,44 @@ def hackers():
|
||||||
users = User.query.all()
|
users = User.query.all()
|
||||||
return User.create_json_output(users)
|
return User.create_json_output(users)
|
||||||
|
|
||||||
|
@bp.route("/updateHacker", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def updateHacker():
|
||||||
|
if not current_user.is_admin:
|
||||||
|
return redirect(url_for("dashboard.home"))
|
||||||
|
|
||||||
|
# get params from json
|
||||||
|
hacker_id = request.json['hacker_id']
|
||||||
|
change_field = request.json['change_field']
|
||||||
|
new_val = request.json['new_val']
|
||||||
|
|
||||||
|
# find the user in db
|
||||||
|
user = User.query.filter_by(id=hacker_id).one()
|
||||||
|
if user is None:
|
||||||
|
return {"status": "error", "msg": "user not found"}
|
||||||
|
|
||||||
|
|
||||||
|
# update the hacker depending on change_field
|
||||||
|
match change_field:
|
||||||
|
case "first_name":
|
||||||
|
user.first_name = new_val
|
||||||
|
case "last_name":
|
||||||
|
user.last_name = new_val
|
||||||
|
case "school":
|
||||||
|
user.school = new_val
|
||||||
|
case "phone":
|
||||||
|
user.phone = new_val
|
||||||
|
|
||||||
|
try:
|
||||||
|
db.session.commit()
|
||||||
|
except IntegrityError as err:
|
||||||
|
db.session.rollback()
|
||||||
|
flash("Could not update user information for user " + hacker_id)
|
||||||
|
return {"status": "error"}
|
||||||
|
|
||||||
|
|
||||||
|
return {"status": "success"}
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import csv
|
import csv
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
|
@ -14,8 +14,22 @@
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
<div class="card text-center">
|
<div class="card text-center">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h1 class="h3 mb-3 fw-normal">Registered Users</h1>
|
|
||||||
<table id="hackers" class="table table-striped">
|
<div style="display:flex;flex-wrap:nowrap;align-items:center;">
|
||||||
|
<div class="dropdown">
|
||||||
|
<a href="#" class="btn btn-primary dropdown-toggle"
|
||||||
|
data-bs-toggle="dropdown">Search<span
|
||||||
|
class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<input style="padding:5px;margin-left:10px;margin-right:10px;" type="text" id="searchbox" name="searchbox-text" placeholder="Search By Email" onkeyup="filterHackers()"/>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TODO: get "Registered Users" properly centered -->
|
||||||
|
<h1 style="flex-grow:1;justify-content:center;" class="h3 mb-3 fw-normal">Registered Users</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table id="hackers" class="table table-striped sortable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Options</th>
|
<th>Options</th>
|
||||||
|
@ -79,16 +93,71 @@
|
||||||
<td>{{ hacker.id }}</td>
|
<td>{{ hacker.id }}</td>
|
||||||
<td>{{ hacker.last_login }}</td>
|
<td>{{ hacker.last_login }}</td>
|
||||||
<td>{{ hacker.email }}</td>
|
<td>{{ hacker.email }}</td>
|
||||||
<td>{{ hacker.first_name + ' ' + hacker.last_name }}</td>
|
<td>
|
||||||
<td>{{ hacker.phone }}</td>
|
<div style="display: flex; justify-content: flex-start;">
|
||||||
|
<input style="padding:5px; margin-left:10px; margin-right:10px;width:fit-content;max-width:100px;" type="text" id="{{hacker.id}}-namebox-first" placeholder="first_name" value="{{hacker.first_name}}" onchange="updateHacker(this.id, 'first_name', this.value)"/>
|
||||||
|
<input style="padding:5px; margin-left:10px; margin-right:10px;width:fit-content;max-width:100px;" type="text" id="{{hacker.id}}-namebox-last" placeholder="last_name" value="{{hacker.last_name}}" onchange="updateHacker(this.id, 'last_name', this.value)"/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input style="padding:5px; margin-left:10px; margin-right:10px;width:fit-content;max-width:100px;" type="text" id="{{hacker.id}}-phonebox" placeholder="phone" value="{{hacker.phone}}" onchange="updateHacker(this.id, 'phone', this.value)"/>
|
||||||
|
</td>
|
||||||
<td>{{ hacker.shirt_size }}</td>
|
<td>{{ hacker.shirt_size }}</td>
|
||||||
<td>{{ hacker.accomodations }}</td>
|
<td>{{ hacker.accomodations }}</td>
|
||||||
<td>{{ hacker.school }}</td>
|
<td>
|
||||||
|
<input style="padding:5px; margin-left:10px; margin-right:10px;width:fit-content;max-width:75px;" type="text" id="{{hacker.id}}-schoolbox" placeholder="school" value="{{hacker.school}}" onchange="updateHacker(this.id, 'school', this.value)"/>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function filterHackers() {
|
||||||
|
//get hacker table and searchbox info
|
||||||
|
const input = document.getElementById("searchbox").value.toLowerCase();
|
||||||
|
const hackertable = document.getElementById("hackers");
|
||||||
|
let rows = hackertable.getElementsByTagName("tr");
|
||||||
|
|
||||||
|
//iterate over all rows
|
||||||
|
for(let i = 1; i < rows.length; i++) {
|
||||||
|
//get the email
|
||||||
|
const cells = rows[i].getElementsByTagName("td");
|
||||||
|
const emailCell = cells[6];
|
||||||
|
|
||||||
|
//if there is an email, display or dont display based on searchbox
|
||||||
|
if(emailCell) {
|
||||||
|
const emailText = emailCell.textContent.toLowerCase();
|
||||||
|
if(!emailText.includes(input)) {
|
||||||
|
rows[i].style.display = "none";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rows[i].style.display = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHacker(id, change_field, new_val) {
|
||||||
|
//tell backend to update a specific field for a hacker
|
||||||
|
const headers = [
|
||||||
|
["Content-Type", "application/json"],
|
||||||
|
];
|
||||||
|
|
||||||
|
let body = {
|
||||||
|
"hacker_id": id.substr(0, id.indexOf('-')),
|
||||||
|
"change_field": change_field,
|
||||||
|
"new_val": new_val,
|
||||||
|
}
|
||||||
|
|
||||||
|
//send the post request, and report the error if there is one
|
||||||
|
fetch('/admin/updateHacker', {method: 'POST', body: JSON.stringify(body), headers: headers}).catch((err) => {
|
||||||
|
window.alert("Error updating user - see console for details");
|
||||||
|
console.log(err);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue