Add manager engagement review workspace phase 5A

This commit is contained in:
A R R R Associates
2026-07-20 12:54:10 +05:30
parent fe23b2b253
commit 4c81659156
4 changed files with 360 additions and 4 deletions
@@ -0,0 +1,82 @@
{% extends "base/layout.html" %}
{% block content %}
<div class="mx-auto max-w-[1600px] space-y-5">
<header class="rounded-2xl bg-slate-900 p-5 text-white shadow-sm">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<p class="text-xs font-semibold uppercase tracking-wider text-slate-300">Manager Engagement Review</p>
<h1 class="mt-1 text-2xl font-semibold">{{ workspace.client_name }}</h1>
<p class="mt-1 text-sm text-slate-300">{{ workspace.service_name }} · {{ workspace.financial_year }}</p>
</div>
<div class="flex flex-wrap gap-2">
<a href="/manager/dashboard?tab=review-queue" class="rounded-xl border border-white/30 px-4 py-2 text-sm font-semibold hover:bg-white/10">Close Workspace</a>
</div>
</div>
<div class="mt-5 grid gap-3 sm:grid-cols-2 lg:grid-cols-5">
<div class="rounded-xl bg-white/10 p-3"><div class="text-xs text-slate-300">Weighted Progress</div><div class="mt-1 text-xl font-semibold">{{ workspace.weighted_progress.progress_percent }}%</div></div>
<div class="rounded-xl bg-white/10 p-3"><div class="text-xs text-slate-300">Pending Review</div><div class="mt-1 text-xl font-semibold">{{ workspace.pending_review_count }}</div></div>
<div class="rounded-xl bg-white/10 p-3"><div class="text-xs text-slate-300">Reviewed</div><div class="mt-1 text-xl font-semibold">{{ workspace.reviewed_count }}</div></div>
<div class="rounded-xl bg-white/10 p-3"><div class="text-xs text-slate-300">Rework</div><div class="mt-1 text-xl font-semibold">{{ workspace.rework_count }}</div></div>
<div class="rounded-xl bg-white/10 p-3"><div class="text-xs text-slate-300">SLA</div><div class="mt-1 text-sm font-semibold">{{ workspace.sla.label }}</div></div>
</div>
</header>
{% if request.query_params.get('reviewed') %}<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-semibold text-emerald-800">Manager review saved.</div>{% endif %}
{% if request.query_params.get('review_error') %}<div class="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm font-semibold text-red-800">Review could not be saved. Rework requires a reason, and locked or not-ready tasks cannot be reviewed.</div>{% endif %}
<section class="grid gap-5 xl:grid-cols-[390px_minmax(0,1fr)]">
<aside class="space-y-4">
<div class="af-card">
<h2 class="font-semibold text-slate-900">Engagement Team</h2>
<div class="mt-3 space-y-2 text-sm">{% for member in workspace.team %}<div class="flex justify-between gap-3"><span class="text-slate-500">{{ member.role }}</span><span class="font-semibold text-right">{{ member.name }}</span></div>{% endfor %}</div>
</div>
<div class="space-y-3">
{% for category in workspace.categories %}
<details class="af-card p-0 overflow-hidden" {% if workspace.active_task and workspace.active_task.category == category.name %}open{% endif %}>
<summary class="cursor-pointer list-none p-4">
<div class="flex items-start justify-between gap-3"><div><div class="font-semibold text-slate-900">{{ category.name }}</div><div class="mt-1 text-xs text-slate-500">{{ category.reviewed }} reviewed · {{ category.pending }} pending · {{ category.rework }} rework</div></div><span class="rounded-full px-2 py-1 text-xs font-semibold {% if category.status == 'reviewed' %}bg-emerald-100 text-emerald-700{% elif category.status == 'rework' %}bg-red-100 text-red-700{% elif category.status == 'pending_review' %}bg-violet-100 text-violet-700{% else %}bg-slate-100 text-slate-600{% endif %}">{{ category.progress_percent }}%</span></div>
</summary>
<div class="border-t border-slate-100 p-2">
{% for task in category.tasks %}
<a href="/manager/reviews/engagements/{{ workspace.subscription_id }}?task_id={{ task.id }}" class="mb-1 block rounded-xl border px-3 py-2 text-sm {% if workspace.active_task and workspace.active_task.id == task.id %}border-indigo-400 bg-indigo-50{% else %}border-transparent hover:border-slate-200 hover:bg-slate-50{% endif %}">
<div class="font-medium text-slate-900">{{ task.task_name }}</div>
<div class="mt-1 flex flex-wrap gap-1 text-[11px]"><span class="rounded bg-slate-100 px-1.5 py-0.5">{{ task.assigned_to }}</span><span class="rounded px-1.5 py-0.5 {% if task.review_state == 'pending_review' %}bg-violet-100 text-violet-700{% elif task.review_state == 'reviewed' %}bg-emerald-100 text-emerald-700{% elif task.review_state == 'rework' %}bg-red-100 text-red-700{% else %}bg-slate-100 text-slate-600{% endif %}">{{ task.review_state_label }}</span></div>
</a>
{% endfor %}
</div>
</details>
{% endfor %}
</div>
</aside>
<main class="space-y-4">
{% set task = workspace.active_task %}
{% if task %}
<section class="af-card">
<div class="flex flex-wrap items-start justify-between gap-3">
<div><div class="text-xs font-semibold uppercase text-slate-500">{{ task.category }}</div><h2 class="mt-1 text-xl font-semibold text-slate-900">{{ task.task_name }}</h2><p class="mt-2 whitespace-pre-line text-sm text-slate-600">{{ task.description }}</p></div>
<div class="flex flex-wrap gap-2"><span class="rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold">{{ task.task_status_label }}</span>{% if task.is_aqmm_task %}<span class="rounded-full bg-indigo-100 px-3 py-1 text-xs font-semibold text-indigo-700">AQMM</span>{% endif %}{% if task.blocks_final_release %}<span class="rounded-full bg-red-100 px-3 py-1 text-xs font-semibold text-red-700">Final Gate</span>{% endif %}</div>
</div>
<div class="mt-5 grid gap-3 sm:grid-cols-2 lg:grid-cols-4"><div class="rounded-xl bg-slate-50 p-3"><div class="text-xs text-slate-500">Prepared By</div><div class="mt-1 font-semibold">{{ task.assigned_to }}</div></div><div class="rounded-xl bg-slate-50 p-3"><div class="text-xs text-slate-500">Evidence</div><div class="mt-1 font-semibold">{{ task.evidence_count }} document(s)</div></div><div class="rounded-xl bg-slate-50 p-3"><div class="text-xs text-slate-500">Comments</div><div class="mt-1 font-semibold">{{ task.comment_count }}</div></div><div class="rounded-xl bg-slate-50 p-3"><div class="text-xs text-slate-500">Manager Review</div><div class="mt-1 font-semibold">{{ task.review_state_label }}</div></div></div>
</section>
<section class="grid gap-4 lg:grid-cols-2">
<div class="af-card"><h3 class="font-semibold text-slate-900">Staff Response & Conclusion</h3><dl class="mt-4 space-y-3 text-sm"><div><dt class="text-xs uppercase text-slate-500">Response Type</dt><dd class="font-semibold">{{ task.response_type }}</dd></div><div><dt class="text-xs uppercase text-slate-500">Response</dt><dd class="font-semibold">{{ task.checklist_response or task.checklist_text_response or task.checklist_number_response or task.checklist_date_response or 'Not entered' }}</dd></div><div><dt class="text-xs uppercase text-slate-500">Checklist Remarks</dt><dd class="whitespace-pre-line">{{ task.checklist_remarks or 'No remarks' }}</dd></div>{% if task.rework_reason %}<div class="rounded-xl border border-red-200 bg-red-50 p-3"><dt class="text-xs uppercase text-red-600">Rework Reason</dt><dd class="mt-1 whitespace-pre-line text-red-800">{{ task.rework_reason }}</dd></div>{% endif %}</dl><div class="mt-4 flex gap-2"><a href="{{ task.documents_href }}" class="af-btn af-btn-secondary">Open Evidence</a><a href="{{ task.task_href }}" class="af-btn af-btn-secondary">Open Full Task</a></div></div>
<div class="af-card"><h3 class="font-semibold text-slate-900">Communication Timeline</h3><div class="mt-4 max-h-80 space-y-3 overflow-y-auto">{% for comment in task.comments %}<div class="rounded-xl bg-slate-50 p-3"><div class="flex justify-between gap-3 text-xs text-slate-500"><span>{{ comment.comment_type.replace('_',' ').title() }}</span><span>{{ comment.created_at_utc }}</span></div><p class="mt-2 whitespace-pre-line text-sm text-slate-700">{{ comment.message }}</p></div>{% else %}<p class="text-sm text-slate-500">No communication recorded.</p>{% endfor %}</div></div>
</section>
<section class="af-card">
<h3 class="font-semibold text-slate-900">Manager Review Decision</h3>
{% if task.is_locked or workspace.is_locked %}<div class="mt-3 rounded-xl border border-amber-200 bg-amber-50 p-3 text-sm text-amber-800">This task or engagement is locked. Review is read-only.</div>{% endif %}
<form method="post" action="/manager/reviews/engagements/{{ workspace.subscription_id }}/tasks/{{ task.id }}" class="mt-4 space-y-4">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="grid gap-3 md:grid-cols-2"><label class="block"><span class="text-sm font-semibold text-slate-700">Decision</span><select name="decision" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2" {% if task.is_locked or workspace.is_locked or task.review_state == 'not_ready' %}disabled{% endif %}><option value="reviewed">Approve / Reviewed</option><option value="rework_required">Return for Rework</option></select></label><label class="block"><span class="text-sm font-semibold text-slate-700">Review state</span><input class="mt-1 w-full rounded-xl border border-slate-200 bg-slate-50 px-3 py-2" value="{{ task.review_state_label }}" readonly></label></div>
<label class="block"><span class="text-sm font-semibold text-slate-700">Manager review note</span><textarea name="review_note" rows="5" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2" placeholder="Required when returning for rework; recommended for significant conclusions." {% if task.is_locked or workspace.is_locked or task.review_state == 'not_ready' %}disabled{% endif %}>{{ task.manager_review_note }}</textarea></label>
<div class="flex flex-wrap gap-2"><button type="submit" name="workflow_action" value="save" class="af-btn af-btn-secondary" {% if task.is_locked or workspace.is_locked or task.review_state == 'not_ready' %}disabled{% endif %}>Save Review</button><button type="submit" name="workflow_action" value="save_next" class="af-btn af-btn-primary" {% if task.is_locked or workspace.is_locked or task.review_state == 'not_ready' %}disabled{% endif %}>Save &amp; Next Review</button><a href="/manager/dashboard?tab=review-queue" class="af-btn af-btn-secondary">Close Workspace</a></div>
</form>
</section>
{% else %}<section class="af-card text-sm text-slate-500">No tasks are available in this engagement.</section>{% endif %}
</main>
</section>
</div>
{% endblock %}
@@ -1,5 +1,5 @@
<div class="space-y-6">
<section class="grid gap-4 md:grid-cols-3"><div class="af-metric-card"><div class="text-xs font-semibold uppercase text-slate-500">Engagements Awaiting Review</div><div class="mt-2 text-3xl font-semibold">{{ overview.engagement_review_count }}</div></div><div class="af-metric-card"><div class="text-xs font-semibold uppercase text-slate-500">Task Review Queue</div><div class="mt-2 text-3xl font-semibold">{{ overview.review_pending_count }}</div></div><div class="af-metric-card border-red-200 bg-red-50"><div class="text-xs font-semibold uppercase text-red-700">SLA Breached</div><div class="mt-2 text-3xl font-semibold text-red-700">{{ overview.sla_breached_count }}</div></div></section>
<section class="af-card overflow-x-auto"><div class="mb-4"><h3 class="text-lg font-semibold text-slate-900">Engagement Review Dashboard</h3><p class="text-sm text-slate-500">Prioritised by review dependency, weighted completion and SLA ageing.</p></div><table class="min-w-full text-sm"><thead><tr class="border-b text-left text-xs uppercase text-slate-500"><th class="px-3 py-2">Engagement</th><th class="px-3 py-2">Review Pending</th><th class="px-3 py-2">Weighted Progress</th><th class="px-3 py-2">SLA / Age</th><th class="px-3 py-2">Action</th></tr></thead><tbody>{% for row in engagement_review_queue %}<tr class="border-b border-slate-100"><td class="px-3 py-3"><div class="font-semibold">{{ row.client_name }}</div><div class="text-xs text-slate-500">{{ row.service_name }} · {{ row.financial_year }}</div></td><td class="px-3 py-3 font-semibold text-violet-700">{{ row.review_pending }}</td><td class="px-3 py-3">{{ row.progress_percent }}%</td><td class="px-3 py-3"><div class="{% if row.sla.status == 'breached' %}text-red-700{% else %}text-amber-700{% endif %}">{{ row.sla.label }}</div><div class="text-xs text-slate-500">{{ row.sla.age_days }} day(s)</div></td><td class="px-3 py-3"><a href="{{ row.href }}" class="af-btn af-btn-primary">Open Workflow</a></td></tr>{% else %}<tr><td colspan="5" class="px-3 py-8 text-center text-slate-500">No engagement review dependencies.</td></tr>{% endfor %}</tbody></table></section>
<section class="af-card overflow-x-auto"><div class="mb-4"><h3 class="text-lg font-semibold text-slate-900">Engagement Review Dashboard</h3><p class="text-sm text-slate-500">Prioritised by review dependency, weighted completion and SLA ageing.</p></div><table class="min-w-full text-sm"><thead><tr class="border-b text-left text-xs uppercase text-slate-500"><th class="px-3 py-2">Engagement</th><th class="px-3 py-2">Review Pending</th><th class="px-3 py-2">Weighted Progress</th><th class="px-3 py-2">SLA / Age</th><th class="px-3 py-2">Action</th></tr></thead><tbody>{% for row in engagement_review_queue %}<tr class="border-b border-slate-100"><td class="px-3 py-3"><div class="font-semibold">{{ row.client_name }}</div><div class="text-xs text-slate-500">{{ row.service_name }} · {{ row.financial_year }}</div></td><td class="px-3 py-3 font-semibold text-violet-700">{{ row.review_pending }}</td><td class="px-3 py-3">{{ row.progress_percent }}%</td><td class="px-3 py-3"><div class="{% if row.sla.status == 'breached' %}text-red-700{% else %}text-amber-700{% endif %}">{{ row.sla.label }}</div><div class="text-xs text-slate-500">{{ row.sla.age_days }} day(s)</div></td><td class="px-3 py-3"><a href="/manager/reviews/engagements/{{ row.subscription_id }}" class="af-btn af-btn-primary">Start / Continue Review</a></td></tr>{% else %}<tr><td colspan="5" class="px-3 py-8 text-center text-slate-500">No engagement review dependencies.</td></tr>{% endfor %}</tbody></table></section>
<div class="af-card"><div class="mb-4"><h3 class="text-lg font-semibold">Detailed Task Review Queue</h3><p class="text-sm text-slate-500">Existing task-level review workflow remains available.</p></div>{% set rows = review_queue %}{% include 'modules/manager_dashboard/templates/manager_dashboard/partials/_task_table.html' %}</div>
</div>