import { useState, useEffect, useCallback } from 'react'; import { createLogger } from '@automaker/utils/logger'; import { GitPullRequest, Loader2, RefreshCw, ExternalLink, GitMerge, X } from 'lucide-react'; import { getElectronAPI, GitHubPR } from '@/lib/electron'; import { useAppStore } from '@/store/app-store'; import { Button } from '@/components/ui/button'; import { Markdown } from '@/components/ui/markdown'; import { cn } from '@/lib/utils'; const logger = createLogger('GitHubPRsView'); export function GitHubPRsView() { const [openPRs, setOpenPRs] = useState([]); const [mergedPRs, setMergedPRs] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const [selectedPR, setSelectedPR] = useState(null); const { currentProject } = useAppStore(); const fetchPRs = useCallback(async () => { if (!currentProject?.path) { setError('No project selected'); setLoading(false); return; } try { setError(null); const api = getElectronAPI(); if (api.github) { const result = await api.github.listPRs(currentProject.path); if (result.success) { setOpenPRs(result.openPRs || []); setMergedPRs(result.mergedPRs || []); } else { setError(result.error || 'Failed to fetch pull requests'); } } } catch (err) { logger.error('Error fetching PRs:', err); setError(err instanceof Error ? err.message : 'Failed to fetch pull requests'); } finally { setLoading(false); setRefreshing(false); } }, [currentProject?.path]); useEffect(() => { fetchPRs(); }, [fetchPRs]); const handleRefresh = useCallback(() => { setRefreshing(true); fetchPRs(); }, [fetchPRs]); const handleOpenInGitHub = useCallback((url: string) => { const api = getElectronAPI(); api.openExternalLink(url); }, []); const formatDate = (dateString: string) => { const date = new Date(dateString); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', }); }; const getReviewStatus = (pr: GitHubPR) => { if (pr.isDraft) return { label: 'Draft', color: 'text-muted-foreground', bg: 'bg-muted' }; switch (pr.reviewDecision) { case 'APPROVED': return { label: 'Approved', color: 'text-green-500', bg: 'bg-green-500/10' }; case 'CHANGES_REQUESTED': return { label: 'Changes requested', color: 'text-orange-500', bg: 'bg-orange-500/10' }; case 'REVIEW_REQUIRED': return { label: 'Review required', color: 'text-yellow-500', bg: 'bg-yellow-500/10' }; default: return null; } }; if (loading) { return (
); } if (error) { return (

Failed to Load Pull Requests

{error}

); } const totalPRs = openPRs.length + mergedPRs.length; return (
{/* PR List */}
{/* Header */}

Pull Requests

{totalPRs === 0 ? 'No pull requests found' : `${openPRs.length} open, ${mergedPRs.length} merged`}

{/* PR List */}
{totalPRs === 0 ? (

No Pull Requests

This repository has no pull requests yet.

) : (
{/* Open PRs */} {openPRs.map((pr) => ( setSelectedPR(pr)} onOpenExternal={() => handleOpenInGitHub(pr.url)} formatDate={formatDate} getReviewStatus={getReviewStatus} /> ))} {/* Merged PRs Section */} {mergedPRs.length > 0 && ( <>
Merged ({mergedPRs.length})
{mergedPRs.map((pr) => ( setSelectedPR(pr)} onOpenExternal={() => handleOpenInGitHub(pr.url)} formatDate={formatDate} getReviewStatus={getReviewStatus} /> ))} )}
)}
{/* PR Detail Panel */} {selectedPR && (
{/* Detail Header */}
{selectedPR.state === 'MERGED' ? ( ) : ( )} #{selectedPR.number} {selectedPR.title} {selectedPR.isDraft && ( Draft )}
{/* PR Detail Content */}
{/* Title */}

{selectedPR.title}

{/* Meta info */}
{selectedPR.state === 'MERGED' ? 'Merged' : selectedPR.isDraft ? 'Draft' : 'Open'} {getReviewStatus(selectedPR) && ( {getReviewStatus(selectedPR)!.label} )} #{selectedPR.number} opened {formatDate(selectedPR.createdAt)} by{' '} {selectedPR.author.login}
{/* Branch info */} {selectedPR.headRefName && (
Branch: {selectedPR.headRefName}
)} {/* Labels */} {selectedPR.labels.length > 0 && (
{selectedPR.labels.map((label) => ( {label.name} ))}
)} {/* Body */} {selectedPR.body ? ( {selectedPR.body} ) : (

No description provided.

)} {/* Open in GitHub CTA */}

View code changes, comments, and reviews on GitHub.

)}
); } interface PRRowProps { pr: GitHubPR; isSelected: boolean; onClick: () => void; onOpenExternal: () => void; formatDate: (date: string) => string; getReviewStatus: (pr: GitHubPR) => { label: string; color: string; bg: string } | null; } function PRRow({ pr, isSelected, onClick, onOpenExternal, formatDate, getReviewStatus, }: PRRowProps) { const reviewStatus = getReviewStatus(pr); return (
{pr.state === 'MERGED' ? ( ) : ( )}
{pr.title} {pr.isDraft && ( Draft )}
#{pr.number} opened {formatDate(pr.createdAt)} by {pr.author.login} {pr.headRefName && ( {pr.headRefName} )}
{/* Review Status */} {reviewStatus && ( {reviewStatus.label} )} {/* Labels */} {pr.labels.map((label) => ( {label.name} ))}
); }