From c957eaa3a13e4e0bd2f4452c9a0a3111c6a7e582 Mon Sep 17 00:00:00 2001 From: AndyMik90 Date: Thu, 18 Dec 2025 23:56:08 +0100 Subject: [PATCH] Add better github issue tracking and UX --- auto-claude-ui/src/renderer/App.tsx | 15 +++-- .../src/renderer/components/GitHubIssues.tsx | 19 +++++- .../github-issues/components/IssueDetail.tsx | 63 +++++++++++++------ .../components/github-issues/types/index.ts | 6 ++ .../src/renderer/stores/task-store.ts | 13 ++++ 5 files changed, 90 insertions(+), 26 deletions(-) diff --git a/auto-claude-ui/src/renderer/App.tsx b/auto-claude-ui/src/renderer/App.tsx index 1c8f8fc4..d2b18284 100644 --- a/auto-claude-ui/src/renderer/App.tsx +++ b/auto-claude-ui/src/renderer/App.tsx @@ -258,8 +258,8 @@ export function App() { const handleGoToTask = (taskId: string) => { // Switch to kanban view setActiveView('kanban'); - // Find and select the task - const task = tasks.find((t) => t.id === taskId); + // Find and select the task (match by id or specId) + const task = tasks.find((t) => t.id === taskId || t.specId === taskId); if (task) { setSelectedTask(task); } @@ -340,10 +340,13 @@ export function App() { )} {activeView === 'github-issues' && selectedProjectId && ( - { - setSettingsInitialProjectSection('github'); - setIsSettingsDialogOpen(true); - }} /> + { + setSettingsInitialProjectSection('github'); + setIsSettingsDialogOpen(true); + }} + onNavigateToTask={handleGoToTask} + /> )} {activeView === 'changelog' && selectedProjectId && ( diff --git a/auto-claude-ui/src/renderer/components/GitHubIssues.tsx b/auto-claude-ui/src/renderer/components/GitHubIssues.tsx index edf15c5f..1d4d4408 100644 --- a/auto-claude-ui/src/renderer/components/GitHubIssues.tsx +++ b/auto-claude-ui/src/renderer/components/GitHubIssues.tsx @@ -1,5 +1,6 @@ -import { useState, useCallback } from 'react'; +import { useState, useCallback, useMemo } from 'react'; import { useProjectStore } from '../stores/project-store'; +import { useTaskStore } from '../stores/task-store'; import { useGitHubIssues, useGitHubInvestigation, useIssueFiltering } from './github-issues/hooks'; import { NotConnectedState, @@ -12,10 +13,11 @@ import { import type { GitHubIssue } from '../../shared/types'; import type { GitHubIssuesProps } from './github-issues/types'; -export function GitHubIssues({ onOpenSettings }: GitHubIssuesProps) { +export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesProps) { const projects = useProjectStore((state) => state.projects); const selectedProjectId = useProjectStore((state) => state.selectedProjectId); const selectedProject = projects.find((p) => p.id === selectedProjectId); + const tasks = useTaskStore((state) => state.tasks); const { issues, @@ -43,6 +45,17 @@ export function GitHubIssues({ onOpenSettings }: GitHubIssuesProps) { const [showInvestigateDialog, setShowInvestigateDialog] = useState(false); const [selectedIssueForInvestigation, setSelectedIssueForInvestigation] = useState(null); + // Build a map of GitHub issue numbers to task IDs for quick lookup + const issueToTaskMap = useMemo(() => { + const map = new Map(); + for (const task of tasks) { + if (task.metadata?.githubIssueNumber) { + map.set(task.metadata.githubIssueNumber, task.specId || task.id); + } + } + return map; + }, [tasks]); + const handleInvestigate = useCallback((issue: GitHubIssue) => { setSelectedIssueForInvestigation(issue); setShowInvestigateDialog(true); @@ -110,6 +123,8 @@ export function GitHubIssues({ onOpenSettings }: GitHubIssuesProps) { ? lastInvestigationResult : null } + linkedTaskId={issueToTaskMap.get(selectedIssue.number)} + onViewTask={onNavigateToTask} /> ) : ( diff --git a/auto-claude-ui/src/renderer/components/github-issues/components/IssueDetail.tsx b/auto-claude-ui/src/renderer/components/github-issues/components/IssueDetail.tsx index fbb45423..fb68baac 100644 --- a/auto-claude-ui/src/renderer/components/github-issues/components/IssueDetail.tsx +++ b/auto-claude-ui/src/renderer/components/github-issues/components/IssueDetail.tsx @@ -1,4 +1,4 @@ -import { ExternalLink, User, Clock, MessageCircle, Sparkles, CheckCircle2 } from 'lucide-react'; +import { ExternalLink, User, Clock, MessageCircle, Sparkles, CheckCircle2, Eye } from 'lucide-react'; import { Badge } from '../../ui/badge'; import { Button } from '../../ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '../../ui/card'; @@ -11,7 +11,17 @@ import { import { formatDate } from '../utils'; import type { IssueDetailProps } from '../types'; -export function IssueDetail({ issue, onInvestigate, investigationResult }: IssueDetailProps) { +export function IssueDetail({ issue, onInvestigate, investigationResult, linkedTaskId, onViewTask }: IssueDetailProps) { + // Determine which task ID to use - either already linked or just created + const taskId = linkedTaskId || (investigationResult?.success ? investigationResult.taskId : undefined); + const hasLinkedTask = !!taskId; + + const handleViewTask = () => { + if (taskId && onViewTask) { + onViewTask(taskId); + } + }; + return (
@@ -77,31 +87,48 @@ export function IssueDetail({ issue, onInvestigate, investigationResult }: Issue {/* Actions */}
- + {hasLinkedTask ? ( + + ) : ( + + )}
- {/* Task Created Result */} - {investigationResult?.success && ( + {/* Task Linked Info */} + {hasLinkedTask && ( - Task Created + Task Linked -

{investigationResult.analysis.summary}

-
- - {investigationResult.analysis.estimatedComplexity} - - - Task ID: {investigationResult.taskId} - -
+ {investigationResult?.success ? ( + <> +

{investigationResult.analysis.summary}

+
+ + {investigationResult.analysis.estimatedComplexity} + + + Task ID: {taskId} + +
+ + ) : ( +
+ + Task ID: {taskId} + +
+ )}
)} diff --git a/auto-claude-ui/src/renderer/components/github-issues/types/index.ts b/auto-claude-ui/src/renderer/components/github-issues/types/index.ts index c3baa16a..100f0205 100644 --- a/auto-claude-ui/src/renderer/components/github-issues/types/index.ts +++ b/auto-claude-ui/src/renderer/components/github-issues/types/index.ts @@ -4,6 +4,8 @@ export type FilterState = 'open' | 'closed' | 'all'; export interface GitHubIssuesProps { onOpenSettings?: () => void; + /** Navigate to view a task in the kanban board */ + onNavigateToTask?: (taskId: string) => void; } export interface IssueListItemProps { @@ -17,6 +19,10 @@ export interface IssueDetailProps { issue: GitHubIssue; onInvestigate: () => void; investigationResult: GitHubInvestigationResult | null; + /** ID of existing task linked to this issue (from metadata.githubIssueNumber) */ + linkedTaskId?: string; + /** Handler to navigate to view the linked task */ + onViewTask?: (taskId: string) => void; } export interface InvestigationDialogProps { diff --git a/auto-claude-ui/src/renderer/stores/task-store.ts b/auto-claude-ui/src/renderer/stores/task-store.ts index 17dbfe27..7b3a04dc 100644 --- a/auto-claude-ui/src/renderer/stores/task-store.ts +++ b/auto-claude-ui/src/renderer/stores/task-store.ts @@ -514,6 +514,19 @@ export function isDraftEmpty(draft: TaskDraft | null): boolean { ); } +// ============================================ +// GitHub Issue Linking Helpers +// ============================================ + +/** + * Find a task by GitHub issue number + * Used to check if a task already exists for a GitHub issue + */ +export function getTaskByGitHubIssue(issueNumber: number): Task | undefined { + const store = useTaskStore.getState(); + return store.tasks.find(t => t.metadata?.githubIssueNumber === issueNumber); +} + // ============================================ // Task State Detection Helpers // ============================================