Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Login } from './components/Auth/Login';
import { Layout } from './components/Layout/Layout';
import { CalendarList } from './components/Calendar/CalendarList';
import { CalendarDetail } from './components/Calendar/CalendarDetail';
import { ChatInterface } from './components/Chat/ChatInterface';
import { authService } from './services/authService';

/**
Expand Down Expand Up @@ -111,19 +110,10 @@ function App() {
)
}
/>
{/* Redirect old /chat route to calendars — chat is now a collapsible panel */}
<Route
path="/chat"
element={
isAuthenticated ? (
<Layout>
<div className="h-[calc(100vh-10rem)] bg-white rounded-lg shadow-md overflow-hidden">
<ChatInterface />
</div>
</Layout>
) : (
<Navigate to="/login" replace />
)
}
element={<Navigate to="/calendars" replace />}
/>
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/Calendar/CalendarDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ export function CalendarDetail() {
const headerLabel = viewMode === 'month' ? formatMonthYear(currentDate) : formatWeekRange(currentDate);

return (
<div className="h-[calc(100vh-7.5rem)] flex flex-col bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<div className="h-full flex flex-col bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
{/* ── Toolbar ── */}
<div className="flex items-center justify-between px-4 py-2.5 border-b border-gray-200 bg-white shrink-0">
<div className="flex items-center gap-3">
Expand Down
58 changes: 12 additions & 46 deletions app/src/components/Chat/ChatInterface.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { useState, useEffect, useRef } from 'react';
import type { ChatMessage, PendingAction } from '../../types';
import { chatService } from '../../services/chatService';
import { conversationService } from '../../services/conversationService';
import { ApprovalModal } from '../Approval/ApprovalModal';
import { ConversationSidebar } from './ConversationSidebar';

const INITIAL_MESSAGE: ChatMessage = {
id: 'initial',
Expand All @@ -18,8 +16,7 @@ export function ChatInterface() {
const [isLoading, setIsLoading] = useState(false);
const [pendingActions, setPendingActions] = useState<PendingAction[]>([]);
const [selectedAction, setSelectedAction] = useState<PendingAction | null>(null);
const [isLoadingMessages, setIsLoadingMessages] = useState(false);
const [sidebarRefreshTrigger, setSidebarRefreshTrigger] = useState(0);
const [isLoadingMessages] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);

const [conversationId, setConversationId] = useState<string | undefined>(undefined);
Expand All @@ -39,33 +36,6 @@ export function ChatInterface() {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};

const handleSelectConversation = async (selectedId: string) => {
if (selectedId === conversationId) return;

setIsLoadingMessages(true);
try {
const backendMessages = await conversationService.getMessages(selectedId);
const chatMessages = conversationService.mapToChatMessages(backendMessages);
setMessages(chatMessages.length > 0 ? chatMessages : [INITIAL_MESSAGE]);
setConversationId(selectedId);

// Fetch any outstanding pending actions for this conversation (survives refresh)
const fetchedActions = await chatService.fetchPendingActions(selectedId);
setPendingActions(fetchedActions);
setSelectedAction(fetchedActions.length > 0 ? fetchedActions[0] : null);
} catch (error) {
console.error('Failed to load conversation messages:', error);
const errorMsg: ChatMessage = {
id: `msg_${Date.now()}_err`,
role: 'assistant',
content: 'Failed to load conversation messages. Please try again.',
timestamp: new Date().toISOString(),
};
setMessages([errorMsg]);
} finally {
setIsLoadingMessages(false);
}
};

const handleNewConversation = () => {
setConversationId(undefined);
Expand Down Expand Up @@ -94,8 +64,6 @@ export function ChatInterface() {
const result = await chatService.sendMessage(userInput, conversationId);
if (result.conversationId && !conversationId) {
setConversationId(result.conversationId);
// Trigger sidebar refresh when a new conversation is created
setSidebarRefreshTrigger(prev => prev + 1);
}
setMessages(prev => [...prev, result.message]);

Expand Down Expand Up @@ -173,22 +141,20 @@ export function ChatInterface() {

return (
<div className="flex h-full">
<ConversationSidebar
currentConversationId={conversationId}
onSelectConversation={handleSelectConversation}
onNewConversation={handleNewConversation}
refreshTrigger={sidebarRefreshTrigger}
/>

<div className="flex flex-col flex-1 min-w-0">
<div className="bg-white border-b border-gray-200 px-6 py-4">
<h2 className="text-xl font-semibold text-gray-900">Calendar Assistant</h2>
<p className="text-sm text-gray-600 mt-1">
Ask me to help manage your calendar
</p>
<div className="bg-white border-b border-gray-200 px-4 py-3 flex items-center justify-between">
<div>
<h2 className="text-base font-semibold text-gray-900">Assistant</h2>
</div>
<button
onClick={handleNewConversation}
className="px-3 py-1.5 text-xs font-medium text-gray-700 border border-gray-300 rounded-md hover:bg-gray-50 transition-colors"
>
New chat
</button>
</div>

<div className="flex-1 overflow-y-auto p-6 space-y-4 bg-gray-50 relative">
<div className="flex-1 overflow-y-auto p-4 space-y-4 bg-gray-50 relative">
{isLoadingMessages && (
<div className="absolute inset-0 bg-gray-50 bg-opacity-80 flex items-center justify-center z-10">
<div className="w-8 h-8 border-4 border-blue-200 border-t-blue-600 rounded-full animate-spin" />
Expand Down
Loading