Commit 798660b68f5e89be5915b80a639a1342c0d63073
1 parent
a2b65202
Link task_id to Graph console and keep #/graph URL in sync with selection
v_task_board's task_id cell now links to #/graph?task_id=<id> for quick navigation to a task's execution graph. v_graph_console reads that param to preselect the task, and syncs the URL (via history.replaceState) on every task load -- including the initial auto-preselect -- so the address bar always reflects the task actually showing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Showing
1 changed file
with
19 additions
and
3 deletions
frontend/app.js
| ... | ... | @@ -1049,7 +1049,11 @@ function mountTaskBoard(queryParams) { |
| 1049 | 1049 | } |
| 1050 | 1050 | rowsEl.innerHTML = tasks.map((t) => ( |
| 1051 | 1051 | '<tr data-task-id="' + t.task_id + '">' |
| 1052 | - + "<td>" + t.task_id + "</td>" | |
| 1052 | + // task_id links straight to Graph 控制台 (#/graph?task_id=...) so a | |
| 1053 | + // task can be located there directly, without going through the row's | |
| 1054 | + // own click-through to #/tasks/:task_id first -- stopPropagation on | |
| 1055 | + // the anchor keeps that row click-through intact for the other cells. | |
| 1056 | + + '<td><a href="#/graph?task_id=' + encodeURIComponent(t.task_id) + '" onclick="event.stopPropagation()">' + t.task_id + "</a></td>" | |
| 1053 | 1057 | + "<td>" + escapeHtml(t.goal_text) + "</td>" |
| 1054 | 1058 | + '<td><span class="badge ' + t.status + '">' + t.status + "</span></td>" |
| 1055 | 1059 | + "<td>" + t.assigned_org_node + "</td>" |
| ... | ... | @@ -2037,7 +2041,7 @@ function renderGraphConsole() { |
| 2037 | 2041 | `; |
| 2038 | 2042 | } |
| 2039 | 2043 | |
| 2040 | -function mountGraphConsole() { | |
| 2044 | +function mountGraphConsole(queryParams) { | |
| 2041 | 2045 | const projectId = getCurrentProjectId(); |
| 2042 | 2046 | if (!projectId) return; |
| 2043 | 2047 | |
| ... | ... | @@ -2046,7 +2050,12 @@ function mountGraphConsole() { |
| 2046 | 2050 | const workGraphPanelsEl = document.getElementById("gc_work_graph_panels"); |
| 2047 | 2051 | const artifactsOverviewEl = document.getElementById("gc_artifacts_overview"); |
| 2048 | 2052 | |
| 2049 | - let currentTaskId = null; | |
| 2053 | + // "#/graph?task_id=<id>"-style deep link (e.g. from v_task_board's task_id | |
| 2054 | + // column) preselects this task on first load, same pattern as | |
| 2055 | + // pickInitialTaskBoardStatus. populateTaskSelect below already falls back | |
| 2056 | + // to the default "first non-done" task if this id doesn't match any task | |
| 2057 | + // in the project, so an unknown/stale id is silently ignored. | |
| 2058 | + let currentTaskId = (queryParams && queryParams.task_id) || null; | |
| 2050 | 2059 | |
| 2051 | 2060 | // Same "track open log viewers, close them all on re-render/route-leave" |
| 2052 | 2061 | // pattern as mountTaskDetail's activeLogViewers/closeAllLogViewers, so a |
| ... | ... | @@ -2083,6 +2092,13 @@ function mountGraphConsole() { |
| 2083 | 2092 | async function loadTaskAndRender(taskId) { |
| 2084 | 2093 | closeAllLogViewers(); |
| 2085 | 2094 | currentTaskId = taskId; |
| 2095 | + // Keep the URL in sync with whichever task is actually showing -- | |
| 2096 | + // including the initial auto-preselect, not just explicit dropdown | |
| 2097 | + // switches -- so the address bar is always a valid deep link back to | |
| 2098 | + // this exact task (same history.replaceState/no-hashchange pattern | |
| 2099 | + // v_task_detail's org-strip click-through already uses above). | |
| 2100 | + const newHash = "#/graph?task_id=" + encodeURIComponent(taskId); | |
| 2101 | + if (window.location.hash !== newHash) history.replaceState(null, "", newHash); | |
| 2086 | 2102 | workGraphPanelsEl.innerHTML = '<div class="hint">加载中…</div>'; |
| 2087 | 2103 | const resp = await fetch("/api/tasks/" + encodeURIComponent(taskId)); |
| 2088 | 2104 | if (resp.status !== 200) { | ... | ... |