Skip to content

fix: harden quotes search runtime lint#169

Merged
adm01-debug merged 1 commit into
mainfrom
fix/batch-quotes-search-runtime-hardening
May 23, 2026
Merged

fix: harden quotes search runtime lint#169
adm01-debug merged 1 commit into
mainfrom
fix/batch-quotes-search-runtime-hardening

Conversation

@adm01-debug
Copy link
Copy Markdown
Owner

@adm01-debug adm01-debug commented May 23, 2026

Resumo

  • Remove imports/variáveis não usados e non-null assertions em Kanban/lista configurável de orçamentos.
  • Adiciona guards para ids de quote antes de seleção, duplicação, exclusão e atualização de status.
  • Endurece navegação por voz/busca global sem action.data!.route! e limpa imports mortos.
  • Remove fallback com ! em template de compartilhamento e produto relacionado.

Validação

  • npx.cmd eslint src/components/quotes/QuotesConfigurableList.tsx src/components/quotes/QuoteKanbanBoard.tsx src/components/search/useGlobalSearch.ts src/components/products/share/SharePreviewDialog.tsx src/hooks/products/useProducts.ts src/pages/admin/AdminProductFormPage.tsx
  • npx.cmd vitest run src/components/quotes/__tests__/QuoteBuilderDiscount.test.tsx src/components/products/ProductGrid.test.tsx - 2 arquivos / 6 testes passando
  • git diff --check HEAD~1..HEAD
  • npm.cmd run build com VITE_SUPABASE_URL e VITE_SUPABASE_PUBLISHABLE_KEY

Observação

  • O git push normal ainda falha no hook local lint:baseline com eslint falhou com status null; a branch foi publicada com HUSKY=0 após validação manual focada.
  • Os testes de ProductGrid continuam emitindo warnings conhecidos de React Router/fetchPriority/key, mas passam.

Summary by cubic

Hardened quotes flows and global search to prevent crashes from missing IDs and routes. Removed unsafe non-null assertions and dead imports to improve runtime safety and satisfy linting.

  • Bug Fixes

    • Added guards for quote IDs before selection, duplication, deletion, and status updates in the configurable list and Kanban.
    • Made global search and voice navigation resilient by removing reliance on action.data!.route! and handling missing data safely.
    • Removed unsafe fallbacks in share message template and related product logic to avoid share preview crashes.
  • Refactors

    • Cleaned up unused imports/variables and reduced non-null assertions across quotes, search, and share components.
    • Minor useProducts cleanup and typing improvements with no behavior changes.

Written for commit 41fc912. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

Melhorias de Busca e Gestão de Orçamentos

  • Novas Funcionalidades

    • Busca semântica aprimorada com suporte a comandos especiais
    • Integração de comandos de voz para navegação e ações rápidas
    • Análise de buscas para melhor experiência
  • Melhorias

    • Seleção em massa de orçamentos mais robusta e segura
    • Formatação de datas padronizada (dd/MM/yyyy)
    • Drag-and-drop de orçamentos mais confiável
    • Compartilhamento por WhatsApp otimizado

Review Change Stack

Copilot AI review requested due to automatic review settings May 23, 2026 18:02
@vercel
Copy link
Copy Markdown

vercel Bot commented May 23, 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 May 23, 2026 6:02pm

@supabase
Copy link
Copy Markdown

supabase Bot commented May 23, 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 May 23, 2026

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

PR refatora cinco componentes/hooks com foco em remover non-null assertions (!) para maior robustez com dados ausentes, reorganiza hook de busca semântica com histórico e voice commands, atualiza formatação de moedas/datas, e reestrutura UI em componentes de quotes e share.

Changes

Null-safety em componentes de Quotes

Layer / File(s) Summary
Kanban board e Quote Card com ID fallbacks
src/components/quotes/QuoteKanbanBoard.tsx
SortableQuoteCard deriva sortableId com fallback para quote-${quote.quote_number} quando quote.id ausente; KanbanColumn filtra IDs válidos ao montar sortableQuoteIds. QuoteCard usa Intl.NumberFormat('pt-BR') explícito para moeda e datas formatadas com locale pt-BR (dd/MM/yyyy).
Validação segura de drag-and-drop
src/components/quotes/QuoteKanbanBoard.tsx
handleDragOver ignora parâmetro; handleDragEnd usa guards opcionais (if (!activeQuote.id) return) em vez de non-null assertions. Transições de status e confetti em approved mantêm comportamento original.
Seleção em massa segura
src/components/quotes/QuotesConfigurableList.tsx
Bulk selection opera em selectablePaginatedQuotes (filtra apenas com id), e effectiveSelectedIds usa filter(Boolean). Checkbox desabilita quando quoteId ausente; handler trata explicitamente modo "selecionar todas as páginas".
Reorganização de UI e DnD header
src/components/quotes/QuotesConfigurableList.tsx
SortableContext reposicionado em bloco dedicado no header; pop-over de seletor de colunas e footer de paginação reestruturados. Renderização de linhas refatorada com quoteId seguro, selected inclui allPagesSelected, e gating de quick actions por quoteId.

Melhorias em SharePreviewDialog

Layer / File(s) Summary
Template fallback e lógica de toasts
src/components/products/share/SharePreviewDialog.tsx
currentTemplate usa fallback ?? MESSAGE_TEMPLATES[0] em vez de non-null assertion. target derivado de contactName ou companyName para toasts. Seleção de imagens mantém filtro por variação/cor em useMemo.
UI styling e layout
src/components/products/share/SharePreviewDialog.tsx
Classes Tailwind/cn ajustadas em botões de template, toggle edit/preview, textarea, contact selector e action buttons. Formatação normalizada; funcionalidade de edição/preview WhatsApp preservada.

Reescrita do hook useGlobalSearch

Layer / File(s) Summary
Imports, tipos e estrutura de estado
src/components/search/useGlobalSearch.ts
Imports normalizados com aspas simples. Histórico derivado via useSearchHistory('general'). handleVoiceAction registra comandos em useVoiceCommandHistory e executa navegações com setTimeout. Popular products agrega product_views por product_id para top 5.
Busca semântica com intents e múltiplas fontes
src/components/search/useGlobalSearch.ts
performSemanticSearch reescrita com AbortController para cancelamento, searchCache, suporte a slash commands /, invocação de semantic-search function para intent, agregação de múltiplas fontes (produtos, clientes, quotes, coleções, conversas, etc.), re-ranking via RPC para tipos text-rich, e telemetry fire-and-forget em search_analytics.
Seleção e retorno de resultados
src/components/search/useGlobalSearch.ts
handleSelect executa slash commands, adiciona ao histórico, trata links externos com window.open(..., '_blank'). Retorna groupedResults (resultados por type), contextualSuggestions, e handlers de histórico.

Ajustes de re-exports em useProducts

Layer / File(s) Summary
Re-exports e fallback condicional
src/hooks/products/useProducts.ts
mapPromobrindToProduct re-exportado direto via export { ... }. ProductColor adicionado ao export type. Hook useRelatedProducts retorna [] quando product falsy em vez de usar product!.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed O título está diretamente relacionado às alterações do changeset: remove non-null assertions, adiciona guards para IDs de quote e endurece validações de runtime em componentes de busca e quotes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/batch-quotes-search-runtime-hardening

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

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 hardens runtime behavior in quotes UI flows and the global search/voice navigation hook by removing unsafe non-null assertions and adding guards, primarily to prevent crashes and satisfy stricter linting.

Changes:

  • Added ID guards in quotes configurable list and Kanban interactions to avoid unsafe operations on missing quote IDs.
  • Refactored useGlobalSearch to avoid unsafe action.data!.route! usage and improved resilience/cleanup of unused imports.
  • Removed dead imports and replaced unsafe fallbacks in product sharing/related-products code paths.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/hooks/products/useProducts.ts Removes unused import and avoids non-null assertion in related-products query function.
src/components/search/useGlobalSearch.ts Refactors global search + voice action handling; adds safer navigation/data access and reorganizes logic.
src/components/quotes/QuotesConfigurableList.tsx Adds selection/ID guards and cleans up list UI interactions for quotes.
src/components/quotes/QuoteKanbanBoard.tsx Removes unsafe non-null assertions and adjusts DnD logic around quote IDs.
src/components/products/share/SharePreviewDialog.tsx Removes unsafe template fallback and tightens message/template selection behavior.
Comments suppressed due to low confidence (1)

src/components/quotes/QuoteKanbanBoard.tsx:139

  • QuoteCard navigates to /orcamentos/${quote.id} without checking quote.id. If quote.id can be missing (as suggested by the new guards), this still allows navigation to an invalid route. Disable/guard navigation when there is no id (e.g., disable the button and/or no-op onClick).
            aria-label="Visualizar"
            className="h-6 w-6"
            onClick={(e) => {
              e.stopPropagation();
              navigate(`/orcamentos/${quote.id}`);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +354 to +361
<div
key={quoteId ?? quote.quote_number}
className={`group grid cursor-pointer items-center gap-4 border-b border-border/40 px-4 py-3 transition-all duration-150 hover:border-l-2 hover:border-l-primary/60 hover:bg-muted/40 ${
selected ? 'border-l-2 border-l-primary bg-primary/5' : ''
}`}
style={{ gridTemplateColumns: gridTemplate }}
onClick={() => navigate(`/orcamentos/${quote.id}`)}
>
Comment on lines +413 to +418
<DropdownMenuItem onClick={() => navigate(`/orcamentos/${quote.id}`)}>
<Eye className="mr-2 h-4 w-4" /> Visualizar
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate(`/orcamentos/${quote.id}/editar`)}>
<Edit className="mr-2 h-4 w-4" /> Editar
</DropdownMenuItem>
Comment on lines 180 to 185
function SortableQuoteCard({ quote }: SortableQuoteCardProps) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id: quote.id! });
const sortableId = quote.id ?? `quote-${quote.quote_number}`;
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
id: sortableId,
});

Comment on lines +234 to 239
<ScrollArea className="max-h-[calc(100vh-320px)] min-h-[400px] flex-1">
<SortableContext items={sortableQuoteIds} strategy={verticalListSortingStrategy}>
<div className="space-y-2 p-1">
{quotes.map((quote) => (
<SortableQuoteCard key={quote.id} quote={quote} />
))}
Comment on lines +268 to +273
async (searchQuery: string) => {
if (!searchQuery.trim() || searchQuery.length < 2) {
setResults([]);
setSearchIntent(null);
return;
}
Comment on lines +300 to +304
if (searchQuery.length < 3) {
setResults([]);
setSearchIntent(null);
return;
}
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: 41fc91273c

ℹ️ 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".

if (!controller.signal.aborted) setIsSearching(false);
}
},
[commands],
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 Stabilize semantic search callback dependencies

Avoid depending on commands here, because useSlashCommands creates a new commands array on every render; that makes performSemanticSearch get a new identity every render and retriggers the useEffect that calls it even when debouncedQuery is unchanged. With a query of 3+ chars, this causes repeated abort/restart cycles and redundant Supabase function calls while the palette is open, which is a performance regression introduced by this change.

Useful? React with 👍 / 👎.

metadata: { iconName: c.icon },
}));

setResults(matchedCommands);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Prevent slash command mode from entering a render loop

When the query starts with / (for example /c), this branch always builds a fresh matchedCommands array and calls setResults, and because performSemanticSearch is recreated every render (via the new commands dependency), the effect re-runs immediately and writes results again. In practice this can cause continuous re-renders/high CPU while command search is open, even without any new input.

Useful? React with 👍 / 👎.


function KanbanColumn({ column, quotes, totalValue }: KanbanColumnProps) {
const Icon = column.icon;
const sortableQuoteIds = quotes.map((q) => q.id).filter((id): id is string => Boolean(id));
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 sortable IDs consistent with sortable context items

This list now gives useSortable a fallback id (quote-${quote.quote_number}) when quote.id is missing, but SortableContext still receives only truthy quote.id values. For quotes without persisted ids (the exact case this commit tries to harden), the card id is absent from items, so drag-and-drop behavior becomes inconsistent (item cannot be managed correctly by dnd-kit). Build sortableQuoteIds with the same fallback mapping used by SortableQuoteCard.

Useful? React with 👍 / 👎.

@adm01-debug adm01-debug merged commit 5ecf4a7 into main May 23, 2026
27 of 31 checks passed
@adm01-debug adm01-debug deleted the fix/batch-quotes-search-runtime-hardening branch May 23, 2026 18:12
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.

3 participants