fix: Issue Manager completed过滤器现在可以显示归档议题

- 添加 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 <noreply@anthropic.com>
This commit is contained in:
catlog22
2026-01-15 10:28:52 +08:00
parent 4d57f47717
commit a228bb946b
2 changed files with 77 additions and 7 deletions

View File

@@ -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;

View File

@@ -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 `
<div class="issue-card" onclick="openIssueDetail('${issue.id}')">
<div class="issue-card ${isArchived ? 'archived' : ''}" onclick="openIssueDetail('${issue.id}'${isArchived ? ', true' : ''})">
<div class="flex items-start justify-between mb-3">
<div class="flex items-center gap-2">
<span class="issue-id font-mono text-sm">${issue.id}</span>
<span class="issue-status ${statusColors[issue.status] || ''}">${issue.status || 'unknown'}</span>
${isArchived ? '<span class="issue-archived-badge">' + (t('issues.archived') || 'Archived') + '</span>' : ''}
</div>
<span class="issue-priority" title="${t('issues.priority') || 'Priority'}: ${issue.priority || 3}">
${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 = '<div class="p-8 text-center text-destructive">Failed to load issue</div>';
return;