fix: Sprint 3 — BUG-03/05/26/27/28/29 módulo Cadastro (26/05/2026)#489
Conversation
…sync localAreasRef via useEffect
…lAreas após criar produto
…hange passa advancedFilters, BUG-29 comentário useEffect inicial
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
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. |
WalkthroughPR implementa persistência de áreas de gravação locais (BUG-03) usando ref-based callback, integra AlertDialog para confirmação de delete, e ajusta filtros de página em lista de produtos. Inclui refatorações cosméticas em ProductFormFullscreen. ChangesPersistência de gravações locais (BUG-03)
Filtros e seleção em produtos
Refatoração cosmética de ProductFormFullscreen
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Razão: Mudanças heterogêneas em 6 arquivos com lógica de persistência async, state-based confirmations, refactores de UI, e ajustes de filtro. Requer validação de Promise.allSettled em flush, não-regressão de navegação em delete, integridade de ref lifecycle, e padronização de campos em INSERT. Refactores cosméticos reduzem densidade efetiva. 🚥 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
Completes Sprint 3 fixes for the Cadastro products module, mainly around preserving engraving areas during new-product creation and improving product-list pagination/selection UX.
Changes:
- Threads
engravingFlushReffrom the product page down to the engraving section and calls it after product creation. - Adds AlertDialog confirmation for engraving-area deletion.
- Updates products manager selection label exposure and page-change filtering behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/pages/admin/AdminProductFormPage.tsx |
Creates and invokes the engraving flush ref before navigating after product creation. |
src/components/admin/products/ProductFormFullscreen.tsx |
Passes the engraving flush ref through the stepper form. |
src/components/admin/products/ProductFormStepContent.tsx |
Forwards the ref to the engraving step. |
src/components/admin/products/sections/ProductEngravingSection.tsx |
Registers the flush callback and adds delete confirmation dialog. |
src/components/admin/products/sections/engraving/useEngravingWizard.ts |
Adds local-area flushing and missing-technique warning. |
src/components/admin/products/useProductsManager.ts |
Adds page-scoped selection label and passes filters on page change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // BUG-03 FIX: register flushLocalAreas with the ref so AdminProductFormPage can call it | ||
| useEffect(() => { | ||
| if (engravingFlushRef) { | ||
| engravingFlushRef.current = w.flushLocalAreas; | ||
| } | ||
| return () => { | ||
| if (engravingFlushRef) { | ||
| engravingFlushRef.current = null; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/pages/admin/AdminProductFormPage.tsx (1)
90-147:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPreserve
supplier_product_urlno carregamento do formulário.Line 204 persiste
supplier_product_url, masproductToFormData()nunca preenche esse campo. Em edição, ele cai no default''do schema e o próximo save apaga a URL já salva.Diff sugerido
supplier_id: p.supplier_id ?? '', supplier_reference: p.supplier_reference ?? '', + supplier_product_url: p.supplier_product_url ?? '', sale_price: getProductPrice(p) ?? 0,🤖 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/pages/admin/AdminProductFormPage.tsx` around lines 90 - 147, productToFormData currently omits supplier_product_url so editing an existing product overwrites the saved URL with the form schema default; update productToFormData (the function) to include supplier_product_url: p.supplier_product_url ?? '' so the existing value is loaded into the form and not cleared on save.src/components/admin/products/ProductFormFullscreen.tsx (1)
251-290:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftNão desmonte a etapa de gravação antes do submit.
Aqui só a etapa ativa fica montada. Como
ProductEngravingSectionguarda o wizard em estado local e limpaengravingFlushRefno unmount (src/components/admin/products/sections/ProductEngravingSection.tsx, Lines 68-78), sair de “Gravação” apaga as áreas locais antes do create. Na prática, o BUG-03 continua falhando se o usuário avançar para outra etapa e salvar de lá.O conserto precisa manter esse estado vivo até o submit — por exemplo, levantando
useEngravingWizardpara cima do switch de etapas ou mantendoProductEngravingSectionmontado fora da troca de steps.🤖 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/admin/products/ProductFormFullscreen.tsx` around lines 251 - 290, O componente de gravação (ProductEngravingSection) está sendo desmontado quando a etapa muda, o que limpa engravingFlushRef e perde o estado local antes do submit; mantenha esse estado vivo movendo a lógica do wizard para cima do switch de etapas: levantar useEngravingWizard para ProductFormFullscreen e passar os handlers/refs (ex: engravingFlushRef, qualquer onFlush/onSubmit do wizard) via props para ProductFormStepContent/ProductEngravingSection, ou alternativamente renderizar ProductEngravingSection fora do AnimatePresence (sempre montado) e só limpar engravingFlushRef após o submit; atualize ProductFormFullscreen (onde AnimatePresence/motion.div é usado e onde ProductFormStepContent é instanciado) e ProductEngravingSection (onde engravingFlushRef era limpo no unmount) para corresponder a essa abordagem.
🧹 Nitpick comments (1)
src/components/admin/products/useProductsManager.ts (1)
353-359: ⚡ Quick winFix do BUG-27 correto, mas falta consistência em outros call sites.
O fix está correto: passar
advancedFiltersexplicitamente evita stale closure quando filtros mudam rapidamente. Porém, outros call sites defetchProductsnão seguem o mesmo padrão:
- Linha 365 (
handlePageSizeChange): não passaadvancedFilters- Linha 392 (
handleDelete): não passaadvancedFilters- Linha 446 (
handleBulkToggleActive): não passaadvancedFiltersFuncionalmente não quebra (closure deveria estar atualizado), mas por consistência de código e manutenibilidade futura, recomendo aplicar o mesmo padrão explícito em todos os lugares.
♻️ Refactor sugerido para consistência
Linha 365 (handlePageSizeChange):
- fetchProducts(1, size, searchTerm); + fetchProducts(1, size, searchTerm, advancedFilters);Linha 392 (handleDelete):
- fetchProducts(currentPage, pageSize, searchTerm); + fetchProducts(currentPage, pageSize, searchTerm, advancedFilters);Linha 446 (handleBulkToggleActive):
- fetchProducts(currentPage, pageSize, searchTerm); + fetchProducts(currentPage, pageSize, searchTerm, advancedFilters);🤖 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/admin/products/useProductsManager.ts` around lines 353 - 359, The fix in handlePageChange correctly passes advancedFilters to fetchProducts to avoid stale closures; make the same explicit change in the other call sites: update handlePageSizeChange, handleDelete, and handleBulkToggleActive so each call to fetchProducts includes the current advancedFilters argument (i.e., call fetchProducts(page, pageSize, searchTerm, advancedFilters) or the equivalent parameters for that handler) rather than relying on the closure.
🤖 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/admin/products/sections/engraving/useEngravingWizard.ts`:
- Around line 204-226: The current flush clears all local areas unconditionally
(setLocalAreas([])) after Promise.allSettled, which loses areas that failed to
persist; modify the logic around Promise.allSettled/results so you identify
which items succeeded (e.g., correlate each result to the corresponding area
from areas via index or id), and only remove those successfully saved from local
state while keeping failed areas for retry; update setLocalAreas to filter out
the saved areas (or set it to the subset of failed areas) instead of clearing
everything, and keep the existing toast behavior using the computed failed
count.
In `@src/pages/admin/AdminProductFormPage.tsx`:
- Around line 176-180: When building the payload for save in
AdminProductFormPage.tsx, persist the actual kit toggle instead of deriving it
only from product_type: set is_kit from data.is_kit (falling back to the
existing product_type check if needed) and ensure product_type is set
consistently (e.g., default to 'kit' when data.is_kit is true, otherwise
'product') so the "É Kit" toggle actually reaches the backend; update the fields
around is_kit and product_type in the object construction where sale_price,
cost_price, etc. are assembled.
---
Outside diff comments:
In `@src/components/admin/products/ProductFormFullscreen.tsx`:
- Around line 251-290: O componente de gravação (ProductEngravingSection) está
sendo desmontado quando a etapa muda, o que limpa engravingFlushRef e perde o
estado local antes do submit; mantenha esse estado vivo movendo a lógica do
wizard para cima do switch de etapas: levantar useEngravingWizard para
ProductFormFullscreen e passar os handlers/refs (ex: engravingFlushRef, qualquer
onFlush/onSubmit do wizard) via props para
ProductFormStepContent/ProductEngravingSection, ou alternativamente renderizar
ProductEngravingSection fora do AnimatePresence (sempre montado) e só limpar
engravingFlushRef após o submit; atualize ProductFormFullscreen (onde
AnimatePresence/motion.div é usado e onde ProductFormStepContent é instanciado)
e ProductEngravingSection (onde engravingFlushRef era limpo no unmount) para
corresponder a essa abordagem.
In `@src/pages/admin/AdminProductFormPage.tsx`:
- Around line 90-147: productToFormData currently omits supplier_product_url so
editing an existing product overwrites the saved URL with the form schema
default; update productToFormData (the function) to include
supplier_product_url: p.supplier_product_url ?? '' so the existing value is
loaded into the form and not cleared on save.
---
Nitpick comments:
In `@src/components/admin/products/useProductsManager.ts`:
- Around line 353-359: The fix in handlePageChange correctly passes
advancedFilters to fetchProducts to avoid stale closures; make the same explicit
change in the other call sites: update handlePageSizeChange, handleDelete, and
handleBulkToggleActive so each call to fetchProducts includes the current
advancedFilters argument (i.e., call fetchProducts(page, pageSize, searchTerm,
advancedFilters) or the equivalent parameters for that handler) rather than
relying on the closure.
🪄 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: 0c49fe73-14b5-4f0f-88de-759e53a2b543
📒 Files selected for processing (6)
src/components/admin/products/ProductFormFullscreen.tsxsrc/components/admin/products/ProductFormStepContent.tsxsrc/components/admin/products/sections/ProductEngravingSection.tsxsrc/components/admin/products/sections/engraving/useEngravingWizard.tssrc/components/admin/products/useProductsManager.tssrc/pages/admin/AdminProductFormPage.tsx
| const results = await Promise.allSettled( | ||
| areas.map(async (area) => { | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| const { id: _id, _techData: _td, ...areaData } = area as PrintAreaTechnique & { _techData?: ExternalTechnique }; | ||
| const { data, error } = await supabase.functions.invoke('external-db-bridge', { | ||
| body: { | ||
| table: 'print_area_techniques', | ||
| operation: 'insert', | ||
| data: { ...areaData, product_id: newProductId }, | ||
| }, | ||
| }); | ||
| if (error) throw new Error(error.message); | ||
| if (!data?.success) throw new Error(data?.error || 'Erro ao salvar área de gravação'); | ||
| }), | ||
| ); | ||
| const failed = results.filter((r) => r.status === 'rejected').length; | ||
| if (failed > 0) { | ||
| toast.warning(`${areas.length - failed}/${areas.length} áreas de gravação salvas — ${failed} falha(s).`); | ||
| } else { | ||
| toast.success(`${areas.length} área(s) de gravação salvas com o produto`); | ||
| } | ||
| setLocalAreas([]); // clear after flush | ||
| }, []); // stable — reads localAreasRef.current which is always up-to-date |
There was a problem hiding this comment.
Não limpe as áreas que falharam no flush.
Se parte do Promise.allSettled falhar, a Line 225 apaga também as áreas que não foram persistidas. Isso recria perda de dados no fluxo de criação: o toast avisa falha parcial, mas não sobra nada para retry depois da navegação.
💡 Ajuste sugerido
const results = await Promise.allSettled(
areas.map(async (area) => {
// eslint-disable-next-line `@typescript-eslint/no-unused-vars`
const { id: _id, _techData: _td, ...areaData } = area as PrintAreaTechnique & { _techData?: ExternalTechnique };
const { data, error } = await supabase.functions.invoke('external-db-bridge', {
body: {
table: 'print_area_techniques',
operation: 'insert',
data: { ...areaData, product_id: newProductId },
},
});
if (error) throw new Error(error.message);
if (!data?.success) throw new Error(data?.error || 'Erro ao salvar área de gravação');
}),
);
- const failed = results.filter((r) => r.status === 'rejected').length;
+ const failedIds = new Set(
+ areas.flatMap((area, index) => (results[index]?.status === 'rejected' ? [area.id] : [])),
+ );
+ const failed = failedIds.size;
if (failed > 0) {
toast.warning(`${areas.length - failed}/${areas.length} áreas de gravação salvas — ${failed} falha(s).`);
} else {
toast.success(`${areas.length} área(s) de gravação salvas com o produto`);
}
- setLocalAreas([]); // clear after flush
+ setLocalAreas((prev) => prev.filter((area) => failedIds.has(area.id)));🤖 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/admin/products/sections/engraving/useEngravingWizard.ts`
around lines 204 - 226, The current flush clears all local areas unconditionally
(setLocalAreas([])) after Promise.allSettled, which loses areas that failed to
persist; modify the logic around Promise.allSettled/results so you identify
which items succeeded (e.g., correlate each result to the corresponding area
from areas via index or id), and only remove those successfully saved from local
state while keeping failed areas for retry; update setLocalAreas to filter out
the saved areas (or set it to the subset of failed areas) instead of clearing
everything, and keep the existing toast behavior using the computed failed
count.
| sale_price: data.sale_price ?? 0, cost_price: data.cost_price ?? null, | ||
| suggested_price: data.suggested_price ?? null, stock_quantity: data.stock_quantity ?? 0, | ||
| stock_unit: data.stock_unit || 'un', product_type: data.product_type || 'product', | ||
| is_kit: data.product_type === 'kit', min_quantity: data.min_quantity ?? 1, | ||
| min_order_quantity: data.min_order_quantity ?? null, |
There was a problem hiding this comment.
Use o toggle real de kit ao persistir is_kit.
A etapa de kits altera is_kit, mas aqui o valor salvo é derivado só de product_type. No create, product_type continua 'product' por default, então marcar “É Kit” não chega no banco.
Diff sugerido
- stock_unit: data.stock_unit || 'un', product_type: data.product_type || 'product',
- is_kit: data.product_type === 'kit', min_quantity: data.min_quantity ?? 1,
+ stock_unit: data.stock_unit || 'un',
+ product_type: data.is_kit ? 'kit' : (data.product_type || 'product'),
+ is_kit: data.is_kit,
+ min_quantity: data.min_quantity ?? 1,📝 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.
| sale_price: data.sale_price ?? 0, cost_price: data.cost_price ?? null, | |
| suggested_price: data.suggested_price ?? null, stock_quantity: data.stock_quantity ?? 0, | |
| stock_unit: data.stock_unit || 'un', product_type: data.product_type || 'product', | |
| is_kit: data.product_type === 'kit', min_quantity: data.min_quantity ?? 1, | |
| min_order_quantity: data.min_order_quantity ?? null, | |
| sale_price: data.sale_price ?? 0, cost_price: data.cost_price ?? null, | |
| suggested_price: data.suggested_price ?? null, stock_quantity: data.stock_quantity ?? 0, | |
| stock_unit: data.stock_unit || 'un', | |
| product_type: data.is_kit ? 'kit' : (data.product_type || 'product'), | |
| is_kit: data.is_kit, | |
| min_quantity: data.min_quantity ?? 1, | |
| min_order_quantity: data.min_order_quantity ?? null, |
🤖 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/pages/admin/AdminProductFormPage.tsx` around lines 176 - 180, When
building the payload for save in AdminProductFormPage.tsx, persist the actual
kit toggle instead of deriving it only from product_type: set is_kit from
data.is_kit (falling back to the existing product_type check if needed) and
ensure product_type is set consistently (e.g., default to 'kit' when data.is_kit
is true, otherwise 'product') so the "É Kit" toggle actually reaches the
backend; update the fields around is_kit and product_type in the object
construction where sale_price, cost_price, etc. are assembled.
Resumo
Sprint 3 do módulo Cadastro. Completa a auditoria iniciada na PR #488, resolvendo os 5 bugs marcados como pendentes.
Commits (5)
useEngravingWizard.tsflushLocalAreas+ warn técnica ausente + sync ref viauseEffectProductEngravingSection.tsxengravingFlushRef+ AlertDialog para excluir áreaProductFormStepContent.tsxengravingFlushRefparaProductEngravingSectionProductFormFullscreen.tsxengravingFlushRefthreaded atéProductFormStepContentAdminProductFormPage.tsxflushLocalAreas(newProduct.id)antes donavigate()useProductsManager.tsselectedOnPageLabel,handlePageChangecom filtros, comentáriouseEffect🔴 BUG-03 — Gravações perdidas ao criar novo produto (CRÍTICO)
Causa: Áreas de gravação configuradas no wizard antes de salvar ficavam em
localAreascomproduct_id='pending'. Quando o produto era criado e a página navegava para edit mode, o componente desmontava e as áreas eram perdidas.Fix (chain completa):
Após
newProduct = await invokeExternalDbSingle(...):flushLocalAreasusalocalAreasRef.current(sempre atualizado viauseEffect) e chamaexternal-db-bridgecomPromise.allSettledpara cada área pendente.🟠 BUG-05 (UI completion) — AlertDialog para excluir área de gravação
Causa:
useEngravingWizardjá expunhadeleteAreaConfirm/confirmDeleteArea/cancelDeleteAreadesde a Sprint 2, masProductEngravingSection.tsxnão renderizava o dialog.Fix:
AlertDialogadicionado ao final do JSX, abrindo quandow.deleteAreaConfirm !== null.🟡 BUG-26 — Label UX para seleção por página
Fix:
selectedOnPageLabeladicionado ao return deuseProductsManager:UI pode exibir este label no header de seleção bulk para evitar confusão com "selecionar todos do catálogo".
🟢 BUG-27 —
handlePageChangenão passavaadvancedFiltersFix:
Filtros ativos agora persistem ao navegar entre páginas.
🟢 BUG-28 — Sem aviso quando técnica de gravação foi deletada do banco
Fix:
console.warnemenrichAreaquandotechById.get(tabela_preco_id)retorna undefined para área salva (não local).🟢 BUG-29 —
useEffectinicial comentadoFix: Comentário explica por que
[]é intencional (fetch único no mount; valores iniciais são seguros para stale-closure).Testes recomendados
confirm()nativo)Summary by cubic
Corrige bugs da Sprint 3 no módulo Cadastro. Áreas de gravação não se perdem ao criar produto, exclusão tem confirmação, e paginação/filtros funcionam como esperado.
engravingFlushRef(exposto porProductEngravingSection) eflushLocalAreas(newProduct.id)chamado emAdminProductFormPageantes donavigate(). SincronizalocalAreasRefviauseEffectpara evitar perda de dados.AlertDialogpara confirmar exclusão de área (consome estado já exposto poruseEngravingWizard).selectedOnPageLabelpara indicar claramente que a seleção é por página.handlePageChangepassaadvancedFiltersparafetchProducts, mantendo filtros ativos ao mudar de página.enrichAreaemiteconsole.warnquando a técnica salva não existe mais no cache (provável remoção no banco).useEffectinicial documenta a dependência vazia intencional (fetch único no mount).Written for commit cf9893c. Summary will update on new commits. Review in cubic
Summary by CodeRabbit
Bug Fixes
Refactor