feat: major UI overhaul with new components and enhanced UX
- 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
This commit is contained in:
171
components/chat/ChatLayout.tsx
Normal file
171
components/chat/ChatLayout.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user