Skip to content

Commit cd28b7f

Browse files
committed
ui updations
1 parent 25a86f2 commit cd28b7f

File tree

5 files changed

+410
-367
lines changed

5 files changed

+410
-367
lines changed

frontend/src/App.tsx

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,20 @@
11
import React, { useState, useEffect } from 'react';
2-
import type { AppScreen } from './types';
32
import { getSessionId, getNewSessionId, clearSession } from './services/api';
43
import ApiKeyInput from './components/ApiKeyInput';
54
import FileUpload from './components/FileUpload';
65
import ChatInterface from './components/ChatInterface';
76

87
const App: React.FC = () => {
9-
const [screen, setScreen] = useState<AppScreen>('api-key');
108
const [apiKey, setApiKey] = useState<string>('');
9+
const [isKeyValid, setIsKeyValid] = useState(false);
10+
const [fileUploaded, setFileUploaded] = useState(false);
11+
const [fileName, setFileName] = useState<string>('');
1112

1213
useEffect(() => {
1314
const init = async () => {
1415
const existingSession = getSessionId();
1516
if (existingSession) {
16-
// Attempt to clear old session data on backend
17-
// We pass empty string for api key as it's not available on reload
18-
try {
19-
await clearSession('');
20-
} catch (e) {
21-
console.warn("Failed to clear previous session", e);
22-
}
17+
try { await clearSession(''); } catch (e) { }
2318
}
2419
getNewSessionId();
2520
};
@@ -28,36 +23,65 @@ const App: React.FC = () => {
2823

2924
const handleKeyValidated = (key: string) => {
3025
setApiKey(key);
31-
setScreen('upload');
26+
setIsKeyValid(true);
3227
};
3328

34-
const handleUploadSuccess = () => {
35-
setScreen('chat');
29+
const handleUploadSuccess = (name: string) => {
30+
setFileName(name);
31+
setFileUploaded(true);
3632
};
3733

38-
const handleClearSession = async () => {
39-
// Clear on backend
34+
const handleReset = async () => {
4035
await clearSession(apiKey);
41-
// Generate new session
4236
getNewSessionId();
43-
// Reset state
4437
setApiKey('');
45-
setScreen('api-key');
38+
setIsKeyValid(false);
39+
setFileUploaded(false);
40+
setFileName('');
4641
};
4742

4843
return (
49-
<>
50-
{/* Background is handled in index.css body */}
51-
{screen === 'api-key' && (
52-
<ApiKeyInput onKeyValidated={handleKeyValidated} />
53-
)}
54-
{screen === 'upload' && (
55-
<FileUpload apiKey={apiKey} onUploadSuccess={handleUploadSuccess} />
56-
)}
57-
{screen === 'chat' && (
58-
<ChatInterface apiKey={apiKey} onClearSession={handleClearSession} />
59-
)}
60-
</>
44+
<div className="app-wrapper">
45+
{/* Header */}
46+
<header className="app-header">
47+
<div className="header-content">
48+
<h1>AI Document Q&A</h1>
49+
<p>Secure RAG System powered by Gemini</p>
50+
</div>
51+
<button onClick={handleReset} className="btn btn-ghost">
52+
↺ Reset Session
53+
</button>
54+
</header>
55+
56+
{/* Main Content */}
57+
<div className="main-grid">
58+
59+
{/* Left Panel: Configuration */}
60+
<aside className="left-panel">
61+
<ApiKeyInput
62+
onKeyValidated={handleKeyValidated}
63+
locked={isKeyValid}
64+
/>
65+
</aside>
66+
67+
{/* Right Panel: Dynamic Interaction */}
68+
<main className="right-panel">
69+
{!fileUploaded ? (
70+
<FileUpload
71+
apiKey={apiKey}
72+
enabled={isKeyValid}
73+
onUploadSuccess={handleUploadSuccess}
74+
/>
75+
) : (
76+
<ChatInterface
77+
apiKey={apiKey}
78+
fileName={fileName}
79+
/>
80+
)}
81+
</main>
82+
83+
</div>
84+
</div>
6185
);
6286
};
6387

frontend/src/components/ApiKeyInput.tsx

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React, { useState } from 'react';
22
import { validateKey } from '../services/api';
33

4-
54
interface ApiKeyInputProps {
65
onKeyValidated: (key: string) => void;
6+
locked: boolean;
77
}
88

9-
const ApiKeyInput: React.FC<ApiKeyInputProps> = ({ onKeyValidated }) => {
9+
const ApiKeyInput: React.FC<ApiKeyInputProps> = ({ onKeyValidated, locked }) => {
1010
const [key, setKey] = useState('');
1111
const [loading, setLoading] = useState(false);
1212
const [error, setError] = useState<string | null>(null);
@@ -19,40 +19,50 @@ const ApiKeyInput: React.FC<ApiKeyInputProps> = ({ onKeyValidated }) => {
1919
setError(null);
2020

2121
try {
22-
const isValid = await validateKey(key);
23-
if (isValid) {
22+
const valid = await validateKey(key);
23+
if (valid) {
2424
onKeyValidated(key);
2525
} else {
26-
setError('Invalid API Key. Please check and try again.');
26+
setError('Invalid API Key');
2727
}
2828
} catch (err) {
29-
setError('Connection failed. Is the backend running?');
29+
setError('Connection failed');
3030
} finally {
3131
setLoading(false);
3232
}
3333
};
3434

3535
return (
36-
<div className="glass-card fade-in">
37-
<h1>Enter API Key</h1>
38-
<p>Securely access the document RAG system.</p>
36+
<div className={`saas-card fade-enter ${locked ? 'api-status-locked' : ''}`}>
37+
<div className="card-title">
38+
<span>API Key Setup</span>
39+
{locked && (
40+
<div className="locked-badge">
41+
✓ Active
42+
</div>
43+
)}
44+
</div>
3945

40-
<form onSubmit={handleSubmit} className="auth-form">
41-
<div className="input-group">
46+
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
47+
<div>
4248
<input
4349
type="password"
4450
value={key}
4551
onChange={(e) => setKey(e.target.value)}
46-
placeholder="Gemini API Key"
52+
placeholder="Enter Gemini API Key"
4753
className="input-field"
48-
disabled={loading}
54+
disabled={loading || locked}
4955
/>
56+
{error && <p style={{ color: 'var(--error)', fontSize: '0.85rem', marginTop: '0.5rem' }}>{error}</p>}
5057
</div>
5158

52-
{error && <div className="error-msg">⚠️ {error}</div>}
53-
54-
<button type="submit" className="btn full-width" disabled={loading || !key}>
55-
{loading ? <div className="spinner" /> : 'Validate Key'}
59+
<button
60+
type="submit"
61+
className="btn btn-primary"
62+
disabled={loading || !key || locked}
63+
style={{ width: '100%' }}
64+
>
65+
{loading ? 'Validating...' : (locked ? 'Authenticated' : 'Validate Key')}
5666
</button>
5767
</form>
5868
</div>

frontend/src/components/ChatInterface.tsx

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import type { ChatMessage } from '../types';
44

55
interface ChatInterfaceProps {
66
apiKey: string;
7-
onClearSession: () => void;
7+
fileName: string;
88
}
99

10-
const ChatInterface: React.FC<ChatInterfaceProps> = ({ apiKey, onClearSession }) => {
10+
const ChatInterface: React.FC<ChatInterfaceProps> = ({ apiKey, fileName }) => {
1111
const [messages, setMessages] = useState<ChatMessage[]>([]);
1212
const [input, setInput] = useState('');
1313
const [loading, setLoading] = useState(false);
@@ -49,49 +49,57 @@ const ChatInterface: React.FC<ChatInterfaceProps> = ({ apiKey, onClearSession })
4949
};
5050

5151
return (
52-
<div className="chat-container fade-in">
53-
<div className="chat-header glass-card">
54-
<h1>Document Chat</h1>
55-
<button onClick={onClearSession} className="btn btn-secondary danger-btn">
56-
Clear Session
57-
</button>
52+
<div className="chat-wrapper fade-enter">
53+
<div className="chat-header-mini">
54+
<span>Conversing with <strong>{fileName}</strong></span>
55+
<span style={{ fontSize: '0.8rem', color: 'var(--success)' }}>● Active</span>
5856
</div>
5957

6058
<div className="chat-messages">
6159
{messages.length === 0 && (
62-
<div className="empty-state">
63-
<p>Ask a question about your uploaded document to get started.</p>
60+
<div style={{
61+
textAlign: 'center',
62+
color: 'var(--text-secondary)',
63+
marginTop: '4rem',
64+
display: 'flex',
65+
flexDirection: 'column',
66+
alignItems: 'center',
67+
gap: '1rem'
68+
}}>
69+
<div style={{ fontSize: '3rem' }}>💬</div>
70+
<p>Ask anything about the document to start.</p>
6471
</div>
6572
)}
6673

6774
{messages.map((msg, idx) => (
68-
<div key={idx} className={`message-row ${msg.role === 'user' ? 'user-row' : 'model-row'}`}>
69-
<div className={`message-bubble ${msg.role}`}>
70-
{msg.content}
71-
</div>
75+
<div key={idx} className={`message-bubble ${msg.role === 'user' ? 'msg-user' : 'msg-model'}`}>
76+
{msg.content}
7277
</div>
7378
))}
79+
7480
{loading && (
75-
<div className="message-row model-row">
76-
<div className={`message-bubble model typing`}>
77-
<div className="dot"></div><div className="dot"></div><div className="dot"></div>
81+
<div className="message-bubble msg-model" style={{ width: 'fit-content' }}>
82+
<div className="typing-dots">
83+
<div className="dot"></div>
84+
<div className="dot"></div>
85+
<div className="dot"></div>
7886
</div>
7987
</div>
8088
)}
8189
<div ref={messagesEndRef} />
8290
</div>
8391

84-
<div className="chat-input-area glass-card">
85-
<form onSubmit={handleSend} className="input-row">
92+
<div className="chat-input-wrapper">
93+
<form onSubmit={handleSend} className="chat-form">
8694
<input
8795
type="text"
8896
className="input-field"
8997
value={input}
9098
onChange={e => setInput(e.target.value)}
91-
placeholder="Ask a question..."
99+
placeholder="Type your question here..."
92100
disabled={loading}
93101
/>
94-
<button type="submit" className="btn" disabled={loading || !input.trim()}>
102+
<button type="submit" className="btn btn-primary" disabled={loading || !input.trim()}>
95103
Send
96104
</button>
97105
</form>

frontend/src/components/FileUpload.tsx

Lines changed: 36 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,76 +3,69 @@ import { uploadFile } from '../services/api';
33

44
interface FileUploadProps {
55
apiKey: string;
6-
onUploadSuccess: () => void;
6+
enabled: boolean;
7+
onUploadSuccess: (filename: string) => void;
78
}
89

9-
const FileUpload: React.FC<FileUploadProps> = ({ apiKey, onUploadSuccess }) => {
10-
const [file, setFile] = useState<File | null>(null);
10+
const FileUpload: React.FC<FileUploadProps> = ({ apiKey, enabled, onUploadSuccess }) => {
1111
const [loading, setLoading] = useState(false);
1212
const [error, setError] = useState<string | null>(null);
13-
const [success, setSuccess] = useState<string | null>(null);
1413

15-
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
14+
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
15+
if (!enabled) return;
16+
1617
if (e.target.files && e.target.files[0]) {
1718
const selectedFile = e.target.files[0];
1819
if (selectedFile.type !== 'application/pdf') {
1920
setError('Only PDF files are allowed.');
20-
setFile(null);
2121
return;
2222
}
23-
setFile(selectedFile);
24-
setError(null);
23+
24+
// Upload immediately on selection
25+
await processUpload(selectedFile);
2526
}
2627
};
2728

28-
const handleUpload = async (e: React.FormEvent) => {
29-
e.preventDefault();
30-
if (!file) return;
31-
29+
const processUpload = async (file: File) => {
3230
setLoading(true);
3331
setError(null);
34-
3532
try {
36-
const response = await uploadFile(apiKey, file);
37-
setSuccess(response.message || 'File processed successfully!');
38-
39-
// Navigate after short delay to show success message
33+
await uploadFile(apiKey, file);
34+
// Short delay for visual feedback
4035
setTimeout(() => {
41-
onUploadSuccess();
42-
}, 1500);
43-
36+
onUploadSuccess(file.name);
37+
}, 800);
4438
} catch (err: any) {
45-
console.error(err);
4639
setError(err.message || 'Upload failed');
47-
setLoading(false); // Only unset loading if error, otherwise we are navigating
40+
setLoading(false);
4841
}
4942
};
5043

5144
return (
52-
<div className="glass-card fade-in">
53-
<h1>Upload Document</h1>
54-
<p>Upload a PDF to start chatting with it.</p>
45+
<div className="upload-container fade-enter">
46+
<label className={`upload-area ${!enabled ? 'disabled' : ''}`}>
47+
<input
48+
type="file"
49+
accept=".pdf"
50+
onChange={handleFileChange}
51+
disabled={!enabled || loading}
52+
style={{ display: 'none' }}
53+
/>
54+
55+
<span className="upload-icon">
56+
{loading ? '⏳' : (enabled ? '☁️' : '🔒')}
57+
</span>
5558

56-
<form onSubmit={handleUpload} className="upload-form">
57-
<label className={`file-drop-area ${file ? 'has-file' : ''}`}>
58-
<input
59-
type="file"
60-
accept=".pdf"
61-
onChange={handleFileChange}
62-
disabled={loading}
63-
/>
64-
<span className="file-msg">
65-
{file ? file.name : 'Click to select a PDF file'}
66-
</span>
67-
</label>
59+
<h3 className="upload-text-main">
60+
{loading ? 'Processing Document...' : (enabled ? 'Click to Upload PDF' : 'Validate Key to Upload')}
61+
</h3>
6862

69-
{error && <div className="error-msg">⚠️ {error}</div>}
70-
{success && <div className="success-msg">{success}</div>}
63+
<p className="upload-text-sub">
64+
{loading ? 'Please wait while we index the content.' : 'Supported format: .pdf'}
65+
</p>
7166

72-
<button type="submit" className="btn full-width" disabled={loading || !file}>
73-
{loading ? <div className="spinner" /> : 'Upload and Process'}
74-
</button>
75-
</form>
67+
{error && <p style={{ color: 'var(--error)', marginTop: '1rem' }}>{error}</p>}
68+
</label>
7669
</div>
7770
);
7871
};

0 commit comments

Comments
 (0)