[eric] frontend: polish subscription + pro cards (hover lift, hover-reveal disconnect)

This commit is contained in:
ciregenz
2026-06-09 14:55:08 -07:00
parent 8389db0c21
commit d0b23ea616
2 changed files with 47 additions and 31 deletions
@@ -105,7 +105,7 @@ const OpenSwarmProCard: React.FC = () => {
desc: 'Your subscription ended. Pick a plan to get back in.',
cta: 'Resubscribe',
title: 'Resubscribe to OpenSwarm Pro',
subtitle: 'Keep using Claude Sonnet, Opus, and Haiku without a Claude account.',
subtitle: 'Keep using the latest Claude models without a Claude account.',
currentPlan: lastPlan,
};
}
@@ -119,7 +119,7 @@ const OpenSwarmProCard: React.FC = () => {
};
}
return {
desc: 'Claude Sonnet, Opus, and Haiku. No Claude account needed.',
desc: 'The latest Claude models. No Claude account needed.',
cta: 'Subscribe',
title: 'Choose your plan',
subtitle: 'One subscription, we handle everything behind the scenes. Cancel anytime.',
@@ -129,22 +129,26 @@ const OpenSwarmProCard: React.FC = () => {
return (
<>
<Box sx={{ p: 1.5, borderRadius: `${c.radius.md}px`, border: `1px solid ${c.border.subtle}` }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Box sx={{
p: 1.5, borderRadius: `${c.radius.md}px`, border: `1px solid ${c.border.subtle}`,
transition: c.transition,
'&:hover': { borderColor: c.border.medium, transform: 'translateY(-1px)', boxShadow: c.shadow.sm },
}}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, minWidth: 0 }}>
<Box sx={{ width: 8, height: 8, borderRadius: '50%', flexShrink: 0, bgcolor: c.border.medium }} />
<Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.8 }}>
<Box sx={{ minWidth: 0 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.7 }}>
<Typography sx={{ fontSize: '0.78rem', fontWeight: 600, color: c.text.primary }}>
OpenSwarm Pro
</Typography>
<Box sx={{ px: 0.7, py: 0.15, borderRadius: 999, bgcolor: `${c.accent.primary}15` }}>
<Typography sx={{ fontSize: '0.6rem', color: c.accent.primary, fontWeight: 600 }}>
<Box sx={{ px: 0.7, py: 0.15, borderRadius: 999, bgcolor: `${c.accent.primary}12` }}>
<Typography sx={{ fontSize: '0.58rem', color: c.accent.primary, fontWeight: 600, letterSpacing: '0.03em' }}>
RECOMMENDED
</Typography>
</Box>
</Box>
<Typography sx={{ fontSize: '0.65rem', color: c.text.muted }}>
<Typography noWrap sx={{ fontSize: '0.65rem', color: c.text.muted }}>
{copy.desc}
</Typography>
</Box>
@@ -9,59 +9,71 @@ import type { SubscriptionProvider } from './subscriptionProviders';
const SubscriptionCard: React.FC<{ provider: SubscriptionProvider; connected: boolean; onConnect: () => void; onDisconnect: () => void; connecting: boolean; userCode?: string; disconnecting?: boolean }> = ({ provider, connected, onConnect, onDisconnect, connecting, userCode, disconnecting }) => {
const c = useClaudeTokens();
const isPreview = (provider as any).preview;
const dotColor = connected ? c.status.success : connecting ? c.accent.primary : c.border.medium;
return (
<Box sx={{
p: 1.5, borderRadius: `${c.radius.md}px`,
border: `1px solid ${connected ? c.status.success + '30' : connecting ? c.accent.primary + '30' : c.border.subtle}`,
bgcolor: connected ? `${c.status.success}04` : connecting ? `${c.accent.primary}04` : 'transparent',
bgcolor: connected ? `${c.status.success}06` : connecting ? `${c.accent.primary}06` : 'transparent',
opacity: isPreview ? 0.5 : 1,
transition: 'all 0.3s ease',
transition: c.transition,
'&:hover': isPreview ? {} : {
borderColor: connected ? c.status.success + '4d' : c.border.medium,
transform: 'translateY(-1px)',
boxShadow: c.shadow.sm,
},
// affirm "Connected" at rest, reveal "Disconnect" on hover so the undo never shouts
'&:hover .sub-rest': { opacity: 0 },
'&:hover .sub-undo': { opacity: 1, pointerEvents: 'auto' },
}}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, minWidth: 0 }}>
<Box sx={{
width: 8, height: 8, borderRadius: '50%', flexShrink: 0,
bgcolor: connected ? c.status.success : connecting ? c.accent.primary : c.border.medium,
width: 8, height: 8, borderRadius: '50%', flexShrink: 0, bgcolor: dotColor,
transition: 'background-color 0.3s ease',
...(connecting ? {
animation: 'pulse-dot 1.5s ease-in-out infinite',
'@keyframes pulse-dot': {
'0%, 100%': { opacity: 1, transform: 'scale(1)' },
'50%': { opacity: 0.4, transform: 'scale(0.8)' },
},
animation: 'sub-pulse 1.4s ease-in-out infinite',
'@keyframes sub-pulse': { '0%, 100%': { opacity: 1 }, '50%': { opacity: 0.35 } },
} : {}),
}} />
<Box>
<Box sx={{ minWidth: 0 }}>
<Typography sx={{ fontSize: '0.78rem', fontWeight: 600, color: c.text.primary }}>{provider.name}</Typography>
<Typography sx={{ fontSize: '0.65rem', color: connecting ? c.accent.primary : c.text.muted, transition: 'color 0.3s ease' }}>
<Typography noWrap sx={{ fontSize: '0.65rem', color: connecting ? c.accent.primary : c.text.muted, transition: 'color 0.2s ease' }}>
{connecting ? 'Waiting for authorization...' : provider.desc}
</Typography>
</Box>
</Box>
{isPreview ? (
<Typography sx={{ fontSize: '0.65rem', color: c.text.ghost, fontStyle: 'italic' }}>
<Typography sx={{ fontSize: '0.65rem', color: c.text.ghost, fontStyle: 'italic', flexShrink: 0 }}>
Coming soon
</Typography>
) : connected ? (
disconnecting ? (
<CircularProgress size={14} sx={{ color: c.text.ghost }} />
) : (
<Typography onClick={onDisconnect} sx={{ fontSize: '0.68rem', color: c.text.tertiary, cursor: 'pointer', '&:hover': { color: c.status.error }, transition: 'color 0.2s ease' }}>
Disconnect
</Typography>
<Box sx={{ position: 'relative', flexShrink: 0, minWidth: 72, height: 16 }}>
<Typography className="sub-rest" sx={{ position: 'absolute', right: 0, top: 0, fontSize: '0.68rem', fontWeight: 500, color: c.status.success, transition: 'opacity 0.18s ease' }}>
Connected
</Typography>
<Typography className="sub-undo" onClick={onDisconnect} sx={{ position: 'absolute', right: 0, top: 0, fontSize: '0.68rem', color: c.text.tertiary, cursor: 'pointer', opacity: 0, pointerEvents: 'none', transition: 'opacity 0.18s ease', '&:hover': { color: c.status.error } }}>
Disconnect
</Typography>
</Box>
)
) : connecting && userCode ? (
<Box sx={{ textAlign: 'right' }}>
<Box sx={{ textAlign: 'right', flexShrink: 0 }}>
<Typography sx={{ fontSize: '0.68rem', color: c.text.muted }}>Enter code:</Typography>
<Typography sx={{ fontSize: '0.85rem', fontWeight: 700, color: c.accent.primary, fontFamily: 'monospace', letterSpacing: '0.1em' }}>{userCode}</Typography>
<Typography sx={{ fontSize: '0.85rem', fontWeight: 700, color: c.accent.primary, fontFamily: c.font.mono, letterSpacing: '0.1em' }}>{userCode}</Typography>
</Box>
) : connecting ? (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.8 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.8, flexShrink: 0 }}>
<CircularProgress size={14} sx={{ color: c.accent.primary }} />
<Typography sx={{ fontSize: '0.68rem', color: c.accent.primary }}>Connecting...</Typography>
</Box>
) : (
<Button onClick={onConnect} variant="outlined" size="small" sx={{ textTransform: 'none', fontSize: '0.7rem', color: c.text.primary, borderColor: c.border.medium, minWidth: 70, '&:hover': { borderColor: c.accent.primary }, transition: 'all 0.2s ease' }}>
<Button onClick={onConnect} variant="outlined" size="small" sx={{ textTransform: 'none', fontSize: '0.7rem', fontWeight: 600, color: c.text.primary, borderColor: c.border.medium, borderRadius: `${c.radius.sm}px`, minWidth: 72, flexShrink: 0, '&:hover': { borderColor: c.accent.primary, bgcolor: `${c.accent.primary}0a` }, transition: 'all 0.2s ease' }}>
Connect
</Button>
)}