From 770ba9af0780733d74400edde6ce231e4b9c22c4 Mon Sep 17 00:00:00 2001 From: A R R R Associates Date: Sun, 5 Jul 2026 21:42:54 +0530 Subject: [PATCH] Add client dashboard ajax tabs v1 --- .../templates/clients/portal_dashboard.html | 129 ++++++++---------- .../clients/portal_partials/billing.html | 28 ++++ .../clients/portal_partials/documents.html | 29 ++++ .../clients/portal_partials/messages.html | 20 +++ .../clients/portal_partials/overview.html | 74 ++++++++++ .../clients/portal_partials/pending.html | 34 +++++ .../clients/portal_partials/reports.html | 14 ++ .../clients/portal_partials/services.html | 30 ++++ app/modules/clients/ui.py | 61 +++++++++ 9 files changed, 350 insertions(+), 69 deletions(-) create mode 100644 app/modules/clients/templates/clients/portal_partials/billing.html create mode 100644 app/modules/clients/templates/clients/portal_partials/documents.html create mode 100644 app/modules/clients/templates/clients/portal_partials/messages.html create mode 100644 app/modules/clients/templates/clients/portal_partials/overview.html create mode 100644 app/modules/clients/templates/clients/portal_partials/pending.html create mode 100644 app/modules/clients/templates/clients/portal_partials/reports.html create mode 100644 app/modules/clients/templates/clients/portal_partials/services.html diff --git a/app/modules/clients/templates/clients/portal_dashboard.html b/app/modules/clients/templates/clients/portal_dashboard.html index 841dcb2..e262a16 100644 --- a/app/modules/clients/templates/clients/portal_dashboard.html +++ b/app/modules/clients/templates/clients/portal_dashboard.html @@ -1,16 +1,20 @@ {% extends "ui/templates/base/layout.html" %} {% block content %} -{% include "modules/clients/templates/clients/_client_tabs.html" %}

Client Portal

My Compliance & Firm Communication

-

Track your compliance status, pending actions, required documents, messages and firm updates.

+

Track pending actions, service status, documents, messages, bills and firm updates from one place.

+
+
+ {% if client_row %} + Update My Profile + Upload Documents + {% endif %}
- {% if client_row %}Update My Profile{% endif %}
@@ -19,75 +23,62 @@ We could not find a client master linked to your login email in the current audit firm. Please contact your firm admin to map this login to the correct client record.
{% else %} -
-
Active Compliance
{{ total_engagements or 0 }}
Services / filings
-
Pending Action
{{ pending_from_client or 0 }}
Required from you
-
With Firm
{{ with_firm or 0 }}
Being handled
-
Documents
{{ recent_documents|length if recent_documents else 0 }}
Recent uploads
-
Outstanding Bills
₹ {{ '%.2f'|format(billing_outstanding_amount or 0) }}
{{ billing_open_count or 0 }} open bill(s)
-
+ {% set client_tabs = [ + ('overview','Overview'), + ('pending','Pending From Me'), + ('services','Services'), + ('documents','Documents'), + ('billing','Invoices & Payments'), + ('messages','Messages'), + ('reports','Reports') + ] %} -
-
-
-

Compliance Status

Simple client-facing status of your active services.

View All
-
-
Pending from You
{{ pending_from_client or 0 }}
-
With Firm
{{ with_firm or 0 }}
-
Clarification
{{ clarification_required or 0 }}
-
Completed
{{ completed_engagements or 0 }}
-
- - -
- -
-

Latest Messages

View all
-
- {% for note in client_visible_comments[:5] %} -
{{ note.task.task_name if note.task else 'Message' }}

{{ note.message }}

{{ note.created_at_utc.strftime('%d-%m-%Y %I:%M %p') if note.created_at_utc else '' }}
- {% else %} -
No messages yet.
- {% endfor %} -
-
+
+
+ {% for code, label in client_tabs %} + + {% endfor %}
- -
+ +
+ {% include "modules/clients/templates/clients/portal_partials/overview.html" %} +
+ + {% endif %}
{% endblock %} diff --git a/app/modules/clients/templates/clients/portal_partials/billing.html b/app/modules/clients/templates/clients/portal_partials/billing.html new file mode 100644 index 0000000..d6784de --- /dev/null +++ b/app/modules/clients/templates/clients/portal_partials/billing.html @@ -0,0 +1,28 @@ +
+
+
Total Bills
{{ billing_total_count or 0 }}
+
Open Bills
{{ billing_open_count or 0 }}
+
Outstanding
₹ {{ '%.2f'|format(billing_outstanding_amount or 0) }}
+
+
+

Invoices & Payments

View bills, receipts and payment options.

Open Billing
+
+ + + + {% for invoice in billing_invoices[:8] if billing_invoices is defined %} + + + + + + + + {% else %} + + {% endfor %} + +
InvoiceDateStatusBalanceAction
{{ invoice.invoice_no }}{{ invoice.invoice_date.strftime('%d-%m-%Y') if invoice.invoice_date else '-' }}{{ invoice.status }}₹ {{ '%.2f'|format(invoice.balance_amount or 0) }}View{% if invoice.balance_amount and invoice.balance_amount > 0 %}|Pay{% endif %}
No invoices found.
+
+
+
diff --git a/app/modules/clients/templates/clients/portal_partials/documents.html b/app/modules/clients/templates/clients/portal_partials/documents.html new file mode 100644 index 0000000..cffb0c2 --- /dev/null +++ b/app/modules/clients/templates/clients/portal_partials/documents.html @@ -0,0 +1,29 @@ +
+
+

Engagement Documents

Recent working documents and files shared for active services.

Open Documents
+ +
+ +
+

Permanent Documents

Profile and permanent records shared with the firm.

View All
+ +
+
diff --git a/app/modules/clients/templates/clients/portal_partials/messages.html b/app/modules/clients/templates/clients/portal_partials/messages.html new file mode 100644 index 0000000..42b67f8 --- /dev/null +++ b/app/modules/clients/templates/clients/portal_partials/messages.html @@ -0,0 +1,20 @@ +
+
+

Messages from Firm

Client-visible task comments, clarifications and updates.

+ Open Messages +
+
+ {% for note in client_visible_comments or [] %} +
+
+
{{ note.task.task_name if note.task else (note.subscription.catalogue.service_name if note.subscription and note.subscription.catalogue else 'Message') }}
+
{{ note.created_at_utc.strftime('%d-%m-%Y %I:%M %p') if note.created_at_utc else '' }}
+
+

{{ note.message }}

+ {% if note.task %}Open related service{% endif %} +
+ {% else %} +
No messages yet.
+ {% endfor %} +
+
diff --git a/app/modules/clients/templates/clients/portal_partials/overview.html b/app/modules/clients/templates/clients/portal_partials/overview.html new file mode 100644 index 0000000..49a6abb --- /dev/null +++ b/app/modules/clients/templates/clients/portal_partials/overview.html @@ -0,0 +1,74 @@ +
+
+
Active Services
{{ total_engagements or 0 }}
Services / filings
+ +
With Firm
{{ with_firm or 0 }}
Being handled
+
Recent Documents
{{ recent_documents|length if recent_documents else 0 }}
Latest documents
+
Outstanding Bills
₹ {{ '%.2f'|format(billing_outstanding_amount or 0) }}
{{ billing_open_count or 0 }} open bill(s)
+
+ +
+
+
+

Needs Your Attention

Open items where the firm may need documents, replies or confirmation from you.

View Services
+
+ {% set shown = namespace(count=0) %} + {% for task in task_rows or [] %} + {% if shown.count < 6 and (task.status or 'pending') in ['pending','blocked','client_pending','clarification_required','rework'] %} + {% set shown.count = shown.count + 1 %} + +
+
{{ task.task_name or (task.catalogue.service_name if task.catalogue else 'Task') }}
{{ (task.status or 'pending')|replace('_',' ')|title }}{% if task.financial_year %} • FY {{ task.financial_year }}{% endif %}
+
Due: {{ task.internal_target_date.strftime('%d-%m-%Y') if task.internal_target_date else '-' }}
+
+
+ {% endif %} + {% endfor %} + {% if shown.count == 0 %} +
No urgent action is pending from you.
+ {% endif %} +
+
+ + +
+ + +
+
diff --git a/app/modules/clients/templates/clients/portal_partials/pending.html b/app/modules/clients/templates/clients/portal_partials/pending.html new file mode 100644 index 0000000..64c6bc4 --- /dev/null +++ b/app/modules/clients/templates/clients/portal_partials/pending.html @@ -0,0 +1,34 @@ +
+
+
+

Pending From Me

+

Documents, replies or clarifications currently expected from you.

+
+ Open Messages +
+
+ + + + + + {% set shown = namespace(count=0) %} + {% for task in task_rows or [] %} + {% if (task.status or 'pending') in ['pending','blocked','client_pending','clarification_required','rework'] %} + {% set shown.count = shown.count + 1 %} + + + + + + + + {% endif %} + {% endfor %} + {% if shown.count == 0 %} + + {% endif %} + +
Service / TaskStatusFY / PeriodDueAction
{{ task.task_name or (task.catalogue.service_name if task.catalogue else 'Task') }}
{{ task.catalogue.service_name if task.catalogue else 'Service' }}
{{ (task.status or 'pending')|replace('_',' ')|title }}{{ task.financial_year or '-' }}{{ task.internal_target_date.strftime('%d-%m-%Y') if task.internal_target_date else '-' }}Open
No pending action from you.
+
+
diff --git a/app/modules/clients/templates/clients/portal_partials/reports.html b/app/modules/clients/templates/clients/portal_partials/reports.html new file mode 100644 index 0000000..caea415 --- /dev/null +++ b/app/modules/clients/templates/clients/portal_partials/reports.html @@ -0,0 +1,14 @@ + diff --git a/app/modules/clients/templates/clients/portal_partials/services.html b/app/modules/clients/templates/clients/portal_partials/services.html new file mode 100644 index 0000000..e41fa18 --- /dev/null +++ b/app/modules/clients/templates/clients/portal_partials/services.html @@ -0,0 +1,30 @@ +
+
+

My Services

Client-facing status of services handled by the firm.

+ Full Compliance Page +
+
+
Pending From You
{{ pending_from_client or 0 }}
+
With Firm
{{ with_firm or 0 }}
+
Clarification
{{ clarification_required or 0 }}
+
Completed
{{ completed_engagements or 0 }}
+
+
+ + + + {% for row in engagements or [] %} + + + + + + + + {% else %} + + {% endfor %} + +
ServiceFY / AYDue DateAssigned TeamAction
{{ row.catalogue.service_name if row.catalogue else 'Service' }}FY {{ row.financial_year or '-' }}{% if row.assessment_year %} / AY {{ row.assessment_year }}{% endif %}{{ row.current_due_date.strftime('%d-%m-%Y') if row.current_due_date else '-' }}{{ row.assigned_partner.full_name if row.assigned_partner else 'Firm team' }}View
No active services found.
+
+
diff --git a/app/modules/clients/ui.py b/app/modules/clients/ui.py index 7109de4..01530f1 100644 --- a/app/modules/clients/ui.py +++ b/app/modules/clients/ui.py @@ -914,6 +914,30 @@ def _active_financial_year(request: Request) -> str | None: value = (value or "").strip() return value or None +CLIENT_PORTAL_TABS = { + "overview": "modules/clients/templates/clients/portal_partials/overview.html", + "pending": "modules/clients/templates/clients/portal_partials/pending.html", + "services": "modules/clients/templates/clients/portal_partials/services.html", + "documents": "modules/clients/templates/clients/portal_partials/documents.html", + "billing": "modules/clients/templates/clients/portal_partials/billing.html", + "messages": "modules/clients/templates/clients/portal_partials/messages.html", + "reports": "modules/clients/templates/clients/portal_partials/reports.html", +} + + +def _client_dashboard_payload(db, client_row, financial_year: str | None = None) -> dict: + summary = build_client_portal_summary(db, client_row, financial_year=financial_year) + billing_summary = build_client_billing_summary(db, client_row, financial_year=financial_year) + return { + "client_visible_comments": list_client_visible_comments(db, client_row, limit=20, financial_year=financial_year), + "recent_documents": list_client_engagement_documents(db, client_row, financial_year=financial_year)[:12], + "permanent_documents": list_client_permanent_documents(db, client_row), + "auditor_card": build_client_auditor_card(db, client_row), + **summary, + **billing_summary, + } + + def _portal_context(request: Request, db, current_user, **extra): ctx = { "request": request, @@ -964,6 +988,43 @@ def client_portal_dashboard(request: Request): db.close() +@portal_router.get("/dashboard/tab/{tab_name}") +def client_portal_dashboard_tab(request: Request, tab_name: str): + db = CommonSessionLocal() + try: + current_user = get_current_user(request, db=db) + if not current_user: + return RedirectResponse(url="/login", status_code=303) + + client_row, redirect = _portal_client_or_redirect(request, db, current_user) + if redirect: + return redirect + + active_tab = tab_name if tab_name in CLIENT_PORTAL_TABS else "overview" + if not client_row: + return templates.TemplateResponse( + CLIENT_PORTAL_TABS["overview"], + _portal_context(request, db, current_user, client_row=None, active_tab=active_tab), + status_code=200, + ) + financial_year = _active_financial_year(request) + payload = _client_dashboard_payload(db, client_row, financial_year=financial_year) + return templates.TemplateResponse( + CLIENT_PORTAL_TABS[active_tab], + _portal_context( + request, + db, + current_user, + client_row=client_row, + active_tab=active_tab, + active_financial_year=financial_year, + **payload, + ), + ) + finally: + db.close() + + @portal_router.get("/billing") def client_portal_billing(request: Request, q: str = "", include_paid: str = "yes"): db = CommonSessionLocal()