diff --git a/app/modules/unified_reports/__init__.py b/app/modules/unified_reports/__init__.py index 9e2c161..5ad7d12 100644 --- a/app/modules/unified_reports/__init__.py +++ b/app/modules/unified_reports/__init__.py @@ -1 +1 @@ -"""Unified role-aware reports centre.""" +"""Unified role-aware reports centre UI.""" diff --git a/app/modules/unified_reports/service.py b/app/modules/unified_reports/service.py index 31f2e93..cc92ef8 100644 --- a/app/modules/unified_reports/service.py +++ b/app/modules/unified_reports/service.py @@ -10,6 +10,7 @@ MANAGER = {"Manager", "Branch Manager"} STAFF = {"Staff", "Employee"} CLIENT = {"Client"} CONSULTANT = {"Consultant"} +ALL_DASHBOARD_ROLES = SYSTEM_ADMIN | FIRM_ADMIN | PARTNER | MANAGER | STAFF | CLIENT | CONSULTANT @dataclass(frozen=True) @@ -20,10 +21,12 @@ class ReportCard: group: str roles: tuple[str, ...] badge: str = "Open" + area: str = "General" + importance: int = 50 def _role_set(roles: Iterable[str] | None) -> set[str]: - return {str(r).strip() for r in (roles or []) if str(r).strip()} + return {str(role).strip() for role in (roles or []) if str(role).strip()} def _has_any(user_roles: set[str], allowed: set[str]) -> bool: @@ -33,7 +36,7 @@ def _has_any(user_roles: set[str], allowed: set[str]) -> bool: def can_access_reports(roles: Iterable[str] | None, permissions: Iterable[str] | None = None) -> bool: role_set = _role_set(roles) permission_set = set(permissions or []) - if role_set.intersection(SYSTEM_ADMIN | FIRM_ADMIN | PARTNER | MANAGER | STAFF | CLIENT | CONSULTANT): + if role_set.intersection(ALL_DASHBOARD_ROLES): return True return bool(permission_set.intersection({ "system.settings.view", @@ -44,77 +47,86 @@ def can_access_reports(roles: Iterable[str] | None, permissions: Iterable[str] | "employees.ess.view", "employees.work.view_self", "consultants.view", + "alerts.view", })) def _all_cards() -> list[ReportCard]: return [ # System Admin - ReportCard("Firm Setup Completeness", "Review firm, branch, FY, admin invite, services and branding readiness.", "/system-admin/dashboard?tab=firm-setup-health", "system", ("System Admin",), "Dashboard"), - ReportCard("Firm List / Tenant Report", "Open platform firm list with branch, user and setup summary.", "/system-admin/dashboard?tab=firms", "system", ("System Admin",), "Dashboard"), - ReportCard("Service Catalogue Readiness", "Check service categories, catalogue items and default task template readiness.", "/system-admin/dashboard?tab=catalogue", "system", ("System Admin",), "Dashboard"), - ReportCard("Platform SMTP Status", "Review platform SMTP configuration used for firm invites and system emails.", "/system-admin/dashboard?tab=smtp", "system", ("System Admin",), "Dashboard"), - ReportCard("Storage Status", "Review persistent storage, branding storage and backup storage readiness.", "/system-admin/dashboard?tab=storage", "system", ("System Admin",), "Dashboard"), - ReportCard("Platform Audit Logs", "Open audit log trail for platform-level actions.", "/system-admin/dashboard?tab=audit-logs", "audit", ("System Admin",), "Dashboard"), - ReportCard("Platform Billing Readiness", "Review platform billing and subscription readiness indicators.", "/system-admin/dashboard?tab=billing", "billing", ("System Admin",), "Dashboard"), + ReportCard("Platform Overview", "Open platform KPIs, firm status, SMTP, storage and setup indicators.", "/system-admin/dashboard", "system", ("System Admin",), "Dashboard", "Platform", 5), + ReportCard("Firm Setup Completeness", "Review firm, branch, FY, admin invite, services and branding readiness.", "/system-admin/dashboard?tab=firm-setup-health", "system", ("System Admin",), "Dashboard", "Setup", 10), + ReportCard("Firm List / Tenant Report", "Open platform firm list with branch, user and setup summary.", "/system-admin/dashboard?tab=firms", "system", ("System Admin",), "Dashboard", "Platform", 20), + ReportCard("Service Catalogue Readiness", "Check service categories, catalogue items and default task template readiness.", "/system-admin/dashboard?tab=catalogue", "system", ("System Admin",), "Dashboard", "Services", 30), + ReportCard("Platform SMTP Status", "Review platform SMTP configuration used for firm invites and system emails.", "/system-admin/dashboard?tab=smtp", "system", ("System Admin",), "Dashboard", "Email", 35), + ReportCard("Storage Status", "Review persistent storage, branding storage and backup storage readiness.", "/system-admin/dashboard?tab=storage", "system", ("System Admin",), "Dashboard", "Storage", 40), + ReportCard("Platform Billing Readiness", "Review platform billing and subscription readiness indicators.", "/system-admin/dashboard?tab=billing", "billing", ("System Admin",), "Dashboard", "Billing", 45), + ReportCard("Platform Audit Logs", "Open audit log trail for platform-level actions.", "/system-admin/dashboard?tab=audit-logs", "audit", ("System Admin",), "Dashboard", "Audit", 50), # Firm Admin - ReportCard("Branch Setup Report", "Review branches, primary branch readiness and branch setup shortcuts.", "/firm-admin/dashboard?tab=branches", "system", ("Firm Admin",), "Dashboard"), - ReportCard("Users & Roles Report", "Review user access, roles and invite/admin readiness for the firm.", "/firm-admin/dashboard?tab=users", "system", ("Firm Admin",), "Dashboard"), - ReportCard("Firm Settings Completion", "Open firm profile, branding, SMTP and setup status.", "/firm-admin/dashboard?tab=firm-settings", "system", ("Firm Admin",), "Dashboard"), - ReportCard("Services Setup Readiness", "Review selected services and task-template setup readiness.", "/firm-admin/dashboard?tab=services", "work", ("Firm Admin",), "Dashboard"), - ReportCard("Financial Year Report", "Review active FY, FY backup/lock and year setup readiness.", "/firm-admin/dashboard?tab=financial-years", "system", ("Firm Admin",), "Dashboard"), - ReportCard("Firm Audit Logs", "Open firm administration audit activity.", "/firm-admin/dashboard?tab=audit-logs", "audit", ("Firm Admin",), "Dashboard"), + ReportCard("Firm Administration Overview", "Open firm setup health, branches, users, services, FY and settings.", "/firm-admin/dashboard", "system", ("Firm Admin",), "Dashboard", "Firm Setup", 5), + ReportCard("Branch Setup Report", "Review branches, primary branch readiness and branch setup shortcuts.", "/firm-admin/dashboard?tab=branches", "system", ("Firm Admin",), "Dashboard", "Branches", 10), + ReportCard("Users & Roles Report", "Review user access, roles and invite/admin readiness for the firm.", "/firm-admin/dashboard?tab=users", "system", ("Firm Admin",), "Dashboard", "Users", 15), + ReportCard("Firm Settings Completion", "Open firm profile, branding, SMTP and setup status.", "/firm-admin/dashboard?tab=firm-settings", "system", ("Firm Admin",), "Dashboard", "Settings", 20), + ReportCard("Services Setup Readiness", "Review selected services and task-template setup readiness.", "/firm-admin/dashboard?tab=services", "work", ("Firm Admin",), "Dashboard", "Services", 25), + ReportCard("Financial Year Report", "Review active FY, FY backup/lock and year setup readiness.", "/firm-admin/dashboard?tab=financial-years", "system", ("Firm Admin",), "Dashboard", "FY", 30), + ReportCard("Firm Administration Audit Logs", "Open firm administration audit activity.", "/firm-admin/dashboard?tab=audit-logs", "audit", ("Firm Admin",), "Dashboard", "Audit", 35), # Partner - ReportCard("Branch Work Report", "Review branch work status, due work and service-wise operational load.", "/partner/dashboard?tab=branch-work", "work", ("Partner",), "Dashboard"), - ReportCard("Partner Client Report", "Review branch clients, service mapping, pending work and status.", "/partner/dashboard?tab=clients", "client", ("Partner",), "Dashboard"), - ReportCard("Partner Staff Workload", "Review staff workload, open tasks, overdue and review pending work.", "/partner/dashboard?tab=staff", "work", ("Partner",), "Dashboard"), - ReportCard("Partner Review Pending", "Open partner-level review queue and pending approvals.", "/partner/dashboard?tab=review", "work", ("Partner",), "Dashboard"), - ReportCard("Branch Billing Report", "Review branch billing, unbilled work and collection indicators.", "/partner/dashboard?tab=billing", "billing", ("Partner",), "Dashboard"), + ReportCard("Partner Operations Overview", "Open branch operations, client work, staff load, review and billing status.", "/partner/dashboard", "work", ("Partner",), "Dashboard", "Operations", 5), + ReportCard("Branch Work Report", "Review branch work status, due work and service-wise operational load.", "/partner/dashboard?tab=branch-work", "work", ("Partner",), "Dashboard", "Work", 10), + ReportCard("Partner Client Report", "Review branch clients, service mapping, pending work and status.", "/partner/dashboard?tab=clients", "client", ("Partner",), "Dashboard", "Clients", 15), + ReportCard("Partner Staff Workload", "Review staff workload, open tasks, overdue and review pending work.", "/partner/dashboard?tab=staff", "work", ("Partner",), "Dashboard", "Staff", 20), + ReportCard("Partner Review Pending", "Open partner-level review queue and pending approvals.", "/partner/dashboard?tab=review", "work", ("Partner",), "Dashboard", "Review", 25), + ReportCard("Branch Billing Report", "Review branch billing, unbilled work and collection indicators.", "/partner/dashboard?tab=billing", "billing", ("Partner",), "Dashboard", "Billing", 30), # Manager - ReportCard("Team Work Report", "Review team work, due tasks, workload and current execution status.", "/manager/dashboard?tab=team-work", "work", ("Manager", "Branch Manager"), "Dashboard"), - ReportCard("Manager Due Calendar", "Review due calendar, upcoming work and ageing buckets.", "/manager/dashboard?tab=due-calendar", "work", ("Manager", "Branch Manager"), "Dashboard"), - ReportCard("Manager Review Queue", "Open tasks waiting for manager review or correction.", "/manager/dashboard?tab=review-queue", "work", ("Manager", "Branch Manager"), "Dashboard"), - ReportCard("Client Pending Under Manager", "Review tasks blocked because client data or documents are pending.", "/manager/dashboard?tab=client-pending", "client", ("Manager", "Branch Manager"), "Dashboard"), - ReportCard("Document Pending Report", "Review document requirements, uploads and pending work papers.", "/manager/dashboard?tab=documents", "work", ("Manager", "Branch Manager"), "Dashboard"), - ReportCard("Escalation Register", "Review overdue, ageing and escalation items under manager control.", "/manager/dashboard?tab=escalations", "audit", ("Manager", "Branch Manager"), "Dashboard"), + ReportCard("Manager Execution Overview", "Open team work, due calendar, review queue and escalations.", "/manager/dashboard", "work", ("Manager", "Branch Manager"), "Dashboard", "Execution", 5), + ReportCard("Team Work Report", "Review team work, due tasks, workload and current execution status.", "/manager/dashboard?tab=team-work", "work", ("Manager", "Branch Manager"), "Dashboard", "Team", 10), + ReportCard("Manager Due Calendar", "Review due calendar, upcoming work and ageing buckets.", "/manager/dashboard?tab=due-calendar", "work", ("Manager", "Branch Manager"), "Dashboard", "Due Dates", 15), + ReportCard("Manager Review Queue", "Open tasks waiting for manager review or correction.", "/manager/dashboard?tab=review-queue", "work", ("Manager", "Branch Manager"), "Dashboard", "Review", 20), + ReportCard("Client Pending Under Manager", "Review tasks blocked because client data or documents are pending.", "/manager/dashboard?tab=client-pending", "client", ("Manager", "Branch Manager"), "Dashboard", "Client Pending", 25), + ReportCard("Document Pending Report", "Review document requirements, uploads and pending work papers.", "/manager/dashboard?tab=documents", "work", ("Manager", "Branch Manager"), "Dashboard", "Documents", 30), + ReportCard("Escalation Register", "Review overdue, ageing and escalation items under manager control.", "/manager/dashboard?tab=escalations", "audit", ("Manager", "Branch Manager"), "Dashboard", "Escalations", 35), # Employee / Staff - ReportCard("My Pending Tasks", "Open my work board and pending assigned tasks.", "/employee/dashboard?tab=work", "my", ("Staff", "Employee"), "Portal"), - ReportCard("My Due Today", "Open work due today from the employee portal.", "/employee/dashboard?tab=work", "my", ("Staff", "Employee"), "Portal"), - ReportCard("My Overdue Tasks", "Open overdue and returned work from the employee portal.", "/employee/dashboard?tab=work", "my", ("Staff", "Employee"), "Portal"), - ReportCard("My Attendance Report", "Open attendance punch details and attendance history.", "/employee/dashboard?tab=attendance", "hr", ("Staff", "Employee"), "Portal"), - ReportCard("My Leave Report", "Open leave, balances and leave activity from HR profile.", "/employee/dashboard?tab=hr", "hr", ("Staff", "Employee"), "Portal"), - ReportCard("My Documents Report", "Open my documents and uploaded work papers.", "/employee/dashboard?tab=work", "my", ("Staff", "Employee"), "Portal"), + ReportCard("Employee Portal Overview", "Open staff dashboard with attendance, work, HR, documents and reports.", "/employee/dashboard", "my", ("Staff", "Employee"), "Portal", "Personal", 5), + ReportCard("My Pending Tasks", "Open my work board and pending assigned tasks.", "/employee/dashboard?tab=work", "my", ("Staff", "Employee"), "Portal", "Work", 10), + ReportCard("My Due Today", "Open due today work from the employee portal work section.", "/employee/dashboard?tab=work", "my", ("Staff", "Employee"), "Portal", "Work", 15), + ReportCard("My Overdue Tasks", "Open overdue and returned work from the employee portal.", "/employee/dashboard?tab=work", "my", ("Staff", "Employee"), "Portal", "Work", 20), + ReportCard("My Attendance Report", "Open attendance punch details and attendance history.", "/employee/dashboard?tab=attendance", "hr", ("Staff", "Employee"), "Portal", "Attendance", 25), + ReportCard("My Leave Report", "Open leave, balances and leave activity from HR profile.", "/employee/dashboard?tab=hr", "hr", ("Staff", "Employee"), "Portal", "Leave", 30), + ReportCard("My Documents Report", "Open my documents and uploaded work papers.", "/employee/dashboard?tab=work", "my", ("Staff", "Employee"), "Portal", "Documents", 35), # Client - ReportCard("My Compliance Status", "Review service-wise compliance and current work status.", "/client/dashboard?tab=services-documents", "client", ("Client",), "Portal"), - ReportCard("Pending From Me", "Open documents, data and replies pending from the client side.", "/client/dashboard?tab=action-centre", "client", ("Client",), "Portal"), - ReportCard("Document History", "Open uploaded documents, firm shared documents and acknowledgements.", "/client/dashboard?tab=services-documents", "client", ("Client",), "Portal"), - ReportCard("Invoice Outstanding", "Open invoices, outstanding amounts and payment status.", "/client/dashboard?tab=billing-messages", "billing", ("Client",), "Portal"), - ReportCard("Payment History", "Open payment and receipt information in the client portal.", "/client/dashboard?tab=billing-messages", "billing", ("Client",), "Portal"), - ReportCard("Messages / Queries", "Open firm messages and query responses.", "/client/dashboard?tab=billing-messages", "client", ("Client",), "Portal"), + ReportCard("Client Portal Overview", "Open client portal with pending actions, services, documents, billing and messages.", "/client/dashboard", "client", ("Client",), "Portal", "Client Portal", 5), + ReportCard("Pending From Me", "Open documents, data and replies pending from the client side.", "/client/dashboard?tab=action-centre", "client", ("Client",), "Portal", "Action Centre", 10), + ReportCard("My Compliance Status", "Review service-wise compliance and current work status.", "/client/dashboard?tab=services-documents", "client", ("Client",), "Portal", "Services", 15), + ReportCard("Document History", "Open uploaded documents, firm shared documents and acknowledgements.", "/client/dashboard?tab=services-documents", "client", ("Client",), "Portal", "Documents", 20), + ReportCard("Invoice Outstanding", "Open invoices, outstanding amounts and payment status.", "/client/dashboard?tab=billing-messages", "billing", ("Client",), "Portal", "Billing", 25), + ReportCard("Payment History", "Open payment and receipt information in the client portal.", "/client/dashboard?tab=billing-messages", "billing", ("Client",), "Portal", "Payments", 30), + ReportCard("Messages / Queries", "Open firm messages and query responses.", "/client/dashboard?tab=billing-messages", "client", ("Client",), "Portal", "Messages", 35), # Consultant - ReportCard("Assigned Work Report", "Review consultant work centre and assigned tasks.", "/consultant/dashboard?tab=work-centre", "my", ("Consultant",), "Portal"), - ReportCard("Consultant Pending Work", "Review pending, due and overdue consultant assignments.", "/consultant/dashboard?tab=work-centre", "my", ("Consultant",), "Portal"), - ReportCard("Consultant Client Report", "Review consultant-linked clients and service requests.", "/consultant/dashboard?tab=clients-requests", "client", ("Consultant",), "Portal"), - ReportCard("Document Pending for Consultant", "Review consultant document and working paper requirements.", "/consultant/dashboard?tab=documents-clarifications", "my", ("Consultant",), "Portal"), - ReportCard("Clarification Pending", "Review clarifications between firm and consultant.", "/consultant/dashboard?tab=documents-clarifications", "my", ("Consultant",), "Portal"), - ReportCard("Consultant Service Requests", "Review consultant service requests and status.", "/consultant/dashboard?tab=clients-requests", "client", ("Consultant",), "Portal"), + ReportCard("Consultant Portal Overview", "Open consultant portal with work centre, clients, requests, documents and reports.", "/consultant/dashboard", "my", ("Consultant",), "Portal", "Consultant Portal", 5), + ReportCard("Assigned Work Report", "Review consultant work centre and assigned tasks.", "/consultant/dashboard?tab=work-centre", "my", ("Consultant",), "Portal", "Work", 10), + ReportCard("Consultant Pending Work", "Review pending, due and overdue consultant assignments.", "/consultant/dashboard?tab=work-centre", "my", ("Consultant",), "Portal", "Work", 15), + ReportCard("Consultant Client Report", "Review consultant-linked clients and service requests.", "/consultant/dashboard?tab=clients-requests", "client", ("Consultant",), "Portal", "Clients", 20), + ReportCard("Document Pending for Consultant", "Review consultant document and working paper requirements.", "/consultant/dashboard?tab=documents-clarifications", "my", ("Consultant",), "Portal", "Documents", 25), + ReportCard("Clarification Pending", "Review clarifications between firm and consultant.", "/consultant/dashboard?tab=documents-clarifications", "my", ("Consultant",), "Portal", "Clarifications", 30), + ReportCard("Consultant Service Requests", "Review consultant service requests and status.", "/consultant/dashboard?tab=clients-requests", "client", ("Consultant",), "Portal", "Requests", 35), # Existing module shortcuts visible to operational users - ReportCard("Client Master", "Open the existing client master list and filters.", "/clients", "client", ("System Admin", "Firm Admin", "Partner", "Manager"), "Module"), - ReportCard("Work Tracker", "Open the existing work tracker for operational task follow-up.", "/services/work-tracker", "work", ("System Admin", "Firm Admin", "Partner", "Manager"), "Module"), - ReportCard("Subscriptions", "Open client service subscriptions and task generation readiness.", "/services/work-tracker/subscriptions", "work", ("System Admin", "Firm Admin", "Partner", "Manager"), "Module"), - ReportCard("Billing Register", "Open existing billing invoices and payments area.", "/billing", "billing", ("System Admin", "Firm Admin", "Partner"), "Module"), - ReportCard("Billing Payments", "Open existing payment receipts and collections.", "/billing/payments", "billing", ("System Admin", "Firm Admin", "Partner"), "Module"), - ReportCard("Employee Attendance", "Open employee attendance administration page.", "/employees/attendance", "hr", ("System Admin", "Firm Admin", "Partner", "Manager"), "Module"), - ReportCard("Employee Progress", "Open employee progress and performance page.", "/employees/progress", "hr", ("System Admin", "Firm Admin", "Partner", "Manager"), "Module"), - ReportCard("Alerts", "Open alerts and notification centre.", "/alerts", "audit", ("System Admin", "Firm Admin", "Partner", "Manager", "Staff", "Employee", "Client", "Consultant"), "Module"), - ReportCard("Audit Logs", "Open existing audit log page.", "/system-settings/audit-logs", "audit", ("System Admin", "Firm Admin"), "Module"), + ReportCard("Client Master", "Open the existing client master list and filters.", "/clients", "client", ("System Admin", "Firm Admin", "Partner", "Manager"), "Module", "Clients", 40), + ReportCard("Documents Module", "Open document management and client/task document records.", "/documents", "client", ("System Admin", "Firm Admin", "Partner", "Manager", "Staff", "Employee"), "Module", "Documents", 45), + ReportCard("Work Tracker", "Open the existing work tracker for operational task follow-up.", "/services/work-tracker", "work", ("System Admin", "Firm Admin", "Partner", "Manager"), "Module", "Tasks", 40), + ReportCard("Subscriptions", "Open client service subscriptions and task generation readiness.", "/services/work-tracker/subscriptions", "work", ("System Admin", "Firm Admin", "Partner", "Manager"), "Module", "Services", 45), + ReportCard("Billing Register", "Open existing billing invoices and payments area.", "/billing", "billing", ("System Admin", "Firm Admin", "Partner"), "Module", "Invoices", 40), + ReportCard("Billing Payments", "Open existing payment receipts and collections.", "/billing/payments", "billing", ("System Admin", "Firm Admin", "Partner"), "Module", "Payments", 45), + ReportCard("Employee Attendance", "Open employee attendance administration page.", "/employees/attendance", "hr", ("System Admin", "Firm Admin", "Partner", "Manager"), "Module", "Attendance", 40), + ReportCard("Employee Progress", "Open employee progress and performance page.", "/employees/progress", "hr", ("System Admin", "Firm Admin", "Partner", "Manager"), "Module", "Progress", 45), + ReportCard("Alerts", "Open alerts and notification centre.", "/alerts", "audit", ("System Admin", "Firm Admin", "Partner", "Manager", "Staff", "Employee", "Client", "Consultant"), "Module", "Alerts", 40), + ReportCard("Audit Logs", "Open existing audit log page.", "/system-settings/audit-logs", "audit", ("System Admin", "Firm Admin"), "Module", "Audit", 45), ] @@ -126,33 +138,45 @@ def _visible(card: ReportCard, roles: set[str]) -> bool: def get_visible_report_cards(roles: Iterable[str] | None, permissions: Iterable[str] | None = None) -> list[ReportCard]: roles_set = _role_set(roles) - cards = [card for card in _all_cards() if _visible(card, roles_set)] + cards = sorted([card for card in _all_cards() if _visible(card, roles_set)], key=lambda c: (c.importance, c.title)) # Permission fallback for older accounts where explicit role names may be absent. permission_set = set(permissions or []) if not cards: if "clients.view" in permission_set: - cards.append(ReportCard("Client Master", "Open the existing client master list and filters.", "/clients", "client", tuple(), "Module")) + cards.append(ReportCard("Client Master", "Open the existing client master list and filters.", "/clients", "client", tuple(), "Module", "Clients", 10)) if "services.view" in permission_set: - cards.append(ReportCard("Work Tracker", "Open the existing work tracker for operational task follow-up.", "/services/work-tracker", "work", tuple(), "Module")) + cards.append(ReportCard("Work Tracker", "Open the existing work tracker for operational task follow-up.", "/services/work-tracker", "work", tuple(), "Module", "Tasks", 10)) if "billing.view" in permission_set: - cards.append(ReportCard("Billing Register", "Open existing billing invoices and payments area.", "/billing", "billing", tuple(), "Module")) + cards.append(ReportCard("Billing Register", "Open existing billing invoices and payments area.", "/billing", "billing", tuple(), "Module", "Invoices", 10)) if "employees.ess.view" in permission_set or "employees.work.view_self" in permission_set: - cards.append(ReportCard("My Employee Portal", "Open your employee dashboard, attendance and work board.", "/employee/dashboard", "my", tuple(), "Portal")) + cards.append(ReportCard("My Employee Portal", "Open your employee dashboard, attendance and work board.", "/employee/dashboard", "my", tuple(), "Portal", "Personal", 10)) return cards def cards_by_group(cards: Iterable[ReportCard]) -> dict[str, list[ReportCard]]: - groups = {"my": [], "work": [], "client": [], "billing": [], "hr": [], "system": [], "audit": []} + groups = {"overview": [], "my": [], "work": [], "client": [], "billing": [], "hr": [], "system": [], "audit": []} for card in cards: groups.setdefault(card.group, []).append(card) return groups +def cards_by_area(cards: Iterable[ReportCard]) -> dict[str, list[ReportCard]]: + area_groups: dict[str, list[ReportCard]] = {} + for card in cards: + area_groups.setdefault(card.area, []).append(card) + return dict(sorted(area_groups.items(), key=lambda item: item[0])) + + +def recommended_cards(cards: Iterable[ReportCard], limit: int = 8) -> list[ReportCard]: + return sorted(list(cards), key=lambda c: (c.importance, c.title))[:limit] + + def get_report_summary(cards: Iterable[ReportCard]) -> dict[str, int]: groups = cards_by_group(cards) + all_cards = list(cards) return { - "total": sum(len(v) for v in groups.values()), + "total": len(all_cards), "my": len(groups.get("my", [])), "work": len(groups.get("work", [])), "client": len(groups.get("client", [])), @@ -160,17 +184,21 @@ def get_report_summary(cards: Iterable[ReportCard]) -> dict[str, int]: "hr": len(groups.get("hr", [])), "system": len(groups.get("system", [])), "audit": len(groups.get("audit", [])), + "dashboards": len([card for card in all_cards if card.badge == "Dashboard"]), + "portals": len([card for card in all_cards if card.badge == "Portal"]), + "modules": len([card for card in all_cards if card.badge == "Module"]), } def get_report_tabs(cards: Iterable[ReportCard]) -> list[dict[str, str | int]]: summary = get_report_summary(cards) return [ + {"key": "overview", "label": "Overview", "count": summary["total"]}, {"key": "my-reports", "label": "My Reports", "count": summary["my"]}, - {"key": "work", "label": "Work Reports", "count": summary["work"]}, - {"key": "client", "label": "Client Reports", "count": summary["client"]}, - {"key": "billing", "label": "Billing Reports", "count": summary["billing"]}, + {"key": "work", "label": "Work", "count": summary["work"]}, + {"key": "client", "label": "Client & Documents", "count": summary["client"]}, + {"key": "billing", "label": "Billing", "count": summary["billing"]}, {"key": "hr", "label": "HR / Attendance", "count": summary["hr"]}, - {"key": "system", "label": "System Reports", "count": summary["system"]}, + {"key": "system", "label": "System & Setup", "count": summary["system"]}, {"key": "audit", "label": "Audit / Alerts", "count": summary["audit"]}, ] diff --git a/app/modules/unified_reports/templates/unified_reports/dashboard.html b/app/modules/unified_reports/templates/unified_reports/dashboard.html index 102f72b..e8ec461 100644 --- a/app/modules/unified_reports/templates/unified_reports/dashboard.html +++ b/app/modules/unified_reports/templates/unified_reports/dashboard.html @@ -4,9 +4,9 @@
-

Role-wise Reports Centre

+

Role-wise Reports Centre V2

Unified Reports Centre

-

Open the reports and existing report modules relevant to your role and current workspace.

+

Use one role-aware place to open your dashboards, existing reports, portals and audit views.

@@ -14,30 +14,36 @@
Available
-
{{ report_summary.work }}
-
Work
+
{{ report_summary.dashboards }}
+
Dashboards
-
{{ report_summary.client }}
-
Client
+
{{ report_summary.portals }}
+
Portals
-
{{ report_summary.billing }}
-
Billing
+
{{ report_summary.modules }}
+
Modules
-
-
- {% for tab in report_tabs %} - - {{ tab.label }} - {{ tab.count }} - - {% endfor %} +
+
+
+ {% for tab in report_tabs %} + + {{ tab.label }} + {{ tab.count }} + + {% endfor %} +
+
+
+ +
@@ -51,6 +57,7 @@ (function () { const panel = document.getElementById('reports-centre-panel'); const tabs = Array.from(document.querySelectorAll('[data-reports-tab]')); + const searchInput = document.getElementById('reports-search'); if (!panel || !tabs.length) return; function setActive(tabName) { @@ -63,6 +70,20 @@ }); } + function applySearch() { + const query = (searchInput && searchInput.value || '').trim().toLowerCase(); + const cards = Array.from(panel.querySelectorAll('[data-report-card]')); + let shown = 0; + cards.forEach((card) => { + const haystack = (card.dataset.searchText || card.textContent || '').toLowerCase(); + const visible = !query || haystack.includes(query); + card.classList.toggle('hidden', !visible); + if (visible) shown += 1; + }); + const empty = panel.querySelector('[data-report-search-empty]'); + if (empty) empty.classList.toggle('hidden', !query || shown > 0); + } + async function loadTab(tabName, pushState) { setActive(tabName); panel.innerHTML = '
Loading...
'; @@ -72,6 +93,7 @@ }); if (!response.ok) throw new Error('Tab load failed'); panel.innerHTML = await response.text(); + applySearch(); if (pushState) { const url = new URL(window.location.href); url.searchParams.set('tab', tabName); @@ -89,12 +111,16 @@ }); }); + if (searchInput) { + searchInput.addEventListener('input', applySearch); + } + window.addEventListener('popstate', function () { const url = new URL(window.location.href); - loadTab(url.searchParams.get('tab') || 'my-reports', false); + loadTab(url.searchParams.get('tab') || 'overview', false); }); - loadTab(panel.dataset.initialTab || 'my-reports', false); + loadTab(panel.dataset.initialTab || 'overview', false); })(); {% endblock %} diff --git a/app/modules/unified_reports/templates/unified_reports/partials/_cards.html b/app/modules/unified_reports/templates/unified_reports/partials/_cards.html index fb0b068..9898d5a 100644 --- a/app/modules/unified_reports/templates/unified_reports/partials/_cards.html +++ b/app/modules/unified_reports/templates/unified_reports/partials/_cards.html @@ -8,16 +8,23 @@
{{ active_cards|length }} available
+ + {% if active_cards %}
{% for card in active_cards %} - +
-
{{ card.title }}
+
+
{{ card.title }}
+
{{ card.area }}
+
{{ card.badge }}

{{ card.description }}

-
Open report →
+
Open →
{% endfor %}
diff --git a/app/modules/unified_reports/templates/unified_reports/partials/client.html b/app/modules/unified_reports/templates/unified_reports/partials/client.html index 3806f7c..50a8d23 100644 --- a/app/modules/unified_reports/templates/unified_reports/partials/client.html +++ b/app/modules/unified_reports/templates/unified_reports/partials/client.html @@ -1,4 +1,4 @@ -{% set section_kicker = "Clients" %} -{% set section_title = "Client Reports" %} -{% set section_description = "Client master, client portal, pending-from-client, documents and compliance status reports." %} +{% set section_kicker = "Clients & Documents" %} +{% set section_title = "Client & Document Reports" %} +{% set section_description = "Client master, client portal, pending-from-client, documents and compliance status report links." %} {% include "modules/unified_reports/templates/unified_reports/partials/_cards.html" %} diff --git a/app/modules/unified_reports/templates/unified_reports/partials/hr.html b/app/modules/unified_reports/templates/unified_reports/partials/hr.html index 3075ed6..8c61628 100644 --- a/app/modules/unified_reports/templates/unified_reports/partials/hr.html +++ b/app/modules/unified_reports/templates/unified_reports/partials/hr.html @@ -1,4 +1,4 @@ {% set section_kicker = "HR & Attendance" %} {% set section_title = "HR / Attendance Reports" %} -{% set section_description = "Employee attendance, leave, progress and personal HR reports." %} +{% set section_description = "Employee attendance, leave, progress and personal HR report links." %} {% include "modules/unified_reports/templates/unified_reports/partials/_cards.html" %} diff --git a/app/modules/unified_reports/templates/unified_reports/partials/my_reports.html b/app/modules/unified_reports/templates/unified_reports/partials/my_reports.html index 29d589c..eaf9c3b 100644 --- a/app/modules/unified_reports/templates/unified_reports/partials/my_reports.html +++ b/app/modules/unified_reports/templates/unified_reports/partials/my_reports.html @@ -1,4 +1,4 @@ -{% set section_kicker = "Personal Reports" %} +{% set section_kicker = "Personal" %} {% set section_title = "My Reports" %} {% set section_description = "Personal work, attendance, client or consultant portal reports available to your login." %} {% include "modules/unified_reports/templates/unified_reports/partials/_cards.html" %} diff --git a/app/modules/unified_reports/templates/unified_reports/partials/overview.html b/app/modules/unified_reports/templates/unified_reports/partials/overview.html new file mode 100644 index 0000000..dfb15e1 --- /dev/null +++ b/app/modules/unified_reports/templates/unified_reports/partials/overview.html @@ -0,0 +1,68 @@ +
+
+
+
Total Available
+
{{ report_summary.total }}
+
Role-aware report cards
+
+
+
Work / Execution
+
{{ report_summary.work }}
+
Task, review and due reports
+
+
+
Client / Documents
+
{{ report_summary.client }}
+
Client, portal and document views
+
+
+
Admin / Audit
+
{{ report_summary.system + report_summary.audit }}
+
Setup and audit shortcuts
+
+
+ +
+
+
+

Recommended

+

Start here

+

Most useful reports and dashboard links for your current role.

+
+
{{ recommended_cards|length }} recommended
+
+ + + + +
+ +
+

Grouped View

+

Report areas

+
+ {% for area, cards in report_area_groups.items() %} +
+
{{ area }}
+
{{ cards|length }} report link{{ '' if cards|length == 1 else 's' }}
+
+ {% endfor %} +
+
+
diff --git a/app/modules/unified_reports/templates/unified_reports/partials/system.html b/app/modules/unified_reports/templates/unified_reports/partials/system.html index 7eb569e..21e53a7 100644 --- a/app/modules/unified_reports/templates/unified_reports/partials/system.html +++ b/app/modules/unified_reports/templates/unified_reports/partials/system.html @@ -1,4 +1,4 @@ {% set section_kicker = "Administration" %} -{% set section_title = "System Reports" %} -{% set section_description = "Firm setup, branch setup, users, roles, SMTP, storage, FY and platform readiness reports." %} +{% set section_title = "System & Setup Reports" %} +{% set section_description = "Firm setup, branch setup, users, roles, SMTP, storage, FY and platform readiness report links." %} {% include "modules/unified_reports/templates/unified_reports/partials/_cards.html" %} diff --git a/app/modules/unified_reports/ui.py b/app/modules/unified_reports/ui.py index a72d42b..7f5e6a2 100644 --- a/app/modules/unified_reports/ui.py +++ b/app/modules/unified_reports/ui.py @@ -11,15 +11,18 @@ from app.core.templating import templates from app.modules.core.rbac.deps import get_user_permissions, get_user_roles from app.modules.unified_reports.service import ( can_access_reports, + cards_by_area, cards_by_group, get_report_summary, get_report_tabs, get_visible_report_cards, + recommended_cards, ) router = APIRouter(prefix="/reports", tags=["unified-reports-ui"]) _ALLOWED_TABS = { + "overview": "overview", "my-reports": "my", "work": "work", "client": "client", @@ -27,6 +30,20 @@ _ALLOWED_TABS = { "hr": "hr", "system": "system", "audit": "audit", + # Backward-compatible aliases from V1 direct links. + "my_reports": "my", +} + +_PARTIALS = { + "overview": "modules/unified_reports/templates/unified_reports/partials/overview.html", + "my-reports": "modules/unified_reports/templates/unified_reports/partials/my_reports.html", + "my_reports": "modules/unified_reports/templates/unified_reports/partials/my_reports.html", + "work": "modules/unified_reports/templates/unified_reports/partials/work.html", + "client": "modules/unified_reports/templates/unified_reports/partials/client.html", + "billing": "modules/unified_reports/templates/unified_reports/partials/billing.html", + "hr": "modules/unified_reports/templates/unified_reports/partials/hr.html", + "system": "modules/unified_reports/templates/unified_reports/partials/system.html", + "audit": "modules/unified_reports/templates/unified_reports/partials/audit.html", } @@ -55,6 +72,8 @@ def _payload(db, current_user): "current_user_permissions": permissions, "report_cards": cards, "report_groups": groups, + "report_area_groups": cards_by_area(cards), + "recommended_cards": recommended_cards(cards), "report_tabs": get_report_tabs(cards), "report_summary": get_report_summary(cards), } @@ -72,13 +91,13 @@ def _base_context(request: Request, db, current_user, **extra): @router.get("") -def reports_home(request: Request, tab: str = "my-reports"): +def reports_home(request: Request, tab: str = "overview"): db = CommonSessionLocal() try: current_user, response = _current_user_or_response(request, db) if response: return response - active_tab = tab if tab in _ALLOWED_TABS else "my-reports" + active_tab = tab if tab in _ALLOWED_TABS else "overview" return templates.TemplateResponse( "modules/unified_reports/templates/unified_reports/dashboard.html", _base_context(request, db, current_user, title="Reports Centre", active_tab=active_tab), @@ -94,10 +113,12 @@ def reports_tab(request: Request, tab_name: str): current_user, response = _current_user_or_response(request, db) if response: return response - active_tab = tab_name if tab_name in _ALLOWED_TABS else "my-reports" + active_tab = tab_name if tab_name in _ALLOWED_TABS else "overview" group_key = _ALLOWED_TABS[active_tab] + cards = get_visible_report_cards(get_user_roles(db, current_user.id), get_user_permissions(db, current_user.id)) + group_cards = cards_by_group(cards) return templates.TemplateResponse( - f"modules/unified_reports/templates/unified_reports/partials/{active_tab.replace('-', '_')}.html", + _PARTIALS[active_tab], _base_context( request, db, @@ -105,7 +126,7 @@ def reports_tab(request: Request, tab_name: str): title="Reports Centre", active_tab=active_tab, active_group=group_key, - active_cards=cards_by_group(get_visible_report_cards(get_user_roles(db, current_user.id), get_user_permissions(db, current_user.id))).get(group_key, []), + active_cards=cards if group_key == "overview" else group_cards.get(group_key, []), ), ) finally: