feat(29-02): update CandidateCard and CandidateListItem to use GearImage
Replace object-cover with GearImage for fit-within rendering on candidate cards and list items. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import type { CandidateDelta } from "../hooks/useImpactDeltas";
|
||||
import { LucideIcon } from "../lib/iconData";
|
||||
import { useUIStore } from "../stores/uiStore";
|
||||
import { RankBadge } from "./CandidateListItem";
|
||||
import { GearImage, imageContainerBg } from "./GearImage";
|
||||
import { ImpactDeltaBadge } from "./ImpactDeltaBadge";
|
||||
import { StatusBadge } from "./StatusBadge";
|
||||
|
||||
@@ -17,6 +18,10 @@ interface CandidateCardProps {
|
||||
imageFilename: string | null;
|
||||
imageUrl?: string | null;
|
||||
productUrl?: string | null;
|
||||
dominantColor?: string | null;
|
||||
cropZoom?: number | null;
|
||||
cropX?: number | null;
|
||||
cropY?: number | null;
|
||||
threadId: number;
|
||||
isActive: boolean;
|
||||
status: "researching" | "ordered" | "arrived";
|
||||
@@ -37,6 +42,10 @@ export function CandidateCard({
|
||||
imageFilename: _imageFilename,
|
||||
imageUrl,
|
||||
productUrl,
|
||||
dominantColor,
|
||||
cropZoom,
|
||||
cropX,
|
||||
cropY,
|
||||
threadId,
|
||||
isActive,
|
||||
status,
|
||||
@@ -149,15 +158,25 @@ export function CandidateCard({
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
<div className="aspect-[4/3] bg-gray-50">
|
||||
<div
|
||||
className="aspect-[4/3] overflow-hidden"
|
||||
style={{
|
||||
backgroundColor: imageUrl
|
||||
? imageContainerBg(dominantColor)
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
{imageUrl ? (
|
||||
<img
|
||||
<GearImage
|
||||
src={imageUrl}
|
||||
alt={name}
|
||||
className="w-full h-full object-cover"
|
||||
dominantColor={dominantColor}
|
||||
cropZoom={cropZoom}
|
||||
cropX={cropX}
|
||||
cropY={cropY}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex flex-col items-center justify-center">
|
||||
<div className="w-full h-full bg-gray-50 flex flex-col items-center justify-center">
|
||||
<LucideIcon
|
||||
name={categoryIcon}
|
||||
size={36}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useFormatters } from "../hooks/useFormatters";
|
||||
import type { CandidateDelta } from "../hooks/useImpactDeltas";
|
||||
import { LucideIcon } from "../lib/iconData";
|
||||
import { useUIStore } from "../stores/uiStore";
|
||||
import { GearImage, imageContainerBg } from "./GearImage";
|
||||
import { ImpactDeltaBadge } from "./ImpactDeltaBadge";
|
||||
import { StatusBadge } from "./StatusBadge";
|
||||
|
||||
@@ -19,6 +20,7 @@ interface CandidateWithCategory {
|
||||
productUrl: string | null;
|
||||
imageFilename: string | null;
|
||||
imageUrl?: string | null;
|
||||
dominantColor?: string | null;
|
||||
status: "researching" | "ordered" | "arrived";
|
||||
pros: string | null;
|
||||
cons: string | null;
|
||||
@@ -84,12 +86,18 @@ export function CandidateListItem({
|
||||
<RankBadge rank={rank} />
|
||||
|
||||
{/* Image thumbnail */}
|
||||
<div className="w-12 h-12 rounded-lg overflow-hidden shrink-0 bg-gray-50 flex items-center justify-center">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg overflow-hidden shrink-0 flex items-center justify-center"
|
||||
style={{
|
||||
backgroundColor: candidate.imageUrl
|
||||
? imageContainerBg(candidate.dominantColor)
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
{candidate.imageUrl ? (
|
||||
<img
|
||||
<GearImage
|
||||
src={candidate.imageUrl}
|
||||
alt={candidate.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<LucideIcon
|
||||
|
||||
Reference in New Issue
Block a user