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:
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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': '开始时间',
|
||||
|
||||
Reference in New Issue
Block a user