951a522
import { useState } from 'react'
import { ErrorPanel, PanelNote } from '@/shared/ui'
import type { MessageKey } from '@/shared/i18n'
import { useAsync } from '@/shared/lib'
import { useT } from '@/shared/i18n'
import { getPullChecks, type PullRef } from '@/entities/pull-request'
import { Copyable } from './parts'
type CheckState = { labelKey: MessageKey | null; tone: string }
const STATUS_STATE: Record<string, CheckState> = {
pending: { labelKey: 'pull.checks.waiting', tone: 'text-shell-ink-muted' },
success: { labelKey: 'pull.checks.success', tone: 'text-[#1a7f37]' },
failure: { labelKey: 'pull.checks.failed', tone: 'text-[#b42318]' },
error: { labelKey: 'pull.checks.error', tone: 'text-[#b42318]' },
}
/** 检查页签:最新 head 的提交状态。 */
export function ChecksTab({ refObj, headSha }: { refObj: PullRef; headSha: string }) {
const { t } = useT()
const [tick, setTick] = useState(0)
const checks = useAsync(() => getPullChecks(refObj), [refObj.space, refObj.repo, refObj.pull, tick])
const failure = checks.error as { code?: number; message?: string } | undefined
if (checks.loading && !checks.data) return <PanelNote text={t('pull.checks.loading')} />
if (failure) return <ErrorPanel status={failure.code ?? 0} what={t('pull.checks.errorChecks')} note={failure.message} onRetry={() => setTick(tick => tick + 1)} />
const data = checks.data
if (!data) return <PanelNote text={t('pull.checks.empty')} />
const statuses = data.statuses ?? []
if (!statuses.length) {
return <PanelNote text={t('pull.checks.versionEmpty', { sha: (data.head_sha || headSha).slice(0, 8) })} />
}
return (
<div className="space-y-3">
<p className="text-[12.5px] text-shell-ink-muted">{t('pull.checks.version')} <Copyable text={data.head_sha || headSha} /></p>
{statuses.map((status, index) => {
const hit = STATUS_STATE[status.state] ?? { labelKey: null, tone: 'text-shell-ink-muted' }
return (
<div key={`${status.context}:${index}`} className="flex flex-wrap items-center gap-2 rounded-xl border border-shell-hairline bg-shell-surface px-4 py-2.5 text-[13px]">
<span className={`h-2 w-2 shrink-0 rounded-full bg-current ${hit.tone}`} aria-hidden />
<span className="font-medium">{status.context}</span>
<span className={`text-[12px] ${hit.tone}`}>{hit.labelKey ? t(hit.labelKey) : status.state}</span>
{status.description ? <span className="text-[12px] text-shell-ink-muted">{status.description}</span> : null}
{status.target_url ? (
<button type="button" onClick={() => status.target_url && window.open(status.target_url, '_blank', 'noopener,noreferrer')} className="ml-auto text-[12px] text-shell-ink-muted underline underline-offset-2 hover:text-shell-ink">
{t('pull.checks.details')}
</button>
) : null}
</div>
)
})}
</div>
)
}