Skip to content

Commit 9080f1e

Browse files
authored
Merge pull request #35 from Hanseooo/feat/session-recovery
feat(frontend): implement chat session recovery and reset UX
2 parents c76a86d + 4c57751 commit 9080f1e

File tree

1 file changed

+66
-14
lines changed

1 file changed

+66
-14
lines changed

frontend/atla/src/components/chat/ChatInterface.tsx

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,53 @@
1-
import { useSendMessage, useSubmitClarification } from '../../hooks/useChat';
1+
import { useEffect } from 'react';
2+
import { useSendMessage, useSubmitClarification, useChatSession } from '../../hooks/useChat';
23
import { useChatStore } from '../../stores/chatStore';
34
import { getErrorMessage } from '../../lib/api';
45
import type { ChatResponse } from '../../types/chat';
56
import { MessageList } from './MessageList';
67
import { ChatInput } from './ChatInput';
8+
import { Button } from '../ui/button';
9+
import { RefreshCcw } from 'lucide-react';
10+
import type { AxiosError } from 'axios';
711

812
export function ChatInterface() {
9-
const { addMessage, sessionId, setSessionId } = useChatStore();
13+
const { addMessage, sessionId, setSessionId, clearMessages, messages } = useChatStore();
14+
15+
// Use the session query to verify if the backend still remembers this session
16+
const {
17+
isError: isSessionError,
18+
isFetching: isFetchingSession,
19+
error: sessionError,
20+
} = useChatSession(sessionId);
21+
22+
// If the backend session doesn't exist (example: server restarted), wipe the local storage and start fresh
23+
useEffect(() => {
24+
if (!isSessionError) return;
1025

26+
const status = (sessionError as AxiosError | null | undefined)?.response?.status;
27+
if (status === 404) {
28+
clearMessages();
29+
addMessage({
30+
role: 'assistant',
31+
content: "Oops! It looks like your previous session expired. Let's start a brand new plan! Where would you like to go?"
32+
});
33+
}
34+
}, [isSessionError, sessionError, clearMessages, addMessage]);
35+
1136
const sendMessageMutation = useSendMessage();
1237
const submitClarificationMutation = useSubmitClarification();
1338

39+
const handleResetSession = () => {
40+
const hasPendingRequest = sendMessageMutation.isPending || submitClarificationMutation.isPending;
41+
if (hasPendingRequest) {
42+
window.alert( 'Please wait for the current response to finish before starting a new trip plan.');
43+
return;
44+
}
45+
46+
if (window.confirm("Are you sure you want to start a new trip plan? This will clear your current conversation.")) {
47+
clearMessages();
48+
}
49+
};
50+
1451
const formatAnswer = (answer: unknown): string => {
1552
if (typeof answer === 'string') return answer;
1653
if (typeof answer === 'number' || typeof answer === 'boolean') return String(answer);
@@ -33,15 +70,15 @@ export function ChatInterface() {
3370

3471
const handleSend = async (userMessage: string) => {
3572
addMessage({ role: 'user', content: userMessage });
36-
73+
3774
try {
3875
const response = await sendMessageMutation.mutateAsync({
3976
message: userMessage,
4077
session_id: sessionId || undefined,
4178
});
4279
handleProcessResponse(response);
4380
} catch (error: unknown) {
44-
addMessage({ role: 'assistant', content: `Sorry, an error occurred: ${getErrorMessage(error)}` });
81+
addMessage({ role: 'assistant', content: `Sorry, an error occurred: ${getErrorMessage(error)}` });
4582
}
4683
};
4784

@@ -57,25 +94,40 @@ export function ChatInterface() {
5794
});
5895
handleProcessResponse(response);
5996
} catch (error: unknown) {
60-
addMessage({ role: 'assistant', content: `Sorry, an error occurred: ${getErrorMessage(error)}` });
97+
addMessage({ role: 'assistant', content: `Sorry, an error occurred: ${getErrorMessage(error)}` });
6198
}
6299
};
63100

64-
const isPending = sendMessageMutation.isPending || submitClarificationMutation.isPending;
65-
const error = (sendMessageMutation.isError || submitClarificationMutation.isError)
66-
? getErrorMessage(sendMessageMutation.error ?? submitClarificationMutation.error)
101+
const isPending = sendMessageMutation.isPending || submitClarificationMutation.isPending || isFetchingSession;
102+
const error = (sendMessageMutation.isError || submitClarificationMutation.isError)
103+
? getErrorMessage(sendMessageMutation.error ?? submitClarificationMutation.error)
67104
: null;
68105

69106
return (
70107
<>
71-
<MessageList
108+
{/* Temporary place of Start New Plan btn until we have a finalized UI/UX design for session management */}
109+
{messages.length > 0 && (
110+
<div className="absolute top-4 right-4 z-20">
111+
<Button
112+
variant="ghost"
113+
size="sm"
114+
onClick={handleResetSession}
115+
className="text-muted-foreground hover:text-destructive text-xs flex items-center gap-1.5"
116+
>
117+
<RefreshCcw className="w-3.5 h-3.5" />
118+
Start New Plan
119+
</Button>
120+
</div>
121+
)}
122+
123+
<MessageList
72124
isPending={isPending}
73-
error={error}
74-
onAnswerQuestion={handleAnswerQuestion}
125+
error={error}
126+
onAnswerQuestion={handleAnswerQuestion}
75127
/>
76-
<ChatInput
77-
onSend={handleSend}
78-
isPending={isPending}
128+
<ChatInput
129+
onSend={handleSend}
130+
isPending={isPending}
79131
/>
80132
</>
81133
);

0 commit comments

Comments
 (0)