Problem
TeamCard.tsx (84 lines) and TeamHubCard.tsx (65 lines) share nearly identical structure:
- Same
motion.div + motionPresets.listItem wrapper
- Same
surface-card flex w-full cursor-pointer flex-col items-start gap-3 rounded-xl p-5 className
- Same emoji icon layout:
h-10 w-10 rounded-lg bg-[var(--bg-tertiary)]
- Same description:
type-body text-[var(--text-secondary)] line-clamp-2
- Same member count row:
type-meta flex items-center gap-1.5 text-[var(--text-muted)]
Only the action buttons differ (chat/edit/delete vs install).
Location
Files:
packages/desktop/src/renderer/layouts/TeamsPanel/TeamCard.tsx
packages/desktop/src/renderer/layouts/TeamsPanel/TeamHubCard.tsx
Fix Approach
Extract a TeamCardShell component:
interface TeamCardShellProps {
emoji: string;
name: string;
subtitle?: ReactNode;
description?: string;
memberCount: number;
actions: ReactNode;
topRight?: ReactNode;
onClick: () => void;
}
function TeamCardShell({ emoji, name, subtitle, description, memberCount, actions, topRight, onClick }: TeamCardShellProps) {
const { t } = useTranslation();
return (
<motion.div {...motionPresets.listItem} onClick={onClick}
className={cn('surface-card flex w-full cursor-pointer flex-col items-start gap-3 rounded-xl p-5', ...)}>
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-3 min-w-0">
<span className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--bg-tertiary)]">
<span className="emoji-lg">{emoji}</span>
</span>
<div className="min-w-0"><h3 ...>{name}</h3>{subtitle}</div>
</div>
{topRight}
</div>
{description && <p className="type-body text-[var(--text-secondary)] line-clamp-2">{description}</p>}
<div className="flex w-full items-center justify-between">
<div className="type-meta flex items-center gap-1.5 text-[var(--text-muted)]">
<Users size={13} className="opacity-60" />
<span>{t('teams.memberCount', { count: memberCount })}</span>
</div>
{actions}
</div>
</motion.div>
);
}
Then TeamCard and TeamHubCard become thin wrappers (~15 lines each).
Verification
- Run
pnpm check — must pass
- Visually verify both cards in Teams panel and Hub still render correctly
Context
- WG: UI & Design System
- Priority: Low
- Estimated effort: ~25 minutes
Problem
TeamCard.tsx(84 lines) andTeamHubCard.tsx(65 lines) share nearly identical structure:motion.div+motionPresets.listItemwrappersurface-card flex w-full cursor-pointer flex-col items-start gap-3 rounded-xl p-5classNameh-10 w-10 rounded-lg bg-[var(--bg-tertiary)]type-body text-[var(--text-secondary)] line-clamp-2type-meta flex items-center gap-1.5 text-[var(--text-muted)]Only the action buttons differ (chat/edit/delete vs install).
Location
Files:
packages/desktop/src/renderer/layouts/TeamsPanel/TeamCard.tsxpackages/desktop/src/renderer/layouts/TeamsPanel/TeamHubCard.tsxFix Approach
Extract a
TeamCardShellcomponent:Then
TeamCardandTeamHubCardbecome thin wrappers (~15 lines each).Verification
pnpm check— must passContext