This commit is contained in:
Anh-Thy04
2025-06-20 00:52:49 +02:00
parent 470a74388f
commit 378fcb0b93
6 changed files with 190 additions and 65 deletions

View File

@@ -1,14 +1,19 @@
from application import db, app from application import db, app
from application.dashboard.models import AllowedPlate, LoggedItem, datetime from application.dashboard.models import AllowedPlate, LoggedItem, datetime
from application.auth.models import User from application.auth.models import User
from datetime import datetime, timedelta
with app.app_context(): with app.app_context():
AllowedPlate.query.delete() AllowedPlate.query.delete()a
db.session.add(AllowedPlate("MUN389")) db.session.add(AllowedPlate("MUN389"))
db.session.commit() db.session.commit()
with app.app_context(): with app.app_context():
LoggedItem.query.delete() LoggedItem.query.delete()
custom_time = datetime.now() - timedelta(hours=1)
db.session.add(LoggedItem("MUN389", custom_time, True))
db.session.add(LoggedItem("MUN389", datetime.now(), True))
db.session.add(LoggedItem("MUN389", datetime.now(), True)) db.session.add(LoggedItem("MUN389", datetime.now(), True))
db.session.add(LoggedItem("MUN389", datetime.now(), False)) db.session.add(LoggedItem("MUN389", datetime.now(), False))
db.session.commit() db.session.commit()

View File

@@ -30,24 +30,57 @@
</div> </div>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<div class="card mb-4"> <div class="card mb-4">
<div class="card-body"> <div class="card-body">
<h5 class="card-title fs-4">Numberplates</h5> <h5 class="card-title fs-4 mb-0">Numberplates</h5>
<div class="log-container" style="max-height: 250px; overflow-y: auto;"> <div class="mt-2 mb-3">
<table class="table table-sm table-hover" id="numberplatesTable"> <form method="get" class="mb-0">
<thead> <div class="row g-2 align-items-center">
<tr> <div class="col">
<th scope="col" class="fs-5">ID</th> <input type="text" name="npfilter" class="form-control" placeholder="Search numberplate..." value="{{ request.args.get('npfilter', '') }}">
<th scope="col" class="fs-5">Numberplate</th> </div>
</tr> <div class="col">
</thead> <input type="text" name="idfilter" class="form-control" placeholder="Search by ID..." value="{{ request.args.get('idfilter', '') }}">
<tbody id="numberplatesBody"> </div>
{% for plate in plates %} <div class="col-auto d-flex gap-2">
<tr> <button type="submit" class="btn btn-secondary">Filter</button>
<td><small class="fs-6">{{ plate.id }}</small></td> </div>
<td><small class="fs-6">{{ plate.plate }}</small></td> </div>
<td> </form>
</div>
{% if request.args.get('npfilter') or request.args.get('idfilter') %}
<div class="mb-2">
<span class="me-2">Active filters:</span>
{% if request.args.get('npfilter') %}
<a href="{{ url_for('dash.dashboard', idfilter=request.args.get('idfilter', '')) }}" class="badge bg-secondary text-decoration-none">
Numberplate: {{ request.args.get('npfilter') }} &times;
</a>
{% endif %}
{% if request.args.get('idfilter') %}
<a href="{{ url_for('dash.dashboard', npfilter=request.args.get('npfilter', '')) }}" class="badge bg-secondary text-decoration-none">
ID: {{ request.args.get('idfilter') }} &times;
</a>
{% endif %}
</div>
{% endif %}
<div class="log-container" style="max-height: 250px; overflow-y: auto;">
<table class="table table-sm table-hover align-middle">
<thead>
<tr>
<th scope="col" class="fs-5" style="width: 21%;">ID</th>
<th scope="col" class="fs-5" style="width: 42%;">Numberplate</th>
<th scope="col" class="fs-5">Actions</th>
</tr>
</thead>
<tbody>
{% for plate in plates %}
<tr>
<td><small class="fs-6">{{ plate.id }}</small></td>
<td><small class="fs-6">{{ plate.plate }}</small></td>
<td>
<a href="#" class="btn btn-sm btn-secondary" <a href="#" class="btn btn-sm btn-secondary"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#editNumberplateModal" data-bs-target="#editNumberplateModal"
@@ -56,14 +89,14 @@
<button type="submit" class="btn btn-sm btn-secondary">Delete</button> <button type="submit" class="btn btn-sm btn-secondary">Delete</button>
</form> </form>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="modal fade" id="editNumberplateModal" tabindex="-1" aria-labelledby="editNumberplateModalLabel" aria-hidden="true"> <div class="modal fade" id="editNumberplateModal" tabindex="-1" aria-labelledby="editNumberplateModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">

View File

@@ -41,6 +41,16 @@
border: 1px solid #444 !important; border: 1px solid #444 !important;
} }
.dark-mode input::placeholder,
.dark-mode .form-control::placeholder {
color: #b0b0b0 !important;
opacity: 1 !important;
}
.dark-mode form select {
background-color: #23272b !important;
color: #e0e0e0 !important;
border: 1px solid #444 !important;
}
.dark-mode .btn { .dark-mode .btn {
background-color: #444 !important; background-color: #444 !important;
color: #e0e0e0 !important; color: #e0e0e0 !important;
@@ -94,6 +104,9 @@
<a class="nav-link active text-white" href="/dash/add"> <a class="nav-link active text-white" href="/dash/add">
Add numberplate Add numberplate
</a> </a>
<a class="nav-link active text-white" href="/dash/logs">
Edit Logs
</a>
</li> </li>
</ul> </ul>
<div class="d-flex justify-content-center mt-3"> <div class="d-flex justify-content-center mt-3">

View File

@@ -50,7 +50,39 @@
<div class="col-md-12"> <div class="col-md-12">
<div class="card mb-4"> <div class="card mb-4">
<div class="card-body"> <div class="card-body">
<h5 class="card-title fs-4">Numberplates</h5> <h5 class="card-title fs-4 mb-0">Numberplates</h5>
<div class="mt-2 mb-3">
<form method="get" class="mb-0">
<div class="row g-2 align-items-center">
<div class="col">
<input type="text" name="npfilter" class="form-control" placeholder="Search numberplate..." value="{{ request.args.get('npfilter', '') }}">
</div>
<div class="col">
<input type="text" name="idfilter" class="form-control" placeholder="Search by ID..." value="{{ request.args.get('idfilter', '') }}">
</div>
<div class="col-auto d-flex gap-2">
<button type="submit" class="btn btn-secondary">Filter</button>
</div>
</div>
</form>
</div>
{% if request.args.get('npfilter') or request.args.get('idfilter') %}
<div class="mb-2">
<span class="me-2">Active filters:</span>
{% if request.args.get('npfilter') %}
<a href="{{ url_for('dash.dashboard', idfilter=request.args.get('idfilter', '')) }}" class="badge bg-secondary text-decoration-none">
Numberplate: {{ request.args.get('npfilter') }} &times;
</a>
{% endif %}
{% if request.args.get('idfilter') %}
<a href="{{ url_for('dash.dashboard', npfilter=request.args.get('npfilter', '')) }}" class="badge bg-secondary text-decoration-none">
ID: {{ request.args.get('idfilter') }} &times;
</a>
{% endif %}
</div>
{% endif %}
<div class="log-container" style="max-height: 250px; overflow-y: auto;"> <div class="log-container" style="max-height: 250px; overflow-y: auto;">
<table class="table table-sm table-hover align-middle"> <table class="table table-sm table-hover align-middle">
<thead> <thead>

View File

@@ -1,37 +1,63 @@
{%extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="display-4 fw-bold" style="color: #313A4D;">Logs</h1>
</div>
<div class="col-md-12">
<div class="card mb-4"></div>
</div>
<div class="col-md-12"> <div class="col-md-12">
<div class="card mb-4"> <div class="card mb-4">
<div class="card-body"> <div class="card-body">
<h5 class="card-title fs-4">Numberplate Logs</h5> <h5 class="card-title fs-4">Logs</h5>
<div class="log-container" style="max-height: 250px; overflow-y: auto;"> <form method="get" class="mb-3">
<table class="table table-sm table-hover"> <div class="row g-2 align-items-center">
<thead> <div class="col-md-4">
<tr> <select name="order" class="form-select">
<th scope="col" class="fs-5">Time</th> <option value="desc" {% if request.args.get('order', 'desc') == 'desc' %}selected{% endif %}>Newest First</option>
<th scope="col" class="fs-5">Numberplate</th> <option value="asc" {% if request.args.get('order') == 'asc' %}selected{% endif %}>Oldest First</option>
<th scope="col" class="fs-5">Gate Status</th> </select>
</tr> </div>
</thead> <div class="col-auto d-flex gap-2">
<tbody> <button type="submit" class="btn btn-secondary">Sort</button>
{% for form in recent_logs %} </div>
<tr> </div>
<td><small class="fs-6">{{ form.timestamp.strftime('%H:%M:%S') }}</small></td> </form>
<td><small class="fs-6">{{ form.plate }}</small></td>
<td> <div class="log-container" style="max-height: 250px; overflow-y: auto;">
<span <table class="table table-sm table-hover">
class="badge {% if log.status == 'success' %}bg-success{% elif log.status == 'warning' %}bg-warning{% elif log.status == 'error' %}bg-danger{% else %}bg-secondary{% endif %} fs-6"> <thead>
{{ form.allowed }} <tr>
</span> <th scope="col" class="fs-5" style="width: 21%">Time</th>
</td> <th scope="col" class="fs-5" style="width: 42%">Numberplate</th>
</tr> <th scope="col" class="fs-5">Gate Status</th>
{% endfor %} <th scope="col" class="fs-5">Actions</th>
</tbody> </tr>
</table> </thead>
<tbody>
{% for log in logs %}
<tr>
<td><small class="fs-6">{{ log.dateLogged.strftime('%H:%M:%S') }}</small></td>
<td><small class="fs-6">{{ log.plate }}</small></td>
<td>
<span class="badge bg-secondary fs-6">
{{ log.allowed }}
</span>
</td>
<td>
<form method="POST" action="{{ url_for('dash.delete_log', id=log.id) }}" style="display:inline;" onsubmit="return confirm('Are you sure you want to delete this log entry?');">
<button type="submit" class="btn btn-sm btn-secondary">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -18,7 +18,14 @@ dash_blueprint = Blueprint("dash", __name__, template_folder="templates")
@dash_blueprint.route("/dashboard") @dash_blueprint.route("/dashboard")
# @login_required # @login_required
def dashboard(): def dashboard():
Plates = AllowedPlate.query.all() npfilter = request.args.get('npfilter', '')
idfilter = request.args.get('idfilter', '')
query = AllowedPlate.query
if npfilter:
query = query.filter(AllowedPlate.plate.contains(npfilter))
if idfilter:
query = query.filter(AllowedPlate.id == idfilter)
Plates = query.all()
logs = ( logs = (
LoggedItem.query.order_by(LoggedItem.dateLogged.desc()) # type: ignore LoggedItem.query.order_by(LoggedItem.dateLogged.desc()) # type: ignore
.limit(50) .limit(50)
@@ -33,6 +40,14 @@ def dashboard():
@dash_blueprint.route("/add", methods=["GET", "POST"]) @dash_blueprint.route("/add", methods=["GET", "POST"])
# @login_required # @login_required
def add(): def add():
npfilter = request.args.get('npfilter', '')
idfilter = request.args.get('idfilter', '')
query = AllowedPlate.query
if npfilter:
query = query.filter(AllowedPlate.plate.contains(npfilter))
if idfilter:
query = query.filter(AllowedPlate.id == idfilter)
form = npForm() form = npForm()
if form.validate_on_submit(): if form.validate_on_submit():
@@ -46,12 +61,20 @@ def add():
db.session.add(ap) db.session.add(ap)
db.session.commit() db.session.commit()
flash("Numberplate succesfully added") flash("Numberplate succesfully added")
# form wasn't valid Plates = query.all()
Plates = AllowedPlate.query.all()
return render_template( return render_template(
"add.html", form=npForm(formdata=None), plates=Plates "add.html", form=npForm(formdata=None), plates=Plates
) )
@dash_blueprint.route("/logs", methods=["GET", "POST"])
def log():
order = request.args.get('order', 'desc')
query = LoggedItem.query
if order == 'asc':
logs = query.order_by(LoggedItem.dateLogged.asc()).limit(50).all()
else:
logs = query.order_by(LoggedItem.dateLogged.desc()).limit(50).all()
return render_template("logs.html", logs=logs)
@dash_blueprint.route("/live", methods=["GET"]) @dash_blueprint.route("/live", methods=["GET"])
@login_required @login_required
@@ -70,13 +93,6 @@ def live_image():
) )
@dash_blueprint.route("/logs", methods=["GET", "POST"])
# @login_required
def logs():
form = LoggedItem.query.all()
return render_template("logs.html", form=form)
@dash_blueprint.route("/edit/<string:plate>", methods=["GET", "POST"]) @dash_blueprint.route("/edit/<string:plate>", methods=["GET", "POST"])
# login_required # login_required
def edit(plate: str): def edit(plate: str):