From 8df0524c2e9612b9b925ef6ff9a40403295147ee Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Jan 2026 22:37:54 +0000 Subject: [PATCH] fix: Fix sidebar navigation and chat slash commands - Sidebar: Remove preventDefault() and manual navigate() from NavLinks The NavLink component from React Router handles navigation internally. Using preventDefault() + navigate() was breaking the native behavior. Now only close sidebar on mobile without blocking navigation. - Chat: Add slash command detection in handleSubmit Previously, all input was sent via onSendMessage even for commands. Now checks if input starts with '/' and calls onCommandExecute instead. This properly executes commands like /task instead of sending as text. https://claude.ai/code/session_01QyDFbYooCiwJNR7CjdcW3Y --- src/components/AppSidebar.tsx | 51 +++++++++------------------------ src/components/CommandInput.tsx | 25 ++++++++++++---- 2 files changed, 33 insertions(+), 43 deletions(-) diff --git a/src/components/AppSidebar.tsx b/src/components/AppSidebar.tsx index 673850196..7cb5ed30c 100644 --- a/src/components/AppSidebar.tsx +++ b/src/components/AppSidebar.tsx @@ -10,7 +10,7 @@ import { cn } from "@/lib/utils"; import { apiClient } from "@/services/api-client"; import { prefetchRouteModule } from "@/utils/route-prefetch"; import { memo, useEffect, useState } from "react"; -import { NavLink, useNavigate } from "react-router-dom"; +import { NavLink } from "react-router-dom"; type NavItemKey = | "navChat" @@ -266,7 +266,6 @@ const SidebarContent = memo(function SidebarContent({ setNavigationMode, }: SidebarContentProps) { const { t } = useLanguage(); - const navigate = useNavigate(); const [advancedOpen, setAdvancedOpen] = useState(false); const basicMainItemKeys = new Set([ "navChat", @@ -404,19 +403,13 @@ const SidebarContent = memo(function SidebarContent({ to={item.url} onMouseEnter={() => handlePrefetch(item.url)} onFocus={() => handlePrefetch(item.url)} - onClick={(e) => { - console.log('[AppSidebar] 🔗 Link clicked:', { url: item.url, titleKey: item.titleKey }); - // ✅ FIX: Navegação programática para garantir que funcione - e.preventDefault(); - navigate(item.url); - // Fechar sidebar no mobile após navegação + onClick={() => { + // Fechar sidebar no mobile - NavLink gerencia a navegação automaticamente if (isMobile) { - setTimeout(() => { - handleLinkClick(); - }, 100); + handleLinkClick(); } }} - end={item.url === '/dashboard'} // ✅ FIX: Adicionar 'end' para match exato + end={item.url === '/dashboard'} className={({ isActive }) => cn( "w-full px-3 py-2.5 rounded-none border-l-2 flex items-center gap-3 transition-all duration-200 group cursor-pointer select-none sidebar-nav-link", getNavCls(isActive) @@ -452,14 +445,9 @@ const SidebarContent = memo(function SidebarContent({ to={item.url} onMouseEnter={() => handlePrefetch(item.url)} onFocus={() => handlePrefetch(item.url)} - onClick={(e) => { - console.log('[AppSidebar] 🔗 Link clicked:', { url: item.url, titleKey: item.titleKey }); - e.preventDefault(); - navigate(item.url); + onClick={() => { if (isMobile) { - setTimeout(() => { - handleLinkClick(); - }, 100); + handleLinkClick(); } }} className={({ isActive }) => cn( @@ -514,14 +502,9 @@ const SidebarContent = memo(function SidebarContent({ to={item.url} onMouseEnter={() => handlePrefetch(item.url)} onFocus={() => handlePrefetch(item.url)} - onClick={(e) => { - console.log('[AppSidebar] 🔗 Link clicked:', { url: item.url, titleKey: item.titleKey }); - e.preventDefault(); - navigate(item.url); + onClick={() => { if (isMobile) { - setTimeout(() => { - handleLinkClick(); - }, 100); + handleLinkClick(); } }} className={({ isActive }) => cn( @@ -560,13 +543,9 @@ const SidebarContent = memo(function SidebarContent({ to={item.url} onMouseEnter={() => handlePrefetch(item.url)} onFocus={() => handlePrefetch(item.url)} - onClick={(e) => { - e.preventDefault(); - navigate(item.url); + onClick={() => { if (isMobile) { - setTimeout(() => { - handleLinkClick(); - }, 100); + handleLinkClick(); } }} className={({ isActive }) => cn( @@ -596,13 +575,9 @@ const SidebarContent = memo(function SidebarContent({ to={item.url} onMouseEnter={() => handlePrefetch(item.url)} onFocus={() => handlePrefetch(item.url)} - onClick={(e) => { - e.preventDefault(); - navigate(item.url); + onClick={() => { if (isMobile) { - setTimeout(() => { - handleLinkClick(); - }, 100); + handleLinkClick(); } }} className={({ isActive }) => cn( diff --git a/src/components/CommandInput.tsx b/src/components/CommandInput.tsx index dc67f2170..6ecc69126 100644 --- a/src/components/CommandInput.tsx +++ b/src/components/CommandInput.tsx @@ -252,6 +252,23 @@ function CommandInputComponent({ console.log('🚀 Submit triggered:', (trimmedInput || '[attachments]').substring(0, 50)); try { + // ✅ FIX: Check if input is a slash command and execute it + if (trimmedInput.startsWith('/') && onCommandExecute) { + console.log('⚡ [CommandInput] Executing slash command:', trimmedInput); + try { + await onCommandExecute(trimmedInput); + console.log('✅ [CommandInput] Command executed successfully'); + } catch (error) { + console.error('❌ [CommandInput] Error executing command:', error); + toast.error('Erro ao executar comando. Tente novamente.'); + } + // Clear state after command execution + setInput(''); + setRawSuggestions([]); + console.log('🧹 [CommandInput] State cleared after command'); + return; + } + let messageTosend = trimmedInput; // 📎 Add uploaded documents as DOCUMENT_ID tags @@ -260,7 +277,7 @@ function CommandInputComponent({ messageTosend = messageTosend ? `${messageTosend} ${tags}` : tags; } - // ✅ FIX: Preparar anexos e metadata + // Preparar anexos e metadata const filesToSend = selectedFiles.length > 0 ? selectedFiles : undefined; const metadataToSend = { attachedProjectId: attachedProjectId || undefined, @@ -276,8 +293,6 @@ function CommandInputComponent({ onSendMessageType: typeof onSendMessage }); - // ✅ FIX: SEMPRE chamar onSendMessage se houver mensagem ou anexos - // Não usar condicional - se chegou até aqui, deve enviar if (!onSendMessage) { console.error('❌ [CommandInput] onSendMessage is not defined!', { onSendMessage, @@ -293,8 +308,8 @@ function CommandInputComponent({ messageTosendLength: messageTosend?.length, filesCount: filesToSend?.length || 0 }); - - // ✅ CRITICAL: Chamar onSendMessage de forma síncrona (não await) + + // Chamar onSendMessage de forma síncrona (não await) // O handleSendMessage no Dashboard já é async e gerencia seu próprio estado try { onSendMessage(messageTosend || '', filesToSend, metadataToSend);