Skip to content
Clone
main
70 lines 3.1 KB
import { ErrorPanel, PanelNote } from '@app/components'
import { getPullChecks, type PullRef } from '@app/domains/pulls/model'
import type { MessageKey } from '@app/i18n'
import { useT } from '@app/i18n'
import { useAsync } from '@app/lib'
import { useState } from 'react'
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>
)
}