From 79ea884dd5e40dfafd0bc945f55342633388dd74 Mon Sep 17 00:00:00 2001 From: dv Date: Wed, 15 Apr 2026 11:27:07 +0300 Subject: [PATCH] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=20=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D1=82=D0=B8=D1=81=D1=82=D0=B8=D0=BA=D0=B8=207?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.html | 9 +++++++++ public/js/auth.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/public/index.html b/public/index.html index 8a69f34..e744c7b 100644 --- a/public/index.html +++ b/public/index.html @@ -36,6 +36,7 @@ +
@@ -135,6 +136,14 @@ UsernameCreatedNew Password +
diff --git a/public/js/auth.js b/public/js/auth.js index 727cc9c..5f22ee8 100644 --- a/public/js/auth.js +++ b/public/js/auth.js @@ -109,6 +109,50 @@ async function loadCommentCounts() { const d = await apiGet('/api/comments/count async function loadClientDict() { const d = await apiGet('/api/clients'); if (Array.isArray(d)) window._allClients = d; } async function loadClientUsers() { const d = await apiGet('/api/client-users'); if (d && typeof d === 'object') window._clientUsers = d; } +async function syncIntraClients() { + const btn = document.getElementById('sync-clients-btn'); + const statusEl = document.getElementById('sync-clients-status'); + if (btn) { btn.disabled = true; btn.textContent = '⏳ Синхронизация...'; } + if (statusEl) statusEl.textContent = ''; + try { + const r = await apiPost('/api/admin/sync-client-users', {}); + if (r?.ok) { + if (statusEl) statusEl.textContent = `✓ Синхронизировано. Записей в базе: ${r.count}`; + await loadClientUsers(); + renderIntraClientsList(); + } else { + if (statusEl) statusEl.textContent = `❌ ${r?.error || 'Ошибка'}`; + } + } catch (e) { + if (statusEl) statusEl.textContent = `❌ ${e.message}`; + } finally { + if (btn) { btn.disabled = false; btn.textContent = '↻ Синхронизировать'; } + } +} + +function renderIntraClientsList() { + const container = document.getElementById('intra-clients-list'); + if (!container) return; + const clientUsers = window._clientUsers || {}; + const allClients = window._allClients || []; + if (!allClients.length) { + container.innerHTML = '
Нет данных. Нажмите «Синхронизировать».
'; + return; + } + const sorted = [...allClients].sort((a, b) => a.name.localeCompare(b.name, 'ru')); + container.innerHTML = sorted.map((c) => { + const users = clientUsers[String(c.client_id)] || []; + const usersHtml = users.length + ? users.map((u) => `
${escHtml(u)}
`).join('') + : '
нет пользователей
'; + return `
+
${escHtml(c.name)} (id: ${c.client_id}, ${users.length} польз.)
+ ${usersHtml} +
`; + }).join(''); +} + + async function loadTasksFromServer(silent = false) { if (!silent && !allData.length) showLoading(); const data = await apiGet('/api/tasks'); @@ -366,6 +410,7 @@ function openSettingsTab(tabName) { document.querySelectorAll('.settings-tab-pane').forEach((pane) => { pane.style.display = pane.dataset.tab === tabName ? 'block' : 'none'; }); + if (tabName === 'intra-clients') renderIntraClientsList(); } async function loadInternalStatusesAdmin() {