fix: simplify existing pull request header

Reflow PR status and checks into a cleaner two-row header
Remove the remote selector from existing PRs
Keep remote context as read-only tracking info
This commit is contained in:
Bohdan Triapitsyn
2026-05-06 00:46:40 +03:00
parent 32929d592e
commit cce782a62b
7 changed files with 32 additions and 87 deletions
@@ -28,12 +28,6 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Input } from '@/components/ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea';
@@ -448,8 +442,6 @@ export const PullRequestSection: React.FC<{
return Array.from(unique).sort((a, b) => a.localeCompare(b));
}, [baseBranch, remoteBranches, selectedRemote?.name, targetBaseBranch, upstreamBranches, useDetectedUpstream]);
const hasMultipleRemotes = remotes.length > 1;
// Update selected remote when remotes change
React.useEffect(() => {
if (remotes.length === 0) {
@@ -1018,12 +1010,6 @@ export const PullRequestSection: React.FC<{
}, delayMs));
}, [refresh]);
// Refetch PR status when selected remote changes
const handleRemoteChange = React.useCallback((remote: GitRemote) => {
didUserOverrideRemoteRef.current = true;
setSelectedRemote((prev) => (prev?.name === remote.name ? prev : remote));
}, []);
React.useEffect(() => {
if (!github?.prStatus || !canShow || remotes.length <= 1) {
return;
@@ -1397,7 +1383,20 @@ export const PullRequestSection: React.FC<{
: prVisualState === 'closed'
? RiGitClosePullRequestLine
: RiGitPullRequestLine;
const prStatusText = pr
? [
`${pr.state}${pr.draft ? ' (draft)' : ''}`,
pr.mergeable === false ? t('gitView.pr.notMergeable') : null,
pr.state === 'open' && typeof pr.mergeableState === 'string' && pr.mergeableState && pr.mergeableState !== 'unknown'
? pr.mergeableState
: null,
].filter(Boolean).join(' · ')
: '';
const checksText = checks
? checks.total > 0
? `${checks.success}/${checks.total} ${t('gitView.pr.checks.label')}`
: `${checks.state} ${t('gitView.pr.checks.label')}`
: '';
const containerClassName = 'border-0 bg-transparent rounded-none';
const headerClassName = 'px-0 py-3 border-b border-border/40 flex flex-col gap-1';
const bodyClassName = 'flex flex-col gap-3 py-3';
@@ -1405,8 +1404,8 @@ export const PullRequestSection: React.FC<{
return (
<section className={containerClassName}>
<div className={headerClassName}>
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2 min-w-0">
<div className="flex items-start justify-between gap-2">
<div className="flex min-w-0 items-center gap-2">
{pr ? (
<Tooltip>
<TooltipTrigger asChild>
@@ -1429,7 +1428,7 @@ export const PullRequestSection: React.FC<{
<span className="typography-meta text-muted-foreground truncate">#{pr.number}</span>
) : null}
</div>
<div className="flex items-center gap-2">
<div className="flex shrink-0 items-center gap-1">
{isLoading ? <RiLoader4Line className="size-4 animate-spin text-muted-foreground" /> : null}
<Tooltip>
<TooltipTrigger asChild>
@@ -1445,83 +1444,23 @@ export const PullRequestSection: React.FC<{
</TooltipTrigger>
<TooltipContent><p>{t('gitView.pr.actions.refresh')}</p></TooltipContent>
</Tooltip>
</div>
</div>
{pr ? (
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 typography-micro text-muted-foreground">
<span style={{ color: prColorVar }}>{prStatusText}</span>
{checks ? (
<span className="inline-flex items-center gap-2 typography-micro text-muted-foreground">
<span className="inline-flex items-center gap-1.5">
<span className={`h-2 w-2 rounded-full ${statusColor(checks.state)}`} />
{checks.total > 0 ? `${checks.success}/${checks.total} checks` : `${checks.state} checks`}
{checksText}
</span>
) : null}
{trackingBranch && selectedRemote && trackingBranch.split('/')[0] !== selectedRemote.name ? (
<span className="typography-micro text-muted-foreground">
<span className="min-w-0 truncate">
{trackingBranch.split('/')[0]} {selectedRemote.name}
</span>
) : null}
{hasMultipleRemotes || detectedUpstream ? (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="xs" className="gap-1">
<span className="typography-micro">
{useDetectedUpstream && detectedUpstream
? `upstream · ${detectedUpstream.owner}/${detectedUpstream.repo}`
: selectedRemote?.name ?? 'target'}
</span>
<RiArrowDownSLine className="size-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="min-w-[200px]">
{remotes.map((remote) => (
<DropdownMenuItem
key={remote.name}
onSelect={() => {
setUseDetectedUpstream(false);
handleRemoteChange(remote);
}}
>
<div className="flex flex-col">
<span className="typography-ui-label text-foreground">
{remote.name}
{!useDetectedUpstream && remote.name === selectedRemote?.name && (
<span className="ml-2 text-primary"></span>
)}
</span>
<span className="typography-meta text-muted-foreground truncate">
{remote.pushUrl}
</span>
</div>
</DropdownMenuItem>
))}
{detectedUpstream ? (
<DropdownMenuItem
key="detected-upstream"
onSelect={() => setUseDetectedUpstream(true)}
>
<div className="flex flex-col">
<span className="typography-ui-label text-foreground">
upstream · {detectedUpstream.owner}/{detectedUpstream.repo}
{useDetectedUpstream && (
<span className="ml-2 text-primary"></span>
)}
</span>
<span className="typography-meta text-muted-foreground truncate">
{detectedUpstream.url}
</span>
</div>
</DropdownMenuItem>
) : null}
</DropdownMenuContent>
</DropdownMenu>
) : null}
</div>
</div>
{pr ? (
<div className="typography-micro text-muted-foreground">
<span style={{ color: prColorVar }}>
{pr.state}{pr.draft ? ' (draft)' : ''}
</span>
{pr.mergeable === false ? ` · ${t('gitView.pr.notMergeable')}` : ''}
{pr.state === 'open' && typeof pr.mergeableState === 'string' && pr.mergeableState && pr.mergeableState !== 'unknown'
? ` · ${pr.mergeableState}`
: ''}
</div>
) : null}
</div>
+1
View File
@@ -615,6 +615,7 @@ export const dict = {
'gitView.pr.checkDetails.empty': 'No check details available',
'gitView.pr.checkDetails.title': 'Failed check details',
'gitView.pr.checkingStatus': 'Checking pull request status...',
'gitView.pr.checks.label': 'checks',
'gitView.pr.checks.completedLabel': 'Completed',
'gitView.pr.checks.conclusionLabel': 'Conclusion',
'gitView.pr.checks.startedLabel': 'Started',
+1
View File
@@ -616,6 +616,7 @@ export const dict: Record<I18nKey, string> = {
"gitView.pr.checkDetails.empty": "No hay detalles del check disponibles",
"gitView.pr.checkDetails.title": "Detalles del check fallido",
"gitView.pr.checkingStatus": "Comprobando estado de la PR...",
"gitView.pr.checks.label": "checks",
"gitView.pr.checks.completedLabel": "Completado",
"gitView.pr.checks.conclusionLabel": "Conclusión",
"gitView.pr.checks.startedLabel": "Iniciado",
+1
View File
@@ -616,6 +616,7 @@ export const dict: Record<I18nKey, string> = {
'gitView.pr.checkDetails.empty': '체크 상세 정보 없음',
'gitView.pr.checkDetails.title': '실패한 체크 상세',
'gitView.pr.checkingStatus': 'PR 상태 확인 중…',
'gitView.pr.checks.label': 'checks',
'gitView.pr.checks.completedLabel': '완료됨',
'gitView.pr.checks.conclusionLabel': '결론',
'gitView.pr.checks.startedLabel': '시작됨',
@@ -616,6 +616,7 @@ export const dict: Record<I18nKey, string> = {
"gitView.pr.checkDetails.empty": "Não há detalhes do check disponíveis",
"gitView.pr.checkDetails.title": "Detalhes do check falhou",
"gitView.pr.checkingStatus": "Verificando status da PR...",
"gitView.pr.checks.label": "checks",
"gitView.pr.checks.completedLabel": "Concluído",
"gitView.pr.checks.conclusionLabel": "Conclusión",
"gitView.pr.checks.startedLabel": "Iniciado",
+1
View File
@@ -616,6 +616,7 @@ export const dict: Record<I18nKey, string> = {
"gitView.pr.checkDetails.empty": "Деталі перевірки відсутні",
"gitView.pr.checkDetails.title": "Деталі перевірки",
"gitView.pr.checkingStatus": "Перевірка статусу PR...",
"gitView.pr.checks.label": "checks",
"gitView.pr.checks.completedLabel": "Виконано",
"gitView.pr.checks.conclusionLabel": "Висновок",
"gitView.pr.checks.startedLabel": "Почато",
@@ -616,6 +616,7 @@ export const dict: Record<I18nKey, string> = {
'gitView.pr.checkDetails.empty': '没有可用的检查详情',
'gitView.pr.checkDetails.title': '失败检查详情',
'gitView.pr.checkingStatus': '正在检查拉取请求状态...',
'gitView.pr.checks.label': 'checks',
'gitView.pr.checks.completedLabel': '完成时间',
'gitView.pr.checks.conclusionLabel': '结论',
'gitView.pr.checks.startedLabel': '开始时间',