Upgrade manager dashboard and wizards to v2
This commit is contained in:
@@ -101,6 +101,28 @@ def _status_label(status: str | None) -> str:
|
||||
return (status or "pending").replace("_", " ").replace("-", " ").title()
|
||||
|
||||
|
||||
|
||||
|
||||
def _days_overdue(due_date) -> int:
|
||||
if not due_date:
|
||||
return 0
|
||||
delta = (date.today() - due_date).days
|
||||
return delta if delta > 0 else 0
|
||||
|
||||
|
||||
def _age_bucket(days: int) -> str:
|
||||
if days >= 30:
|
||||
return "30+ days"
|
||||
if days >= 15:
|
||||
return "15-29 days"
|
||||
if days >= 8:
|
||||
return "8-14 days"
|
||||
if days >= 4:
|
||||
return "4-7 days"
|
||||
if days >= 1:
|
||||
return "1-3 days"
|
||||
return "Current"
|
||||
|
||||
def _task_href(task: ClientServiceTaskInstance) -> str:
|
||||
task_id = getattr(task, "id", None)
|
||||
if task_id:
|
||||
@@ -136,6 +158,8 @@ def _task_row(task: ClientServiceTaskInstance) -> dict[str, Any]:
|
||||
"comment_count": len([c for c in getattr(task, "comments", []) if not getattr(c, "is_deleted", False)]),
|
||||
"is_overdue": bool(due_date and due_date < today and _is_open(status)),
|
||||
"is_due_today": bool(due_date and due_date == today and _is_open(status)),
|
||||
"days_overdue": _days_overdue(due_date) if _is_open(status) else 0,
|
||||
"age_bucket": _age_bucket(_days_overdue(due_date)) if _is_open(status) else "Closed",
|
||||
"href": _task_href(task),
|
||||
}
|
||||
|
||||
@@ -239,9 +263,10 @@ def _client_rows(db: Session, scope, task_rows: list[dict[str, Any]]) -> list[di
|
||||
def _report_cards() -> list[dict[str, str]]:
|
||||
return [
|
||||
{"group": "Execution", "title": "Team Work Status", "desc": "Open, overdue, in-progress and blocked assignments for the active branch/team.", "href": "/manager/dashboard?tab=team-work"},
|
||||
{"group": "Execution", "title": "Due Today / This Week", "desc": "Work requiring immediate attention and allocation follow-up.", "href": "/manager/dashboard?tab=team-work"},
|
||||
{"group": "Due Control", "title": "Due Calendar", "desc": "Due today, due this week and overdue work in one control view.", "href": "/manager/dashboard?tab=due-calendar"},
|
||||
{"group": "Review", "title": "Manager Review Queue", "desc": "Completed or review-ready work waiting for manager action.", "href": "/manager/dashboard?tab=review-queue"},
|
||||
{"group": "Client Follow-up", "title": "Client Pending Report", "desc": "Tasks blocked due to documents, clarification or client data pending.", "href": "/manager/dashboard?tab=client-pending"},
|
||||
{"group": "Escalation", "title": "Escalation Register", "desc": "Unassigned, overdue and aged client-pending items requiring manager intervention.", "href": "/manager/dashboard?tab=escalations"},
|
||||
{"group": "Team", "title": "Staff Workload Report", "desc": "Staff-wise active, overdue, review and client-pending workload.", "href": "/manager/dashboard?tab=team-work"},
|
||||
{"group": "Documents", "title": "Document Checklist Report", "desc": "Document-related blocked tasks and communication timeline shortcuts.", "href": "/manager/dashboard?tab=documents"},
|
||||
]
|
||||
@@ -249,10 +274,12 @@ def _report_cards() -> list[dict[str, str]]:
|
||||
|
||||
def _wizard_cards() -> list[dict[str, str]]:
|
||||
return [
|
||||
{"title": "Task Assignment Wizard", "desc": "Open the detailed allocation board to assign or reassign staff.", "href": "/manager/work"},
|
||||
{"title": "Review Wizard", "desc": "Review completed work and send corrections through the task timeline.", "href": "/manager/dashboard?tab=review-queue"},
|
||||
{"title": "Client Query Wizard", "desc": "Handle client-pending tasks and record clarification requirements.", "href": "/manager/dashboard?tab=client-pending"},
|
||||
{"title": "Document Checklist Wizard", "desc": "Verify pending documents and open engagement document storage.", "href": "/documents"},
|
||||
{"title": "Task Assignment Wizard", "desc": "Allocate unassigned tasks and rebalance heavy workload using the existing manager work board.", "href": "/manager/work"},
|
||||
{"title": "Due Control Wizard", "desc": "Review due today, due this week and overdue items before escalation.", "href": "/manager/dashboard?tab=due-calendar"},
|
||||
{"title": "Review Wizard", "desc": "Open review-ready tasks and proceed through the existing task communication timeline.", "href": "/manager/dashboard?tab=review-queue"},
|
||||
{"title": "Client Query Wizard", "desc": "Handle client-pending items and record clarification/document requirements.", "href": "/manager/dashboard?tab=client-pending"},
|
||||
{"title": "Document Checklist Wizard", "desc": "Verify document-related blocked tasks and open the existing documents area.", "href": "/documents"},
|
||||
{"title": "Escalation Wizard", "desc": "Review aged overdue, unassigned and client-pending items requiring partner/branch attention.", "href": "/manager/dashboard?tab=escalations"},
|
||||
{"title": "Work Closure Wizard", "desc": "Open engagement progress and close work after review completion.", "href": "/employees/progress"},
|
||||
{"title": "Alert / Follow-up Wizard", "desc": "Open alerts for reminders, escalations and follow-up actions.", "href": "/alerts"},
|
||||
]
|
||||
@@ -279,6 +306,26 @@ def build_manager_dashboard_payload(db: Session, request, current_user) -> dict[
|
||||
|
||||
tenant = getattr(scope, "tenant", None)
|
||||
branch = getattr(scope, "branch", None)
|
||||
age_buckets = [
|
||||
{"label": "1-3 days", "count": len([r for r in overdue_rows if r.get("age_bucket") == "1-3 days"])},
|
||||
{"label": "4-7 days", "count": len([r for r in overdue_rows if r.get("age_bucket") == "4-7 days"])},
|
||||
{"label": "8-14 days", "count": len([r for r in overdue_rows if r.get("age_bucket") == "8-14 days"])},
|
||||
{"label": "15-29 days", "count": len([r for r in overdue_rows if r.get("age_bucket") == "15-29 days"])},
|
||||
{"label": "30+ days", "count": len([r for r in overdue_rows if r.get("age_bucket") == "30+ days"])},
|
||||
]
|
||||
|
||||
status_summary: dict[str, int] = {}
|
||||
service_summary: dict[str, int] = {}
|
||||
for row in open_rows:
|
||||
status_summary[row.get("status_label") or "Pending"] = status_summary.get(row.get("status_label") or "Pending", 0) + 1
|
||||
service_summary[row.get("service_name") or "Service"] = service_summary.get(row.get("service_name") or "Service", 0) + 1
|
||||
|
||||
escalation_rows = sorted(
|
||||
(unassigned_rows + overdue_rows + client_pending_rows),
|
||||
key=lambda r: (r.get("days_overdue") or 0, r.get("is_overdue") or False, r.get("due_date") or date.max),
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
overview = {
|
||||
"tenant": tenant,
|
||||
"branch": branch,
|
||||
@@ -296,6 +343,7 @@ def build_manager_dashboard_payload(db: Session, request, current_user) -> dict[
|
||||
"review_pending_count": len(review_rows),
|
||||
"staff_count": len(staff_rows),
|
||||
"client_count": len(client_rows),
|
||||
"escalation_count": len(escalation_rows),
|
||||
"today": today,
|
||||
}
|
||||
|
||||
@@ -311,6 +359,10 @@ def build_manager_dashboard_payload(db: Session, request, current_user) -> dict[
|
||||
"review_queue": review_rows[:60],
|
||||
"client_pending": client_pending_rows[:60],
|
||||
"documents_pending": client_pending_rows[:40],
|
||||
"escalations": escalation_rows[:80],
|
||||
"age_buckets": age_buckets,
|
||||
"status_summary": sorted([{"label": k, "count": v} for k, v in status_summary.items()], key=lambda x: x["count"], reverse=True)[:10],
|
||||
"service_summary": sorted([{"label": k, "count": v} for k, v in service_summary.items()], key=lambda x: x["count"], reverse=True)[:10],
|
||||
"staff_rows": staff_rows,
|
||||
"clients": client_rows,
|
||||
"reports": _report_cards(),
|
||||
|
||||
Reference in New Issue
Block a user