mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +08:00
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:
@@ -128,6 +128,29 @@
|
|||||||
box-shadow: 0 4px 12px hsl(var(--foreground) / 0.08);
|
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 {
|
.issue-card-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
// ========== Issue State ==========
|
// ========== Issue State ==========
|
||||||
var issueData = {
|
var issueData = {
|
||||||
issues: [],
|
issues: [],
|
||||||
|
historyIssues: [], // Archived/completed issues from history
|
||||||
queue: { tasks: [], solutions: [], conflicts: [], execution_groups: [], grouped_items: {} },
|
queue: { tasks: [], solutions: [], conflicts: [], execution_groups: [], grouped_items: {} },
|
||||||
selectedIssue: null,
|
selectedIssue: null,
|
||||||
selectedSolution: 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() {
|
async function loadQueueData() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/queue?path=' + encodeURIComponent(projectPath));
|
const response = await fetch('/api/queue?path=' + encodeURIComponent(projectPath));
|
||||||
@@ -93,10 +106,21 @@ function renderIssueView() {
|
|||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const issues = issueData.issues || [];
|
const issues = issueData.issues || [];
|
||||||
|
const historyIssues = issueData.historyIssues || [];
|
||||||
|
|
||||||
// Apply both status and search filters
|
// Apply both status and search filters
|
||||||
let filteredIssues = issueData.statusFilter === 'all'
|
let filteredIssues;
|
||||||
? issues
|
if (issueData.statusFilter === 'all') {
|
||||||
: issues.filter(i => i.status === issueData.statusFilter);
|
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) {
|
if (issueData.searchQuery) {
|
||||||
const query = issueData.searchQuery.toLowerCase();
|
const query = issueData.searchQuery.toLowerCase();
|
||||||
@@ -309,12 +333,15 @@ function renderIssueCard(issue) {
|
|||||||
failed: 'failed'
|
failed: 'failed'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isArchived = issue._isArchived;
|
||||||
|
|
||||||
return `
|
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-start justify-between mb-3">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="issue-id font-mono text-sm">${issue.id}</span>
|
<span class="issue-id font-mono text-sm">${issue.id}</span>
|
||||||
<span class="issue-status ${statusColors[issue.status] || ''}">${issue.status || 'unknown'}</span>
|
<span class="issue-status ${statusColors[issue.status] || ''}">${issue.status || 'unknown'}</span>
|
||||||
|
${isArchived ? '<span class="issue-archived-badge">' + (t('issues.archived') || 'Archived') + '</span>' : ''}
|
||||||
</div>
|
</div>
|
||||||
<span class="issue-priority" title="${t('issues.priority') || 'Priority'}: ${issue.priority || 3}">
|
<span class="issue-priority" title="${t('issues.priority') || 'Priority'}: ${issue.priority || 3}">
|
||||||
${renderPriorityStars(issue.priority || 3)}
|
${renderPriorityStars(issue.priority || 3)}
|
||||||
@@ -360,8 +387,12 @@ function renderPriorityStars(priority) {
|
|||||||
return stars;
|
return stars;
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterIssuesByStatus(status) {
|
async function filterIssuesByStatus(status) {
|
||||||
issueData.statusFilter = status;
|
issueData.statusFilter = status;
|
||||||
|
// Load history data when filtering by 'completed' status
|
||||||
|
if (status === 'completed' && issueData.historyIssues.length === 0) {
|
||||||
|
await loadIssueHistory();
|
||||||
|
}
|
||||||
renderIssueView();
|
renderIssueView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,7 +756,7 @@ async function saveQueueOrder(groupId, newOrder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ========== Detail Panel ==========
|
// ========== Detail Panel ==========
|
||||||
async function openIssueDetail(issueId) {
|
async function openIssueDetail(issueId, isArchived = false) {
|
||||||
const panel = document.getElementById('issueDetailPanel');
|
const panel = document.getElementById('issueDetailPanel');
|
||||||
if (!panel) return;
|
if (!panel) return;
|
||||||
|
|
||||||
@@ -733,7 +764,23 @@ async function openIssueDetail(issueId) {
|
|||||||
panel.classList.remove('hidden');
|
panel.classList.remove('hidden');
|
||||||
lucide.createIcons();
|
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) {
|
if (!detail) {
|
||||||
panel.innerHTML = '<div class="p-8 text-center text-destructive">Failed to load issue</div>';
|
panel.innerHTML = '<div class="p-8 text-center text-destructive">Failed to load issue</div>';
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user