106 lines
9.3 KiB
HTML
106 lines
9.3 KiB
HTML
{% extends "ui/templates/base/layout.html" %}
|
||
{% block content %}
|
||
{% include "modules/managers/templates/managers/_manager_tabs.html" %}
|
||
<div class="space-y-6">
|
||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||
<div>
|
||
<h1 class="text-2xl font-semibold text-slate-900">Engagement Progress</h1>
|
||
<p class="mt-1 text-sm text-slate-500">Client-wise and engagement-wise progress based on existing service task instances.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid gap-4 md:grid-cols-4 xl:grid-cols-8">
|
||
<div class="rounded-2xl border border-slate-200 bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Clients</div><div class="mt-1 text-2xl font-semibold">{{ progress_payload.summary.clients }}</div></div>
|
||
<div class="rounded-2xl border border-slate-200 bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Engagements</div><div class="mt-1 text-2xl font-semibold">{{ progress_payload.summary.engagements }}</div></div>
|
||
<div class="rounded-2xl border border-slate-200 bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Tasks</div><div class="mt-1 text-2xl font-semibold">{{ progress_payload.summary.tasks }}</div></div>
|
||
<div class="rounded-2xl border border-blue-200 bg-blue-50 p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-blue-600">Avg Progress</div><div class="mt-1 text-2xl font-semibold text-blue-700">{{ progress_payload.summary.average_progress }}%</div></div>
|
||
<div class="rounded-2xl border border-slate-200 bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Open</div><div class="mt-1 text-2xl font-semibold">{{ progress_payload.summary.open }}</div></div>
|
||
<div class="rounded-2xl border border-red-200 bg-red-50 p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-red-600">Overdue</div><div class="mt-1 text-2xl font-semibold text-red-700">{{ progress_payload.summary.overdue }}</div></div>
|
||
<div class="rounded-2xl border border-amber-200 bg-amber-50 p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-amber-600">Due Today</div><div class="mt-1 text-2xl font-semibold text-amber-700">{{ progress_payload.summary.due_today }}</div></div>
|
||
<div class="rounded-2xl border border-emerald-200 bg-emerald-50 p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-emerald-600">Completed</div><div class="mt-1 text-2xl font-semibold text-emerald-700">{{ progress_payload.summary.completed }}</div></div>
|
||
</div>
|
||
|
||
<form method="get" action="/employees/progress" class="rounded-2xl border border-slate-200 bg-white p-4 shadow-soft">
|
||
<div class="grid gap-3 md:grid-cols-[1fr_180px_240px_auto]">
|
||
<input type="search" name="q" value="{{ q or '' }}" placeholder="Search client, engagement or task" class="rounded-xl border border-slate-300 px-3 py-2 text-sm focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-100">
|
||
<select name="status" class="rounded-xl border border-slate-300 px-3 py-2 text-sm">
|
||
<option value="open" {% if status == 'open' %}selected{% endif %}>Open engagements</option>
|
||
<option value="all" {% if status == 'all' %}selected{% endif %}>All tasks</option>
|
||
<option value="overdue" {% if status == 'overdue' %}selected{% endif %}>Overdue only</option>
|
||
<option value="due_today" {% if status == 'due_today' %}selected{% endif %}>Due today</option>
|
||
<option value="closed" {% if status == 'closed' %}selected{% endif %}>Closed tasks</option>
|
||
<option value="pending" {% if status == 'pending' %}selected{% endif %}>Pending</option>
|
||
<option value="in_progress" {% if status == 'in_progress' %}selected{% endif %}>In Progress</option>
|
||
<option value="blocked" {% if status == 'blocked' %}selected{% endif %}>Blocked</option>
|
||
<option value="completed" {% if status == 'completed' %}selected{% endif %}>Completed</option>
|
||
</select>
|
||
<select name="assigned_to_user_id" class="rounded-xl border border-slate-300 px-3 py-2 text-sm">
|
||
<option value="0">All assignees</option>
|
||
{% for user in assignable_users %}
|
||
<option value="{{ user.id }}" {% if selected_assigned_to_user_id == user.id %}selected{% endif %}>{{ user.full_name or user.email }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
<button type="submit" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-700">Filter</button>
|
||
</div>
|
||
</form>
|
||
|
||
<div class="space-y-4">
|
||
{% for client_group in progress_payload.clients %}
|
||
<details class="group rounded-2xl border border-slate-200 bg-white shadow-soft" {% if loop.first %}open{% endif %}>
|
||
<summary class="flex cursor-pointer list-none flex-wrap items-center justify-between gap-4 rounded-2xl px-5 py-4 hover:bg-slate-50">
|
||
<div>
|
||
<div class="text-lg font-semibold text-slate-900">{{ client_group.client_name }}</div>
|
||
<div class="text-xs text-slate-500">{{ client_group.client_code or 'No client code' }} · {{ client_group.engagements|length }} engagement{{ '' if client_group.engagements|length == 1 else 's' }} · {{ client_group.task_count }} task{{ '' if client_group.task_count == 1 else 's' }}</div>
|
||
</div>
|
||
<div class="min-w-[240px]">
|
||
<div class="flex justify-between text-xs font-semibold text-slate-500"><span>Client progress</span><span>{{ client_group.progress_percent }}%</span></div>
|
||
<div class="mt-1 h-2 rounded-full bg-slate-100"><div class="h-2 rounded-full bg-brand-600" style="width: {{ client_group.progress_percent }}%"></div></div>
|
||
<div class="mt-1 text-xs text-slate-500">Open {{ client_group.open_count }} · Completed {{ client_group.completed_count }} · Overdue {{ client_group.overdue_count }}</div>
|
||
</div>
|
||
<span class="text-lg text-slate-400 transition group-open:rotate-90">›</span>
|
||
</summary>
|
||
|
||
<div class="space-y-3 border-t border-slate-100 p-4">
|
||
{% for engagement_group in client_group.engagements %}
|
||
<details class="group/eng rounded-xl border border-slate-200 bg-slate-50" {% if engagement_group.overdue_count > 0 or loop.first %}open{% endif %}>
|
||
<summary class="flex cursor-pointer list-none flex-wrap items-center justify-between gap-4 px-4 py-3 hover:bg-slate-100">
|
||
<div>
|
||
<div class="font-semibold text-slate-900">{{ engagement_group.label }}</div>
|
||
<div class="text-xs text-slate-500">Status: {{ engagement_group.status.replace('_',' ').title() }} · Due: {{ engagement_group.due_date or '-' }}</div>
|
||
</div>
|
||
<div class="min-w-[260px]">
|
||
<div class="flex justify-between text-xs font-semibold text-slate-500"><span>{{ engagement_group.progress_status.replace('_',' ').title() }}</span><span>{{ engagement_group.progress_percent }}%</span></div>
|
||
<div class="mt-1 h-2 rounded-full bg-slate-200"><div class="h-2 rounded-full {% if engagement_group.progress_status == 'completed' %}bg-emerald-600{% elif engagement_group.progress_status == 'overdue' %}bg-red-600{% else %}bg-brand-600{% endif %}" style="width: {{ engagement_group.progress_percent }}%"></div></div>
|
||
<div class="mt-1 text-xs text-slate-500">Open {{ engagement_group.open_count }} · Completed {{ engagement_group.completed_count }} · Unassigned {{ engagement_group.unassigned_count }}</div>
|
||
</div>
|
||
<span class="text-slate-400 transition group-open/eng:rotate-90">›</span>
|
||
</summary>
|
||
<div class="overflow-x-auto border-t border-slate-200">
|
||
<table class="min-w-full divide-y divide-slate-200 text-sm">
|
||
<thead class="bg-slate-100 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">
|
||
<tr><th class="px-4 py-3">Task</th><th class="px-4 py-3">Assignee</th><th class="px-4 py-3">Status</th><th class="px-4 py-3">Priority</th><th class="px-4 py-3">Target</th></tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-slate-100 bg-white">
|
||
{% for task in engagement_group.tasks %}
|
||
<tr class="{% if task.is_overdue %}bg-red-50{% elif task.is_due_today %}bg-amber-50{% endif %}">
|
||
<td class="px-4 py-3"><div class="font-medium text-slate-900">{{ task.task_name }}</div>{% if task.description %}<div class="mt-1 text-xs text-slate-500">{{ task.description }}</div>{% endif %}</td>
|
||
<td class="px-4 py-3">{{ task.assigned_to.full_name if task.assigned_to else 'Unassigned' }}</td>
|
||
<td class="px-4 py-3"><span class="rounded-full bg-slate-100 px-2.5 py-1 text-xs font-semibold text-slate-700">{{ task.status_label }}</span></td>
|
||
<td class="px-4 py-3">{{ task.priority_label }}</td>
|
||
<td class="px-4 py-3">{{ task.internal_target_date or '-' }}<div class="text-xs text-slate-500">{{ task.date_bucket }}</div></td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</details>
|
||
{% endfor %}
|
||
</div>
|
||
</details>
|
||
{% else %}
|
||
<div class="rounded-2xl border border-slate-200 bg-white p-8 text-center shadow-soft"><h3 class="font-semibold text-slate-900">No progress data found</h3><p class="mt-1 text-sm text-slate-500">No engagement task matches the selected filter.</p></div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|