Add compact searchable consultant client list

This commit is contained in:
A R R R Associates
2026-07-31 12:22:24 +05:30
parent d791670003
commit 07668318aa
4 changed files with 336 additions and 34 deletions
@@ -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>