Add compact searchable consultant client list
This commit is contained in:
@@ -326,6 +326,122 @@ def list_client_links_for_consultant(db: Session, *, consultant_id: int) -> list
|
||||
).scalars().all()
|
||||
|
||||
|
||||
def consultant_linked_clients_page(
|
||||
db: Session,
|
||||
*,
|
||||
consultant: ConsultantProfile,
|
||||
q: str = "",
|
||||
client_group_id: int | None = None,
|
||||
status: str = "active",
|
||||
page: int = 1,
|
||||
per_page: int = 25,
|
||||
sort_by: str = "client_name",
|
||||
sort_order: str = "asc",
|
||||
) -> dict:
|
||||
"""Return a compact, searchable and paginated list of linked firm clients.
|
||||
|
||||
Link permission columns remain unchanged and continue to govern the existing
|
||||
service, due-date, communication and document workflows. They are deliberately
|
||||
not exposed in the client-list UI.
|
||||
"""
|
||||
q = (q or "").strip()
|
||||
status = (status or "").strip().lower()
|
||||
per_page = min(max(int(per_page or 25), 1), 100)
|
||||
page = max(int(page or 1), 1)
|
||||
|
||||
stmt = (
|
||||
select(ClientConsultantLink, Client, ClientGroup)
|
||||
.join(Client, Client.id == ClientConsultantLink.client_id)
|
||||
.outerjoin(ClientGroup, ClientGroup.id == Client.client_group_id)
|
||||
.where(
|
||||
ClientConsultantLink.tenant_id == consultant.tenant_id,
|
||||
ClientConsultantLink.consultant_id == consultant.id,
|
||||
ClientConsultantLink.is_active.is_(True),
|
||||
Client.tenant_id == consultant.tenant_id,
|
||||
Client.is_archived.is_(False),
|
||||
)
|
||||
)
|
||||
if status:
|
||||
stmt = stmt.where(Client.status == status)
|
||||
if client_group_id:
|
||||
stmt = stmt.where(Client.client_group_id == int(client_group_id))
|
||||
if q:
|
||||
like = f"%{q}%"
|
||||
stmt = stmt.where(
|
||||
or_(
|
||||
Client.client_code.ilike(like),
|
||||
Client.client_name.ilike(like),
|
||||
Client.trade_name.ilike(like),
|
||||
Client.pan.ilike(like),
|
||||
Client.gstin.ilike(like),
|
||||
ClientGroup.group_name.ilike(like),
|
||||
ClientGroup.group_code.ilike(like),
|
||||
)
|
||||
)
|
||||
|
||||
total = int(db.execute(select(func.count()).select_from(stmt.subquery())).scalar_one() or 0)
|
||||
pages = max(ceil(total / per_page), 1)
|
||||
page = min(page, pages)
|
||||
|
||||
sort_columns = {
|
||||
"client_code": Client.client_code,
|
||||
"client_name": Client.client_name,
|
||||
"pan": Client.pan,
|
||||
"client_group": ClientGroup.group_name,
|
||||
"status": Client.status,
|
||||
}
|
||||
sort_column = sort_columns.get(sort_by, Client.client_name)
|
||||
ordering = sort_column.desc() if (sort_order or "asc").lower() == "desc" else sort_column.asc()
|
||||
|
||||
rows = []
|
||||
result = db.execute(
|
||||
stmt.order_by(ordering, Client.id.asc())
|
||||
.offset((page - 1) * per_page)
|
||||
.limit(per_page)
|
||||
).all()
|
||||
for link, client, group in result:
|
||||
rows.append({
|
||||
"link": link,
|
||||
"client": client,
|
||||
"client_group_id": getattr(group, "id", None),
|
||||
"client_group_code": getattr(group, "group_code", None),
|
||||
"client_group_name": getattr(group, "group_name", None),
|
||||
"relationship_label": (link.relationship_type or "accounts_consultant").replace("_", " ").title(),
|
||||
})
|
||||
|
||||
groups_stmt = (
|
||||
select(ClientGroup.id, ClientGroup.group_code, ClientGroup.group_name)
|
||||
.join(Client, Client.client_group_id == ClientGroup.id)
|
||||
.join(ClientConsultantLink, ClientConsultantLink.client_id == Client.id)
|
||||
.where(
|
||||
ClientConsultantLink.tenant_id == consultant.tenant_id,
|
||||
ClientConsultantLink.consultant_id == consultant.id,
|
||||
ClientConsultantLink.is_active.is_(True),
|
||||
Client.tenant_id == consultant.tenant_id,
|
||||
Client.is_archived.is_(False),
|
||||
ClientGroup.is_active.is_(True),
|
||||
)
|
||||
.distinct()
|
||||
.order_by(ClientGroup.group_name.asc())
|
||||
)
|
||||
groups = [
|
||||
{"id": int(group_id), "code": code, "name": name}
|
||||
for group_id, code, name in db.execute(groups_stmt).all()
|
||||
]
|
||||
return {
|
||||
"rows": rows,
|
||||
"groups": groups,
|
||||
"meta": {"total": total, "page": page, "per_page": per_page, "pages": pages},
|
||||
"filters": {
|
||||
"q": q,
|
||||
"client_group_id": client_group_id,
|
||||
"status": status,
|
||||
"sort_by": sort_by if sort_by in sort_columns else "client_name",
|
||||
"sort_order": "desc" if (sort_order or "asc").lower() == "desc" else "asc",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def link_client_to_consultant(
|
||||
db: Session,
|
||||
*,
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
{% include "modules/consultants/templates/consultants/portal_partials/clients_requests.html" %}
|
||||
{% elif active_tab == 'documents-clarifications' %}
|
||||
{% include "modules/consultants/templates/consultants/portal_partials/documents_clarifications.html" %}
|
||||
{% elif active_tab == 'reports' %}
|
||||
{% include "modules/consultants/templates/consultants/portal_partials/reports.html" %}
|
||||
{% else %}
|
||||
{% include "modules/consultants/templates/consultants/portal_partials/overview.html" %}
|
||||
{% endif %}
|
||||
|
||||
@@ -1,10 +1,148 @@
|
||||
<div class="grid gap-6 xl:grid-cols-2">
|
||||
<div class="af-card">
|
||||
{% set linked = payload.linked_clients_page %}
|
||||
{% set filters = linked.filters %}
|
||||
{% set meta = linked.meta %}
|
||||
|
||||
<div class="space-y-6">
|
||||
<section class="af-card">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-slate-900">Linked Firm Clients</h3>
|
||||
<p class="mt-1 text-sm text-slate-500">Firm clients explicitly linked to your consultant profile.</p>
|
||||
</div>
|
||||
<span class="w-fit rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold text-slate-600">{{ meta.total }} client{% if meta.total != 1 %}s{% endif %}</span>
|
||||
</div>
|
||||
|
||||
<form method="get" action="/consultant/dashboard" class="mt-5 grid gap-3 md:grid-cols-2 xl:grid-cols-6">
|
||||
<input type="hidden" name="tab" value="clients">
|
||||
<div class="md:col-span-2 xl:col-span-2">
|
||||
<label for="linked-client-search" class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Search</label>
|
||||
<input id="linked-client-search" name="linked_q" value="{{ filters.q }}" placeholder="Client name, code, PAN, GSTIN or group"
|
||||
class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800 focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-100">
|
||||
</div>
|
||||
<div>
|
||||
<label for="linked-client-group" class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Group</label>
|
||||
<select id="linked-client-group" name="linked_group_id" class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800">
|
||||
<option value="">All groups</option>
|
||||
{% for group in linked.groups %}
|
||||
<option value="{{ group.id }}" {% if filters.client_group_id == group.id %}selected{% endif %}>{{ group.name }}{% if group.code %} ({{ group.code }}){% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="linked-client-status" class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Status</label>
|
||||
<select id="linked-client-status" name="linked_status" class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800">
|
||||
<option value="active" {% if filters.status == 'active' %}selected{% endif %}>Active</option>
|
||||
<option value="" {% if not filters.status %}selected{% endif %}>All statuses</option>
|
||||
<option value="inactive" {% if filters.status == 'inactive' %}selected{% endif %}>Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="linked-client-sort" class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Sort</label>
|
||||
<select id="linked-client-sort" name="linked_sort_by" class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800">
|
||||
<option value="client_name" {% if filters.sort_by == 'client_name' %}selected{% endif %}>Client name</option>
|
||||
<option value="client_code" {% if filters.sort_by == 'client_code' %}selected{% endif %}>Client code</option>
|
||||
<option value="pan" {% if filters.sort_by == 'pan' %}selected{% endif %}>PAN</option>
|
||||
<option value="client_group" {% if filters.sort_by == 'client_group' %}selected{% endif %}>Client group</option>
|
||||
<option value="status" {% if filters.sort_by == 'status' %}selected{% endif %}>Status</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-end gap-2">
|
||||
<input type="hidden" name="linked_sort_order" value="{{ filters.sort_order }}">
|
||||
<input type="hidden" name="linked_per_page" value="{{ meta.per_page }}">
|
||||
<button type="submit" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-700">Find</button>
|
||||
<a href="/consultant/dashboard?tab=clients" class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Clear</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-5 overflow-x-auto rounded-2xl border border-slate-200">
|
||||
<table class="min-w-full divide-y divide-slate-200 text-left">
|
||||
<thead class="bg-slate-50">
|
||||
<tr class="text-xs font-semibold uppercase tracking-wide text-slate-500">
|
||||
<th class="whitespace-nowrap px-4 py-3">Code</th>
|
||||
<th class="min-w-[260px] px-4 py-3">Client Name</th>
|
||||
<th class="whitespace-nowrap px-4 py-3">PAN</th>
|
||||
<th class="min-w-[180px] px-4 py-3">Client Group</th>
|
||||
<th class="whitespace-nowrap px-4 py-3">Role</th>
|
||||
<th class="whitespace-nowrap px-4 py-3">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100 bg-white">
|
||||
{% for row in linked.rows %}
|
||||
<tr class="hover:bg-slate-50">
|
||||
<td class="whitespace-nowrap px-4 py-3 text-sm font-semibold text-slate-700">{{ row.client.client_code or '-' }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<div class="font-semibold text-slate-900">{{ row.client.client_name }}</div>
|
||||
{% if row.client.trade_name and row.client.trade_name != row.client.client_name %}<div class="text-xs text-slate-500">{{ row.client.trade_name }}</div>{% endif %}
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-4 py-3 text-sm text-slate-700">{{ row.client.pan or '-' }}</td>
|
||||
<td class="px-4 py-3 text-sm text-slate-700">
|
||||
{% if row.client_group_name %}
|
||||
<div class="font-medium">{{ row.client_group_name }}</div>
|
||||
{% if row.client_group_code %}<div class="text-xs text-slate-500">{{ row.client_group_code }}</div>{% endif %}
|
||||
{% else %}-{% endif %}
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-4 py-3 text-sm text-slate-700">{{ row.relationship_label }}</td>
|
||||
<td class="whitespace-nowrap px-4 py-3">
|
||||
{% if row.client.status == 'active' %}
|
||||
<span class="rounded-full bg-emerald-100 px-2.5 py-1 text-xs font-semibold text-emerald-700">Active</span>
|
||||
{% else %}
|
||||
<span class="rounded-full bg-slate-100 px-2.5 py-1 text-xs font-semibold text-slate-600">{{ (row.client.status or 'inactive').replace('_',' ').title() }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="6" class="px-4 py-10 text-center text-sm text-slate-500">No linked firm clients match the selected filters.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div class="text-sm text-slate-600">
|
||||
{% if meta.total %}
|
||||
Showing <span class="font-semibold text-slate-900">{{ ((meta.page - 1) * meta.per_page) + 1 }}</span> to
|
||||
<span class="font-semibold text-slate-900">{{ meta.total if meta.page * meta.per_page > meta.total else meta.page * meta.per_page }}</span>
|
||||
of <span class="font-semibold text-slate-900">{{ meta.total }}</span> clients
|
||||
{% else %}No clients found{% endif %}
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<form method="get" action="/consultant/dashboard" class="flex items-center gap-2">
|
||||
<input type="hidden" name="tab" value="clients">
|
||||
<input type="hidden" name="linked_q" value="{{ filters.q }}">
|
||||
{% if filters.client_group_id %}<input type="hidden" name="linked_group_id" value="{{ filters.client_group_id }}">{% endif %}
|
||||
<input type="hidden" name="linked_status" value="{{ filters.status }}">
|
||||
<input type="hidden" name="linked_sort_by" value="{{ filters.sort_by }}">
|
||||
<input type="hidden" name="linked_sort_order" value="{{ filters.sort_order }}">
|
||||
<input type="hidden" name="linked_page" value="1">
|
||||
<label for="linked-per-page" class="text-sm text-slate-600">Rows</label>
|
||||
<select id="linked-per-page" name="linked_per_page" onchange="this.form.submit()" class="rounded-lg border border-slate-300 bg-white px-2 py-1.5 text-sm text-slate-700">
|
||||
{% for size in [25, 50, 100] %}<option value="{{ size }}" {% if meta.per_page == size %}selected{% endif %}>{{ size }}</option>{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
{% set query = namespace(value='tab=clients&linked_q=' ~ (filters.q|urlencode) ~ '&linked_status=' ~ (filters.status|urlencode) ~ '&linked_per_page=' ~ meta.per_page ~ '&linked_sort_by=' ~ (filters.sort_by|urlencode) ~ '&linked_sort_order=' ~ (filters.sort_order|urlencode)) %}
|
||||
{% if filters.client_group_id %}{% set query.value = query.value ~ '&linked_group_id=' ~ filters.client_group_id %}{% endif %}
|
||||
{% if meta.page > 1 %}<a href="/consultant/dashboard?{{ query.value }}&linked_page={{ meta.page - 1 }}" class="rounded-lg border border-slate-300 px-3 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Previous</a>{% endif %}
|
||||
{% for number in range(1, meta.pages + 1) %}
|
||||
{% if number == meta.page %}
|
||||
<span class="rounded-lg bg-brand-600 px-3 py-2 text-sm font-semibold text-white">{{ number }}</span>
|
||||
{% elif number == 1 or number == meta.pages or (number >= meta.page - 2 and number <= meta.page + 2) %}
|
||||
<a href="/consultant/dashboard?{{ query.value }}&linked_page={{ number }}" class="rounded-lg px-3 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-100">{{ number }}</a>
|
||||
{% elif number == meta.page - 3 or number == meta.page + 3 %}<span class="px-1 text-slate-400">…</span>{% endif %}
|
||||
{% endfor %}
|
||||
{% if meta.page < meta.pages %}<a href="/consultant/dashboard?{{ query.value }}&linked_page={{ meta.page + 1 }}" class="rounded-lg border border-slate-300 px-3 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Next</a>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="af-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div><h3 class="text-lg font-semibold text-slate-900">Managed Clients</h3><p class="mt-1 text-sm text-slate-500">Clients maintained by the consultant workspace.</p></div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-slate-900">Managed Clients</h3>
|
||||
<p class="mt-1 text-sm text-slate-500">Clients maintained separately within the consultant workspace.</p>
|
||||
</div>
|
||||
<a href="/consultant/managed-clients" class="text-sm font-semibold text-brand-700 hover:underline">View all</a>
|
||||
</div>
|
||||
<div class="mt-4 space-y-3">
|
||||
<div class="mt-4 grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
||||
{% for client in payload.managed_clients %}
|
||||
<a href="/consultant/managed-clients/{{ client.id }}" class="block rounded-xl border border-slate-200 p-3 hover:bg-slate-50">
|
||||
<div class="font-semibold text-slate-900">{{ client.client_name }}</div>
|
||||
@@ -12,31 +150,8 @@
|
||||
<div class="mt-2 text-xs text-slate-600">Conversion: {{ client.conversion_status.replace('_',' ').title() if client.conversion_status else 'Not Requested' }}</div>
|
||||
</a>
|
||||
{% else %}
|
||||
<div class="rounded-2xl border border-dashed border-slate-300 p-6 text-sm text-slate-500">No managed clients yet.</div>
|
||||
<div class="rounded-2xl border border-dashed border-slate-300 p-6 text-sm text-slate-500 md:col-span-2 xl:col-span-3">No managed clients yet.</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="af-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div><h3 class="text-lg font-semibold text-slate-900">Linked Firm Clients</h3><p class="mt-1 text-sm text-slate-500">Only explicitly linked firm clients are visible.</p></div>
|
||||
<span class="rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold text-slate-600">{{ payload.active_links|length }} active</span>
|
||||
</div>
|
||||
<div class="mt-4 space-y-3">
|
||||
{% for link in payload.active_links %}
|
||||
<div class="rounded-xl border border-slate-200 p-3">
|
||||
<div class="font-semibold text-slate-900">{{ link.client.client_name if link.client else '-' }}</div>
|
||||
<div class="text-xs text-slate-500">{{ link.client.client_code if link.client else '' }} • {{ link.relationship_type.replace('_',' ').title() }}</div>
|
||||
<div class="mt-2 flex flex-wrap gap-2 text-xs">
|
||||
<span class="rounded-full bg-slate-100 px-2 py-1 font-semibold text-slate-600">Client {{ 'visible' if link.can_view_client else 'hidden' }}</span>
|
||||
<span class="rounded-full bg-slate-100 px-2 py-1 font-semibold text-slate-600">Services {{ 'visible' if link.can_view_services else 'hidden' }}</span>
|
||||
<span class="rounded-full bg-slate-100 px-2 py-1 font-semibold text-slate-600">Due dates {{ 'visible' if link.can_view_due_dates else 'hidden' }}</span>
|
||||
<span class="rounded-full bg-slate-100 px-2 py-1 font-semibold text-slate-600">Communications {{ 'visible' if link.can_view_communications else 'hidden' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="rounded-2xl border border-dashed border-slate-300 p-6 text-sm text-slate-500">No firm clients are linked yet.</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -27,6 +27,7 @@ from app.modules.consultants.service import (
|
||||
approve_managed_client_conversion,
|
||||
consultant_can_add_managed_client,
|
||||
consultant_dashboard_payload,
|
||||
consultant_linked_clients_page,
|
||||
create_consultant_service_request,
|
||||
create_or_update_consultant,
|
||||
create_or_update_managed_client,
|
||||
@@ -1349,6 +1350,12 @@ def _consultant_empty_payload() -> dict:
|
||||
return {
|
||||
"links": [],
|
||||
"active_links": [],
|
||||
"linked_clients_page": {
|
||||
"rows": [],
|
||||
"groups": [],
|
||||
"meta": {"total": 0, "page": 1, "per_page": 25, "pages": 1},
|
||||
"filters": {"q": "", "client_group_id": None, "status": "active", "sort_by": "client_name", "sort_order": "asc"},
|
||||
},
|
||||
"comments": [],
|
||||
"recent_firm_messages": [],
|
||||
"recent_consultant_replies": [],
|
||||
@@ -1390,8 +1397,30 @@ def _consultant_empty_payload() -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _build_consultant_portal_payload(db, consultant) -> dict:
|
||||
def _build_consultant_portal_payload(
|
||||
db,
|
||||
consultant,
|
||||
*,
|
||||
linked_q: str = "",
|
||||
linked_group_id: int | None = None,
|
||||
linked_status: str = "active",
|
||||
linked_page: int = 1,
|
||||
linked_per_page: int = 25,
|
||||
linked_sort_by: str = "client_name",
|
||||
linked_sort_order: str = "asc",
|
||||
) -> dict:
|
||||
payload = consultant_dashboard_payload(db, consultant=consultant)
|
||||
payload["linked_clients_page"] = consultant_linked_clients_page(
|
||||
db,
|
||||
consultant=consultant,
|
||||
q=linked_q,
|
||||
client_group_id=linked_group_id,
|
||||
status=linked_status,
|
||||
page=linked_page,
|
||||
per_page=linked_per_page,
|
||||
sort_by=linked_sort_by,
|
||||
sort_order=linked_sort_order,
|
||||
)
|
||||
try:
|
||||
payload["work_board"] = get_consultant_work_board(db, consultant=consultant)
|
||||
except Exception:
|
||||
@@ -1405,7 +1434,17 @@ def _build_consultant_portal_payload(db, consultant) -> dict:
|
||||
|
||||
|
||||
@portal_router.get("/dashboard")
|
||||
def consultant_dashboard(request: Request, tab: str = "overview"):
|
||||
def consultant_dashboard(
|
||||
request: Request,
|
||||
tab: str = "overview",
|
||||
linked_q: str = "",
|
||||
linked_group_id: int | None = None,
|
||||
linked_status: str = "active",
|
||||
linked_page: int = 1,
|
||||
linked_per_page: int = 25,
|
||||
linked_sort_by: str = "client_name",
|
||||
linked_sort_order: str = "asc",
|
||||
):
|
||||
db = CommonSessionLocal()
|
||||
try:
|
||||
user = get_current_user(request, db=db)
|
||||
@@ -1416,7 +1455,17 @@ def consultant_dashboard(request: Request, tab: str = "overview"):
|
||||
tenant_id = int(getattr(user, "tenant_id", 0) or 0)
|
||||
consultant = get_consultant_by_user(db, tenant_id=tenant_id, user_id=user.id)
|
||||
active_tab = tab if tab in CONSULTANT_PORTAL_TABS else "overview"
|
||||
payload = _build_consultant_portal_payload(db, consultant) if consultant else _consultant_empty_payload()
|
||||
payload = _build_consultant_portal_payload(
|
||||
db,
|
||||
consultant,
|
||||
linked_q=linked_q,
|
||||
linked_group_id=linked_group_id,
|
||||
linked_status=linked_status,
|
||||
linked_page=linked_page,
|
||||
linked_per_page=linked_per_page,
|
||||
linked_sort_by=linked_sort_by,
|
||||
linked_sort_order=linked_sort_order,
|
||||
) if consultant else _consultant_empty_payload()
|
||||
return _render(
|
||||
request,
|
||||
"modules/consultants/templates/consultants/portal_dashboard.html",
|
||||
@@ -1432,7 +1481,17 @@ def consultant_dashboard(request: Request, tab: str = "overview"):
|
||||
|
||||
|
||||
@portal_router.get("/dashboard/tab/{tab_name}")
|
||||
def consultant_dashboard_tab(request: Request, tab_name: str):
|
||||
def consultant_dashboard_tab(
|
||||
request: Request,
|
||||
tab_name: str,
|
||||
linked_q: str = "",
|
||||
linked_group_id: int | None = None,
|
||||
linked_status: str = "active",
|
||||
linked_page: int = 1,
|
||||
linked_per_page: int = 25,
|
||||
linked_sort_by: str = "client_name",
|
||||
linked_sort_order: str = "asc",
|
||||
):
|
||||
db = CommonSessionLocal()
|
||||
try:
|
||||
user = get_current_user(request, db=db)
|
||||
@@ -1443,7 +1502,17 @@ def consultant_dashboard_tab(request: Request, tab_name: str):
|
||||
tenant_id = int(getattr(user, "tenant_id", 0) or 0)
|
||||
consultant = get_consultant_by_user(db, tenant_id=tenant_id, user_id=user.id)
|
||||
active_tab = tab_name if tab_name in CONSULTANT_PORTAL_TABS else "overview"
|
||||
payload = _build_consultant_portal_payload(db, consultant) if consultant else _consultant_empty_payload()
|
||||
payload = _build_consultant_portal_payload(
|
||||
db,
|
||||
consultant,
|
||||
linked_q=linked_q,
|
||||
linked_group_id=linked_group_id,
|
||||
linked_status=linked_status,
|
||||
linked_page=linked_page,
|
||||
linked_per_page=linked_per_page,
|
||||
linked_sort_by=linked_sort_by,
|
||||
linked_sort_order=linked_sort_order,
|
||||
) if consultant else _consultant_empty_payload()
|
||||
return _render(
|
||||
request,
|
||||
CONSULTANT_PORTAL_TABS[active_tab],
|
||||
|
||||
Reference in New Issue
Block a user