From bad5b51746ed663e48a163b433eece2513f28682 Mon Sep 17 00:00:00 2001 From: NguyenND Date: Fri, 12 Jun 2026 11:24:35 -0400 Subject: [PATCH] 06/12 Redesign dashboard --- app/routes/dashboard.py | 116 ++++----------- app/templates/dashboard.html | 265 +++-------------------------------- 2 files changed, 49 insertions(+), 332 deletions(-) diff --git a/app/routes/dashboard.py b/app/routes/dashboard.py index fcec208..6e524d1 100644 --- a/app/routes/dashboard.py +++ b/app/routes/dashboard.py @@ -24,7 +24,6 @@ def index(): now = now_eastern() today_start = now.replace(hour=0, minute=0, second=0, microsecond=0) today_end = today_start + timedelta(days=1) - thirty_days_ago = now - timedelta(days=30) is_inspector = current_user.role == 'inspector' is_privileged = current_user.role in ['admin', 'director'] @@ -97,26 +96,35 @@ def index(): 'low': sum(1 for i in open_issues_all if i.severity == 'low'), } - # ── Average score (last 30 days, inspector: own work in contracted facilities) - score_q = db.session.query(func.avg(Inspection.overall_score)).filter( - Inspection.status == 'completed', - Inspection.overall_score.isnot(None), - Inspection.inspection_date >= thirty_days_ago, + # ── Issues resolved today ───────────────────────────────────────────────── + resolved_today_q = Issue.query.filter( + Issue.status == 'resolved', + Issue.resolved_at >= today_start, + Issue.resolved_at < today_end, ) if is_inspector: if not inspector_facility_ids: - score_q = score_q.filter(False) + resolved_today_q = resolved_today_q.filter(False) else: - score_q = score_q.filter( - Inspection.facility_id.in_(inspector_facility_ids), - Inspection.inspector_id == current_user.id, - ) + from app.models.facility import Area as _Area + resolved_today_q = resolved_today_q.outerjoin( + _Area, Issue.area_id == _Area.id + ).filter(db.or_( + Issue.facility_id.in_(inspector_facility_ids), + _Area.facility_id.in_(inspector_facility_ids), + )) elif is_customer: - if customer_facility_ids: - score_q = score_q.filter(Inspection.facility_id.in_(customer_facility_ids)) + if not customer_facility_ids: + resolved_today_q = resolved_today_q.filter(False) else: - score_q = score_q.filter(False) - avg_score = score_q.scalar() + from app.models.facility import Area as _Area + resolved_today_q = resolved_today_q.outerjoin( + _Area, Issue.area_id == _Area.id + ).filter(db.or_( + Issue.facility_id.in_(customer_facility_ids), + _Area.facility_id.in_(customer_facility_ids), + )) + resolved_today = resolved_today_q.count() # ── Recent inspections ───────────────────────────────────────────────── recent_q = Inspection.query.order_by(Inspection.inspection_date.desc()) @@ -199,61 +207,6 @@ def index(): sla_breached = sum(1 for i in all_open_issues if sla_status(i) == 'breached') sla_at_risk = sum(1 for i in all_open_issues if sla_status(i) == 'at_risk') - # ── Score trend (last 30 days, grouped by day) ──────────────────────── - trend_q = ( - db.session.query( - func.date(Inspection.inspection_date).label('day'), - func.avg(Inspection.overall_score).label('avg'), - ) - .filter( - Inspection.status == 'completed', - Inspection.overall_score.isnot(None), - Inspection.inspection_date >= thirty_days_ago, - ) - ) - if is_inspector: - if not inspector_facility_ids: - trend_q = trend_q.filter(False) - else: - trend_q = trend_q.filter( - Inspection.facility_id.in_(inspector_facility_ids), - Inspection.inspector_id == current_user.id, - ) - elif is_customer: - if customer_facility_ids: - trend_q = trend_q.filter(Inspection.facility_id.in_(customer_facility_ids)) - else: - trend_q = trend_q.filter(False) - trend_rows = trend_q.group_by(func.date(Inspection.inspection_date))\ - .order_by(func.date(Inspection.inspection_date)).all() - trend_labels = [str(r.day) for r in trend_rows] - trend_data = [round(float(r.avg), 2) for r in trend_rows] - - # ── Facility performance (last 30 days, privileged users only) ───────── - facility_perf = [] - if is_privileged or is_project_manager: - perf_rows = ( - db.session.query( - Facility.name, - func.count(Inspection.id).label('count'), - func.avg(Inspection.overall_score).label('avg'), - ) - .join(Inspection, Inspection.facility_id == Facility.id) - .filter( - Inspection.status == 'completed', - Inspection.overall_score.isnot(None), - Inspection.inspection_date >= thirty_days_ago, - Facility.active == True, - ) - .group_by(Facility.id, Facility.name) - .order_by(func.avg(Inspection.overall_score).desc()) - .all() - ) - facility_perf = [ - {'name': r.name, 'count': r.count, 'avg': round(float(r.avg), 1)} - for r in perf_rows - ] - # ── My open issues (inspector dashboard widget) ─────────────────────────── # Issues assigned to the current inspector that are not yet resolved, # ordered by SLA urgency (breached first, then at-risk, then ok). @@ -270,40 +223,23 @@ def index(): .all() ) - # ── Facilities list for the trend-by-facility chart selector ──────────── - if is_privileged or is_project_manager: - all_facilities = Facility.query.filter_by(active=True).order_by(Facility.name).all() - elif is_inspector and inspector_facility_ids: - all_facilities = Facility.query.filter( - Facility.id.in_(inspector_facility_ids), Facility.active == True - ).order_by(Facility.name).all() - elif is_customer and customer_facility_ids: - all_facilities = Facility.query.filter( - Facility.id.in_(customer_facility_ids), Facility.active == True - ).order_by(Facility.name).all() - else: - all_facilities = [] - return render_template( 'dashboard.html', today_inspections = today_inspections, completed_today = completed_today, open_issues = open_issues, severity_breakdown = severity_breakdown, - avg_score = round(avg_score, 2) if avg_score else None, + resolved_today = resolved_today, + pending_followups = pending_followups, recent_inspections = recent_inspections, total_facilities = total_facilities, total_templates = total_templates, total_users = total_users, sla_breached = sla_breached, sla_at_risk = sla_at_risk, - trend_labels = trend_labels, - trend_data = trend_data, - facility_perf = facility_perf, customer_facilities = customer_facilities, - pending_followups = pending_followups, - all_facilities = all_facilities, my_issues = my_issues, + today_str = now.strftime('%Y-%m-%d'), ) diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index 505a7a4..f499cda 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -13,8 +13,8 @@ {# ── Top stat cards ─────────────────────────────────────────────────────── #}
-
- +
+
-
- +
+
-
+
- - -{# ── Pending Follow-ups alert (non-customer) ────────────────────────────── #} -{% if pending_followups and pending_followups > 0 and current_user.role != 'customer' %} -
-
- - -{% endif %} {# ── SLA Summary ─────────────────────────────────────────────────────────── #} {% if sla_breached > 0 or sla_at_risk > 0 %} @@ -180,101 +180,6 @@
-{# ── Score trend chart + Facility performance ───────────────────────────── #} -
-
-
-
- Inspection Score Trend (Last 30 Days) -
-
- {% if trend_labels %} - - {% else %} -
- - No completed inspections with scores in the last 30 days. -
- {% endif %} -
-
-
- - {% if current_user.role in ['admin', 'director'] and facility_perf %} -
-
-
- Facility Performance (30d) -
-
- - - - - - - - - - {% for f in facility_perf %} - - - - - - {% endfor %} - -
FacilityInspectionsAvg Score
{{ f.name }}{{ f.count }} - - {{ f.avg }}% - -
-
-
-
- {% endif %} -
- -{# ── Facility Score Trend (30/60/90 days) ─────────────────────────────────── #} -{% if all_facilities %} -
-
-
-
- Facility Score Trend -
- -
- - - -
-
-
-
-
- - Select a facility to view its score trend. -
- - - -
-
-
-
-{% endif %} {# ── My open issues (inspector widget) ──────────────────────────────────── #} {% if my_issues %} @@ -444,128 +349,4 @@ {% endif %} - - -{% if trend_labels %} - -{% endif %} - -{# ── Facility trend chart (AJAX-driven) ── #} -{% if all_facilities %} - -{% endif %} {% endblock %} \ No newline at end of file