import type { Ref } from 'react' import type { Issue } from '../types' import { useSortable } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { Lock } from 'lucide-react' import { forwardRef } from 'react' import { cn } from '@/lib/utils' import { Card } from './ui/card' interface IssueCardProps { issue: Issue selected?: boolean onSelect?: (id: string) => void /** * Visual nesting level for prerequisite-chain rendering in the todo column. * Each level adds 16px of left margin. Defaults to 0 (flat). */ depth?: number /** 锁定态:前置工单尚未完成。显示锁图标 + tooltip。 */ locked?: boolean /** 锁定时关联的前置工单号,用于 tooltip 文案 "等待 #N 完成"。 */ lockedPrerequisite?: number /** 只读态:同事的工单。禁止拖拽,并在右上角贴归属人头像。 */ readOnly?: boolean /** 归属人 Gitea login(只读卡片才显示)。 */ ownerLogin?: string /** 归属人头像 data URI;缺失时回退首字母圆点。 */ ownerAvatar?: string } /** 同事工单的归属人标记:有头像用头像,没有就用 login 首字母。 */ function OwnerBadge({ login, avatar }: { login: string, avatar?: string }) { return avatar ? ( {login} ) : ( {login.slice(0, 1)} ) } export const IssueCard = forwardRef( ({ issue, selected, onSelect, depth = 0, locked, lockedPrerequisite, readOnly, ownerLogin, ownerAvatar }, ref) => { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: issue.id, data: { column: issue.column }, disabled: readOnly }) const style: React.CSSProperties = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.4 : 1, marginLeft: depth * 16, } // Merge dnd-kit's ref with the forwarded ref so the parent can scrollIntoView. function setRefs(node: HTMLDivElement | null): void { setNodeRef(node) if (typeof ref === 'function') ref(node) else if (ref) (ref as React.MutableRefObject).current = node } return ( } style={style} {...attributes} {...listeners} onClick={() => onSelect?.(issue.id)} className={cn( 'relative select-none px-3 py-2 text-xs', readOnly ? 'cursor-default' : 'cursor-grab active:cursor-grabbing', locked && 'opacity-75', selected && 'ring-2 ring-[var(--vscode-focusBorder)] ring-offset-1 ring-offset-[var(--vscode-sideBar-background)]', )} data-tooltip={issue.column === 'review' ? (issue.reviewPassed === true ? '已通过' : '审查中') : undefined} >
{locked ? ( ) : null} {readOnly && ownerLogin ? : null}
{issue.handoffAttachmentId ? ( 待接管 ) : null}
{issue.source === 'youtrack' ? issue.externalId : `#${issue.number}`} {' '} {issue.title}
) }, ) IssueCard.displayName = 'IssueCard'