Jun 29 - Admin view user's notes
This commit is contained in:
@@ -1647,6 +1647,42 @@ def purge_app_log(older_than_days: int = 30):
|
||||
conn.close()
|
||||
|
||||
|
||||
# ─── Today's Check Notes ──────────────────────────────────────────────────────
|
||||
|
||||
def get_todays_check_notes() -> list:
|
||||
"""Return all shift_checks from today that have a non-empty user_note."""
|
||||
conn = None
|
||||
try:
|
||||
conn = get_connection()
|
||||
cur = conn.cursor(dictionary=True)
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT
|
||||
sc.id AS check_id,
|
||||
sc.checked_at,
|
||||
sc.user_note,
|
||||
w.id AS website_id,
|
||||
w.name AS website_name,
|
||||
w.url AS website_url,
|
||||
COALESCE(u.full_name, u.username) AS full_name,
|
||||
u.username
|
||||
FROM shift_checks sc
|
||||
JOIN websites w ON w.id = sc.website_id
|
||||
JOIN users u ON u.id = sc.user_id
|
||||
WHERE DATE(sc.checked_at) = CURDATE()
|
||||
AND sc.user_note IS NOT NULL
|
||||
AND TRIM(sc.user_note) <> ''
|
||||
ORDER BY sc.checked_at DESC
|
||||
"""
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
cur.close()
|
||||
return rows
|
||||
finally:
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
|
||||
# ─── Missed Shifts ────────────────────────────────────────────────────────────
|
||||
|
||||
def get_missed_shifts_today() -> list:
|
||||
|
||||
@@ -4,7 +4,7 @@ routes/admin_dashboard.py — Admin dashboard: today's completion stats.
|
||||
|
||||
import logging
|
||||
from flask import Blueprint, render_template
|
||||
from models import get_admin_dashboard_stats, get_missed_shifts_today
|
||||
from models import get_admin_dashboard_stats, get_missed_shifts_today, get_todays_check_notes
|
||||
from utils.decorators import admin_required
|
||||
|
||||
logger = logging.getLogger("routes.admin_dashboard")
|
||||
@@ -25,4 +25,10 @@ def dashboard():
|
||||
except Exception as e:
|
||||
logger.error(f"Missed shifts error: {e}")
|
||||
missed = []
|
||||
return render_template("admin/dashboard.html", stats=stats, missed=missed)
|
||||
try:
|
||||
check_notes = get_todays_check_notes()
|
||||
except Exception as e:
|
||||
logger.error(f"Check notes error: {e}")
|
||||
check_notes = []
|
||||
return render_template("admin/dashboard.html", stats=stats, missed=missed,
|
||||
check_notes=check_notes)
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<div class="flex gap-1">
|
||||
<button class="btn btn-ghost btn-sm" onclick="location.reload()">↻ Refresh</button>
|
||||
<form method="post" action="{{ url_for('admin_shifts.send_incomplete_reminders') }}" style="display:inline"
|
||||
onsubmit="return confirm('Send reminder emails to all staff with incomplete checks in shifts ending within 40 minutes?')">
|
||||
onsubmit="return confirm('Send reminder emails to all staff with incomplete checks in shifts ending within 45 minutes?')">
|
||||
<button class="btn btn-secondary btn-sm" type="submit">📧 Remind Incomplete</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -99,6 +99,40 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Today's Check Notes -->
|
||||
{% if check_notes %}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">Today's Check Notes</h2>
|
||||
<span class="text-muted text-sm">{{ check_notes|length }} note{{ 's' if check_notes|length != 1 }}</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>User</th>
|
||||
<th>Website</th>
|
||||
<th>Note</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for n in check_notes %}
|
||||
<tr>
|
||||
<td class="text-muted text-sm" style="white-space:nowrap">
|
||||
{{ n.checked_at.strftime('%H:%M') if n.checked_at else '' }}
|
||||
</td>
|
||||
<td style="white-space:nowrap">{{ n.full_name }}</td>
|
||||
<td>
|
||||
<a href="{{ n.website_url }}" target="_blank" rel="noopener" class="link">{{ n.website_name }}</a>
|
||||
</td>
|
||||
<td style="white-space:pre-wrap; word-break:break-word">{{ n.user_note }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
document.getElementById('today-date').textContent =
|
||||
new Date().toLocaleDateString('en-US', {weekday:'long', year:'numeric', month:'long', day:'numeric'});
|
||||
|
||||
Reference in New Issue
Block a user