Skip to content

Commit 451c78c

Browse files
Phase 3 - Add AI Chat integration across the app
- Introduced `AIChatProvider` to manage chat visibility and state. - Updated `App.tsx` to wrap the app in `AIChatProvider`. - Modified `TabNavigation.tsx` to open chat on tab click. - Enhanced `ChatPage.tsx` to automatically open chat on navigation. - Updated `AIChatBanner` to trigger chat opening. - Refactored `AIChatContainer` to accept visibility and close handler as props. - Improved `HomePage.tsx` with AI chat access and updated UI elements.
1 parent 1124a81 commit 451c78c

File tree

7 files changed

+151
-115
lines changed

7 files changed

+151
-115
lines changed

frontend/src/App.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import AuthProvider from 'common/providers/AuthProvider';
1313
import AxiosProvider from 'common/providers/AxiosProvider';
1414
import ToastProvider from 'common/providers/ToastProvider';
1515
import ScrollProvider from 'common/providers/ScrollProvider';
16+
import { AIChatProvider } from 'common/providers/AIChatProvider';
1617
import Toasts from 'common/components/Toast/Toasts';
1718
import AppRouter from 'common/components/Router/AppRouter';
1819

@@ -58,9 +59,11 @@ const App = (): JSX.Element => {
5859
<AxiosProvider>
5960
<ToastProvider>
6061
<ScrollProvider>
61-
<AppRouter />
62-
<Toasts />
63-
<ReactQueryDevtools initialIsOpen={false} buttonPosition="bottom-left" />
62+
<AIChatProvider>
63+
<AppRouter />
64+
<Toasts />
65+
<ReactQueryDevtools initialIsOpen={false} buttonPosition="bottom-left" />
66+
</AIChatProvider>
6467
</ScrollProvider>
6568
</ToastProvider>
6669
</AxiosProvider>

frontend/src/common/components/Router/TabNavigation.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
22
import { Redirect, Route } from 'react-router';
3+
import { useAIChat } from 'common/providers/AIChatProvider';
34

45
import './TabNavigation.scss';
56
import AppMenu from '../Menu/AppMenu';
@@ -29,6 +30,8 @@ import ChatPage from 'pages/Chat/ChatPage';
2930
* @see {@link AppMenu}
3031
*/
3132
const TabNavigation = (): JSX.Element => {
33+
const { openChat } = useAIChat();
34+
3235
return (
3336
<>
3437
<AppMenu />
@@ -93,7 +96,14 @@ const TabNavigation = (): JSX.Element => {
9396
/>
9497
</div>
9598
</IonTabButton>
96-
<IonTabButton className="ls-tab-navigation__bar-button" tab="chat" href="/tabs/chat">
99+
<IonTabButton
100+
className="ls-tab-navigation__bar-button"
101+
tab="chat"
102+
onClick={(e) => {
103+
e.preventDefault();
104+
openChat();
105+
}}
106+
>
97107
<Icon
98108
className="ls-tab-navigation__bar-button-icon"
99109
icon="comment"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React, { createContext, useState, useContext, ReactNode } from 'react';
2+
import AIChatContainer from 'pages/Chat/components/AIChatContainer/AIChatContainer';
3+
4+
// Context to manage the visibility of the AI Chat globally
5+
interface AIChatContextType {
6+
openChat: () => void;
7+
closeChat: () => void;
8+
isVisible: boolean;
9+
}
10+
11+
const AIChatContext = createContext<AIChatContextType | undefined>(undefined);
12+
13+
// Custom hook for components to access the AI Chat context
14+
export const useAIChat = () => {
15+
const context = useContext(AIChatContext);
16+
if (!context) {
17+
throw new Error('useAIChat must be used within an AIChatProvider');
18+
}
19+
return context;
20+
};
21+
22+
interface AIChatProviderProps {
23+
children: ReactNode;
24+
}
25+
26+
/**
27+
* Provider component that makes AI Chat available throughout the app
28+
*/
29+
export const AIChatProvider: React.FC<AIChatProviderProps> = ({ children }) => {
30+
const [isVisible, setIsVisible] = useState(false);
31+
32+
const openChat = () => {
33+
setIsVisible(true);
34+
};
35+
36+
const closeChat = () => {
37+
setIsVisible(false);
38+
};
39+
40+
return (
41+
<AIChatContext.Provider value={{ openChat, closeChat, isVisible }}>
42+
{children}
43+
<AIChatContainer
44+
isVisible={isVisible}
45+
onClose={closeChat}
46+
/>
47+
</AIChatContext.Provider>
48+
);
49+
};

frontend/src/pages/Chat/ChatPage.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react';
1+
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonButton } from '@ionic/react';
22
import { useTranslation } from 'react-i18next';
3-
import AIChatContainer from './components/AIChatContainer/AIChatContainer';
3+
import { useAIChat } from 'common/providers/AIChatProvider';
4+
import { useEffect } from 'react';
45

56
/**
67
* The `ChatPage` component displays the chat interface.
@@ -10,6 +11,12 @@ import AIChatContainer from './components/AIChatContainer/AIChatContainer';
1011
*/
1112
const ChatPage = (): JSX.Element => {
1213
const { t } = useTranslation();
14+
const { openChat } = useAIChat();
15+
16+
// Automatically open the chat when navigating to this page
17+
useEffect(() => {
18+
openChat();
19+
}, [openChat]);
1320

1421
return (
1522
<IonPage>
@@ -22,13 +29,14 @@ const ChatPage = (): JSX.Element => {
2229
<div className="ion-padding">
2330
<h1>{t('pages.chat.subtitle')}</h1>
2431
<p>{t('pages.chat.description')}</p>
32+
33+
<IonButton
34+
expand="block"
35+
onClick={openChat}
36+
>
37+
{t('pages.chat.openButton')}
38+
</IonButton>
2539
</div>
26-
27-
{/*
28-
We're using the same AIChatContainer component here
29-
This demonstrates how we can reuse the component across different views
30-
*/}
31-
<AIChatContainer />
3240
</IonContent>
3341
</IonPage>
3442
);

frontend/src/pages/Chat/components/AIChatBanner/AIChatBanner.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import React from 'react';
22
import './AIChatBanner.scss';
33

44
interface AIChatBannerProps {
5-
onOpenChat: () => void;
5+
onClick: () => void;
66
}
77

88
/**
99
* AIChatBanner component displays a banner on the home screen for accessing the AI chat
1010
*/
11-
const AIChatBanner: React.FC<AIChatBannerProps> = ({ onOpenChat }) => {
11+
const AIChatBanner: React.FC<AIChatBannerProps> = ({ onClick }) => {
1212
return (
13-
<div className="ai-chat-banner" onClick={onOpenChat}>
13+
<div className="ai-chat-banner" onClick={onClick}>
1414
<div className="ai-chat-banner__content">
1515
<div className="ai-chat-banner__icon">
1616
<span className="ai-chat-banner__icon-symbol"></span>

frontend/src/pages/Chat/components/AIChatContainer/AIChatContainer.tsx

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@ import AIChat from '../AIChat/AIChat';
33
import { ChatProvider } from '../../context/ChatContext';
44
import './AIChatContainer.scss';
55

6+
interface AIChatContainerProps {
7+
isVisible: boolean;
8+
onClose: () => void;
9+
}
10+
611
/**
712
* AIChatContainer manages the AI chat modal state and handles outside clicks
813
*/
9-
const AIChatContainer: React.FC = () => {
10-
const [isVisible, setIsVisible] = useState(false);
14+
const AIChatContainer: React.FC<AIChatContainerProps> = ({ isVisible, onClose }) => {
1115
const [isExpanded, setIsExpanded] = useState(false);
1216
const containerRef = useRef<HTMLDivElement>(null);
1317

1418
const handleClose = () => {
15-
setIsVisible(false);
19+
onClose();
1620
// Reset to collapsed state when closing
1721
setIsExpanded(false);
1822
};
@@ -21,10 +25,6 @@ const AIChatContainer: React.FC = () => {
2125
setIsExpanded(prev => !prev);
2226
};
2327

24-
const handleOpen = () => {
25-
setIsVisible(true);
26-
};
27-
2828
const handleClickOutside = (event: MouseEvent) => {
2929
if (
3030
containerRef.current &&
@@ -42,19 +42,15 @@ const AIChatContainer: React.FC = () => {
4242
};
4343
}, [isVisible]);
4444

45+
// Reset expanded state when visibility changes
46+
useEffect(() => {
47+
if (!isVisible) {
48+
setIsExpanded(false);
49+
}
50+
}, [isVisible]);
51+
4552
return (
4653
<ChatProvider>
47-
{/* AI Chat toggle button for bottom nav */}
48-
<div className="ai-chat-container__toggle">
49-
<button
50-
className="ai-chat-container__toggle-button"
51-
onClick={handleOpen}
52-
aria-label="Open AI Chat"
53-
>
54-
AI
55-
</button>
56-
</div>
57-
5854
{/* AI Chat modal */}
5955
{isVisible && (
6056
<div className="ai-chat-container" ref={containerRef}>

0 commit comments

Comments
 (0)