Refine ComparisonColumnCard recommended state and CTA alignment
- Replace recommended banner with floating badge (star + primary fill) so CTA buttons align across recommended and non-recommended columns. - Inline verified icon on recommended cards only (left of provider name, brand-600). Non-recommended verified providers keep the top badge alone. - Override recommended card border to brand-600 for consistency with the rest of the primary system (token default is brand-500). - Show dash in rating slot when provider has no rating — keeps heights consistent across the row. - Invisible Remove placeholder on recommended card so primary CTAs align with cards that have a visible Remove link. - Remove link dropped from body2 to caption (12px). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -36,10 +36,11 @@ function formatPrice(amount: number): string {
|
|||||||
/**
|
/**
|
||||||
* Desktop column header card for the ComparisonTable.
|
* Desktop column header card for the ComparisonTable.
|
||||||
*
|
*
|
||||||
* Shows provider info (verified badge, name, location, rating), package name,
|
* Shows provider info (verified/recommended badge, name, location, rating),
|
||||||
* total price, CTA button, and optional Remove link. The verified badge floats
|
* package name, total price, CTA button, and optional Remove link. The badge
|
||||||
* above the card's top edge. Recommended packages get a copper banner and warm
|
* floats above the card's top edge — "Recommended" (primary fill) replaces
|
||||||
* selected card state.
|
* "Verified" (soft) when the package is recommended. Recommended packages
|
||||||
|
* also get a warm selected card state with a brand-600 border.
|
||||||
*
|
*
|
||||||
* Used as the sticky header for each column in the desktop comparison grid.
|
* Used as the sticky header for each column in the desktop comparison grid.
|
||||||
* Mobile comparison uses ComparisonPackageCard instead.
|
* Mobile comparison uses ComparisonPackageCard instead.
|
||||||
@@ -61,13 +62,19 @@ export const ComparisonColumnCard = React.forwardRef<HTMLDivElement, ComparisonC
|
|||||||
...(Array.isArray(sx) ? sx : [sx]),
|
...(Array.isArray(sx) ? sx : [sx]),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{/* Floating verified badge — overlaps card top edge */}
|
{/* Floating badge — Recommended (primary fill) takes priority over Verified (soft) */}
|
||||||
{pkg.provider.verified && (
|
{(pkg.isRecommended || pkg.provider.verified) && (
|
||||||
<Badge
|
<Badge
|
||||||
color="brand"
|
color="brand"
|
||||||
variant="soft"
|
variant={pkg.isRecommended ? 'filled' : 'soft'}
|
||||||
size="small"
|
size="small"
|
||||||
icon={<VerifiedOutlinedIcon sx={{ fontSize: 14 }} />}
|
icon={
|
||||||
|
pkg.isRecommended ? (
|
||||||
|
<StarRoundedIcon sx={{ fontSize: 14 }} />
|
||||||
|
) : (
|
||||||
|
<VerifiedOutlinedIcon sx={{ fontSize: 14 }} />
|
||||||
|
)
|
||||||
|
}
|
||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: -12,
|
top: -12,
|
||||||
@@ -77,7 +84,7 @@ export const ComparisonColumnCard = React.forwardRef<HTMLDivElement, ComparisonC
|
|||||||
boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
|
boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Verified
|
{pkg.isRecommended ? 'Recommended' : 'Verified'}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -85,24 +92,16 @@ export const ComparisonColumnCard = React.forwardRef<HTMLDivElement, ComparisonC
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
selected={pkg.isRecommended}
|
selected={pkg.isRecommended}
|
||||||
padding="none"
|
padding="none"
|
||||||
sx={{ overflow: 'hidden', flex: 1, display: 'flex', flexDirection: 'column' }}
|
sx={{
|
||||||
|
overflow: 'hidden',
|
||||||
|
flex: 1,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
...(pkg.isRecommended && {
|
||||||
|
borderColor: 'var(--fa-color-brand-600)',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{pkg.isRecommended && (
|
|
||||||
<Box sx={{ bgcolor: 'var(--fa-color-brand-600)', py: 0.75, textAlign: 'center' }}>
|
|
||||||
<Typography
|
|
||||||
variant="labelSm"
|
|
||||||
sx={{
|
|
||||||
color: 'var(--fa-color-white)',
|
|
||||||
fontWeight: 600,
|
|
||||||
letterSpacing: '0.05em',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Recommended
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -111,39 +110,59 @@ export const ComparisonColumnCard = React.forwardRef<HTMLDivElement, ComparisonC
|
|||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
px: 2.5,
|
px: 2.5,
|
||||||
py: 2.5,
|
py: 2.5,
|
||||||
pt: pkg.provider.verified ? 3 : 2.5,
|
pt: pkg.provider.verified || pkg.isRecommended ? 3 : 2.5,
|
||||||
gap: 0.5,
|
gap: 0.5,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Provider name (truncated with tooltip) */}
|
{/* Provider name with optional verified icon (truncated with tooltip) */}
|
||||||
<Tooltip
|
<Box
|
||||||
title={pkg.provider.name}
|
sx={{
|
||||||
arrow
|
display: 'flex',
|
||||||
placement="top"
|
alignItems: 'center',
|
||||||
disableHoverListener={pkg.provider.name.length < 24}
|
justifyContent: 'center',
|
||||||
|
gap: 0.75,
|
||||||
|
maxWidth: '100%',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
{pkg.isRecommended && (
|
||||||
variant="label"
|
<VerifiedOutlinedIcon
|
||||||
sx={{
|
sx={{
|
||||||
fontWeight: 600,
|
fontSize: 16,
|
||||||
overflow: 'hidden',
|
color: 'var(--fa-color-brand-600)',
|
||||||
textOverflow: 'ellipsis',
|
flexShrink: 0,
|
||||||
whiteSpace: 'nowrap',
|
}}
|
||||||
maxWidth: '100%',
|
aria-label="Verified provider"
|
||||||
}}
|
/>
|
||||||
|
)}
|
||||||
|
<Tooltip
|
||||||
|
title={pkg.provider.name}
|
||||||
|
arrow
|
||||||
|
placement="top"
|
||||||
|
disableHoverListener={pkg.provider.name.length < 24}
|
||||||
>
|
>
|
||||||
{pkg.provider.name}
|
<Typography
|
||||||
</Typography>
|
variant="label"
|
||||||
</Tooltip>
|
sx={{
|
||||||
|
fontWeight: 600,
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
minWidth: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{pkg.provider.name}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{/* Location */}
|
{/* Location */}
|
||||||
<Typography variant="caption" color="text.secondary">
|
<Typography variant="caption" color="text.secondary">
|
||||||
{pkg.provider.location}
|
{pkg.provider.location}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{/* Rating */}
|
{/* Rating (or dash placeholder to keep card heights consistent) */}
|
||||||
{pkg.provider.rating != null && (
|
{pkg.provider.rating != null ? (
|
||||||
<Box sx={{ display: 'inline-flex', alignItems: 'center', gap: 0.5 }}>
|
<Box sx={{ display: 'inline-flex', alignItems: 'center', gap: 0.5 }}>
|
||||||
<StarRoundedIcon
|
<StarRoundedIcon
|
||||||
sx={{ fontSize: 16, color: 'var(--fa-color-brand-500)' }}
|
sx={{ fontSize: 16, color: 'var(--fa-color-brand-500)' }}
|
||||||
@@ -154,6 +173,10 @@ export const ComparisonColumnCard = React.forwardRef<HTMLDivElement, ComparisonC
|
|||||||
{pkg.provider.reviewCount != null && ` (${pkg.provider.reviewCount})`}
|
{pkg.provider.reviewCount != null && ` (${pkg.provider.reviewCount})`}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Typography variant="body2" color="text.secondary" aria-label="No reviews yet">
|
||||||
|
—
|
||||||
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Divider sx={{ width: '100%', my: 1 }} />
|
<Divider sx={{ width: '100%', my: 1 }} />
|
||||||
@@ -182,10 +205,10 @@ export const ComparisonColumnCard = React.forwardRef<HTMLDivElement, ComparisonC
|
|||||||
{pkg.provider.verified ? 'Make Arrangement' : 'Make Enquiry'}
|
{pkg.provider.verified ? 'Make Arrangement' : 'Make Enquiry'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{!pkg.isRecommended && onRemove && (
|
{!pkg.isRecommended && onRemove ? (
|
||||||
<Link
|
<Link
|
||||||
component="button"
|
component="button"
|
||||||
variant="body2"
|
variant="caption"
|
||||||
color="text.secondary"
|
color="text.secondary"
|
||||||
underline="hover"
|
underline="hover"
|
||||||
onClick={() => onRemove(pkg.id)}
|
onClick={() => onRemove(pkg.id)}
|
||||||
@@ -193,6 +216,11 @@ export const ComparisonColumnCard = React.forwardRef<HTMLDivElement, ComparisonC
|
|||||||
>
|
>
|
||||||
Remove
|
Remove
|
||||||
</Link>
|
</Link>
|
||||||
|
) : (
|
||||||
|
/* Invisible spacer keeps CTA aligned with cards that show Remove */
|
||||||
|
<Box sx={{ mt: 0.5, visibility: 'hidden' }} aria-hidden>
|
||||||
|
<Typography variant="caption">Remove</Typography>
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user