Split partner review board panels and add back button
This commit is contained in:
+1
@@ -19,6 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<a href="/partner/reviews" class="rounded-2xl border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">← Back to Review Board</a>
|
||||||
<a href="{{ workspace.documents_href }}" class="rounded-2xl bg-emerald-600 px-4 py-2 text-sm font-semibold text-white">Final Documents / UDIN</a>
|
<a href="{{ workspace.documents_href }}" class="rounded-2xl bg-emerald-600 px-4 py-2 text-sm font-semibold text-white">Final Documents / UDIN</a>
|
||||||
<a href="/partner/dashboard?tab=review" class="rounded-2xl border border-slate-300 px-4 py-2 text-sm font-semibold text-slate-700">Close Workspace</a>
|
<a href="/partner/dashboard?tab=review" class="rounded-2xl border border-slate-300 px-4 py-2 text-sm font-semibold text-slate-700">Close Workspace</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,136 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
{% include "modules/partners/templates/partners/_partner_tabs.html" %}
|
{% include "modules/partners/templates/partners/_partner_tabs.html" %}
|
||||||
|
|
||||||
|
{% macro engagement_row(task, column, payload) %}
|
||||||
|
{% set stats = namespace(total=0, completed=0, blocked=0, review=0, rework=0, overdue=false, assignees=[]) %}
|
||||||
|
{% for stat_column in payload.columns %}
|
||||||
|
{% for item in stat_column.tasks %}
|
||||||
|
{% if item.subscription_id == task.subscription_id %}
|
||||||
|
{% set stats.total = stats.total + 1 %}
|
||||||
|
{% if (item.status or '')|lower in ['completed', 'not_applicable', 'cancelled'] %}
|
||||||
|
{% set stats.completed = stats.completed + 1 %}
|
||||||
|
{% endif %}
|
||||||
|
{% if (item.status or '')|lower == 'blocked' %}
|
||||||
|
{% set stats.blocked = stats.blocked + 1 %}
|
||||||
|
{% endif %}
|
||||||
|
{% if stat_column.code == 'pending_review' %}
|
||||||
|
{% set stats.review = stats.review + 1 %}
|
||||||
|
{% endif %}
|
||||||
|
{% if stat_column.code == 'rework_sent' %}
|
||||||
|
{% set stats.rework = stats.rework + 1 %}
|
||||||
|
{% endif %}
|
||||||
|
{% if item.is_overdue %}
|
||||||
|
{% set stats.overdue = true %}
|
||||||
|
{% endif %}
|
||||||
|
{% if item.assignee_display and item.assignee_display not in stats.assignees %}
|
||||||
|
{% set _ = stats.assignees.append(item.assignee_display) %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<tr class="align-top hover:bg-slate-50/70">
|
||||||
|
<td class="px-4 py-4 text-sm">
|
||||||
|
<a href="/partner/reviews/engagements/{{ task.subscription_id }}" class="font-semibold text-slate-900 hover:text-brand-700">{{ task.client_display }}</a>
|
||||||
|
<div class="mt-1 text-xs text-slate-500">{{ task.engagement_label }}</div>
|
||||||
|
<div class="mt-1 text-[11px] text-slate-400">Engagement #{{ task.subscription_id }}</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-4 text-sm">
|
||||||
|
{% if column.code == 'pending_review' %}
|
||||||
|
<span class="rounded-full bg-amber-100 px-2.5 py-1 text-xs font-semibold text-amber-700">Pending Review</span>
|
||||||
|
{% elif column.code == 'clarification_required' %}
|
||||||
|
<span class="rounded-full bg-red-100 px-2.5 py-1 text-xs font-semibold text-red-700">Clarification Required</span>
|
||||||
|
{% elif column.code == 'rework_sent' %}
|
||||||
|
<span class="rounded-full bg-orange-100 px-2.5 py-1 text-xs font-semibold text-orange-700">Rework Sent</span>
|
||||||
|
{% elif column.code == 'approved' %}
|
||||||
|
<span class="rounded-full bg-blue-100 px-2.5 py-1 text-xs font-semibold text-blue-700">In Progress</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="rounded-full bg-emerald-100 px-2.5 py-1 text-xs font-semibold text-emerald-700">Completed</span>
|
||||||
|
{% endif %}
|
||||||
|
{% if stats.overdue %}
|
||||||
|
<div class="mt-2"><span class="rounded-full bg-red-50 px-2 py-1 text-[11px] font-semibold text-red-700">Overdue item</span></div>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-4 text-sm">
|
||||||
|
<div class="font-semibold text-slate-900">{{ stats.completed }} / {{ stats.total }}</div>
|
||||||
|
<div class="mt-1 text-xs text-slate-500">closed task{{ '' if stats.completed == 1 else 's' }} / visible task{{ '' if stats.total == 1 else 's' }}</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-4 text-sm">
|
||||||
|
<div class="flex flex-wrap gap-1.5">
|
||||||
|
{% if stats.review %}<span class="rounded-full bg-amber-50 px-2 py-1 text-[11px] font-semibold text-amber-700">{{ stats.review }} review</span>{% endif %}
|
||||||
|
{% if stats.blocked %}<span class="rounded-full bg-red-50 px-2 py-1 text-[11px] font-semibold text-red-700">{{ stats.blocked }} blocked</span>{% endif %}
|
||||||
|
{% if stats.rework %}<span class="rounded-full bg-orange-50 px-2 py-1 text-[11px] font-semibold text-orange-700">{{ stats.rework }} rework</span>{% endif %}
|
||||||
|
{% if not stats.review and not stats.blocked and not stats.rework %}<span class="text-xs text-slate-500">No review exception</span>{% endif %}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-4 text-sm text-slate-700">
|
||||||
|
{% for assignee in stats.assignees[:3] %}
|
||||||
|
<div>{{ assignee }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% if stats.assignees|length > 3 %}
|
||||||
|
<div class="mt-1 text-xs text-slate-500">+{{ stats.assignees|length - 3 }} more</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if not stats.assignees %}<span class="text-slate-500">Unassigned</span>{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-4 text-sm">
|
||||||
|
<div class="text-xs font-semibold text-slate-700">{{ task.priority_label }}</div>
|
||||||
|
<div class="mt-1 text-xs text-slate-500">{% if task.internal_target_date %}Target {{ task.internal_target_date }}{% else %}No task target{% endif %}</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-4 text-right">
|
||||||
|
<a href="/partner/reviews/engagements/{{ task.subscription_id }}" class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-semibold text-slate-700 hover:bg-slate-50">Open Review</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro panel(title, subtitle, ids, payload, tone) %}
|
||||||
|
<section class="overflow-hidden rounded-3xl border border-slate-200 bg-white shadow-soft">
|
||||||
|
<div class="flex flex-col gap-2 border-b border-slate-200 px-5 py-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-base font-semibold text-slate-900">{{ title }}</h3>
|
||||||
|
<p class="mt-1 text-xs text-slate-500">{{ subtitle }}</p>
|
||||||
|
</div>
|
||||||
|
{% if tone == 'review' %}
|
||||||
|
<span class="w-fit rounded-full bg-amber-100 px-3 py-1 text-xs font-semibold text-amber-700">{{ ids|length }} engagement{{ '' if ids|length == 1 else 's' }}</span>
|
||||||
|
{% elif tone == 'progress' %}
|
||||||
|
<span class="w-fit rounded-full bg-blue-100 px-3 py-1 text-xs font-semibold text-blue-700">{{ ids|length }} engagement{{ '' if ids|length == 1 else 's' }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="w-fit rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-700">{{ ids|length }} engagement{{ '' if ids|length == 1 else 's' }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="min-w-full divide-y divide-slate-200">
|
||||||
|
<thead class="bg-slate-50">
|
||||||
|
<tr>
|
||||||
|
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Client / Engagement</th>
|
||||||
|
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Status</th>
|
||||||
|
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Task Progress</th>
|
||||||
|
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Review Items</th>
|
||||||
|
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Assigned Team</th>
|
||||||
|
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Priority / Target</th>
|
||||||
|
<th class="px-4 py-3 text-right text-xs font-semibold uppercase tracking-wide text-slate-500">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-slate-100">
|
||||||
|
{% set rendered = namespace(seen=[]) %}
|
||||||
|
{% for column in payload.columns %}
|
||||||
|
{% for task in column.tasks %}
|
||||||
|
{% if task.subscription_id and task.subscription_id in ids and task.subscription_id not in rendered.seen %}
|
||||||
|
{% set _ = rendered.seen.append(task.subscription_id) %}
|
||||||
|
{{ engagement_row(task, column, payload) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if rendered.seen|length == 0 %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="7" class="px-6 py-10 text-center text-sm text-slate-500">No engagements in this section.</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div class="flex flex-col gap-3 rounded-3xl border border-slate-200 bg-white p-5 shadow-soft lg:flex-row lg:items-center lg:justify-between">
|
<div class="flex flex-col gap-3 rounded-3xl border border-slate-200 bg-white p-5 shadow-soft lg:flex-row lg:items-center lg:justify-between">
|
||||||
<div>
|
<div>
|
||||||
@@ -14,7 +144,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Build engagement-level counts without changing the existing backend payload. #}
|
|
||||||
{% set metrics = namespace(pending_review=[], clarification_required=[], rework_sent=[], approved=[], completed=[]) %}
|
{% set metrics = namespace(pending_review=[], clarification_required=[], rework_sent=[], approved=[], completed=[]) %}
|
||||||
{% for column in payload.columns %}
|
{% for column in payload.columns %}
|
||||||
{% for task in column.tasks %}
|
{% for task in column.tasks %}
|
||||||
@@ -62,117 +191,61 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="overflow-hidden rounded-3xl border border-slate-200 bg-white shadow-soft">
|
{# Each engagement appears in exactly one panel: review first, then in progress, then completed. #}
|
||||||
<div class="overflow-x-auto">
|
{% set groups = namespace(review=[], progress=[], completed=[]) %}
|
||||||
<table class="min-w-full divide-y divide-slate-200">
|
{% for column in payload.columns %}
|
||||||
<thead class="bg-slate-50">
|
{% if column.code in ['pending_review', 'clarification_required', 'rework_sent'] %}
|
||||||
<tr>
|
{% for task in column.tasks %}
|
||||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Client / Engagement</th>
|
{% if task.subscription_id and task.subscription_id not in groups.review %}
|
||||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Status</th>
|
{% set _ = groups.review.append(task.subscription_id) %}
|
||||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Task Progress</th>
|
{% endif %}
|
||||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Review Items</th>
|
{% endfor %}
|
||||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Assigned Team</th>
|
{% endif %}
|
||||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Priority / Target</th>
|
{% endfor %}
|
||||||
<th class="px-4 py-3 text-right text-xs font-semibold uppercase tracking-wide text-slate-500">Action</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="divide-y divide-slate-100">
|
|
||||||
{% set board = namespace(seen=[]) %}
|
|
||||||
{% for column in payload.columns %}
|
|
||||||
{% for task in column.tasks %}
|
|
||||||
{% if task.subscription_id and task.subscription_id not in board.seen %}
|
|
||||||
{% set _ = board.seen.append(task.subscription_id) %}
|
|
||||||
{% set stats = namespace(total=0, completed=0, blocked=0, review=0, rework=0, overdue=false, assignees=[]) %}
|
|
||||||
{% for stat_column in payload.columns %}
|
|
||||||
{% for item in stat_column.tasks %}
|
|
||||||
{% if item.subscription_id == task.subscription_id %}
|
|
||||||
{% set stats.total = stats.total + 1 %}
|
|
||||||
{% if (item.status or '')|lower in ['completed', 'not_applicable', 'cancelled'] %}
|
|
||||||
{% set stats.completed = stats.completed + 1 %}
|
|
||||||
{% endif %}
|
|
||||||
{% if (item.status or '')|lower == 'blocked' %}
|
|
||||||
{% set stats.blocked = stats.blocked + 1 %}
|
|
||||||
{% endif %}
|
|
||||||
{% if stat_column.code == 'pending_review' %}
|
|
||||||
{% set stats.review = stats.review + 1 %}
|
|
||||||
{% endif %}
|
|
||||||
{% if stat_column.code == 'rework_sent' %}
|
|
||||||
{% set stats.rework = stats.rework + 1 %}
|
|
||||||
{% endif %}
|
|
||||||
{% if item.is_overdue %}
|
|
||||||
{% set stats.overdue = true %}
|
|
||||||
{% endif %}
|
|
||||||
{% if item.assignee_display and item.assignee_display not in stats.assignees %}
|
|
||||||
{% set _ = stats.assignees.append(item.assignee_display) %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<tr class="align-top hover:bg-slate-50/70">
|
{% for column in payload.columns %}
|
||||||
<td class="px-4 py-4 text-sm">
|
{% if column.code == 'approved' %}
|
||||||
<a href="/partner/reviews/engagements/{{ task.subscription_id }}" class="font-semibold text-slate-900 hover:text-brand-700">{{ task.client_display }}</a>
|
{% for task in column.tasks %}
|
||||||
<div class="mt-1 text-xs text-slate-500">{{ task.engagement_label }}</div>
|
{% if task.subscription_id and task.subscription_id not in groups.review and task.subscription_id not in groups.progress %}
|
||||||
<div class="mt-1 text-[11px] text-slate-400">Engagement #{{ task.subscription_id }}</div>
|
{% set _ = groups.progress.append(task.subscription_id) %}
|
||||||
</td>
|
{% endif %}
|
||||||
<td class="px-4 py-4 text-sm">
|
{% endfor %}
|
||||||
{% if column.code == 'pending_review' %}
|
{% endif %}
|
||||||
<span class="rounded-full bg-amber-100 px-2.5 py-1 text-xs font-semibold text-amber-700">Pending Review</span>
|
{% endfor %}
|
||||||
{% elif column.code == 'clarification_required' %}
|
|
||||||
<span class="rounded-full bg-red-100 px-2.5 py-1 text-xs font-semibold text-red-700">Clarification Required</span>
|
|
||||||
{% elif column.code == 'rework_sent' %}
|
|
||||||
<span class="rounded-full bg-orange-100 px-2.5 py-1 text-xs font-semibold text-orange-700">Rework Sent</span>
|
|
||||||
{% elif column.code == 'approved' %}
|
|
||||||
<span class="rounded-full bg-blue-100 px-2.5 py-1 text-xs font-semibold text-blue-700">In Progress</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="rounded-full bg-emerald-100 px-2.5 py-1 text-xs font-semibold text-emerald-700">Completed</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if stats.overdue %}
|
|
||||||
<div class="mt-2"><span class="rounded-full bg-red-50 px-2 py-1 text-[11px] font-semibold text-red-700">Overdue item</span></div>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-4 text-sm">
|
|
||||||
<div class="font-semibold text-slate-900">{{ stats.completed }} / {{ stats.total }}</div>
|
|
||||||
<div class="mt-1 text-xs text-slate-500">closed task{{ '' if stats.completed == 1 else 's' }} / visible task{{ '' if stats.total == 1 else 's' }}</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-4 text-sm">
|
|
||||||
<div class="flex flex-wrap gap-1.5">
|
|
||||||
{% if stats.review %}<span class="rounded-full bg-amber-50 px-2 py-1 text-[11px] font-semibold text-amber-700">{{ stats.review }} review</span>{% endif %}
|
|
||||||
{% if stats.blocked %}<span class="rounded-full bg-red-50 px-2 py-1 text-[11px] font-semibold text-red-700">{{ stats.blocked }} blocked</span>{% endif %}
|
|
||||||
{% if stats.rework %}<span class="rounded-full bg-orange-50 px-2 py-1 text-[11px] font-semibold text-orange-700">{{ stats.rework }} rework</span>{% endif %}
|
|
||||||
{% if not stats.review and not stats.blocked and not stats.rework %}<span class="text-xs text-slate-500">No review exception</span>{% endif %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-4 text-sm text-slate-700">
|
|
||||||
{% for assignee in stats.assignees[:3] %}
|
|
||||||
<div>{{ assignee }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% if stats.assignees|length > 3 %}
|
|
||||||
<div class="mt-1 text-xs text-slate-500">+{{ stats.assignees|length - 3 }} more</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if not stats.assignees %}<span class="text-slate-500">Unassigned</span>{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-4 text-sm">
|
|
||||||
<div class="text-xs font-semibold text-slate-700">{{ task.priority_label }}</div>
|
|
||||||
<div class="mt-1 text-xs text-slate-500">{% if task.internal_target_date %}Target {{ task.internal_target_date }}{% else %}No task target{% endif %}</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-4 text-right">
|
|
||||||
<a href="/partner/reviews/engagements/{{ task.subscription_id }}" class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-semibold text-slate-700 hover:bg-slate-50">Open Review</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% if board.seen|length == 0 %}
|
{% for column in payload.columns %}
|
||||||
<tr>
|
{% if column.code == 'completed' %}
|
||||||
<td colspan="7" class="px-6 py-12 text-center text-sm text-slate-500">No engagement review items found.</td>
|
{% for task in column.tasks %}
|
||||||
</tr>
|
{% if task.subscription_id and task.subscription_id not in groups.review and task.subscription_id not in groups.progress and task.subscription_id not in groups.completed %}
|
||||||
{% endif %}
|
{% set _ = groups.completed.append(task.subscription_id) %}
|
||||||
</tbody>
|
{% endif %}
|
||||||
</table>
|
{% endfor %}
|
||||||
</div>
|
{% endif %}
|
||||||
</section>
|
{% endfor %}
|
||||||
|
|
||||||
|
{{ panel(
|
||||||
|
'Review Required',
|
||||||
|
'Pending review, clarification-required and rework engagements are shown first.',
|
||||||
|
groups.review,
|
||||||
|
payload,
|
||||||
|
'review'
|
||||||
|
) }}
|
||||||
|
|
||||||
|
{{ panel(
|
||||||
|
'In Progress',
|
||||||
|
'Engagements currently moving with the team and not presently waiting in the review queue.',
|
||||||
|
groups.progress,
|
||||||
|
payload,
|
||||||
|
'progress'
|
||||||
|
) }}
|
||||||
|
|
||||||
|
{{ panel(
|
||||||
|
'Completed',
|
||||||
|
'Completed engagement rows remain available separately for review history and reference.',
|
||||||
|
groups.completed,
|
||||||
|
payload,
|
||||||
|
'completed'
|
||||||
|
) }}
|
||||||
|
|
||||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-xs text-slate-500">
|
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-xs text-slate-500">
|
||||||
Review remains task-level inside the engagement. This board only groups the existing task records into one engagement row for easier partner navigation.
|
Review remains task-level inside the engagement. This board only groups the existing task records into one engagement row for easier partner navigation.
|
||||||
|
|||||||
Reference in New Issue
Block a user