фикс статистики 4
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
dv 2026-04-15 10:49:26 +03:00
parent b88001909f
commit d571581395
4 changed files with 34 additions and 2 deletions

View file

@ -64,7 +64,7 @@ let missingCommentsFetchInFlight = false;
async function init() {
await loadInternalStatusesConfig(false, true);
await Promise.all([loadMarks(), loadNotes(), loadMentions(), loadUserList(), loadCommentCounts()]);
await Promise.all([loadMarks(), loadNotes(), loadMentions(), loadUserList(), loadCommentCounts(), loadClientDict()]);
await loadTasksFromServer();
if (currentUser.is_admin) {
@ -106,6 +106,7 @@ async function loadMarks() { const d = await apiGet('/api/marks'); if (d) marksC
async function loadNotes() { const d = await apiGet('/api/notes'); if (d) notesCache = d; }
async function loadUserList() { const d = await apiGet('/api/users/list'); if (d) knownUsers = d.map((u) => u.toLowerCase()); }
async function loadCommentCounts() { const d = await apiGet('/api/comments/counts'); if (d && typeof d === 'object') commentCounts = d; }
async function loadClientDict() { const d = await apiGet('/api/clients'); if (Array.isArray(d)) window._allClients = d; }
async function loadTasksFromServer(silent = false) {
if (!silent && !allData.length) showLoading();

View file

@ -840,8 +840,16 @@ function renderClientUsersCard() {
if (!wrap) return;
// createdby — строка с именем создателя, приходит прямо из API
// Собираем: организация → { total, users: { createdby → count } }
// Базовый список клиентов берём из _allClients (все usergroup из dictionaries),
// чтобы показывать клиентов с нулём заявок тоже.
const cMap = {};
// Сначала заполняем всех известных клиентов с нулями
(window._allClients || []).forEach((c) => {
cMap[c.name] = { total: 0, users: {} };
});
// Затем считаем реальные заявки
allData.forEach((item) => {
const t = item.task;
const org = t._clientname || t.clientname || 'Неизвестно';

View file

@ -97,6 +97,12 @@ function createRefreshService({ db, fetch, getSettings, sseBroadcast, logger = c
INSERT OR IGNORE INTO sync_state(id, cache_version, refresh_in_progress)
VALUES(1, 0, 0);
CREATE TABLE IF NOT EXISTS client_dict (
client_id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS tasks_index (
task_id TEXT PRIMARY KEY,
task_number TEXT NOT NULL,
@ -143,6 +149,10 @@ function createRefreshService({ db, fetch, getSettings, sseBroadcast, logger = c
`),
clearIndex: db.prepare('DELETE FROM tasks_index'),
insertIndex: db.prepare('INSERT INTO tasks_index(task_id, task_number, updated_at) VALUES(?,?,?)'),
upsertClient: db.prepare(`
INSERT INTO client_dict(client_id, name, updated_at) VALUES(?,?,?)
ON CONFLICT(client_id) DO UPDATE SET name=excluded.name, updated_at=excluded.updated_at
`),
upsertIntraStatus: db.prepare(`
INSERT INTO intra_status_dict(status_id,name,updated_at) VALUES(?,?,?)
ON CONFLICT(status_id) DO UPDATE SET name=excluded.name,updated_at=excluded.updated_at
@ -452,6 +462,14 @@ function createRefreshService({ db, fetch, getSettings, sseBroadcast, logger = c
const dictMap = {};
(json.dictionaries || []).forEach((d) => { dictMap[d.id] = d.name; });
// Сохраняем клиентов (usergroup) в отдельную таблицу
const now = safeIsoNow();
const clientEntries = (json.dictionaries || []).filter((d) => d.type === 'usergroup' && !d.isarchived);
const upsertClients = db.transaction((entries) => {
for (const d of entries) stmts.upsertClient.run(d.id, d.name, now);
});
if (clientEntries.length) upsertClients(clientEntries);
tasks.forEach((task) => {
if (task.clientid && dictMap[task.clientid]) task._clientname = dictMap[task.clientid];
if (task.executor && dictMap[task.executor]) task._executorname = dictMap[task.executor];

View file

@ -524,6 +524,11 @@ app.get('/api/status', auth, (req, res) => {
});
// ── Tasks ─────────────────────────────────────────────────────────
app.get('/api/clients', auth, (req, res) => {
const rows = db.prepare('SELECT client_id, name FROM client_dict ORDER BY name ASC').all();
res.json(rows);
});
app.get('/api/tasks', auth, (req, res) => {
const cache = db.prepare('SELECT * FROM tasks_cache WHERE id=1').get();
const sync = refreshService.getStatusSnapshot();