- Add comprehensive Company Wiki feature with complete state management - CompanyWikiManager, empty states, invite modals - Implement new Chat system with enhanced layout and components - ChatLayout, ChatSidebar, MessageThread, FileUploadInput - Create modern Login and OTP verification flows - LoginNew page, OTPVerification component - Add new Employee Forms system with enhanced controller - Introduce Figma-based design components and multiple choice inputs - Add new font assets (NeueMontreal) and robot images for onboarding - Enhance existing components with improved styling and functionality - Update build configuration and dependencies - Remove deprecated ModernLogin component
171 lines
7.6 KiB
TypeScript
171 lines
7.6 KiB
TypeScript
import React, { useState, useEffect, useRef } from 'react';
|
|
import { useOrg } from '../../contexts/OrgContext';
|
|
import { Employee } from '../../types';
|
|
import ChatSidebar from './ChatSidebar';
|
|
import MessageThread from './MessageThread';
|
|
import FileUploadInput from './FileUploadInput';
|
|
|
|
interface Message {
|
|
id: string;
|
|
text: string;
|
|
isUser: boolean;
|
|
timestamp: number;
|
|
files?: string[];
|
|
}
|
|
|
|
interface ChatLayoutProps {
|
|
children?: React.ReactNode;
|
|
}
|
|
|
|
const ChatLayout: React.FC<ChatLayoutProps> = ({ children }) => {
|
|
const { employees } = useOrg();
|
|
const [selectedEmployees, setSelectedEmployees] = useState<Employee[]>([]);
|
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
const [inputValue, setInputValue] = useState('');
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [uploadedFiles, setUploadedFiles] = useState<string[]>([]);
|
|
|
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
|
|
const scrollToBottom = () => {
|
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
};
|
|
|
|
useEffect(() => {
|
|
scrollToBottom();
|
|
}, [messages]);
|
|
|
|
const handleNavigation = (page: string) => {
|
|
// Handle navigation to different pages
|
|
console.log('Navigate to:', page);
|
|
};
|
|
|
|
const handleSendMessage = async () => {
|
|
if (!inputValue.trim() && uploadedFiles.length === 0) return;
|
|
|
|
const userMessage: Message = {
|
|
id: Date.now().toString(),
|
|
text: inputValue,
|
|
isUser: true,
|
|
timestamp: Date.now(),
|
|
files: uploadedFiles.length > 0 ? [...uploadedFiles] : undefined
|
|
};
|
|
|
|
setMessages(prev => [...prev, userMessage]);
|
|
setInputValue('');
|
|
setUploadedFiles([]);
|
|
setIsLoading(true);
|
|
|
|
// Simulate AI response
|
|
setTimeout(() => {
|
|
const aiMessage: Message = {
|
|
id: (Date.now() + 1).toString(),
|
|
text: "I understand you're asking about the employee data. Based on the information provided, I can help analyze the performance metrics and provide insights.\n\nHere are some key findings from your team's data:\n\n• **Performance Trends**: Overall team productivity has increased by 15% this quarter\n• **Cultural Health**: Employee satisfaction scores are above industry average\n• **Areas for Growth**: Communication and cross-team collaboration could be improved\n\nWould you like me to dive deeper into any of these areas?",
|
|
isUser: false,
|
|
timestamp: Date.now()
|
|
};
|
|
setMessages(prev => [...prev, aiMessage]);
|
|
setIsLoading(false);
|
|
}, 2000);
|
|
};
|
|
|
|
const handleKeyPress = (e: React.KeyboardEvent) => {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault();
|
|
handleSendMessage();
|
|
}
|
|
};
|
|
|
|
const handleRemoveFile = (index: number) => {
|
|
setUploadedFiles(prev => prev.filter((_, i) => i !== index));
|
|
};
|
|
|
|
const handleFilesSelected = (files: File[]) => {
|
|
// For demo purposes, we'll just add the file names
|
|
// In a real implementation, you'd upload the files and get URLs back
|
|
const fileNames = files.map(file => file.name);
|
|
setUploadedFiles(prev => [...prev, ...fileNames]);
|
|
};
|
|
|
|
const hasMessages = messages.length > 0;
|
|
|
|
return (
|
|
<div className="w-full h-screen bg-Neutrals-NeutralSlate0 inline-flex overflow-hidden">
|
|
{/* Sidebar */}
|
|
<ChatSidebar currentPage="chat" onNavigate={handleNavigation} />
|
|
|
|
{/* Main Content Area */}
|
|
<div className="flex-1 flex flex-col overflow-hidden">
|
|
{/* Header with Employee Selection */}
|
|
<div className="px-6 py-4 bg-Neutrals-NeutralSlate0 border-b border-Neutrals-NeutralSlate200 flex justify-between items-center">
|
|
<div className="flex items-center gap-3">
|
|
<h1 className="text-xl font-semibold text-Neutrals-NeutralSlate950">Chat</h1>
|
|
{selectedEmployees.length > 0 && (
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm text-Neutrals-NeutralSlate500">Analyzing:</span>
|
|
<div className="flex items-center gap-1">
|
|
{selectedEmployees.slice(0, 3).map((emp, index) => (
|
|
<div key={emp.id} className="px-2 py-1 bg-Brand-Orange/10 rounded-full text-xs text-Brand-Orange">
|
|
{emp.name}
|
|
</div>
|
|
))}
|
|
{selectedEmployees.length > 3 && (
|
|
<div className="px-2 py-1 bg-Neutrals-NeutralSlate100 rounded-full text-xs text-Neutrals-NeutralSlate600">
|
|
+{selectedEmployees.length - 3} more
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Messages Area */}
|
|
<div className="flex-1 overflow-y-auto px-6 py-4">
|
|
{hasMessages ? (
|
|
<div className="max-w-4xl mx-auto">
|
|
<MessageThread
|
|
messages={messages}
|
|
isLoading={isLoading}
|
|
/>
|
|
<div ref={messagesEndRef} />
|
|
</div>
|
|
) : (
|
|
children
|
|
)}
|
|
</div>
|
|
|
|
{/* Input Area */}
|
|
<div className="px-6 py-4 bg-Neutrals-NeutralSlate0 border-t border-Neutrals-NeutralSlate200">
|
|
<div className="max-w-4xl mx-auto">
|
|
<div className="flex items-end gap-3">
|
|
<FileUploadInput
|
|
value={inputValue}
|
|
onChange={setInputValue}
|
|
onKeyDown={handleKeyPress}
|
|
placeholder="Ask about your team's performance, culture, or any insights..."
|
|
disabled={isLoading}
|
|
uploadedFiles={uploadedFiles}
|
|
onRemoveFile={handleRemoveFile}
|
|
onFilesSelected={handleFilesSelected}
|
|
/>
|
|
|
|
{/* Send Button */}
|
|
<button
|
|
onClick={handleSendMessage}
|
|
disabled={!inputValue.trim() && uploadedFiles.length === 0}
|
|
className="px-4 py-3 bg-Brand-Orange text-white rounded-xl hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex-shrink-0"
|
|
>
|
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M18.3346 1.66797L9.16797 10.8346M18.3346 1.66797L12.5013 18.3346L9.16797 10.8346M18.3346 1.66797L1.66797 7.5013L9.16797 10.8346" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ChatLayout; |