feat(dashboard): детали задачи по клику — метаданные, промоушены, отчёт закрытия, JS-раскрытие строки (t-260929220004-033c0f)
This commit is contained in:
parent
2d70740e76
commit
62e9d756d7
2 changed files with 54 additions and 5 deletions
|
|
@ -39,6 +39,15 @@
|
|||
th{background:var(--ink);color:var(--paper);text-align:left;padding:8px 9px;font-family:ui-monospace,Menlo,monospace;font-size:10.5px;letter-spacing:.09em;text-transform:uppercase}
|
||||
td{border-bottom:1px solid var(--line);padding:8px 9px;vertical-align:top}
|
||||
tr:hover td{background:#fff3ea}
|
||||
tr.task{cursor:pointer}
|
||||
tr.task.open td{background:#fff3ea}
|
||||
tr.taskdetail td{background:var(--card);border-bottom:1px solid var(--ink)}
|
||||
.taskdetail .box{display:grid;grid-template-columns:150px 1fr;gap:4px 14px;padding:8px 2px 12px;font-size:13px}
|
||||
.taskdetail .k{font-family:ui-monospace,Menlo,monospace;font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--dim);padding-top:3px}
|
||||
.taskdetail pre{white-space:pre-wrap;word-break:break-word;margin:0;font-family:ui-monospace,Menlo,monospace;font-size:12px;line-height:1.5}
|
||||
.hint{color:var(--dim);font-size:12px;font-family:ui-monospace,Menlo,monospace}
|
||||
.arrow{display:inline-block;transition:transform .15s;color:var(--dim);font-size:10px}
|
||||
tr.task.open .arrow{transform:rotate(90deg)}
|
||||
.m{font-family:ui-monospace,Menlo,monospace;font-size:11.5px}
|
||||
.dim{color:var(--dim)}
|
||||
|
||||
|
|
@ -83,7 +92,7 @@
|
|||
<div id="ladder"><div class="skel">загружаю…</div></div>
|
||||
|
||||
<h2>Задачи <span class="m dim" id="taskcount"></span></h2>
|
||||
<div class="lead">сортировка: ready → running → failed → done, внутри — новые сверху</div>
|
||||
<div class="lead">сортировка: ready → running → failed → done, внутри — новые сверху · <span class="hint">клик по строке — детали задачи</span></div>
|
||||
<div id="tasks"><div class="skel">загружаю…</div></div>
|
||||
|
||||
<h2>Открытые эскалации <span class="m dim" id="esccount"></span></h2>
|
||||
|
|
@ -120,6 +129,30 @@ $("refresh").textContent = REFRESH;
|
|||
function pill(status){ return `<span class="pill ${esc(status)}">${esc(status)}</span>`; }
|
||||
function size(s){ return `<span class="pill size${esc(s)}">${esc(s)}</span>`; }
|
||||
|
||||
// детали задачи по клику (т-260929220004): раскрытие строки без перезагрузки
|
||||
const openTasks = new Set(); // id раскрытых строк (переживает автообновление)
|
||||
function toggleTask(id){
|
||||
if(openTasks.has(id)) openTasks.delete(id); else openTasks.add(id);
|
||||
const el = document.getElementById("tr-" + id);
|
||||
if(el) el.classList.toggle("open", openTasks.has(id));
|
||||
const det = document.getElementById("det-" + id);
|
||||
if(det) det.style.display = openTasks.has(id) ? "" : "none";
|
||||
}
|
||||
function taskDetail(t){
|
||||
const row = [
|
||||
["id", t.id],
|
||||
["размер / статус", `${t.size} · ${t.status}${t.level ? " · уровень " + t.level : ""}`],
|
||||
["создана", fmtTime(t.created)],
|
||||
["завершена", t.finished ? fmtTime(t.finished) : "—"],
|
||||
["session", t.session_id || "—"],
|
||||
t.promoted_to ? ["промоушен", `${t.promotions || 1}× → ${t.promoted_to}`] : null,
|
||||
t.error ? ["ошибка", t.error] : null,
|
||||
t.report ? ["отчёт (" + (t.report_level || "?") + ")", t.report] : null,
|
||||
].filter(Boolean);
|
||||
const box = row.map(([k, v]) => `<div class="k">${esc(k)}</div><div><pre>${esc(v)}</pre></div>`).join("");
|
||||
return `<tr class="taskdetail" id="det-${esc(t.id)}" style="display:${openTasks.has(t.id) ? "" : "none"}"><td colspan="7"><div class="box">${box}</div></td></tr>`;
|
||||
}
|
||||
|
||||
function setConn(cls, text){ $("dot").className = "dot " + cls; $("conn").textContent = text; }
|
||||
|
||||
async function tick(){
|
||||
|
|
@ -168,18 +201,20 @@ function render(s){
|
|||
</tr>`).join("")}</table>`
|
||||
: `<div class="skel">нет данных</div>`;
|
||||
|
||||
// задачи
|
||||
// задачи (клик по строке — детали, т-260929220004)
|
||||
$("taskcount").textContent = `(${s.tasks.length})`;
|
||||
$("tasks").innerHTML = s.tasks.length ? `<table>
|
||||
<tr><th>id</th><th>задача</th><th>размер</th><th>статус</th><th>уровень</th><th>создана</th></tr>
|
||||
${s.tasks.map(t => `<tr>
|
||||
<tr><th></th><th>id</th><th>задача</th><th>размер</th><th>статус</th><th>уровень</th><th>создана</th></tr>
|
||||
${s.tasks.map(t => `<tr class="task${openTasks.has(t.id) ? " open" : ""}" id="tr-${esc(t.id)}" onclick="toggleTask('${esc(t.id)}')">
|
||||
<td class="arrow">▶</td>
|
||||
<td class="m dim">${esc(t.id)}</td>
|
||||
<td><b>${esc(t.title)}</b>${t.error ? `<br><span class="m" style="color:var(--err)">${esc(String(t.error).slice(0,140))}</span>` : ""}</td>
|
||||
<td>${size(t.size)}</td>
|
||||
<td>${pill(t.status)}</td>
|
||||
<td class="m">${esc(t.level || "—")}</td>
|
||||
<td class="m dim">${fmtTime(t.created)}</td>
|
||||
</tr>`).join("")}</table>`
|
||||
</tr>
|
||||
${taskDetail(t)}`).join("")}</table>`
|
||||
: `<div class="skel">задач нет</div>`;
|
||||
|
||||
// эскалации
|
||||
|
|
|
|||
|
|
@ -51,13 +51,27 @@ def build_state() -> dict:
|
|||
"""
|
||||
model = es.run_projections()
|
||||
|
||||
# отчёт тика, закрывшего задачу: digest с summary_ref == result_ref
|
||||
digest_by_ref = {}
|
||||
for d in (model.get("digests") or {}).values():
|
||||
if d.get("summary_ref"):
|
||||
digest_by_ref[d["summary_ref"]] = d
|
||||
|
||||
tasks = []
|
||||
for t in model.get("tasks", {}).values():
|
||||
report = digest_by_ref.get(t.get("result_ref")) or {}
|
||||
tasks.append({
|
||||
"id": t.get("id"), "title": t.get("title", ""),
|
||||
"size": t.get("size", "?"), "status": t.get("status", "?"),
|
||||
"level": t.get("level"), "created": t.get("created"),
|
||||
"finished": t.get("finished"), "error": t.get("error"),
|
||||
# детали для раскрытия по клику (т-260929220004)
|
||||
"details": (t.get("details") or "")[:2000],
|
||||
"promotions": t.get("promotions"), "promoted_to": t.get("promoted_to"),
|
||||
"session_id": t.get("session_id"),
|
||||
"report_level": report.get("level"),
|
||||
"report_ts": report.get("ts"),
|
||||
"report": (report.get("text") or "")[:1500],
|
||||
})
|
||||
# два стабильных прохода: новые сверху внутри статуса,
|
||||
# статусы по порядку ready→running→failed→done
|
||||
|
|
|
|||
Loading…
Reference in a new issue