{% extends "base.html" %} {% block title %}Issue #{{ issue.id }}{% endblock %} {% block extra_css %} {% endblock %} {% block content %} {% set can_edit = current_user.role in ['admin','director','auditor'] or issue.assigned_to == current_user.id %} {# The filtered list URL this page was opened from (phase: filter preservation). Threaded into every action so an update or delete returns to the same filtered page, and used by the Back button. #} {% set back_url = request.args.get('next') or url_for('issues.index') %} {# Is the viewer OUR staff? Drives the internal-only chrome on this page: the "comments are visible to everyone" warning and the per-comment "Customer visible" / "Staff only" badges. Both are instructions about how WE work and must never reach a customer account. Written as an explicit ALLOWLIST of our own roles, deliberately: * It FAILS CLOSED. The obvious form, `not current_user.is_customer_account`, fails OPEN — if the attribute is missing for any reason (a process still running an older models/user.py after a template-only reload, say) Jinja yields Undefined, `not Undefined` is true, and the internal text is shown to exactly the people it must be hidden from. An allowlist of literal role strings can only ever be true for a role we listed. * `external_inspector` is absent ON PURPOSE. This is NOT the rule-87 case: rule 87 is about capability/scoping, where a Customer Inspector must behave exactly like our own inspector. Here the question is "does this person work for us?", which is the one place the two genuinely differ. Do not "fix" this by adding external_inspector to the list. #} {% set viewer_is_our_staff = current_user.role in ['admin', 'director', 'project_manager', 'auditor', 'inspector'] %}
{# ══════════════════════════════════ LEFT COLUMN ══════════════════════════════════ #}
{# ── Issue details ──────────────────────────────────────────────────── #}
Issue #{{ issue.id }} — {{ issue.severity|title }} Severity
{{ issue.status|replace('_',' ')|title }}
Reported
{{ issue.reported_at.strftime('%Y-%m-%d %H:%M') }}
Reported By
{% if issue.reporter %} {{ issue.reporter.display_name }} {% if issue.reporter.role == 'customer' %} Customer {% else %} {{ issue.reporter.role|replace('_',' ')|title }} {% endif %} {% else %} {% endif %}
Contract
{% if issue.resolved_facility and issue.resolved_facility.project %} {{ issue.resolved_facility.project.name }} {% else %}—{% endif %}
Facility
{{ issue.resolved_facility.name if issue.resolved_facility else '—' }}
Area
{{ issue.area.name if issue.area else '—' }}
{% if issue.inspection %}
Inspection
#{{ issue.inspection_id }}
{% endif %}
Handled By
{% if issue.handler_type == 'facility' %} Facility Staff {% elif issue.handler_type == 'vendor' %} External Vendor {% else %} Janitorial Staff {% endif %}
{{ 'Follow-up Owner' if issue.handler_type in ['facility','vendor'] else 'Assigned To' }}
{{ issue.assigned_user.display_name if issue.assigned_user else '— Unassigned —' }}
{% if (issue.handler_type or 'internal') == 'internal' and issue.internal_handler_name %}
Staff
{{ issue.internal_handler_name }} {% if issue.internal_handler_contact %} {{ issue.internal_handler_contact }} {% endif %}
{% endif %} {% if issue.handler_type == 'facility' and issue.facility_handler_name %}
Facility Contact
{{ issue.facility_handler_name }} {% if issue.facility_handler_contact %} {{ issue.facility_handler_contact }} {% endif %} {% if issue.facility_handler_notes %}
{{ issue.facility_handler_notes }}
{% endif %}
{% endif %} {% if issue.handler_type == 'vendor' and issue.vendor_name %}
Contractor
{{ issue.vendor_name }} {% if issue.vendor_contact %} {{ issue.vendor_contact }} {% endif %} {% if issue.vendor_notes %}
{{ issue.vendor_notes }}
{% endif %}
{% endif %} {% if issue.resolved_at %}
Resolved
{{ issue.resolved_at.strftime('%Y-%m-%d %H:%M') }}
{% endif %}

Description

{{ issue.description }}

{% if issue.photo_path or issue.mobile_photo_paths %}
Photo Evidence
{% if issue.photo_path %} {% endif %} {% for photo in (issue.mobile_photo_paths or []) %} {% endfor %}
{% endif %} {% if issue.result_notes or issue.result_photos %}
Resolution Details
{% if issue.result_notes %}

{{ issue.result_notes }}

{% endif %} {% if issue.result_photos %}
{% for photo in issue.result_photos %} Result photo {% endfor %}
{% endif %} {% endif %} {# ── Verification panel ── #} {% if issue.verified_at %}
Verified by {{ issue.verifier.display_name if issue.verifier else 'unknown' }} on {{ issue.verified_at.strftime('%Y-%m-%d %H:%M') }}. {% if issue.verification_note %}
{{ issue.verification_note }}{% endif %}
{% elif issue.status == 'pending_verification' %}
Awaiting director verification. {% if current_user.role in ['admin','director','auditor'] %}
{% endif %}
{% endif %}
{# ── Comments ───────────────────────────────────────────────────────── #}
Comments {{ comments|length }}
{# Comment list #} {% if comments %}
{% set avatar_colors = ['#4f46e5','#0891b2','#059669','#d97706','#dc2626','#7c3aed','#db2777'] %} {% for c in comments %} {% set avatar_color = avatar_colors[c.author.id % (avatar_colors | length)] %}
{{ c.author.display_name[0] | upper }}
{{ c.author.display_name }} {% if c.author.role == 'customer' %} Customer {% else %} {{ c.author.role|replace('_',' ')|title }} {% endif %} {# Visibility indicator — staff only. While comments_open is set, EVERY comment is visible to the customer, so the old "Staff only" badge would be a lie. It is suppressed rather than shown incorrectly. #} {% if viewer_is_our_staff and not comments_open %} {% if c.is_customer_visible %} Customer visible {% else %} Staff only {% endif %} {% endif %}
{{ c.status_at_time|replace('_',' ')|title }} {{ c.created_at.strftime('%b %d, %Y %H:%M') }}

{{ c.body }}

{% endfor %}

{% elif current_user.role == 'customer' %}

No comments yet.


{% endif %} {# ── Add Comment form ─────────────────────────────────────────────── #} {% set can_customer_comment = current_user.role == 'customer' and (is_following or issue.reported_by == current_user.id) %} {% if can_edit %} {# Staff comment form with visibility checkbox #}

Add Comment

{# While comments_open is set, every comment reaches the customer, so the "Share with customer" tick decides nothing. Saying so plainly matters: a staff member must not write something they believe is private. The checkbox is still posted and recorded, so turning the setting off restores its meaning immediately. OUR STAFF ONLY. `can_edit` is also true for a Customer Inspector assigned to the issue, and this banner is an internal-process warning ("do not post internal-only notes") — showing it to a customer account exposes how we work and reads as nonsense to them, since nothing they write was ever private. #} {% if comments_open and viewer_is_our_staff %}
Comments are currently visible to everyone, including the customer. Do not post internal-only notes here.
{% endif %}
{% elif can_customer_comment %} {# Customer comment form — visible to all by design #}

Add Comment

{% elif current_user.role == 'customer' %}

To add comments, click on the Follow button

{% else %}

Only assigned staff can add comments.

{% endif %}
{# /col-lg-8 #} {# ══════════════════════════════════ RIGHT COLUMN ═════════════════════════════════ #}
{# ── Follow / Unfollow ──────────────────────────────────────────────── #}
{% if is_following %}Following{% else %}Not following{% endif %} {{ issue.followers.count() }} follower{{ 's' if issue.followers.count() != 1 else '' }}
{% if is_following %}
{% else %}
{% endif %}
{# ── Update Form ────────────────────────────────────────────────────── #} {% if can_edit %}
Update Issue
{{ form.status.label(class="form-label fw-semibold") }} {{ form.status(class="form-select") }}
{% if current_user.role in ['admin','director','project_manager','auditor'] %} {# ── Who handles this issue ── #}
{{ form.handler_type.label(class="form-label fw-semibold") }} {{ form.handler_type(class="form-select", id="handler_type_select") }}
{% endif %} {% if current_user.role in ['admin','director','auditor'] %}
{{ form.assigned_to(class="form-select") }}
{% endif %} {% if current_user.role in ['admin','director','project_manager','auditor'] %} {# ── Janitorial-staff handler (shown when Handled By = Janitorial Staff) ── #} {# ── Facility-staff handler (shown when Handled By = Facility Staff) ── #} {# ── External vendor (shown when Handled By = External Vendor) ── #} {% endif %}
{{ form.result_notes.label(class="form-label fw-semibold") }} {{ form.result_notes(class="form-control", rows=3, placeholder="Describe what was done to resolve this issue…", value=issue.result_notes or '') }}
Attach one or more photos showing the resolution.
{% if issue.result_photos %}
{{ issue.result_photos|length }} photo(s) already uploaded
{% endif %}
{% if issue.status in ['in_progress', 'resolved'] and current_user.role not in ['customer'] %}
{% endif %}
{% endif %}
{# /col-lg-4 #}
Back to Issues Export PDF {% if current_user.role in ['admin', 'director'] %} {% endif %}
{% if current_user.role in ['admin', 'director'] %} {% endif %} {% block extra_js %} {% endblock %} {% endblock %}