Skip to content

Commit eafbb81

Browse files
Feat: Add follow up in chatbot (#154)
* Feat: Add follow up in chatbot * Add vars mapping
1 parent 5b8e6f0 commit eafbb81

File tree

3 files changed

+103
-4
lines changed

3 files changed

+103
-4
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
NEXT_PUBLIC_LANDING_POST_CHATS_WEBHOOK=
22
NEXT_PUBLIC_LANDING_GET_CHATS_WEBHOOK=
3+
NEXT_PUBLIC_LANDING_GET_FOLLOWUP_WEBHOOK=
34
NEXT_PUBLIC_INFERENCEAI_POST_CHATS_WEBHOOK=
45
NEXT_PUBLIC_INFERENCEAI_GET_CHATS_WEBHOOK=

.github/workflows/deploy.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ env:
1111
NEXT_PUBLIC_INFERENCEAI_GET_CHATS_WEBHOOK: ${{ vars.NEXT_PUBLIC_INFERENCEAI_GET_CHATS_WEBHOOK }}
1212
NEXT_PUBLIC_LANDING_POST_CHATS_WEBHOOK: ${{ vars.NEXT_PUBLIC_LANDING_POST_CHATS_WEBHOOK }}
1313
NEXT_PUBLIC_LANDING_GET_CHATS_WEBHOOK: ${{ vars.NEXT_PUBLIC_LANDING_GET_CHATS_WEBHOOK }}
14+
NEXT_PUBLIC_LANDING_GET_FOLLOWUP_WEBHOOK: ${{ vars.NEXT_PUBLIC_LANDING_GET_FOLLOWUP_WEBHOOK }}
1415

1516
jobs:
1617
deploy:
@@ -33,6 +34,7 @@ jobs:
3334
NEXT_PUBLIC_INFERENCEAI_GET_CHATS_WEBHOOK: ${{ vars.NEXT_PUBLIC_INFERENCEAI_GET_CHATS_WEBHOOK }}
3435
NEXT_PUBLIC_LANDING_POST_CHATS_WEBHOOK: ${{ vars.NEXT_PUBLIC_LANDING_POST_CHATS_WEBHOOK }}
3536
NEXT_PUBLIC_LANDING_GET_CHATS_WEBHOOK: ${{ vars.NEXT_PUBLIC_LANDING_GET_CHATS_WEBHOOK }}
37+
NEXT_PUBLIC_LANDING_GET_FOLLOWUP_WEBHOOK: ${{ vars.NEXT_PUBLIC_LANDING_GET_FOLLOWUP_WEBHOOK }}
3638

3739
- name: Create CNAME file
3840
run: echo "hyperjump.tech" > out/CNAME

app/[lang]/(hyperjump)/components/landing-ai-agent.tsx

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { sendGAEvent } from "@next/third-parties/google";
1212

1313
// Types
1414
type PrefillAIAgentEvent = CustomEvent<{ message: string }>;
15+
type ShowFollowUpMessagesEvent = CustomEvent<{ sessionId: string }>;
1516

1617
type TMessageResponse = {
1718
messages: TMessage[];
@@ -156,6 +157,27 @@ const fetchAskStream = async (
156157
}
157158
};
158159

160+
const fetchFollowUpMessages = async (sessionId: string): Promise<string[]> => {
161+
const url = new URL(
162+
`${String(process.env.NEXT_PUBLIC_LANDING_GET_FOLLOWUP_WEBHOOK)}`
163+
);
164+
url.searchParams.set("sessionId", sessionId);
165+
166+
const response = await fetch(url, {
167+
method: "GET",
168+
headers: {
169+
"Content-Type": "application/json"
170+
}
171+
});
172+
173+
if (!response.ok) {
174+
return [];
175+
}
176+
177+
const data = await response.json();
178+
return data.data;
179+
};
180+
159181
// Cookies
160182
function setCookie(cname: string, cvalue: string, exdays: number) {
161183
const d = new Date();
@@ -193,6 +215,7 @@ interface HyperBotToggleProps {
193215
export default function LandingAIAgent({ gaEvent }: HyperBotToggleProps) {
194216
const [sessionId, setSessionId] = useState<string | undefined>(undefined);
195217
const [messages, setMessages] = useState<TMessage[]>([]);
218+
const [followUpMessages, setFollowUpMessages] = useState<string[]>([]);
196219
const [text, setText] = useState<string>("");
197220
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
198221

@@ -259,6 +282,17 @@ export default function LandingAIAgent({ gaEvent }: HyperBotToggleProps) {
259282
scrollToBottom();
260283
}, [messages]);
261284

285+
// Effect to scroll to bottom when follow-up messages are loaded
286+
useEffect(() => {
287+
if (!chatContainerRef.current) return;
288+
if (followUpMessages.length === 0) return;
289+
290+
// Use requestAnimationFrame to ensure DOM has updated
291+
requestAnimationFrame(() => {
292+
scrollToBottom();
293+
});
294+
}, [followUpMessages]);
295+
262296
// Effect to lock body scroll on mobile when chat is open
263297
useEffect(() => {
264298
// Only apply on mobile (< 720px)
@@ -291,6 +325,10 @@ export default function LandingAIAgent({ gaEvent }: HyperBotToggleProps) {
291325
inputRef.current.value = "";
292326
}
293327

328+
// Set follow up messages to be empty
329+
// as it will be populated again when the AI agent responds
330+
setFollowUpMessages([]);
331+
294332
setIsSubmitting(true);
295333
try {
296334
const prevMessages = messages;
@@ -332,6 +370,9 @@ export default function LandingAIAgent({ gaEvent }: HyperBotToggleProps) {
332370
toast("Failed to ask question. Please try again later.");
333371
} finally {
334372
setIsSubmitting(false);
373+
window.dispatchEvent(
374+
new CustomEvent("showFollowUpMessages", { detail: { sessionId } })
375+
);
335376
}
336377
},
337378
[messages, sessionId]
@@ -358,6 +399,33 @@ export default function LandingAIAgent({ gaEvent }: HyperBotToggleProps) {
358399
};
359400
}, [handleSubmit]);
360401

402+
// Listen for custom event to show follow up messages
403+
useEffect(() => {
404+
const handleShowFollowUpMessages = (event: Event) => {
405+
const customEvent = event as ShowFollowUpMessagesEvent;
406+
const { sessionId } = customEvent.detail;
407+
408+
if (sessionId) {
409+
fetchFollowUpMessages(sessionId)
410+
.then((followUpMessages) => {
411+
setFollowUpMessages(followUpMessages);
412+
})
413+
.catch(() => {
414+
setFollowUpMessages([]);
415+
});
416+
}
417+
};
418+
419+
window.addEventListener("showFollowUpMessages", handleShowFollowUpMessages);
420+
421+
return () => {
422+
window.removeEventListener(
423+
"showFollowUpMessages",
424+
handleShowFollowUpMessages
425+
);
426+
};
427+
}, [sessionId]);
428+
361429
return (
362430
<>
363431
{/* Chat window with animation */}
@@ -408,6 +476,28 @@ export default function LandingAIAgent({ gaEvent }: HyperBotToggleProps) {
408476
)}
409477
</div>
410478
))}
479+
480+
{/* Follow up messages */}
481+
{followUpMessages.length > 0 && !isSubmitting && (
482+
<div className="flex flex-col gap-2">
483+
<p className="text-sm font-medium text-gray-800">Follow up</p>
484+
{followUpMessages.map((text, id) => (
485+
<button
486+
key={id}
487+
type="button"
488+
className="cursor-pointer text-left text-sm whitespace-normal text-gray-600 hover:underline"
489+
onClick={() => {
490+
window.dispatchEvent(
491+
new CustomEvent("prefillAIAgent", {
492+
detail: { message: text }
493+
})
494+
);
495+
}}>
496+
{text}
497+
</button>
498+
))}
499+
</div>
500+
)}
411501
</div>
412502

413503
{/* Input */}
@@ -421,8 +511,11 @@ export default function LandingAIAgent({ gaEvent }: HyperBotToggleProps) {
421511
disabled={isSubmitting}
422512
className="rounded-md border border-gray-400 bg-transparent text-gray-600 hover:cursor-pointer"
423513
onClick={() => {
424-
setText(text);
425-
inputRef.current?.focus();
514+
window.dispatchEvent(
515+
new CustomEvent("prefillAIAgent", {
516+
detail: { message: text }
517+
})
518+
);
426519
}}>
427520
{text}
428521
</Button>
@@ -473,8 +566,11 @@ export default function LandingAIAgent({ gaEvent }: HyperBotToggleProps) {
473566
disabled={isSubmitting}
474567
className="rounded-md border border-gray-200 bg-transparent text-gray-600 hover:cursor-pointer"
475568
onClick={() => {
476-
setText(text);
477-
inputRef.current?.focus();
569+
window.dispatchEvent(
570+
new CustomEvent("prefillAIAgent", {
571+
detail: { message: text }
572+
})
573+
);
478574
}}>
479575
{text}
480576
</Button>

0 commit comments

Comments
 (0)