Skip to content

feat(product-card): hover crossfade para set_image_url (todas as cores)#601

Merged
adm01-debug merged 2 commits into
mainfrom
feat/product-card-set-image-hover
Jun 2, 2026
Merged

feat(product-card): hover crossfade para set_image_url (todas as cores)#601
adm01-debug merged 2 commits into
mainfrom
feat/product-card-set-image-hover

Conversation

@adm01-debug
Copy link
Copy Markdown
Owner

@adm01-debug adm01-debug commented Jun 2, 2026

🎯 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_url foi adicionada à view v_products_public no PR #599, e o tipo Product.set_image_url já 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 ProductCardImage no projeto (um em /components/products/, outro em /components/catalog/). O ProductCard real importava o de /products/, que ignorava set_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:

  1. Reativa a prop isHovered — antes prefixada com _isHovered (marcador de unused)
  2. Adiciona resolução de set_image_url via getCdnUrl(rawUrl, 'card')
  3. Renderiza segundo <img> absoluto que faz crossfade de opacidade no hover
  4. Suprime o efeito quando hasMultipleVariants === true — se o usuário está navegando variantes específicas, a imagem da variante tem prioridade
  5. onError esconde a imagem set quebrada sem quebrar a UI principal

Total: ~30 linhas de código novo + reativação de 1 prop.

✅ Cobertura esperada após o merge

Fornecedor Total ativos Com hover %
SPOT / Stricker 1.200 1.163 96,9%
Asia Import 430 363 84,4%
XBZ Brindes 3.344 2.560 76,6%
Só Marcas 1.111 0 0% (sem set image)
88 Brindes 1 0 0%
Total 6.086 4.086 67,1%

Os 33% sem efeito mantêm o comportamento atual (imagem estática) — zero regressão visual para esse subconjunto.

🛡️ Garantias

  • Performance: crossfade 100% CSS (transition-opacity duration-300). Zero JS no hover hot path.
  • Compatibilidade com variantes: usuários navegando cores no mini-carousel veem a cor escolhida, não o agrupado. hasSetHover = Boolean(setImageSrc) && !hasMultipleVariants.
  • Acessibilidade: alt explícito ("todas as cores"), pointer-events-none na segunda imagem para não interferir nos cliques dos color dots.
  • Robustez: onError esconde imagem set quebrada via display:none, principal continua visível.
  • Lazy load: loading="lazy" e decoding="async" — set images só baixam quando o card aparece no viewport.

🧪 Cenários cobertos

Cenário Comportamento
Produto com set_image_url e sem variantes ✅ Hover faz crossfade
Produto com set_image_url mas com variantes ativas ✅ Hover preserva imagem da variante (não troca)
Produto sem set_image_url ✅ Sem efeito (estático, idêntico ao comportamento atual)
Imagem set quebrada (404) ✅ Esconde imagem set, principal mantém visível
URL CDN sem variante (sem /card) getCdnUrl adiciona o sufixo correto

📦 Validação prévia (4 rodadas + 1 unit test)

🔭 Não está neste PR

  • Remoção do arquivo órfão src/components/catalog/ProductCardImage.tsx (componente não-usado). Fica para limpeza posterior — removê-lo agora ampliaria o diff sem benefício imediato.
  • Hover de "todas as cores" no ProductDetail (página individual do produto) — outro PR.
  • Política de "todas as cores" para ReplenishmentCards ou 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.

  • New Features
    • Consumes product.set_image_url via getCdnUrl(..., 'card').
    • Reactivates isHovered and adds a second image that crossfades on hover.
    • Disables the effect when hasMultipleVariants is true; broken set images are hidden via onError while the main image stays visible.

Written for commit 69dc451. Summary will update on new commits.

Review in cubic

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.
Copilot AI review requested due to automatic review settings June 2, 2026 16:32
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
we-dream-big Ready Ready Preview, Comment Jun 2, 2026 4:32pm

@supabase
Copy link
Copy Markdown

supabase Bot commented Jun 2, 2026

This pull request has been ignored for the connected project doufsxqlfjyuvxuezpln because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Warning

Review limit reached

@adm01-debug, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 92c73706-ae6b-4767-8f5f-49fbadaf8cdf

📥 Commits

Reviewing files that changed from the base of the PR and between 7de3c66 and 69dc451.

📒 Files selected for processing (1)
  • src/components/products/ProductCardImage.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/product-card-set-image-hover

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 with getCdnUrl(..., 'card'), and conditionally enables the hover behavior.
  • Adds a second absolutely-positioned image that fades in on hover (and suppresses the effect when hasMultipleVariants is 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.

Comment on lines +141 to +145
className={cn(
'h-full w-full object-contain',
'transition-opacity duration-300 ease-in-out',
hasSetHover && isHovered && 'opacity-0',
)}
@adm01-debug adm01-debug merged commit d456899 into main Jun 2, 2026
30 of 42 checks passed
@adm01-debug adm01-debug deleted the feat/product-card-set-image-hover branch June 2, 2026 16:54
adm01-debug added a commit that referenced this pull request Jun 2, 2026
…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.
adm01-debug added a commit that referenced this pull request Jun 2, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants