NeonClary
Restore cybersecurity user profile UX and personalize advisor responses
6004480
Raw
History Blame Contribute Delete
5.86 kB
import React, { useState, useEffect, useRef } from 'react';
import { X, Send, MessageCircle } from 'lucide-react';
const OnboardingChat = ({ authToken, onClose, userName }) => {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
const [progress, setProgress] = useState(0);
const [complete, setComplete] = useState(false);
const endRef = useRef(null);
useEffect(() => {
startOnboarding();
}, []);
useEffect(() => {
endRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
const startOnboarding = async () => {
try {
const resp = await fetch(`${process.env.REACT_APP_API_URL}/api/onboarding/start`, {
headers: { 'Authorization': `Bearer ${authToken}` },
});
if (resp.ok) {
const data = await resp.json();
setMessages(data.messages || [{ role: 'agent', text: data.reply }]);
setProgress(data.progress);
setComplete(data.complete || false);
}
} catch (e) {
setMessages([{ role: 'agent', text: "Hi! What is your security role and what are you trying to accomplish right now?" }]);
}
};
const sendMessage = async () => {
if (!input.trim() || loading) return;
const userText = input;
setInput('');
setMessages(prev => [...prev, { role: 'user', text: userText }]);
setLoading(true);
try {
const resp = await fetch(`${process.env.REACT_APP_API_URL}/api/onboarding/chat`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${authToken}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ user_input: userText }),
});
if (resp.ok) {
const data = await resp.json();
setMessages(prev => [...prev, { role: 'agent', text: data.reply }]);
setProgress(data.progress);
setComplete(data.complete);
}
} catch (e) {
setMessages(prev => [...prev, { role: 'agent', text: "Sorry, I had trouble processing that. Try again?" }]);
} finally {
setLoading(false);
}
};
return (
<div style={{
position: 'fixed', inset: 0, zIndex: 9999,
background: 'rgba(0,0,0,0.5)', display: 'flex',
alignItems: 'center', justifyContent: 'center',
}}>
<div style={{
background: 'var(--bg-primary)', borderRadius: 16,
width: '90%', maxWidth: 500, height: '70vh', maxHeight: 600,
display: 'flex', flexDirection: 'column', overflow: 'hidden',
boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
}}>
{/* Header */}
<div style={{
padding: '14px 18px', borderBottom: '1px solid var(--border-primary)',
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<MessageCircle size={18} style={{ color: 'var(--accent-primary)' }} />
<span style={{ fontWeight: 600, color: 'var(--text-primary)', fontSize: 14 }}>
Tell us about yourself
</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<div style={{
background: 'var(--bg-secondary)', borderRadius: 8, padding: '4px 10px',
fontSize: 11, fontWeight: 600, color: 'var(--accent-primary)',
}}>{progress}% complete</div>
<button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-secondary)' }}>
<X size={18} />
</button>
</div>
</div>
{/* Messages */}
<div style={{ flex: 1, overflowY: 'auto', padding: 16, display: 'flex', flexDirection: 'column', gap: 10 }}>
{messages.map((m, i) => (
<div key={i} style={{
alignSelf: m.role === 'user' ? 'flex-end' : 'flex-start',
maxWidth: '80%',
background: m.role === 'user' ? 'var(--accent-primary)' : 'var(--bg-secondary)',
color: m.role === 'user' ? '#fff' : 'var(--text-primary)',
padding: '10px 14px', borderRadius: 12, fontSize: 13, lineHeight: 1.5,
}}>
{m.text}
</div>
))}
{loading && (
<div style={{ alignSelf: 'flex-start', color: 'var(--text-secondary)', fontSize: 12 }}>
Thinking...
</div>
)}
<div ref={endRef} />
</div>
{/* Input */}
{!complete && (
<div style={{
padding: '10px 14px', borderTop: '1px solid var(--border-primary)',
display: 'flex', gap: 8,
}}>
<input
value={input}
onChange={e => setInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && sendMessage()}
placeholder="Type your answer..."
disabled={loading}
style={{
flex: 1, padding: '8px 12px', borderRadius: 8,
border: '1px solid var(--border-primary)', background: 'var(--bg-secondary)',
color: 'var(--text-primary)', fontSize: 13, outline: 'none',
}}
/>
<button
onClick={sendMessage}
disabled={!input.trim() || loading}
style={{
padding: '8px 12px', borderRadius: 8, border: 'none',
background: input.trim() ? 'var(--accent-primary)' : 'var(--bg-secondary)',
color: input.trim() ? '#fff' : 'var(--text-secondary)',
cursor: input.trim() ? 'pointer' : 'default',
}}
>
<Send size={16} />
</button>
</div>
)}
</div>
</div>
);
};
export default OnboardingChat;