diff --git a/app/modules/partner_dashboard/templates/partner_dashboard/partials/clients.html b/app/modules/partner_dashboard/templates/partner_dashboard/partials/clients.html
index f56985f..f45a4e5 100644
--- a/app/modules/partner_dashboard/templates/partner_dashboard/partials/clients.html
+++ b/app/modules/partner_dashboard/templates/partner_dashboard/partials/clients.html
@@ -45,7 +45,9 @@
{% for c in clients %}
{% set row_status = (c.status or 'active')|lower %}
-
+
| {{ c.client_code }} |
{{ c.client_name }} |
{{ c.pan or c.gstin or '-' }} |
@@ -53,49 +55,26 @@
{{ c.task_count }} |
{{ c.overdue_count }} |
- {% if row_status == 'active' %}Active
- {% elif row_status == 'archived' %}Archived
- {% else %}Inactive{% endif %}
+ {% if row_status == 'active' %}
+ Active
+ {% elif row_status == 'archived' %}
+ Archived
+ {% else %}
+ Inactive
+ {% endif %}
+ |
+
+ Open
|
- Open |
{% else %}
| No assigned clients found. |
{% endfor %}
- | No clients match the search. |
+
+ | No clients match the search. |
+
-
+
diff --git a/app/ui/static/js/partner_dashboard_clients.js b/app/ui/static/js/partner_dashboard_clients.js
new file mode 100644
index 0000000..75b63cf
--- /dev/null
+++ b/app/ui/static/js/partner_dashboard_clients.js
@@ -0,0 +1,107 @@
+(() => {
+ "use strict";
+
+ const ROOT_SELECTOR = "[data-partner-client-list]";
+
+ function initialiseRoot(root) {
+ if (!(root instanceof Element) || root.dataset.liveFilterReady === "1") {
+ return;
+ }
+
+ const search = root.querySelector("[data-client-search]");
+ const status = root.querySelector("[data-client-status]");
+ const clear = root.querySelector("[data-client-clear]");
+ const scroll = root.querySelector("[data-client-scroll]");
+ const count = root.querySelector("[data-visible-count]");
+ const empty = root.querySelector("[data-no-results]");
+ const rows = Array.from(root.querySelectorAll("[data-client-row]"));
+
+ if (!search || !status || !clear || !count) {
+ return;
+ }
+
+ root.dataset.liveFilterReady = "1";
+ let debounceTimer = null;
+
+ function applyFilter() {
+ const term = String(search.value || "").trim().toLocaleLowerCase();
+ const wantedStatus = String(status.value || "").trim().toLocaleLowerCase();
+ let visible = 0;
+
+ rows.forEach((row) => {
+ const searchable = String(row.dataset.search || "").toLocaleLowerCase();
+ const rowStatus = String(row.dataset.status || "").toLocaleLowerCase();
+ const matchesText = !term || searchable.includes(term);
+ const matchesStatus = !wantedStatus || rowStatus === wantedStatus;
+ const shouldShow = matchesText && matchesStatus;
+
+ row.hidden = !shouldShow;
+ if (shouldShow) {
+ visible += 1;
+ }
+ });
+
+ count.textContent = `${visible} ${visible === 1 ? "client" : "clients"}`;
+
+ if (empty) {
+ empty.hidden = visible !== 0;
+ }
+
+ if (scroll) {
+ scroll.scrollTop = 0;
+ }
+ }
+
+ search.addEventListener("input", () => {
+ window.clearTimeout(debounceTimer);
+ debounceTimer = window.setTimeout(applyFilter, 200);
+ });
+
+ status.addEventListener("change", applyFilter);
+
+ clear.addEventListener("click", () => {
+ window.clearTimeout(debounceTimer);
+ search.value = "";
+ status.value = "active";
+ applyFilter();
+ search.focus();
+ });
+
+ applyFilter();
+ }
+
+ function initialiseWithin(scope = document) {
+ if (scope instanceof Element && scope.matches(ROOT_SELECTOR)) {
+ initialiseRoot(scope);
+ }
+
+ if (typeof scope.querySelectorAll === "function") {
+ scope.querySelectorAll(ROOT_SELECTOR).forEach(initialiseRoot);
+ }
+ }
+
+ function start() {
+ initialiseWithin(document);
+
+ const observer = new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ mutation.addedNodes.forEach((node) => {
+ if (node instanceof Element) {
+ initialiseWithin(node);
+ }
+ });
+ });
+ });
+
+ observer.observe(document.documentElement, {
+ childList: true,
+ subtree: true,
+ });
+ }
+
+ if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", start, { once: true });
+ } else {
+ start();
+ }
+})();