Skip to content

Commit 1113689

Browse files
committed
add suggested prompts
1 parent 5a29db7 commit 1113689

File tree

4 files changed

+91
-27
lines changed

4 files changed

+91
-27
lines changed

frontend/src/pages/Chat/components/InputField.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
1-
import { useState } from "react";
21
import useMessages, { type IMessage } from "../../../hooks/useMessages";
32

43
interface Props {
54
setMessages: React.Dispatch<React.SetStateAction<IMessage[]>>;
65
isLoading: boolean;
76
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
7+
value: string;
88
inputRef: React.RefObject<HTMLInputElement | null>;
9+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
910
}
1011

1112
export default function InputField({
1213
setMessages,
1314
isLoading,
1415
setIsLoading,
1516
inputRef,
17+
value,
18+
onChange,
1619
}: Props) {
17-
const [text, setText] = useState("");
1820
const { addMessage } = useMessages();
1921

2022
const handleSend = async () => {
21-
if (!text.trim()) return;
23+
if (!value.trim()) return;
2224

23-
const userMessage = text;
25+
const userMessage = value;
2426
const userMessageId = Date.now().toString();
2527
const botMessageId = (Date.now() + 1).toString();
2628

27-
setText("");
29+
// Clear the input in parent
30+
onChange({ target: { value: "" } } as React.ChangeEvent<HTMLInputElement>);
2831
setIsLoading(true);
2932

3033
// Add user message
@@ -44,7 +47,7 @@ export default function InputField({
4447
]);
4548

4649
try {
47-
const reader = await addMessage(text);
50+
const reader = await addMessage(userMessage);
4851
if (!reader) return;
4952
const decoder = new TextDecoder();
5053
let fullText = "";
@@ -70,9 +73,9 @@ export default function InputField({
7073
prev.map((msg) =>
7174
msg.messageId === botMessageId
7275
? {
73-
...msg,
74-
content: "Sorry, I encountered an error. Please try again.",
75-
}
76+
...msg,
77+
content: "Sorry, I encountered an error. Please try again.",
78+
}
7679
: msg,
7780
),
7881
);
@@ -85,11 +88,11 @@ export default function InputField({
8588
<div className="flex gap-2 mt-4 h-11 items-stretch mx-auto max-w-[700px]">
8689
<input
8790
type="text"
88-
value={text}
89-
onChange={(e) => setText(e.target.value)}
91+
value={value}
92+
onChange={onChange}
9093
onKeyDown={(e) => {
9194
if (e.key === "Enter") {
92-
e.preventDefault(); // prevent form submission or newline
95+
e.preventDefault();
9396
handleSend();
9497
}
9598
}}
@@ -101,7 +104,7 @@ export default function InputField({
101104
<button
102105
className="px-6 bg-[#1F584F] hover:bg-[#4F8B82] text-white rounded-md cursor-pointer transition-color duration-300"
103106
onClick={handleSend}
104-
disabled={isLoading || !text.trim()}
107+
disabled={isLoading || !value.trim()}
105108
>
106109
{isLoading ? "..." : "Send"}
107110
</button>

frontend/src/pages/Chat/components/MessageWindow.tsx

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import MessageContent from "./MessageContent";
55
import useSession from "../../../hooks/useSession";
66
import ExportMessagesButton from "./ExportMessagesButton";
77
import CitySelectField from "./CitySelectField";
8+
import SuggestedPrompts from "./SuggestedPrompts";
89

910
interface Props {
1011
messages: IMessage[];
@@ -22,6 +23,7 @@ export default function MessageWindow({
2223
onStatuteClick,
2324
}: Props) {
2425
const [isLoading, setIsLoading] = useState(false);
26+
const [inputValue, setInputValue] = useState("");
2527
const { handleNewSession } = useSession();
2628
const inputRef = useRef<HTMLInputElement | null>(null);
2729
const messagesRef = useRef<HTMLDivElement | null>(null);
@@ -44,6 +46,14 @@ export default function MessageWindow({
4446
}
4547
}, [messages]);
4648

49+
const handlePromptClick = (prompt: string) => {
50+
setInputValue(prompt);
51+
if (inputRef.current) {
52+
inputRef.current.value = prompt;
53+
inputRef.current.focus();
54+
}
55+
};
56+
4757
return (
4858
<>
4959
<div className="flex-1">
@@ -53,28 +63,25 @@ export default function MessageWindow({
5363
</div>
5464
) : (
5565
<div
56-
className={`max-h-[calc(100dvh-240px)] sm:max-h-[calc(100dvh-20rem)] mx-auto max-w-[700px] ${
57-
isOngoing ? "overflow-y-scroll" : "overflow-y-none"
58-
}`}
66+
className={`max-h-[calc(100dvh-240px)] sm:max-h-[calc(100dvh-20rem)] mx-auto max-w-[700px] ${isOngoing ? "overflow-y-scroll" : "overflow-y-none"
67+
}`}
5968
ref={messagesRef}
6069
>
6170
{isOngoing ? (
6271
<div className="flex flex-col gap-4">
6372
{messages.map((message) => (
6473
<div
65-
className={`flex w-full ${
66-
message.role === "assistant"
67-
? "justify-start"
68-
: "justify-end"
69-
}`}
74+
className={`flex w-full ${message.role === "assistant"
75+
? "justify-start"
76+
: "justify-end"
77+
}`}
7078
key={message.messageId}
7179
>
7280
<div
73-
className={`message-bubble p-3 rounded-2xl max-w-[95%] ${
74-
message.role === "assistant"
75-
? "bg-slate-200 rounded-tl-sm"
76-
: "bg-[#1F584F] text-white rounded-tr-sm"
77-
}`}
81+
className={`message-bubble p-3 rounded-2xl max-w-[95%] ${message.role === "assistant"
82+
? "bg-slate-200 rounded-tl-sm"
83+
: "bg-[#1F584F] text-white rounded-tr-sm"
84+
}`}
7885
>
7986
<MessageContent
8087
message={message}
@@ -92,11 +99,16 @@ export default function MessageWindow({
9299
<div>
93100
{messages.length > 0 ? (
94101
<>
102+
{messages.length === 1 && inputValue === "" && (
103+
<SuggestedPrompts onPromptClick={handlePromptClick} />
104+
)}
95105
<InputField
96106
setMessages={setMessages}
97107
isLoading={isLoading}
98108
setIsLoading={setIsLoading}
99109
inputRef={inputRef}
110+
value={inputValue}
111+
onChange={e => setInputValue(e.target.value)}
100112
/>
101113
<div className="flex justify-center gap-4 mt-4">
102114
<button
@@ -112,7 +124,9 @@ export default function MessageWindow({
112124
</div>
113125
</>
114126
) : (
115-
<CitySelectField setMessages={setMessages} />
127+
<>
128+
<CitySelectField setMessages={setMessages} />
129+
</>
116130
)}
117131
</div>
118132
</>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
interface SuggestedPromptsProps {
2+
onPromptClick: (prompt: string) => void;
3+
}
4+
5+
const prompts = [
6+
"Do I qualify for Section 8?",
7+
"I have a leak in my roof. Help me address this with my landlord.",
8+
"I received an eviction notice for non-payment of rent. What should I do?",
9+
"I received a 'no-cause' eviction notice. How much money is my landlord required to pay me to move out?",
10+
];
11+
12+
export default function SuggestedPrompts({ onPromptClick }: SuggestedPromptsProps) {
13+
return (
14+
<div className="xl:w-1/3 items-center m-auto ">
15+
<div className=" flex flex-col gap-4 fade-in-up items-center ">
16+
{prompts.map((prompt, idx) => (
17+
<button
18+
key={idx}
19+
className="inline-flex px-3 border border-[#1f584f] rounded-4xl cursor-pointer py-1 font-medium sm:bg-white hover:bg-[#bac9b2] "
20+
onClick={() =>
21+
onPromptClick(
22+
Array.isArray(prompt) ? prompt.join(" ") : prompt
23+
)
24+
}
25+
type="button"
26+
>
27+
{prompt}
28+
</button>
29+
))}
30+
</div>
31+
</div>
32+
);
33+
}

frontend/src/style.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,17 @@ body {
3939
.message-bubble a {
4040
@apply inline text-blue-600 underline cursor-pointer transition-colors hover:bg-blue-200 rounded;
4141
}
42+
43+
@keyframes fadeInUp {
44+
from {
45+
opacity: 0;
46+
transform: translateY(16px);
47+
}
48+
to {
49+
opacity: 1;
50+
transform: translateY(0);
51+
}
52+
}
53+
.fade-in-up {
54+
animation: fadeInUp 0.5s cubic-bezier(0.22, 1, 0.36, 1);
55+
}

0 commit comments

Comments
 (0)