06/09 Remove issue details update notes, add comment section
This commit is contained in:
@@ -1,8 +1,30 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Issue #{{ issue.id }}{% endblock %}
|
{% block title %}Issue #{{ issue.id }}{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_css %}
|
||||||
|
<style>
|
||||||
|
.comment-avatar {
|
||||||
|
width: 38px; height: 38px; border-radius: 50%;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
font-weight: 700; font-size: .9rem; color: #fff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.comment-bubble {
|
||||||
|
background: #f8f9fa; border: 1px solid #e9ecef;
|
||||||
|
border-radius: .5rem; padding: .75rem 1rem;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
{% set can_edit = current_user.role in ['admin','director'] or issue.assigned_to == current_user.id %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
{# ══════════════════════════════════ LEFT COLUMN ══════════════════════════════════ #}
|
||||||
<div class="col-lg-8">
|
<div class="col-lg-8">
|
||||||
|
|
||||||
|
{# ── Issue details ──────────────────────────────────────────────────── #}
|
||||||
<div class="card shadow-sm mb-4">
|
<div class="card shadow-sm mb-4">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center
|
<div class="card-header d-flex justify-content-between align-items-center
|
||||||
bg-{{ 'danger' if issue.severity in ['critical','high'] else 'warning' if issue.severity == 'medium' else 'secondary' }}
|
bg-{{ 'danger' if issue.severity in ['critical','high'] else 'warning' if issue.severity == 'medium' else 'secondary' }}
|
||||||
@@ -133,35 +155,81 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# ── Update History ─────────────────────────────────────────────────── #}
|
{# ── Comments ───────────────────────────────────────────────────────── #}
|
||||||
{% if comments %}
|
<div class="card shadow-sm mb-4" id="comments-section">
|
||||||
<div class="card shadow-sm mb-4">
|
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
||||||
<div class="card-header bg-light">
|
<h6 class="mb-0">
|
||||||
<h6 class="mb-0" id="update-history"><i class="bi bi-clock-history"></i> Update History</h6>
|
<i class="bi bi-chat-left-text me-1"></i>Comments
|
||||||
|
<span class="badge bg-secondary rounded-pill ms-1">{{ comments|length }}</span>
|
||||||
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
<ul class="list-group list-group-flush">
|
|
||||||
|
{# Comment list #}
|
||||||
|
{% if comments %}
|
||||||
|
<div class="p-3 pb-0" id="comments-list">
|
||||||
|
{% set avatar_colors = ['#4f46e5','#0891b2','#059669','#d97706','#dc2626','#7c3aed','#db2777'] %}
|
||||||
{% for c in comments %}
|
{% for c in comments %}
|
||||||
<li class="list-group-item">
|
{% set avatar_color = avatar_colors[c.author.id % (avatar_colors | length)] %}
|
||||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
<div class="d-flex gap-3 mb-3">
|
||||||
<span class="fw-semibold text-dark">
|
<div class="comment-avatar" style="background:{{ avatar_color }};">
|
||||||
<i class="bi bi-person-circle"></i> {{ c.author.display_name }}
|
{{ c.author.display_name[0] | upper }}
|
||||||
</span>
|
</div>
|
||||||
<span class="d-flex align-items-center gap-2">
|
<div class="comment-bubble">
|
||||||
<span class="badge bg-{{ 'success' if c.status_at_time == 'resolved' else 'warning text-dark' if c.status_at_time == 'in_progress' else 'danger' }} rounded-pill" style="font-size:.65rem;">
|
<div class="d-flex justify-content-between align-items-start flex-wrap gap-1 mb-1">
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<span class="fw-semibold" style="font-size:.9rem;">{{ c.author.display_name }}</span>
|
||||||
|
{% if c.author.role == 'customer' %}
|
||||||
|
<span class="badge bg-info text-dark" style="font-size:.65rem;">Customer</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge bg-secondary" style="font-size:.65rem;">{{ c.author.role|replace('_',' ')|title }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<span class="badge bg-{{ 'success' if c.status_at_time == 'resolved' else 'info text-dark' if c.status_at_time == 'pending_verification' else 'warning text-dark' if c.status_at_time == 'in_progress' else 'danger' }}"
|
||||||
|
style="font-size:.65rem;">
|
||||||
{{ c.status_at_time|replace('_',' ')|title }}
|
{{ c.status_at_time|replace('_',' ')|title }}
|
||||||
</span>
|
</span>
|
||||||
<small class="text-muted">{{ c.created_at.strftime('%Y-%m-%d %H:%M') }}</small>
|
<small class="text-muted">{{ c.created_at.strftime('%b %d, %Y %H:%M') }}</small>
|
||||||
</span>
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mb-0" style="white-space:pre-wrap; font-size:.9rem;">{{ c.body }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="mb-0 text-secondary" style="white-space:pre-wrap; font-size:.9rem;">{{ c.body }}</p>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
<hr class="mx-3 my-0">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# Add Comment form — submits to the same view POST endpoint #}
|
||||||
|
{% if can_edit %}
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="fw-semibold small mb-2">Add Comment</p>
|
||||||
|
<form method="post">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
|
{# Keep current status and assigned_to so the route doesn't change them #}
|
||||||
|
<input type="hidden" name="status" value="{{ issue.status }}">
|
||||||
|
<input type="hidden" name="assigned_to" value="{{ issue.assigned_to or 0 }}">
|
||||||
|
<div class="mb-2">
|
||||||
|
<textarea name="update_notes" class="form-control" rows="3"
|
||||||
|
placeholder="Write a comment…" required></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary btn-sm">
|
||||||
|
<i class="bi bi-send me-1"></i>Post Comment
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% elif current_user.role != 'customer' %}
|
||||||
|
<div class="card-body py-2">
|
||||||
|
<p class="text-muted small mb-0">
|
||||||
|
<i class="bi bi-lock me-1"></i>Only assigned staff can add comments.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>{# /col-lg-8 #}
|
||||||
|
|
||||||
|
{# ══════════════════════════════════ RIGHT COLUMN ═════════════════════════════════ #}
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
|
|
||||||
{# ── Follow / Unfollow ──────────────────────────────────────────────── #}
|
{# ── Follow / Unfollow ──────────────────────────────────────────────── #}
|
||||||
@@ -195,7 +263,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# ── Update Form ────────────────────────────────────────────────────── #}
|
{# ── Update Form ────────────────────────────────────────────────────── #}
|
||||||
{% set can_edit = current_user.role in ['admin','director'] or issue.assigned_to == current_user.id %}
|
|
||||||
{% if can_edit %}
|
{% if can_edit %}
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
<div class="card-header bg-light"><h6 class="mb-0">Update Issue</h6></div>
|
<div class="card-header bg-light"><h6 class="mb-0">Update Issue</h6></div>
|
||||||
@@ -212,10 +279,6 @@
|
|||||||
{{ form.assigned_to(class="form-select") }}
|
{{ form.assigned_to(class="form-select") }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="mb-3">
|
|
||||||
{{ form.update_notes.label(class="form-label fw-semibold") }}
|
|
||||||
{{ form.update_notes(class="form-control", rows=3, placeholder="Optional update notes…") }}
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{{ form.result_notes.label(class="form-label fw-semibold") }}
|
{{ form.result_notes.label(class="form-label fw-semibold") }}
|
||||||
{{ form.result_notes(class="form-control", rows=3,
|
{{ form.result_notes(class="form-control", rows=3,
|
||||||
@@ -249,7 +312,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
|
</div>{# /col-lg-4 #}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex align-items-center gap-2 mt-2">
|
<div class="d-flex align-items-center gap-2 mt-2">
|
||||||
@@ -265,7 +329,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if current_user.role in ['admin', 'director'] %}
|
{% if current_user.role in ['admin', 'director'] %}
|
||||||
<!-- Delete Confirmation Modal -->
|
|
||||||
<div class="modal fade" id="deleteIssueModal" tabindex="-1" aria-labelledby="deleteIssueModalLabel" aria-hidden="true">
|
<div class="modal fade" id="deleteIssueModal" tabindex="-1" aria-labelledby="deleteIssueModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -305,24 +368,18 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// ── Immediate bell refresh after a successful update ──────────────────
|
// Refresh bell badge after a successful update
|
||||||
// After form submit + redirect, Flask flashes 'Issue updated.' and the
|
|
||||||
// URL stays at /issues/<id>. We detect the flash message presence and
|
|
||||||
// call the global fetchNotifications() defined in base.html so the bell
|
|
||||||
// badge updates instantly without waiting for the 60-second poll cycle.
|
|
||||||
if (document.querySelector('.alert-success')) {
|
if (document.querySelector('.alert-success')) {
|
||||||
if (typeof fetchNotifications === 'function') {
|
if (typeof fetchNotifications === 'function') {
|
||||||
fetchNotifications();
|
fetchNotifications();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Auto-scroll to Update History after save ──────────────────────────
|
// Scroll to bottom of comments list after posting a comment
|
||||||
// If there's a success flash AND comments exist, scroll the history
|
|
||||||
// section into view so the newly added comment is immediately visible.
|
|
||||||
if (document.querySelector('.alert-success')) {
|
if (document.querySelector('.alert-success')) {
|
||||||
var historyEl = document.getElementById('update-history');
|
var section = document.getElementById('comments-section');
|
||||||
if (historyEl) {
|
if (section) {
|
||||||
historyEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
section.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user