diff --git a/webapp/chat-app/src/App.css b/webapp/chat-app/src/App.css index 21f1998b..976b00bc 100644 --- a/webapp/chat-app/src/App.css +++ b/webapp/chat-app/src/App.css @@ -60,14 +60,26 @@ .tab-content { flex: 1; overflow: auto; - display: none; /* Hide by default */ + width: 100%; + transition: opacity 0.3s ease-in-out; flex-direction: column; padding: 1rem; animation: fadeIn 0.3s ease-in-out; + display: none; } .tab-content.active { display: flex; + opacity: 1; + visibility: visible; + position: relative; +} + +.tab-content-container { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; } @keyframes fadeIn { diff --git a/webapp/chat-app/src/components/Chat/index.tsx b/webapp/chat-app/src/components/Chat/index.tsx index d4881896..d302e70a 100644 --- a/webapp/chat-app/src/components/Chat/index.tsx +++ b/webapp/chat-app/src/components/Chat/index.tsx @@ -1,7 +1,6 @@ import React, {useEffect} from 'react'; import styled from 'styled-components'; import {Message} from '../../types'; -import Tabs from '../Tabs'; import MessageList from '../MessageList'; import InputArea from '../InputArea'; import {useSelector} from 'react-redux'; @@ -37,12 +36,88 @@ interface ChatProps { } const Chat: React.FC = ({messages, onSendMessage}) => { - const [activeTab, setActiveTab] = React.useState('chat'); + console.group(`${LOG_PREFIX} Rendering Chat component`); + + const [activeTab, setActiveTab] = React.useState(() => { + const savedTab = localStorage.getItem('activeTab'); + console.log(`${LOG_PREFIX} Initial active tab:`, { + savedTab, + fallback: 'chat' + }); + return savedTab || 'chat'; + }); + // Validate tab on mount + React.useEffect(() => { + const validTabs = ['chat', 'files', 'settings']; + console.log(`${LOG_PREFIX} Validating active tab:`, { + current: activeTab, + valid: validTabs.includes(activeTab), + allowedTabs: validTabs + }); + if (!validTabs.includes(activeTab)) { + console.warn(`${LOG_PREFIX} Invalid active tab "${activeTab}". Resetting to chat.`); + setActiveTab('chat'); + } + }, []); + // Add debug logging for active tab changes + React.useEffect(() => { + console.log(`${LOG_PREFIX} Active tab changed:`, activeTab); + }, [activeTab]); const config = useSelector((state: RootState) => state.config); // Add error boundary const [error, setError] = React.useState(null); // Add error handling for messages prop const safeMessages = messages || []; + const handleTabChange = (tabId: string) => { + console.group(`${LOG_PREFIX} Tab Change Handler`); + console.log('Current tab:', activeTab); + console.log('Requested tab:', tabId); + + if (tabId === activeTab) { + console.log('Tab already active - no change needed'); + console.groupEnd(); + return; + } + try { + console.log(`${LOG_PREFIX} Changing tab to:`, tabId); + setActiveTab(tabId); + // Force a re-render of the tab content + setTimeout(() => { + const content = document.querySelector(`[data-tab="${tabId}"]`); + if (content) { + content.classList.add('active'); + } + }, 0); + localStorage.setItem('activeTab', tabId); + console.log(`${LOG_PREFIX} Tab change complete:`, { + newTab: tabId, + savedToStorage: true + }); + } catch (error) { + console.error(`${LOG_PREFIX} Error changing tab:`, error); + } + console.groupEnd(); + }; + // Add diagnostic logging for TabContent rendering + const renderTabContent = (id: string, content: React.ReactNode) => { + const isActive = activeTab === id; + console.log(`${LOG_PREFIX} Rendering tab content:`, { + id, + isActive, + hasContent: !!content + }); + return ( + + {content} + + ); + }; + if (error) { console.error(`${LOG_PREFIX} Error encountered:`, error); @@ -99,30 +174,11 @@ const Chat: React.FC = ({messages, onSendMessage}) => { return ( - - - {activeTab === 'chat' && ( - <> - - - - )} - {activeTab === 'files' && ( -
-

Files

-

Browse and manage your chat files here.

-
- )} - {activeTab === 'settings' && ( -
-

Settings

-

Configure your chat preferences here.

-
- )} -
-
+ +
); + console.groupEnd(); }; // Add display name for better debugging Chat.displayName = 'Chat'; diff --git a/webapp/chat-app/src/components/Tabs/index.tsx b/webapp/chat-app/src/components/Tabs/index.tsx deleted file mode 100644 index 5e9ba8a3..00000000 --- a/webapp/chat-app/src/components/Tabs/index.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import React, {useEffect} from 'react'; -import styled, {DefaultTheme} from 'styled-components'; - -const LOG_PREFIX = '[Tabs]'; - - -const TabContainer = styled.div` - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; - - .tab-content:not(.active) { - display: none; /* Ensure inactive tab content is hidden */ - } -`; - -const TabList = styled.div<{ theme: DefaultTheme }>` - display: flex; - flex-wrap: wrap; - border-bottom: 1px solid ${(props: { theme: DefaultTheme }) => props.theme.colors.border}; - background: ${props => props.theme.colors.surface}; - padding: 0 1rem; -`; - -const TabButton = styled.div<{ active: boolean; theme: DefaultTheme }>` - display: inline-block; - padding: 0.5rem 1rem; - border: none; - background: none; - cursor: pointer; - position: relative; - font-weight: ${props => props.active ? props.theme.typography.fontWeight.bold : props.theme.typography.fontWeight.regular}; - color: ${props => props.active ? props.theme.colors.primary : props.theme.colors.text.primary}; - - &:after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 2px; - background: ${props => props.active ? props.theme.colors.primary : 'transparent'}; - transition: background-color 0.2s ease-in-out; - } - - transition: all 0.2s ease-in-out; - - &:hover { - background: ${props => props.theme.colors.surface}; - - &:after { - background: ${props => props.active ? props.theme.colors.primary : props.theme.colors.secondary}; - } - } -`; - -const TabContent = styled.div` - flex: 1; - overflow: auto; - display: flex; - flex-direction: column; - padding: 1rem; - data-tab: ${(props: { 'data-tab'?: string }) => props['data-tab']}; - - .tab-content { - animation: fadeIn 0.3s ease-in-out; - } - - @keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } - } -`; - -interface TabsProps { - tabs: { id: string; label: string }[]; - activeTab: string; - onTabChange: (tabId: string) => void; - children: React.ReactNode; -} - -const Tabs: React.FC = ({tabs, activeTab, onTabChange, children}) => { - useEffect(() => { - console.group(`${LOG_PREFIX} Component Lifecycle`); - console.log('Component mounted/updated', { - state: { - tabsCount: tabs.length, - activeTab, - availableTabs: tabs.map(t => t.id) - } - }); - - // Restore the selected tab from localStorage - const savedTab = localStorage.getItem('activeTab'); - if (savedTab && tabs.some(tab => tab.id === savedTab)) { - console.log(`${LOG_PREFIX} Restoring saved tab:`, savedTab); - onTabChange(savedTab); - } else if (!tabs.some(tab => tab.id === activeTab)) { - console.warn(`${LOG_PREFIX} Active tab "${activeTab}" not found in available tabs. Defaulting to first tab.`); - onTabChange(tabs[0].id); - } - - return () => { - console.log(`${LOG_PREFIX} Component unmounting`); - console.groupEnd(); - }; - }, [tabs, activeTab]); - - const handleTabClick = (tabId: string) => { - console.group(`${LOG_PREFIX} Tab Interaction`); - console.log('Tab clicked', { - previousTab: activeTab, - newTab: tabId, - timestamp: new Date().toISOString() - }); - onTabChange(tabId); - localStorage.setItem('activeTab', tabId); - console.groupEnd(); - }; - - let elements = tabs.map(tab => { - if (process.env.NODE_ENV === 'development') { - console.debug(`${LOG_PREFIX} Rendering tab`, { - id: tab.id, - label: tab.label, - active: activeTab === tab.id - }); - } - return ( - handleTabClick(tab.id)} - className="tab-button" - data-for-tab={tab.id} - > - {tab.label} - - ) - }); - return ( - - - {elements} - - - {children} - - - ); -}; - -Tabs.displayName = 'Tabs'; - - -export default Tabs; \ No newline at end of file diff --git a/webapp/chat-app/src/hooks/index.ts b/webapp/chat-app/src/hooks/index.ts index d1d48b56..39f5759f 100644 --- a/webapp/chat-app/src/hooks/index.ts +++ b/webapp/chat-app/src/hooks/index.ts @@ -5,6 +5,8 @@ logger.info('Initializing hooks module'); logger.debug('Starting hooks loading process from hooks/index.ts'); export {useWebSocket} from './useWebSocket'; +export {useTheme} from './useTheme'; // Log successful hook exports logger.debug('Successfully exported useWebSocket hook'); +logger.debug('Successfully exported useTheme hook'); logger.info('Hooks module initialization complete'); \ No newline at end of file diff --git a/webapp/chat-app/src/styled.d.ts b/webapp/chat-app/src/styled.d.ts index f69b2b52..0f05d724 100644 --- a/webapp/chat-app/src/styled.d.ts +++ b/webapp/chat-app/src/styled.d.ts @@ -2,6 +2,7 @@ import 'styled-components'; // Extend the styled-components DefaultTheme interface declare module 'styled-components' { export interface DefaultTheme { + activeTab?: string; sizing: { spacing: { xs: string; diff --git a/webapp/chat-app/src/types/styled.d.ts b/webapp/chat-app/src/types/styled.d.ts index 96d73d07..6fd224ae 100644 --- a/webapp/chat-app/src/types/styled.d.ts +++ b/webapp/chat-app/src/types/styled.d.ts @@ -1,6 +1,12 @@ import 'styled-components'; +import {HTMLAttributes} from 'react'; declare module 'styled-components' { + export interface StyledComponentProps extends HTMLAttributes { + 'data-tab'?: string; + theme?: DefaultTheme; + } + export interface DefaultTheme { sizing: { spacing: { @@ -57,6 +63,7 @@ declare module 'styled-components' { info: string; }; name: string; + activeTab?: string; config: { stickyInput: boolean; singleInput: boolean;