feat(product-card): hover crossfade para set_image_url (todas as cores)#601
Conversation
When a product has set_image_url (grouped photo with all color variations), hovering the card now performs a smooth opacity crossfade from the main image to the set image. Suppressed when the user is actively browsing color variants (variant-specific image takes priority). - Adds set_image_url consumption to ProductCardImage - Reactivates isHovered prop (was prefixed with _ unused marker) - CSS-only crossfade — zero JS in the hover hot path - onError fallback hides broken set image, main image stays visible - ~4.086 products (67%) gain the effect: SPOT 96.9%, XBZ 76.6%, Asia 84.4%
getCdnUrl lives in @/utils/image-utils, not @/lib/cdn. Aligns with the existing import in ProductCard.tsx.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
Warning Review limit reached
More reviews will be available in 31 minutes and 7 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
1 issue found across 1 file
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/components/products/ProductCardImage.tsx">
<violation number="1" location="src/components/products/ProductCardImage.tsx:175">
P2: Broken `set_image_url` can make the product image disappear on hover because the base image remains forced to `opacity-0` after overlay load error.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| }} | ||
| onError={(e) => { | ||
| // Hide broken set image gracefully — main image will remain visible | ||
| (e.currentTarget as HTMLImageElement).style.display = 'none'; |
There was a problem hiding this comment.
P2: Broken set_image_url can make the product image disappear on hover because the base image remains forced to opacity-0 after overlay load error.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/products/ProductCardImage.tsx, line 175:
<comment>Broken `set_image_url` can make the product image disappear on hover because the base image remains forced to `opacity-0` after overlay load error.</comment>
<file context>
@@ -114,22 +133,50 @@ export const ProductCardImage = memo(function ProductCardImage({
+ }}
+ onError={(e) => {
+ // Hide broken set image gracefully — main image will remain visible
+ (e.currentTarget as HTMLImageElement).style.display = 'none';
+ }}
+ />
</file context>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 69dc451565
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| className={cn( | ||
| 'h-full w-full object-contain', | ||
| 'transition-opacity duration-300 ease-in-out', | ||
| hasSetHover && isHovered && 'opacity-0', |
There was a problem hiding this comment.
Keep the main image visible after hover image errors
For products whose set_image_url resolves to a 404 or invalid CDN/proxy URL, the new onError handler only hides the overlay image, but hasSetHover stays true; while the card remains hovered this class still forces the primary OptimizedImage to opacity-0, leaving the image area blank. Since this path explicitly handles broken set images, track a failed/loaded state or avoid fading the main image until the hover image has successfully loaded.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR enables a hover image swap on catalog product cards by consuming product.set_image_url in the actual ProductCardImage implementation used by ProductCard, showing a grouped “all colors” image on hover while preserving existing variant/mini-carousel behavior.
Changes:
- Reads
product.set_image_url, transforms it withgetCdnUrl(..., 'card'), and conditionally enables the hover behavior. - Adds a second absolutely-positioned image that fades in on hover (and suppresses the effect when
hasMultipleVariantsis true). - Updates the main image styling to support opacity transition during hover.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| className={cn( | ||
| 'h-full w-full object-contain', | ||
| 'transition-opacity duration-300 ease-in-out', | ||
| hasSetHover && isHovered && 'opacity-0', | ||
| )} |
…tsByIds exports FloatingCompareBar.tsx has been importing `useProductsContextSafe` and calling `ctx?.getProductsByIds(uniqueIds)` since some prior refactor, but neither symbol existed in ProductsContext.tsx — causing the Vite build to fail at module resolution: "useProductsContextSafe" is not exported by "src/contexts/ProductsContext.tsx" imported by "src/components/compare/FloatingCompareBar.tsx" This broke every production deploy from commit d456899 onwards (PR #601 hover crossfade + PR #602 audit fixes + Lovable visual edits), freezing the live site on an older build. Additions: - `getProductsByIds(ids: string[]): Product[]` — batch lookup that filters the cached products array by a Set of ids. Returns [] for empty input. - `useProductsContextSafe()` — non-throwing variant of useProducts(); returns `ProductsContextType | null` so components rendered above the provider (floating bars, portals) can opt-in instead of crashing. `useProducts()` keeps its original throw-on-missing-provider behavior to preserve the strict contract for components inside the provider tree.
Commit 8818bea ("simplify HMR guard") was misleadingly named — it actually REWROTE the entire ProductsContext.tsx, replacing a rich lazy-fetching API with a minimal one and removing 4 exports that 21 consumers depend on: REMOVED in 8818bea: CONSUMERS BROKEN: - export const ProductsContext useSearch.ts, useSellerCartsPage.ts - export function useProductsContext() 15 files - export function useProductsContextSafe() FloatingCompareBar.tsx - API: getProductById, getProductsByIds, all of the above registerProducts, batched-fetch, HMR recovery via setKey Every production deploy since 8818bea has been state=ERROR (build failure at "useProductsContextSafe is not exported"). The live site is frozen on the 253cebc deploy (last READY). PRs #601 (set_image hover) and #602 are queued behind this bug. This commit restores the full ProductsContext.tsx from commit 253cebc verbatim, preserving the rich API: - export const ProductsContext (raw context for direct useContext) - export ProductsProvider (lazy-fetching with 50ms batching) - export useProductsContext() (strict: returns fallback in dev, fallback in prod; never crashes) - export useProductsContextSafe() (safe: returns null outside provider) API: - products: Product[] - isLoading: boolean - getProductById(id): Product | undefined (lazy-fetches if missing) - getProductsByIds(ids): Product[] (lazy-fetches missing in batch) - registerProducts(products): void (caller-side cache hydration) The fetchPromobrindProducts({ filters, limit }) call in this file is backward-compatible with the current main's products.ts (verified: that file was not changed by 8818bea — only the context consumer was rewritten). Restoring this unblocks all 21 ProductsContext consumers and the entire production deploy pipeline.
🎯 Objetivo
Ativar o efeito de hover que troca a imagem principal pela imagem com todas as cores juntas (
set_image_url) ao passar o mouse nos cards do catálogo.A coluna
set_image_urlfoi adicionada à viewv_products_publicno PR #599, e o tipoProduct.set_image_urljá estava preparado. Mas nenhum componente de UI consumia esse campo — o efeito visual nunca chegou no usuário final.Diagnóstico completo em sessão prévia: existiam dois componentes
ProductCardImageno projeto (um em/components/products/, outro em/components/catalog/). OProductCardreal importava o de/products/, que ignoravaset_image_url. O outro arquivo tinha lógica de hover correta mas era órfão (sem importadores).Este PR aplica a lógica direto no componente real, preservando 100% das features existentes (variantes de cor, badges, mini-carousel, scale animation).
🔧 Modificações
src/components/products/ProductCardImage.tsx:isHovered— antes prefixada com_isHovered(marcador de unused)set_image_urlviagetCdnUrl(rawUrl, 'card')<img>absoluto que faz crossfade de opacidade no hoverhasMultipleVariants === true— se o usuário está navegando variantes específicas, a imagem da variante tem prioridadeonErroresconde a imagem set quebrada sem quebrar a UI principalTotal: ~30 linhas de código novo + reativação de 1 prop.
✅ Cobertura esperada após o merge
Os 33% sem efeito mantêm o comportamento atual (imagem estática) — zero regressão visual para esse subconjunto.
🛡️ Garantias
transition-opacity duration-300). Zero JS no hover hot path.hasSetHover = Boolean(setImageSrc) && !hasMultipleVariants.altexplícito ("todas as cores"),pointer-events-nonena segunda imagem para não interferir nos cliques dos color dots.onErroresconde imagem set quebrada viadisplay:none, principal continua visível.loading="lazy"edecoding="async"— set images só baixam quando o card aparece no viewport.🧪 Cenários cobertos
set_image_urle sem variantesset_image_urlmas com variantes ativasset_image_url/card)getCdnUrladiciona o sufixo correto📦 Validação prévia (4 rodadas + 1 unit test)
Product.set_image_url?: string | null— ✅ confirmado emsrc/types/product-catalog.tsgetCdnUrl(url, 'card')— ✅'card'é membro válido deCdnVariantPRODUCT_SELECT_LIGHTWEIGHT— ✅ incluiset_image_url(PR fix: v_products_public 400 – colunas active + set_image_url ausentes; parar retry em 4xx #599)v_products_public— ✅ expõeset_image_url(PR fix: v_products_public 400 – colunas active + set_image_url ausentes; parar retry em 4xx #599)🔭 Não está neste PR
src/components/catalog/ProductCardImage.tsx(componente não-usado). Fica para limpeza posterior — removê-lo agora ampliaria o diff sem benefício imediato.ProductDetail(página individual do produto) — outro PR.ReplenishmentCardsou outras grids — escopo futuro se útil.Summary by cubic
Adds a hover crossfade on catalog product cards to the “all colors” image (
set_image_url). Keeps variant browsing behavior unchanged and uses CSS-only transitions with a safe fallback.product.set_image_urlviagetCdnUrl(..., 'card').isHoveredand adds a second image that crossfades on hover.hasMultipleVariantsis true; broken set images are hidden viaonErrorwhile the main image stays visible.Written for commit 69dc451. Summary will update on new commits.