From 9f8cbe16751b9e49021e1e2328283474a7c40843 Mon Sep 17 00:00:00 2001 From: NguyenND Date: Wed, 5 Aug 2026 19:30:57 -0400 Subject: [PATCH] Aug 5 - Update (new design) 2 --- .claude/settings.json | 3 +- app/__init__.py | 8 +- app/templates/modern/inspections/list.html | 326 ++++++++++++++++++ app/templates/modern/issues/list.html | 370 +++++++++++++++++++++ 4 files changed, 704 insertions(+), 3 deletions(-) create mode 100644 app/templates/modern/inspections/list.html create mode 100644 app/templates/modern/issues/list.html diff --git a/.claude/settings.json b/.claude/settings.json index 6162dc8..19e2d57 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -14,7 +14,8 @@ "Bash(python -c \"import ast,io; ast.parse\\(io.open\\('app/routes/scheduled_inspections.py',encoding='utf-8'\\).read\\(\\)\\); print\\('route OK'\\)\")", "Bash(python -c \"import ast,io; ast.parse\\(io.open\\('app/utils/notifications.py',encoding='utf-8'\\).read\\(\\)\\); print\\('notif OK'\\)\")", "Bash(SECRET_KEY=x DATABASE_URL=sqlite:///:memory: DIGEST_SECRET=x MAIL_SERVER=localhost MAIL_USERNAME=x MAIL_PASSWORD=x MAIL_PORT=587 APP_BASE_URL=http://localhost MAIL_DEFAULT_SENDER=x@x.com python -c ' *)", - "Bash(python -c \"import ast,io; ast.parse\\(io.open\\('app/routes/dashboard.py',encoding='utf-8'\\).read\\(\\)\\); print\\('dashboard route OK'\\)\")" + "Bash(python -c \"import ast,io; ast.parse\\(io.open\\('app/routes/dashboard.py',encoding='utf-8'\\).read\\(\\)\\); print\\('dashboard route OK'\\)\")", + "Bash(python -c ' *)" ] } } diff --git a/app/__init__.py b/app/__init__.py index 6154a26..4c049ed 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -183,12 +183,16 @@ def create_app(config_name='default'): """Give base.html the shell to extend.""" from app.utils.time_utils import now_eastern theme = getattr(g, 'jqc_theme', 'classic') + _now = now_eastern() return { 'jqc_theme': theme, 'jqc_layout': 'layouts/modern.html' if theme == 'modern' else 'layouts/classic.html', - # Long-form date shown in the modern dashboard header. - 'now_display': now_eastern().strftime('%A, %B %-d, %Y'), + # Long-form date shown in the modern dashboard header. The day is + # interpolated rather than formatted with '%-d' — that flag is a + # glibc extension and raises ValueError on Windows, which would + # 500 every page (this context processor runs on both themes). + 'now_display': f'{_now.strftime("%A, %B")} {_now.day}, {_now.year}', } # ── Inject unread notification count into every template context ────── diff --git a/app/templates/modern/inspections/list.html b/app/templates/modern/inspections/list.html new file mode 100644 index 0000000..a1a9a05 --- /dev/null +++ b/app/templates/modern/inspections/list.html @@ -0,0 +1,326 @@ +{% extends "base.html" %} +{% block title %}Inspections{% endblock %} + +{# + MODERN inspections list (design A/B test). + + Same context variables, same query params, same form field names and the same + three JS blocks as templates/inspections/list.html — only the chrome differs. + Nothing was dropped: every filter, column, badge, the pagination links and the + delete modal are carried over verbatim. `insp-list-link` is preserved on the + View/Continue buttons so filter-state restore on Back still works. +#} + +{% block content %} + +{# ── Header ───────────────────────────────────────────────────────────── #} +
+
+
Inspections
+
+ {% if inspections.total %} + {{ inspections.total }} inspection{{ 's' if inspections.total != 1 }} matching your filters + {% else %} + No inspections match your filters + {% endif %} +
+
+ {% if current_user.role != 'customer' %} + + {% endif %} +
+ +{# ── Filters ──────────────────────────────────────────────────────────── #} +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ {% if inspectors %} +
+ + +
+ {% endif %} +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + Clear + + Export PDF + +
+
+
+
+ +{# ── Results ──────────────────────────────────────────────────────────── #} +
+ {% if inspections.items %} +
+ + + + + + + + + + {% for ins in inspections.items %} + + + + + + + + + + + + + {% endfor %} + +
#DateContractFacilityAreaTemplateInspectorScoreStatus
#{{ ins.id }}{{ ins.inspection_date.strftime('%Y-%m-%d %H:%M') }}{{ ins.facility.project.name if ins.facility and ins.facility.project else '—' }}{{ ins.facility.name }}{% if ins.area %}{{ ins.area.name }}{% else %}{% endif %} + {{ ins.template.name }} + {% if ins.scheduled_inspection_id %} + + Scheduled + + {% endif %} + {{ ins.inspector.display_name }} + {% if ins.overall_score %} + + {{ ins.overall_score }}% + + {% else %}{% endif %} + + + {{ 'Submitted' if ins.status == 'completed' else ins.status|replace('_',' ')|title }} + + {% if ins.status == 'in_progress' %} + {% set hours_open = ((now - ins.inspection_date).total_seconds() / 3600) %} + {% if hours_open > 24 %} + + Stale + + {% endif %} + {% endif %} + {% if ins.follow_up_required and not ins.follow_ups.count() %} + + Follow-up + + {% endif %} + + {% if ins.status == 'in_progress' or ins.status == 'flagged' %} + Continue + {% else %} + View + {% endif %} + {% if current_user.role in ['admin', 'director'] %} + + {% endif %} +
+
+ + {# Pagination #} + {% if inspections.pages > 1 %} +
+ +
+ {% endif %} + + {% else %} +
+ + No inspections found. + +
+ {% endif %} +
+ +{% if current_user.role in ['admin', 'director'] %} + + +{% endif %} +{% endblock %} + +{% block extra_js %} + + + + +{% if current_user.role in ['admin', 'director'] %} + +{% endif %} +{% endblock %} diff --git a/app/templates/modern/issues/list.html b/app/templates/modern/issues/list.html new file mode 100644 index 0000000..a856f0a --- /dev/null +++ b/app/templates/modern/issues/list.html @@ -0,0 +1,370 @@ +{% extends "base.html" %} +{% block title %}Issues{% endblock %} + +{# + MODERN issues list (design A/B test). + + Same context variables, query params, form field names and JS as + templates/issues/list.html — only the chrome differs. Every filter, column, + badge, the quick-assign control, follow/unfollow, delete and the pagination + links are carried over verbatim. +#} + +{% block content %} + +{# ── Header ───────────────────────────────────────────────────────────── #} +
+
+
Issues
+
+ {% if issues.total %} + {{ issues.total }} issue{{ 's' if issues.total != 1 }} matching your filters + {% else %} + No issues match your filters + {% endif %} +
+
+ {% if current_user.role in ['admin','director','customer','auditor'] %} + + Log Issue + + {% endif %} +
+ +{# ── Filters ──────────────────────────────────────────────────────────── #} +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + Clear + + Export PDF + +
+
+
+ +{# ── Results ──────────────────────────────────────────────────────────── #} +
+ {% if issues.items %} +
+ + + + + + + + + + + + + + + + + + {% for issue in issues.items %} + {% set is_following = issue.id in followed_ids %} + {% set sla = sla_status(issue) %} + + + + + + + + + + + + + + {% endfor %} + +
#ReportedSeverityContractFacility / AreaDescriptionStatusSLAReporterAssigned
#{{ issue.id }}{{ issue.reported_at.strftime('%Y-%m-%d %H:%M') }} + + {{ issue.severity|title }} + + + {% set _c = issue.resolved_facility.project if issue.resolved_facility else none %} + {{ _c.name if _c else '—' }} + + {{ issue.resolved_facility.name if issue.resolved_facility else '—' }}
+ {{ issue.area.name if issue.area else '—' }} +
+ 60 %} title="{{ issue.description }}" style="cursor:help;"{% endif %}> + {{ issue.description[:60] }}{% if issue.description|length > 60 %}…{% endif %} + + + + {{ issue.status|replace('_',' ')|title }} + + + {% if sla == 'breached' %} + Breached + {% elif sla == 'at_risk' %} + {% set hrs = sla_hours_remaining(issue) %} + {{ hrs|abs|round(1) }}h left + {% elif sla == 'ok' %} + OK + {% else %} + + {% endif %} + + {% if issue.reporter %} + {{ issue.reporter.display_name }} + {% else %}{% endif %} + + {% if current_user.role in ['admin', 'director', 'auditor'] and issue.status != 'resolved' %} +
+ + +
+ {% else %} + {% if issue.assigned_user %}{{ issue.assigned_user.display_name }} + {% else %}{% endif %} + {% endif %} + {% if issue.handler_type == 'facility' %} +
Facility
+ {% elif issue.handler_type == 'vendor' %} +
Vendor
+ {% endif %} +
+ {# Following badge + inline unfollow #} + {% if is_following %} + + Following + +
+ + + +
+ {% endif %} + + + {% if current_user.role in ['admin','director','auditor'] or issue.assigned_to == current_user.id %} + Edit + {% else %} + View + {% endif %} + + {% if current_user.role in ['admin', 'director'] %} +
+ + +
+ {% endif %} +
+
+ + {% if issues.pages > 1 %} +
+ +
+ {% endif %} + + {% else %} +
+ + No issues found. + +
+ {% endif %} +
+{% endblock %} + +{% block extra_js %} + + +{% if current_user.role in ['admin', 'director', 'auditor'] %} + +{% endif %} +{% endblock %}