Commit 798660b68f5e89be5915b80a639a1342c0d63073

Authored by bboonnee
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) {
... ...