feat(issue-manager): Add queue history modal for viewing and switching queues

- Add GET /api/queue/history endpoint to fetch all queues from index
- Add GET /api/queue/:id endpoint to fetch specific queue details
- Add POST /api/queue/switch endpoint to switch active queue
- Add History button in queue toolbar
- Add queue history modal with list view and detail view
- Add switch functionality to change active queue
- Add CSS styles for queue history components

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
catlog22
2025-12-27 22:56:32 +08:00
parent d705a3e7d9
commit 7620ff703d
3 changed files with 571 additions and 0 deletions

View File

@@ -236,6 +236,82 @@ export async function handleIssueRoutes(ctx: RouteContext): Promise<boolean> {
return true;
}
// GET /api/queue/history - Get queue history (all queues from index)
if (pathname === '/api/queue/history' && req.method === 'GET') {
const queuesDir = join(issuesDir, 'queues');
const indexPath = join(queuesDir, 'index.json');
if (!existsSync(indexPath)) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ queues: [], active_queue_id: null }));
return true;
}
try {
const index = JSON.parse(readFileSync(indexPath, 'utf8'));
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(index));
} catch {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ queues: [], active_queue_id: null }));
}
return true;
}
// GET /api/queue/:id - Get specific queue by ID
const queueDetailMatch = pathname.match(/^\/api\/queue\/([^/]+)$/);
if (queueDetailMatch && req.method === 'GET' && queueDetailMatch[1] !== 'history' && queueDetailMatch[1] !== 'reorder') {
const queueId = queueDetailMatch[1];
const queuesDir = join(issuesDir, 'queues');
const queueFilePath = join(queuesDir, `${queueId}.json`);
if (!existsSync(queueFilePath)) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: `Queue ${queueId} not found` }));
return true;
}
try {
const queue = JSON.parse(readFileSync(queueFilePath, 'utf8'));
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(groupQueueByExecutionGroup(queue)));
} catch {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Failed to read queue' }));
}
return true;
}
// POST /api/queue/switch - Switch active queue
if (pathname === '/api/queue/switch' && req.method === 'POST') {
handlePostRequest(req, res, async (body: any) => {
const { queueId } = body;
if (!queueId) return { error: 'queueId required' };
const queuesDir = join(issuesDir, 'queues');
const indexPath = join(queuesDir, 'index.json');
const queueFilePath = join(queuesDir, `${queueId}.json`);
if (!existsSync(queueFilePath)) {
return { error: `Queue ${queueId} not found` };
}
try {
const index = existsSync(indexPath)
? JSON.parse(readFileSync(indexPath, 'utf8'))
: { active_queue_id: null, queues: [] };
index.active_queue_id = queueId;
writeFileSync(indexPath, JSON.stringify(index, null, 2));
return { success: true, active_queue_id: queueId };
} catch (err) {
return { error: 'Failed to switch queue' };
}
});
return true;
}
// POST /api/queue/reorder - Reorder queue items
if (pathname === '/api/queue/reorder' && req.method === 'POST') {
handlePostRequest(req, res, async (body: any) => {