From a228bb946b8ce0863e844f9b3d0396503f0dbafc Mon Sep 17 00:00:00 2001 From: catlog22 Date: Thu, 15 Jan 2026 10:28:52 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Issue=20Manager=20completed=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E5=99=A8=E7=8E=B0=E5=9C=A8=E5=8F=AF=E4=BB=A5=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=BD=92=E6=A1=A3=E8=AE=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 loadIssueHistory() 函数从 /api/issues/history 加载归档议题 - 修改 filterIssuesByStatus() 在选择 completed 过滤器时加载历史数据 - 修改 renderIssueView() 合并当前已完成议题和归档议题 - 修改 renderIssueCard() 显示 "Archived" 标签区分归档议题 - 修改 openIssueDetail() 支持从缓存加载归档议题详情 - 添加 .issue-card.archived 和 .issue-archived-badge CSS样式 Fixes: https://github.com/catlog22/Claude-Code-Workflow/issues/76 Co-Authored-By: Claude Opus 4.5 --- .../dashboard-css/32-issue-manager.css | 23 +++++++ .../dashboard-js/views/issue-manager.js | 61 ++++++++++++++++--- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/ccw/src/templates/dashboard-css/32-issue-manager.css b/ccw/src/templates/dashboard-css/32-issue-manager.css index 026f1fc6..7ea95e25 100644 --- a/ccw/src/templates/dashboard-css/32-issue-manager.css +++ b/ccw/src/templates/dashboard-css/32-issue-manager.css @@ -128,6 +128,29 @@ box-shadow: 0 4px 12px hsl(var(--foreground) / 0.08); } +/* Archived Issue Card */ +.issue-card.archived { + opacity: 0.85; + background: hsl(var(--muted) / 0.3); +} + +.issue-card.archived:hover { + opacity: 1; +} + +.issue-archived-badge { + display: inline-flex; + align-items: center; + padding: 0.125rem 0.375rem; + background: hsl(var(--muted)); + color: hsl(var(--muted-foreground)); + font-size: 0.625rem; + font-weight: 500; + border-radius: 0.25rem; + text-transform: uppercase; + letter-spacing: 0.025em; +} + .issue-card-header { display: flex; align-items: flex-start; diff --git a/ccw/src/templates/dashboard-js/views/issue-manager.js b/ccw/src/templates/dashboard-js/views/issue-manager.js index 4669c0eb..5afaac7a 100644 --- a/ccw/src/templates/dashboard-js/views/issue-manager.js +++ b/ccw/src/templates/dashboard-js/views/issue-manager.js @@ -6,6 +6,7 @@ // ========== Issue State ========== var issueData = { issues: [], + historyIssues: [], // Archived/completed issues from history queue: { tasks: [], solutions: [], conflicts: [], execution_groups: [], grouped_items: {} }, selectedIssue: null, selectedSolution: null, @@ -58,6 +59,18 @@ async function loadIssueData() { } } +async function loadIssueHistory() { + try { + const response = await fetch('/api/issues/history?path=' + encodeURIComponent(projectPath)); + if (!response.ok) throw new Error('Failed to load issue history'); + const data = await response.json(); + issueData.historyIssues = data.issues || []; + } catch (err) { + console.error('Failed to load issue history:', err); + issueData.historyIssues = []; + } +} + async function loadQueueData() { try { const response = await fetch('/api/queue?path=' + encodeURIComponent(projectPath)); @@ -93,10 +106,21 @@ function renderIssueView() { if (!container) return; const issues = issueData.issues || []; + const historyIssues = issueData.historyIssues || []; + // Apply both status and search filters - let filteredIssues = issueData.statusFilter === 'all' - ? issues - : issues.filter(i => i.status === issueData.statusFilter); + let filteredIssues; + if (issueData.statusFilter === 'all') { + filteredIssues = issues; + } else if (issueData.statusFilter === 'completed') { + // For 'completed' filter, include both current completed issues and archived history issues + const currentCompleted = issues.filter(i => i.status === 'completed'); + // Mark history issues as archived for visual distinction + const archivedIssues = historyIssues.map(i => ({ ...i, _isArchived: true })); + filteredIssues = [...currentCompleted, ...archivedIssues]; + } else { + filteredIssues = issues.filter(i => i.status === issueData.statusFilter); + } if (issueData.searchQuery) { const query = issueData.searchQuery.toLowerCase(); @@ -309,12 +333,15 @@ function renderIssueCard(issue) { failed: 'failed' }; + const isArchived = issue._isArchived; + return ` -
+
${issue.id} ${issue.status || 'unknown'} + ${isArchived ? '' + (t('issues.archived') || 'Archived') + '' : ''}
${renderPriorityStars(issue.priority || 3)} @@ -360,8 +387,12 @@ function renderPriorityStars(priority) { return stars; } -function filterIssuesByStatus(status) { +async function filterIssuesByStatus(status) { issueData.statusFilter = status; + // Load history data when filtering by 'completed' status + if (status === 'completed' && issueData.historyIssues.length === 0) { + await loadIssueHistory(); + } renderIssueView(); } @@ -725,7 +756,7 @@ async function saveQueueOrder(groupId, newOrder) { } // ========== Detail Panel ========== -async function openIssueDetail(issueId) { +async function openIssueDetail(issueId, isArchived = false) { const panel = document.getElementById('issueDetailPanel'); if (!panel) return; @@ -733,7 +764,23 @@ async function openIssueDetail(issueId) { panel.classList.remove('hidden'); lucide.createIcons(); - const detail = await loadIssueDetail(issueId); + let detail; + if (isArchived) { + // For archived issues, get detail from historyIssues (already loaded) + const historyIssue = issueData.historyIssues.find(i => i.id === issueId); + if (historyIssue) { + // Mark as archived and provide minimal detail structure + detail = { + ...historyIssue, + _isArchived: true, + solutions: historyIssue.solutions || [], + tasks: historyIssue.tasks || [] + }; + } + } else { + detail = await loadIssueDetail(issueId); + } + if (!detail) { panel.innerHTML = '
Failed to load issue
'; return;