refactor(compare): Etapa 13 — unify compare folder to product-catalog runtime type (−64 TSC errors)#149
Conversation
|
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. |
WalkthroughCorreção de erros TypeScript na seção de comparação de produtos pela importação do tipo correto ChangesCorreção de Tipagem e Refatoração de Compare Folder
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes A PR aborda refatoração heterogênea de múltiplos componentes interdependentes no folder Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR refactors the compare UI to use the runtime product catalog type instead of the DB-oriented product type, reducing TypeScript baseline errors and documenting the architectural finding around duplicate Product types.
Changes:
- Swaps compare-page/component imports from
@/types/productor looseRecord<string, unknown>props to@/types/product-catalog. - Reformats/tightens compare UI components and removes a few unused/invalid JSX references.
- Regenerates
.tsc-baseline.jsonand updates operational documentation/status for Etapa 13.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
.tsc-baseline.json |
Updates TypeScript baseline counts after compare-folder cleanup. |
STATUS.md |
Marks Etapa 13 complete and updates remaining audit-plan estimates. |
docs/redeploy/REDEPLOY-ETAPA-13-COMPARE-FOLDER.md |
Documents the compare-folder refactor and Product type discovery. |
src/pages/products/ComparePage.tsx |
Switches compare page to product-catalog types and reformats JSX. |
src/components/compare/AIComparisonAdvisor.tsx |
Switches AI advisor product prop type to product-catalog Product. |
src/components/compare/CompareTableView.tsx |
Switches table view to product-catalog Product and removes invalid category/supplier fields. |
src/components/compare/ComparisonDuelView.tsx |
Switches duel view to product-catalog Product and removes unused import. |
src/components/compare/ComparisonMobileView.tsx |
Switches mobile comparison view to product-catalog Product/ProductColor. |
src/components/compare/ComparisonPresentationLauncher.tsx |
Switches presentation launcher to product-catalog Product. |
src/components/compare/ComparisonRadarChart.tsx |
Switches radar chart to product-catalog Product. |
src/components/compare/ComparisonScoreCard.tsx |
Replaces loose product records with product-catalog Product props. |
src/components/compare/ExportComparisonButton.tsx |
Replaces loose product records with product-catalog Product props. |
src/components/compare/FloatingCompareBar.tsx |
Switches floating compare bar to product-catalog Product. |
src/components/compare/OtherSuppliersRow.tsx |
Replaces loose product record and as any with product-catalog Product. |
src/components/compare/SimilarProductsRail.tsx |
Replaces loose product records with product-catalog Product props. |
src/components/compare/StockRiskBadge.tsx |
Replaces loose product record with product-catalog Product. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const { items: scoreItems = [] } = useComparisonScore(products) || { items: [] }; | ||
| const winnerIdx = (scoreItems && scoreItems.length > 0) | ||
| ? scoreItems.reduce((best, cur, idx, arr) => cur.score > arr[best].score ? idx : best, 0) | ||
| : -1; | ||
| const winnerIdx = | ||
| scoreItems && scoreItems.length > 0 | ||
| ? scoreItems.reduce((best, cur, idx, arr) => (cur.score > arr[best].score ? idx : best), 0) | ||
| : -1; |
| { | ||
| key: 'leadTime', | ||
| label: 'Lead time (dias)', | ||
| format: (p) => (p.leadTimeDays ? `${p.leadTimeDays}d` : '—'), | ||
| raw: (p) => p.leadTimeDays ?? 999, | ||
| better: 'lower', |
| | `src/types/product-catalog.ts` | Runtime/UI (modelo após `mapPromobrindToProduct`) | camelCase (`isKit`, `minQuantity`, `stockStatus`), objetos aninhados (`category: {id, name}`, `supplier: {id, name}`, `tags: {publicoAlvo, datasComemorativas, ...}`), não-nullable | | ||
|
|
||
| O *runtime data* que flui pela aplicação é `product-catalog.Product` (origem: `useProducts()` que internamente chama `mapPromobrindToProduct(rawDB) -> product-catalog.Product`). `ProductsContext`, `useProducts` e `useSupplierComparison` já usam `product-catalog.Product`. |
There was a problem hiding this comment.
No issues found across 16 files
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
adm01-debug
left a comment
There was a problem hiding this comment.
Provavelmente superado. A unificação do subsistema de comparação ao tipo Product de product-catalog foi entregue em main via #197 (−24 erros baseline; migra ComparePage + AIComparisonAdvisor, ComparisonDuelView, ComparisonMobileView, ComparisonPresentationLauncher, ComparisonRadarChart, FloatingCompareBar). O CompareTableView (Etapa 13) também já está em main via #174 + #185 (campos camelCase em runtime). Recomendo conferir o diff deste PR contra o main atual e fechar se estiver contido. O #159 (que aponta para este branch) fica moot junto.
a410a03 to
e8c16b9
Compare
…Export, import switch in Mobile/Similar
…sentation import switch
… on zero TS baseline
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/compare/CompareTableView.tsx`:
- Around line 181-185: Remove the keyboard-inaccessibility by ensuring the
remove button becomes visible on keyboard focus: modify the button in
CompareTableView.tsx (the element that calls onRemove(entry.index)) to add focus
and focus-visible visibility classes (e.g., add "focus:opacity-100
focus-visible:opacity-100" to the existing className) and include a visible
focus indicator (e.g., "focus:outline-none focus-visible:ring" or equivalent
design-system focus classes) so it appears when tabbed to while preserving the
hover behavior.
In `@src/components/compare/ExportComparisonButton.tsx`:
- Around line 30-42: O CSV permite que células com prefixos "=", "+", "-", "@"
sejam interpretadas como fórmulas; add uma sanitização antes de serializar:
implemente uma helper chamada sanitizeCsvCell(value) e use-a na construção de
rows/csv (onde rows, headers e csv são montados em ExportComparisonButton.tsx)
para normalizar null/undefined para string vazia, escapar aspas internas e, se o
valor string começar com one of ['=', '+', '-', '@'], prefixá-lo com uma aspas
simples (') ou outro caractere seguro antes de envolver em aspas CSV; aplique
sanitizeCsvCell dentro do .map((v) => ...) que constrói cada célula para
garantir que todas as células exportadas sejam seguras.
- Around line 63-77: The busy flag is cleared in the finally block before the
async canvas.toBlob callback completes, allowing concurrent exports; update
ExportComparisonButton to only call setBusy(false) after the toBlob flow
finishes (success or failure) by moving the setBusy(false) into the toBlob
callback paths and also ensure it's called when blob is null or when
URL.createObjectURL / a.click / revokeObjectURL could fail; keep the try/catch
to log exceptions, but remove the finally block clearing busy and instead clear
busy inside the callback (and inside catch) so busy accurately reflects the
active export operation.
In `@src/components/compare/FloatingCompareBar.tsx`:
- Around line 91-102: The remove button in FloatingCompareBar (the <button> with
onClick calling removeByIndex and containing the <X> icon) is missing an
accessible name; add an aria-label attribute (e.g. aria-label="Remover item da
comparação") to that button so screen readers convey the action clearly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a1fe4bb0-7b21-4b8b-b2c8-a1ad69f855de
📒 Files selected for processing (16)
.tsc-baseline.jsonSTATUS.mddocs/redeploy/REDEPLOY-ETAPA-13-COMPARE-FOLDER.mdsrc/components/compare/AIComparisonAdvisor.tsxsrc/components/compare/CompareTableView.tsxsrc/components/compare/ComparisonDuelView.tsxsrc/components/compare/ComparisonMobileView.tsxsrc/components/compare/ComparisonPresentationLauncher.tsxsrc/components/compare/ComparisonRadarChart.tsxsrc/components/compare/ComparisonScoreCard.tsxsrc/components/compare/ExportComparisonButton.tsxsrc/components/compare/FloatingCompareBar.tsxsrc/components/compare/OtherSuppliersRow.tsxsrc/components/compare/SimilarProductsRail.tsxsrc/components/compare/StockRiskBadge.tsxsrc/pages/products/ComparePage.tsx
| <button | ||
| aria-label="Remover da comparação" | ||
| onClick={() => onRemove(entry.index)} | ||
| className="absolute -right-1 -top-1 z-10 rounded-full border border-border bg-background p-1 opacity-0 transition-opacity hover:bg-destructive/20 group-hover:opacity-100" | ||
| > |
There was a problem hiding this comment.
Botão de remover pode ficar invisível ao navegar por teclado
Com opacity-0 + group-hover:opacity-100, o controle pode não ficar perceptível no foco via teclado, o que quebra a ação para parte dos usuários.
💡 Patch sugerido
- className="absolute -right-1 -top-1 z-10 rounded-full border border-border bg-background p-1 opacity-0 transition-opacity hover:bg-destructive/20 group-hover:opacity-100"
+ className="absolute -right-1 -top-1 z-10 rounded-full border border-border bg-background p-1 opacity-0 transition-opacity hover:bg-destructive/20 group-hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button | |
| aria-label="Remover da comparação" | |
| onClick={() => onRemove(entry.index)} | |
| className="absolute -right-1 -top-1 z-10 rounded-full border border-border bg-background p-1 opacity-0 transition-opacity hover:bg-destructive/20 group-hover:opacity-100" | |
| > | |
| <button | |
| aria-label="Remover da comparação" | |
| onClick={() => onRemove(entry.index)} | |
| className="absolute -right-1 -top-1 z-10 rounded-full border border-border bg-background p-1 opacity-0 transition-opacity hover:bg-destructive/20 group-hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary" | |
| > |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/compare/CompareTableView.tsx` around lines 181 - 185, Remove
the keyboard-inaccessibility by ensuring the remove button becomes visible on
keyboard focus: modify the button in CompareTableView.tsx (the element that
calls onRemove(entry.index)) to add focus and focus-visible visibility classes
(e.g., add "focus:opacity-100 focus-visible:opacity-100" to the existing
className) and include a visible focus indicator (e.g., "focus:outline-none
focus-visible:ring" or equivalent design-system focus classes) so it appears
when tabbed to while preserving the hover behavior.
| const headers = ['SKU', 'Nome', 'Preço', 'Qtd. mín.', 'Estoque', 'Cores', 'Categoria']; | ||
| const rows = products.map((p) => [ | ||
| p.sku ?? '', | ||
| p.name, | ||
| p.price, | ||
| p.minQuantity, | ||
| p.stock, | ||
| (p.colors?.length ?? 0), | ||
| p.category?.name ?? "", | ||
| p.colors?.length ?? 0, | ||
| p.category?.name ?? '', | ||
| ]); | ||
| const csv = [headers, ...rows].map(r => | ||
| r.map(v => `"${String(v ?? "").replace(/"/g, '""')}"`).join(",") | ||
| ).join("\n"); | ||
| const blob = new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8" }); | ||
| const csv = [headers, ...rows] | ||
| .map((r) => r.map((v) => `"${String(v ?? '').replace(/"/g, '""')}"`).join(',')) | ||
| .join('\n'); |
There was a problem hiding this comment.
Sanitize de célula CSV para bloquear fórmula maliciosa
Os valores exportados ainda permitem prefixos =, +, - e @. Ao abrir no Excel/Sheets, isso pode virar execução de fórmula (CSV injection).
💡 Patch sugerido
+ const escapeCsvCell = (value: unknown) => {
+ const raw = String(value ?? '');
+ const neutralized = /^[=+\-@]/.test(raw) ? `'${raw}` : raw;
+ return `"${neutralized.replace(/"/g, '""')}"`;
+ };
+
const exportCSV = () => {
const headers = ['SKU', 'Nome', 'Preço', 'Qtd. mín.', 'Estoque', 'Cores', 'Categoria'];
const rows = products.map((p) => [
p.sku ?? '',
p.name,
p.price,
p.minQuantity,
p.stock,
p.colors?.length ?? 0,
p.category?.name ?? '',
]);
const csv = [headers, ...rows]
- .map((r) => r.map((v) => `"${String(v ?? '').replace(/"/g, '""')}"`).join(','))
+ .map((r) => r.map(escapeCsvCell).join(','))
.join('\n');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/compare/ExportComparisonButton.tsx` around lines 30 - 42, O
CSV permite que células com prefixos "=", "+", "-", "@" sejam interpretadas como
fórmulas; add uma sanitização antes de serializar: implemente uma helper chamada
sanitizeCsvCell(value) e use-a na construção de rows/csv (onde rows, headers e
csv são montados em ExportComparisonButton.tsx) para normalizar null/undefined
para string vazia, escapar aspas internas e, se o valor string começar com one
of ['=', '+', '-', '@'], prefixá-lo com uma aspas simples (') ou outro caractere
seguro antes de envolver em aspas CSV; aplique sanitizeCsvCell dentro do
.map((v) => ...) que constrói cada célula para garantir que todas as células
exportadas sejam seguras.
| canvas.toBlob((blob) => { | ||
| if (!blob) return; | ||
| const url = URL.createObjectURL(blob); | ||
| const a = document.createElement("a"); | ||
| const a = document.createElement('a'); | ||
| a.href = url; | ||
| a.download = `comparacao-${new Date().toISOString().slice(0, 10)}.png`; | ||
| a.click(); | ||
| URL.revokeObjectURL(url); | ||
| toast.success("PNG exportado"); | ||
| toast.success('PNG exportado'); | ||
| }); | ||
| } catch (e) { | ||
| console.error(e); | ||
| toast.error("Falha ao exportar PNG"); | ||
| toast.error('Falha ao exportar PNG'); | ||
| } finally { | ||
| setBusy(false); |
There was a problem hiding this comment.
Estado busy é liberado antes do PNG terminar
setBusy(false) no finally roda antes da callback de canvas.toBlob, então o usuário pode clicar de novo e disparar exportações concorrentes.
💡 Patch sugerido
const exportPNG = async () => {
setBusy(true);
try {
const html2canvas = (await import('html2canvas')).default;
const el = document.querySelector(targetSelector) as HTMLElement | null;
if (!el) {
toast.error('Área não encontrada');
return;
}
const canvas = await html2canvas(el, { backgroundColor: '`#ffffff`', scale: 2, useCORS: true });
- canvas.toBlob((blob) => {
- if (!blob) return;
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = `comparacao-${new Date().toISOString().slice(0, 10)}.png`;
- a.click();
- URL.revokeObjectURL(url);
- toast.success('PNG exportado');
- });
+ const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve));
+ if (!blob) {
+ toast.error('Falha ao gerar imagem PNG');
+ return;
+ }
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `comparacao-${new Date().toISOString().slice(0, 10)}.png`;
+ a.click();
+ URL.revokeObjectURL(url);
+ toast.success('PNG exportado');
} catch (e) {
console.error(e);
toast.error('Falha ao exportar PNG');
} finally {
setBusy(false);
}
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| canvas.toBlob((blob) => { | |
| if (!blob) return; | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `comparacao-${new Date().toISOString().slice(0, 10)}.png`; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| toast.success("PNG exportado"); | |
| toast.success('PNG exportado'); | |
| }); | |
| } catch (e) { | |
| console.error(e); | |
| toast.error("Falha ao exportar PNG"); | |
| toast.error('Falha ao exportar PNG'); | |
| } finally { | |
| setBusy(false); | |
| const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve)); | |
| if (!blob) { | |
| toast.error('Falha ao gerar imagem PNG'); | |
| return; | |
| } | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `comparacao-${new Date().toISOString().slice(0, 10)}.png`; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| toast.success('PNG exportado'); | |
| } catch (e) { | |
| console.error(e); | |
| toast.error('Falha ao exportar PNG'); | |
| } finally { | |
| setBusy(false); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/compare/ExportComparisonButton.tsx` around lines 63 - 77, The
busy flag is cleared in the finally block before the async canvas.toBlob
callback completes, allowing concurrent exports; update ExportComparisonButton
to only call setBusy(false) after the toBlob flow finishes (success or failure)
by moving the setBusy(false) into the toBlob callback paths and also ensure it's
called when blob is null or when URL.createObjectURL / a.click / revokeObjectURL
could fail; keep the try/catch to log exceptions, but remove the finally block
clearing busy and instead clear busy inside the callback (and inside catch) so
busy accurately reflects the active export operation.
| <button | ||
| onClick={() => removeByIndex(entry.index)} | ||
| className={cn( | ||
| 'absolute -right-1.5 -top-1.5 h-5 w-5 rounded-full', | ||
| 'bg-destructive text-destructive-foreground', | ||
| 'flex items-center justify-center', | ||
| 'opacity-0 transition-opacity group-hover:opacity-100', | ||
| 'hover:bg-destructive/90', | ||
| )} | ||
| > | ||
| <X className="h-3 w-3" /> | ||
| </button> |
There was a problem hiding this comment.
Botão de remover item precisa de nome acessível.
Na Line 91, o botão com ícone X não tem aria-label. Para leitor de tela, a ação fica ambígua. Sugestão: aria-label="Remover item da comparação".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/compare/FloatingCompareBar.tsx` around lines 91 - 102, The
remove button in FloatingCompareBar (the <button> with onClick calling
removeByIndex and containing the <X> icon) is missing an accessible name; add an
aria-label attribute (e.g. aria-label="Remover item da comparação") to that
button so screen readers convey the action clearly.
e8c16b9 to
1447a00
Compare
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Closes Etapa 13 of the 20-step audit plan (PR #124). Escopo expandiu de 1 arquivo (CompareTableView.tsx, 26 erros) para 13 arquivos (compare folder inteira) por causa de uma descoberta arquitetural: o repo tem dois tipos
Productdistintos coexistindo, e toda a pastasrc/components/compare/estava importando do tipo errado.TL;DR
.tsc-baseline.jsonregenerado +STATUS.mdatualizado + 1 doc novoRecord<string, unknown>Descoberta arquitetural
O repo tem dois arquivos de tipos
Product:src/types/product.tsis_kit,min_quantity), maiorianull-able, sem objetos aninhadossrc/types/product-catalog.tsmapPromobrindToProduct)isKit,minQuantity), objetos aninhados, não-nullableO runtime data que flui pela aplicação é
product-catalog.Product(origem:useProducts()).ProductsContext,useSupplierComparisonetc já usavam o tipo certo. Mas toda a pastasrc/components/compare/(7 arquivos) +src/pages/products/ComparePage.tsximportavam desrc/types/product.ts(DB type). Por structural typing, o app rodava normal — mas TSC reclamava em todo acesso acamelCase/ objeto aninhado.A doc do plano original hipotetizou refactor renomeando
camelCase→snake_case+ null-safety patches. Resultado empírico: o código já estava correto para o runtime, apenas os imports estavam errados.Mudanças por arquivo
CompareTableView.tsxcategory.icon,supplier.verified) + cleanupShieldCheckórfãoStockRiskBadge.tsxRecord<string, unknown>→ProductOtherSuppliersRow.tsxRecord<string, unknown>→Product, removeas anyComparisonScoreCard.tsxRecord<string, unknown>→ProductExportComparisonButton.tsxRecord<string, unknown>→ProductSimilarProductsRail.tsxRecord<string, unknown>→ProductComparisonPresentationLauncher.tsxComparisonMobileView.tsxComparisonDuelView.tsxMinusimport órfão)ComparisonRadarChart.tsxAIComparisonAdvisor.tsxFloatingCompareBar.tsxref→_ref)ComparePage.tsxDrive-by lint fix adicional:
ExportComparisonButton:formatCurrency→_formatCurrency(arg unused — lint error pré-existente no main).¹ SimilarProductsRail: 4 residuais são pré-existentes — inferência do TanStack Query em
useProducts(). Fora de escopo.² PresentationLauncher: 5 residuais —
ProductScore.itemstypo + implicit any em.reduce(). Bugs separados.³ DuelView: 3 residuais —
p.leadTimeDaysinexistente em ambos os Product types. Bug real, requerleadTimeProxy(stockStatus).⁴ AIAdvisor: 1 residual — query result tipado como
{}. Bug separado.Drive-by lint fixes (errors pré-existentes no main)
Husky pre-commit rodou
eslint --fixnos arquivos tocados e revelou 3 erros pré-existentes que não passaram pelo CI anterior:ComparisonDuelView:Minusimport nunca usadoExportComparisonButton: argformatCurrencydeclarado mas nunca usadoFloatingCompareBar: argrefdeclarado mas nunca usado emforwardRefOs 3 foram corrigidos como parte desta PR (renomeando para
_unusedquando aplicável).Implicações para o plano de 20 etapas
A doc original supôs que as Etapas 9-13 eram "refactor camelCase ↔ snake_case" (~3-4h cada). Na prática, Etapa 13 levou ~1h.
Antes de atacar Etapas 10-12 (
AddressTab.tsx,BasicDataTab.tsx,AdminProductFormPage.tsx):Se for o mesmo padrão de import errado, custo cai de ~3h → ~30min por etapa.
A Etapa 9 (
price-response.adapter.ts) é arquiteturalmente diferente (adapter, não componente UI) — trate separadamente.Sugestão de Etapa 13.5
Unificar os dois tipos
Product. A coexistência é uma armadilha que tropeça em todo refactor. Possível abordagem: deletarsrc/types/product.ts, redirecionar os ~30 consumers paraproduct-catalog, gerarsrc/types/product-db.tsvia Supabase typegen para uso restrito ao mapper.Validação
Doc
Detalhes completos em
docs/redeploy/REDEPLOY-ETAPA-13-COMPARE-FOLDER.md.Branch composition
5 commits sequenciais (serão squashed no merge):
6910419- part 1/4: small files8a9edc4- part 2/5: escape-hatch removal in Score/Export + Mobile/Similar697f4e9- part 3/5: Duel/AIAdvisor/FloatingBar/Presentationc821377- docs: STATUS + REDEPLOY-ETAPA-13-COMPARE-FOLDERa410a03- part 5/5: CompareTableView + ComparePage + baseline regenSummary by cubic
Unified the compare folder to use the runtime
@/types/product-catalog.Product, replacing escape-hatch types and fixing camelCase vs snake_case mismatches. Drops 64 TypeScript errors with no runtime changes.ComparePageto@/types/product-catalog.Record<string, unknown>withProductinExportComparisonButton,ComparisonScoreCard,OtherSuppliersRow,StockRiskBadge, andSimilarProductsRail.CompareTableView(removed invalid fields) and pruned unused imports/args across files.STATUS.mdand addeddocs/redeploy/REDEPLOY-ETAPA-13-COMPARE-FOLDER.md.Written for commit f9815a5. Summary will update on new commits. Review in cubic
Summary by CodeRabbit
Release Notes
Bug Fixes
Style
Refactor