Krish-05 commited on
Commit
8fa0bd2
·
verified ·
1 Parent(s): 9458431

Update frontend/src/App.jsx

Browse files
Files changed (1) hide show
  1. frontend/src/App.jsx +292 -56
frontend/src/App.jsx CHANGED
@@ -1,71 +1,307 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
3
- import { onAuthStateChanged } from 'firebase/auth';
4
- import { auth } from './services/firebase';
5
- import Header from './components/layout/Header';
6
- import Login from './components/auth/Login';
7
- import SignUp from './components/auth/SignUp';
8
- import ChatInterface from './components/chat/ChatInterface';
9
- import History from './components/chat/History';
10
- import Sidebar from './components/layout/Sidebar'; // Assuming Sidebar is in components/layout
11
- import './index.css';
12
 
13
- function App() {
14
- const [user, setUser] = useState(null);
15
- const [loading, setLoading] = useState(true);
16
- const [currentChatId, setCurrentChatId] = useState(null); // State to manage current chat session
 
 
 
 
 
17
 
 
 
 
 
 
 
 
 
 
 
18
  useEffect(() => {
19
- const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
20
- setUser(currentUser);
21
- setLoading(false);
22
- });
23
- return () => unsubscribe();
24
- }, []);
25
-
26
- // Function to start a new chat (or handle existing chat selection)
27
- const startNewChat = () => {
28
- setCurrentChatId(null); // Resets to a new chat session
29
- // In a real app, you might generate a new chatId here or navigate
 
 
 
 
 
 
 
 
 
30
  };
31
 
32
- // Function to load a specific chat from history
33
- const loadChat = (chatId) => {
34
- setCurrentChatId(chatId);
 
 
 
 
35
  };
36
 
37
- if (loading) {
38
- return <div>Loading...</div>; // Or a proper spinner component
39
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  return (
42
  <Router>
43
  <div className="app-wrapper">
44
  <Header user={user} />
45
-
46
- {/* This div will contain both the Sidebar and the main content area (chat/history) */}
47
- <div className="content-area">
48
- {/* Render Sidebar only if user is logged in */}
49
- {user && <Sidebar startNewChat={startNewChat} loadChat={loadChat} />}
50
-
51
- {/* The main content area where chat or history will be rendered */}
52
- <main className="main-content-flex"> {/* New class for flex container of routes */}
53
- <Routes>
54
- <Route path="/login" element={<Login />} />
55
- <Route path="/signup" element={<SignUp />} />
56
- <Route
57
- path="/chat"
58
- element={user ? <ChatInterface currentChatId={currentChatId} /> : <Navigate to="/login" />}
59
- />
60
- <Route
61
- path="/history"
62
- element={user ? <History loadChat={loadChat} /> : <Navigate to="/login" />}
63
- />
64
- {/* Redirect root to chat if logged in, otherwise to login */}
65
- <Route path="/" element={user ? <Navigate to="/chat" /> : <Navigate to="/login" />} />
66
- </Routes>
67
- </main>
68
- </div>
69
  </div>
70
  </Router>
71
  );
 
1
+ import React, { useState, useEffect, useRef, useCallback } from 'react';
2
+ import { BrowserRouter as Router, Routes, Route, Navigate, Link, useNavigate } from 'react-router-dom';
3
+ import SpeechRecognition, { useSpeechRecognition } from 'react-speech-recognition';
4
+ import './App.css';
 
 
 
 
 
 
 
5
 
6
+ // To keep the code clean, we define a simple "user" object.
7
+ // In a real app, this would come from your Firebase auth state.
8
+ const mockUser = {
9
+ isLoggedIn: true,
10
+ };
11
+
12
+ // =================================================================================
13
+ // Chat Components (ChatPage and ChatInput)
14
+ // =================================================================================
15
 
16
+ const ChatInput = ({ onSendMessage, isLoading }) => {
17
+ const [message, setMessage] = useState('');
18
+ const {
19
+ transcript,
20
+ listening,
21
+ resetTranscript,
22
+ browserSupportsSpeechRecognition,
23
+ } = useSpeechRecognition();
24
+
25
+ // This effect automatically sends the message when the user stops speaking.
26
  useEffect(() => {
27
+ // Check if listening has just stopped and there is a transcript to send.
28
+ if (!listening && transcript) {
29
+ onSendMessage(transcript);
30
+ resetTranscript(); // Clear the transcript for the next message
31
+ }
32
+ }, [listening, transcript, onSendMessage, resetTranscript]);
33
+
34
+ // Update the input field with the live transcript while listening.
35
+ useEffect(() => {
36
+ if (listening) {
37
+ setMessage(transcript);
38
+ }
39
+ }, [transcript, listening]);
40
+
41
+ if (!browserSupportsSpeechRecognition) {
42
+ return <div className="error-message">Speech recognition is not supported by your browser.</div>;
43
+ }
44
+
45
+ const handleTextChange = (e) => {
46
+ setMessage(e.target.value);
47
  };
48
 
49
+ const handleSubmit = (e) => {
50
+ e.preventDefault();
51
+ if (message.trim()) {
52
+ onSendMessage(message);
53
+ setMessage('');
54
+ resetTranscript();
55
+ }
56
  };
57
 
58
+ // Handlers for the voice input button
59
+ const handleVoiceMouseDown = () => {
60
+ if (!listening) {
61
+ resetTranscript();
62
+ setMessage(''); // Clear text field on new voice input
63
+ SpeechRecognition.startListening({ continuous: true });
64
+ }
65
+ };
66
+
67
+ const handleVoiceMouseUp = () => {
68
+ if (listening) {
69
+ SpeechRecognition.stopListening();
70
+ }
71
+ };
72
+
73
+ return (
74
+ <div className="chat-input-area">
75
+ <form onSubmit={handleSubmit} className="chat-form">
76
+ <input
77
+ type="text"
78
+ value={message}
79
+ onChange={handleTextChange}
80
+ placeholder={listening ? "Listening..." : "Type or hold the mic to talk..."}
81
+ disabled={isLoading || listening}
82
+ />
83
+ <div className="button-container">
84
+ {/* Voice Input Button */}
85
+ <button
86
+ type="button"
87
+ className={`voice-button ${listening ? 'recording' : ''}`}
88
+ onMouseDown={handleVoiceMouseDown}
89
+ onMouseUp={handleVoiceMouseUp}
90
+ onTouchStart={handleVoiceMouseDown} // For mobile
91
+ onTouchEnd={handleVoiceMouseUp} // For mobile
92
+ disabled={isLoading}
93
+ >
94
+ {/* Microphone SVG */}
95
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
96
+ <path d="M8.25 4.5a3.75 3.75 0 1 1 7.5 0v8.25a3.75 3.75 0 1 1-7.5 0V4.5Z" />
97
+ <path d="M6 10.5a.75.75 0 0 1 .75.75v1.5a5.25 5.25 0 1 0 10.5 0v-1.5a.75.75 0 0 1 1.5 0v1.5a6.75 6.75 0 1 1-13.5 0v-1.5A.75.75 0 0 1 6 10.5Z" />
98
+ </svg>
99
+ </button>
100
+
101
+ {/* Text Send Button */}
102
+ <button
103
+ type="submit"
104
+ className="send-button"
105
+ disabled={isLoading || !message.trim() || listening}
106
+ >
107
+ {/* Arrow SVG */}
108
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
109
+ <path d="M3.478 2.404a.75.75 0 0 0-.926.941l2.432 7.905H13.5a.75.75 0 0 1 0 1.5H4.984l-2.432 7.905a.75.75 0 0 0 .926.94 60.519 60.519 0 0 0 18.445-8.986.75.75 0 0 0 0-1.218A60.517 60.517 0 0 0 3.478 2.404Z" />
110
+ </svg>
111
+ </button>
112
+ </div>
113
+ </form>
114
+ </div>
115
+ );
116
+ };
117
+
118
+
119
+ const ChatPage = () => {
120
+ const [chatHistory, setChatHistory] = useState([]);
121
+ const [isSending, setIsSending] = useState(false);
122
+ const chatEndRef = useRef(null);
123
+
124
+ // Automatically scroll to the latest message
125
+ useEffect(() => {
126
+ chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
127
+ }, [chatHistory]);
128
+
129
+ const handleSendMessage = useCallback(async (prompt) => {
130
+ if (!prompt.trim() || isSending) return;
131
+
132
+ const userMessage = { role: 'user', message: prompt };
133
+ setChatHistory(prev => [...prev, userMessage]);
134
+ setIsSending(true);
135
+
136
+ // Placeholder for the bot's response
137
+ setChatHistory(prev => [...prev, { role: 'assistant', message: '...' }]);
138
+
139
+ // --- Simulate API call for a response ---
140
+ // In a real app, you would make a fetch/axios call to your backend here.
141
+ setTimeout(() => {
142
+ const botResponse = `This is a simulated response to: "${prompt}"`;
143
+ // Replace the placeholder with the actual response
144
+ setChatHistory(prev => {
145
+ const newHistory = [...prev];
146
+ newHistory[newHistory.length - 1] = { role: 'assistant', message: botResponse };
147
+ return newHistory;
148
+ });
149
+ setIsSending(false);
150
+ }, 1500); // Simulate network delay
151
+
152
+ }, [isSending]);
153
+
154
+ return (
155
+ <div className="chat-main">
156
+ <div className="chat-messages">
157
+ {chatHistory.length === 0 ? (
158
+ <div className="empty-chat-placeholder">
159
+ <h1>Dobby is here to help!</h1>
160
+ <p>Start the conversation by typing or using your voice.</p>
161
+ </div>
162
+ ) : (
163
+ chatHistory.map((chat, index) => (
164
+ <div key={index} className={`message-wrapper ${chat.role}`}>
165
+ <div className="chat-avatar">{chat.role === 'assistant' ? '🤖' : '👤'}</div>
166
+ <div className="message-bubble">{chat.message}</div>
167
+ </div>
168
+ ))
169
+ )}
170
+ <div ref={chatEndRef} />
171
+ </div>
172
+ <ChatInput onSendMessage={handleSendMessage} isLoading={isSending} />
173
+ </div>
174
+ );
175
+ };
176
+
177
+
178
+ // =================================================================================
179
+ // Auth Components (Login and SignUp)
180
+ // =================================================================================
181
+
182
+ const AuthForm = ({ isLogin = false }) => {
183
+ const navigate = useNavigate();
184
+ const [email, setEmail] = useState('');
185
+ const [password, setPassword] = useState('');
186
+ const [error, setError] = useState('');
187
+
188
+ const handleSubmit = (e) => {
189
+ e.preventDefault();
190
+ setError(''); // Clear previous errors
191
+ if (!email || !password) {
192
+ setError('Please fill in all fields.');
193
+ return;
194
+ }
195
+ // --- Mock Authentication ---
196
+ // In a real app, this is where you'd call Firebase's signInWithEmailAndPassword or createUserWithEmailAndPassword
197
+ console.log(`Attempting to ${isLogin ? 'log in' : 'sign up'} with:`, { email, password });
198
+ // Simulate a successful login/signup and navigate to the chat page
199
+ mockUser.isLoggedIn = true;
200
+ navigate('/chat');
201
+ };
202
+
203
+ return (
204
+ <div className="auth-container">
205
+ <form className="auth-form" onSubmit={handleSubmit}>
206
+ <h2>{isLogin ? 'Log In' : 'Sign Up'}</h2>
207
+ <div className="form-group">
208
+ <label htmlFor="email">Email</label>
209
+ <input
210
+ type="email"
211
+ id="email"
212
+ value={email}
213
+ onChange={(e) => setEmail(e.target.value)}
214
+ required
215
+ />
216
+ </div>
217
+ <div className="form-group">
218
+ <label htmlFor="password">Password</label>
219
+ <input
220
+ type="password"
221
+ id="password"
222
+ value={password}
223
+ onChange={(e) => setPassword(e.target.value)}
224
+ required
225
+ />
226
+ </div>
227
+ {error && <p className="error-message">{error}</p>}
228
+ <button type="submit" className="btn-solid-green full-width">
229
+ {isLogin ? 'Log In' : 'Sign Up'}
230
+ </button>
231
+ <div className="auth-switch">
232
+ {isLogin ? (
233
+ <p>Don't have an account? <Link to="/signup">Sign Up</Link></p>
234
+ ) : (
235
+ <p>Already have an account? <Link to="/login">Log In</Link></p>
236
+ )}
237
+ </div>
238
+ </form>
239
+ </div>
240
+ );
241
+ };
242
+
243
+ const Login = () => <AuthForm isLogin={true} />;
244
+ const SignUp = () => <AuthForm isLogin={false} />;
245
+
246
+ // =================================================================================
247
+ // Header and Main App Component
248
+ // =================================================================================
249
+
250
+ const Header = ({ user }) => {
251
+ const navigate = useNavigate();
252
+
253
+ const handleLogout = () => {
254
+ // In a real app, call Firebase's signOut()
255
+ mockUser.isLoggedIn = false;
256
+ navigate('/login');
257
+ };
258
+
259
+ return (
260
+ <header className="app-header">
261
+ <div className="logo">
262
+ <div className="brand-names">
263
+ <span className="main-brand">Dobby</span>
264
+ <span className="sub-brand">Your Assistant</span>
265
+ </div>
266
+ </div>
267
+ <nav>
268
+ {user && user.isLoggedIn ? (
269
+ <button onClick={handleLogout} className="btn-solid-green">Log Out</button>
270
+ ) : (
271
+ <>
272
+ <Link to="/login" className="link-green">Log In</Link>
273
+ <Link to="/signup" className="btn-solid-green">Sign Up</Link>
274
+ </>
275
+ )}
276
+ </nav>
277
+ </header>
278
+ );
279
+ };
280
+
281
+
282
+ function App() {
283
+ // This state would be managed by onAuthStateChanged in a real Firebase app.
284
+ const [user] = useState(mockUser);
285
 
286
  return (
287
  <Router>
288
  <div className="app-wrapper">
289
  <Header user={user} />
290
+ <main className="main-content">
291
+ <Routes>
292
+ <Route path="/login" element={<Login />} />
293
+ <Route path="/signup" element={<SignUp />} />
294
+ <Route
295
+ path="/chat"
296
+ element={user.isLoggedIn ? <ChatPage /> : <Navigate to="/login" />}
297
+ />
298
+ {/* Redirect root to chat if logged in, otherwise to login */}
299
+ <Route
300
+ path="/"
301
+ element={user.isLoggedIn ? <Navigate to="/chat" /> : <Navigate to="/login" />}
302
+ />
303
+ </Routes>
304
+ </main>
 
 
 
 
 
 
 
 
 
305
  </div>
306
  </Router>
307
  );