fix(ai): adaptive max_tokens per backend (reasoning profiles get 1024+, native llama.cpp stays compact; per-profile max_tokens override); include entry texts in ai_status SSE notify so journal updates live without reload; audit log action filter; links pagination; settings validation & UI improvements
This commit is contained in:
+23
-6
@@ -3,6 +3,8 @@ let groups = [];
|
||||
let editingId = null;
|
||||
let studentsLoaded = false;
|
||||
let linkSettings = null;
|
||||
let linkPage = 1;
|
||||
const LINK_PAGE_SIZE = 20;
|
||||
|
||||
async function loadLinkSettings() {
|
||||
if (!linkSettings) {
|
||||
@@ -16,21 +18,30 @@ function linkUrl(token) { return `${location.origin}/s/${token}`; }
|
||||
|
||||
async function loadLinks() {
|
||||
await loadLinkSettings();
|
||||
const res = await fetch(`${API}/api/links`, { headers: hdr() });
|
||||
links = await res.json();
|
||||
const p = new URLSearchParams();
|
||||
p.set('limit', LINK_PAGE_SIZE);
|
||||
p.set('offset', (linkPage - 1) * LINK_PAGE_SIZE);
|
||||
const res = await fetch(`${API}/api/links?${p}`, { headers: hdr() });
|
||||
const data = await res.json();
|
||||
links = data.items;
|
||||
const total = data.total;
|
||||
const el = document.getElementById('linksList');
|
||||
if (!links.length) { el.innerHTML = '<div class="empty">Ссылок пока нет. Создайте её в Журнале кнопкой «Ссылка».</div>'; return; }
|
||||
if (!links.length) { el.innerHTML = `<div class="empty">${linkPage > 1 ? 'На этой странице ссылок нет.' : 'Ссылок пока нет. Создайте её в Журнале кнопкой «Ссылка».'}</div>`; renderPager(document.getElementById('pager'), 1, 1, goLinkPage); return; }
|
||||
const now = Date.now();
|
||||
el.innerHTML = links.map(l => {
|
||||
const namesHidden = l.show_student_names != null ? !l.show_student_names : linkSettings.share_show_student_names === 'false';
|
||||
const expired = l.expires_at && new Date(l.expires_at).getTime() < now;
|
||||
const daysLeft = l.expires_at ? Math.ceil((new Date(l.expires_at).getTime() - now) / 86400000) : null;
|
||||
const soon = !expired && daysLeft !== null && daysLeft <= 1;
|
||||
return `
|
||||
<div class="item">
|
||||
<div class="item${expired ? ' expired' : ''}">
|
||||
<div style="min-width:0">
|
||||
<div class="name">${esc(l.name)}</div>
|
||||
<div class="name">${esc(l.name)}${expired ? ' <span class="badge-expired">истекла</span>' : soon ? ' <span class="badge-soon">истекает</span>' : ''}</div>
|
||||
<div style="font-size:.8rem;color:var(--muted);margin-top:2px">
|
||||
${l.group_name ? `Группа: <b>${esc(l.group_name)}</b>` : ''}
|
||||
${l.student_name ? `${l.group_name ? ' · ' : ''}Ученик: <b>${esc(l.student_name)}</b>` : ''}
|
||||
${l.date_from || l.date_to ? ` · период: <b>${l.date_from ? l.date_from.split('-').reverse().join('.') : '…'}</b> — <b>${l.date_to ? l.date_to.split('-').reverse().join('.') : '…'}</b>` : ' · весь период'}
|
||||
${l.expires_at ? ` · истекает ${new Date(l.expires_at).toLocaleDateString('ru')}` : ''}
|
||||
${expired ? ` · <span style="color:#ef4444;font-weight:600">срок действия истёк ${new Date(l.expires_at).toLocaleDateString('ru')}</span>` : l.expires_at ? ` · истекает ${new Date(l.expires_at).toLocaleDateString('ru')}` : ''}
|
||||
${namesHidden ? ` · <span style="color:#f59e0b">имена скрыты</span>` : ''}
|
||||
${l.access_password_hash ? ` · <span style="color:#2563eb">🔒 пароль</span>` : ''}
|
||||
· создана ${new Date(l.created_at).toLocaleString('ru')}
|
||||
@@ -44,6 +55,12 @@ async function loadLinks() {
|
||||
</div>
|
||||
</div>
|
||||
`;}).join('');
|
||||
renderPager(document.getElementById('pager'), linkPage, Math.max(1, Math.ceil(total / LINK_PAGE_SIZE)), goLinkPage);
|
||||
}
|
||||
|
||||
function goLinkPage(p) {
|
||||
linkPage = p;
|
||||
loadLinks();
|
||||
}
|
||||
|
||||
function fmtDate(d) { const [y, m, day] = (d || '').split('-'); return d ? `${day}.${m}.${y}` : ''; }
|
||||
|
||||
Reference in New Issue
Block a user