Update the home page suggestion cards

- Rather than chunky generic cards, make the suggested actions more action oriented, around the problem a user might want to solve. Give them follow-up options. Design still in progress.
This commit is contained in:
sabaimran
2024-12-21 18:57:19 -08:00
parent 2c7c16d93e
commit 95826393e1
6 changed files with 387 additions and 845 deletions

View File

@@ -8,14 +8,25 @@ import useSWR from "swr";
import { ArrowCounterClockwise } from "@phosphor-icons/react";
import { Card, CardTitle } from "@/components/ui/card";
import SuggestionCard from "@/app/components/suggestions/suggestionCard";
import {
StepOneSuggestionCard,
StepOneSuggestionRevertCard,
StepTwoSuggestionCard,
} from "@/app/components/suggestions/suggestionCard";
import Loading from "@/app/components/loading/loading";
import {
AttachedFileText,
ChatInputArea,
ChatInputFocus,
ChatOptions,
} from "@/app/components/chatInputArea/chatInputArea";
import { Suggestion, suggestionsData } from "@/app/components/suggestions/suggestionsData";
import {
StepOneSuggestion,
stepOneSuggestions,
StepTwoSuggestion,
getStepTwoSuggestions,
SuggestionType,
} from "@/app/components/suggestions/suggestionsData";
import LoginPrompt from "@/app/components/loginPrompt/loginPrompt";
import {
@@ -38,6 +49,7 @@ import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/s
import { AppSidebar } from "./components/appSidebar/appSidebar";
import { Separator } from "@/components/ui/separator";
import { KhojLogoType } from "./components/logo/khojLogo";
import { Button } from "@/components/ui/button";
interface ChatBodyDataProps {
chatOptionsData: ChatOptions | null;
@@ -59,10 +71,19 @@ function FisherYatesShuffle(array: any[]) {
function ChatBodyData(props: ChatBodyDataProps) {
const [message, setMessage] = useState("");
const [prefilledMessage, setPrefilledMessage] = useState("");
const [chatInputFocus, setChatInputFocus] = useState<ChatInputFocus>(ChatInputFocus.MESSAGE);
const [images, setImages] = useState<string[]>([]);
const [processingMessage, setProcessingMessage] = useState(false);
const [greeting, setGreeting] = useState("");
const [shuffledOptions, setShuffledOptions] = useState<Suggestion[]>([]);
const [stepOneSuggestionOptions, setStepOneSuggestionOptions] = useState<StepOneSuggestion[]>(
[],
);
const [stepTwoSuggestionOptions, setStepTwoSuggestionOptions] = useState<StepTwoSuggestion[]>(
[],
);
const [selectedStepOneSuggestion, setSelectedStepOneSuggestion] =
useState<StepOneSuggestion | null>(null);
const [hoveredAgent, setHoveredAgent] = useState<string | null>(null);
const debouncedHoveredAgent = useDebounce(hoveredAgent, 500);
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
@@ -103,8 +124,8 @@ function ChatBodyData(props: ChatBodyDataProps) {
};
function shuffleAndSetOptions() {
const shuffled = FisherYatesShuffle(suggestionsData);
setShuffledOptions(shuffled.slice(0, 3));
const shuffled = FisherYatesShuffle(stepOneSuggestions);
setStepOneSuggestionOptions(shuffled.slice(0, 3));
}
useEffect(() => {
@@ -148,8 +169,8 @@ function ChatBodyData(props: ChatBodyDataProps) {
setAgentIcons(agentIcons);
}, [agentsData, props.isMobileWidth]);
function shuffleSuggestionsCards() {
shuffleAndSetOptions();
function showAllSuggestionsCards() {
setStepOneSuggestionOptions(stepOneSuggestions);
}
useEffect(() => {
@@ -193,27 +214,22 @@ function ChatBodyData(props: ChatBodyDataProps) {
return () => scrollAreaEl?.removeEventListener("scroll", handleScroll);
}, []);
function fillArea(link: string, type: string, prompt: string) {
if (!link) {
let message_str = "";
prompt = prompt.charAt(0).toLowerCase() + prompt.slice(1);
function clickStepOneSuggestion(suggestion: StepOneSuggestion) {
let message_str = "";
let prompt =
suggestion.description.charAt(0).toLowerCase() + suggestion.description.slice(1);
if (type === "Online Search") {
message_str = "/online " + prompt;
} else if (type === "Paint") {
message_str = "/image " + prompt;
} else {
message_str = prompt;
}
// Get the textarea element
const message_area = document.getElementById("message") as HTMLTextAreaElement;
if (message_area) {
// Update the value directly
message_area.value = message_str;
setMessage(message_str);
}
if (suggestion.type === "Paint") {
message_str = "/image " + prompt;
} else {
message_str = prompt;
}
setPrefilledMessage(message_str);
const stepTwoSuggestions = getStepTwoSuggestions(suggestion.type);
setSelectedStepOneSuggestion(suggestion);
setStepTwoSuggestionOptions(stepTwoSuggestions);
setChatInputFocus(ChatInputFocus.FILE);
}
return (
@@ -306,13 +322,15 @@ function ChatBodyData(props: ChatBodyDataProps) {
</ScrollArea>
)}
</div>
<div className={`mx-auto ${props.isMobileWidth ? "w-full" : "w-fit max-w-screen-md"}`}>
<div className={`mx-auto ${props.isMobileWidth ? "w-full" : "w-full max-w-screen-md"}`}>
{!props.isMobileWidth && (
<div
className={`w-full ${styles.inputBox} shadow-lg bg-background align-middle items-center justify-center px-3 py-1 dark:bg-neutral-700 border-stone-100 dark:border-none dark:shadow-none rounded-2xl`}
>
<ChatInputArea
isLoggedIn={props.isLoggedIn}
prefillMessage={prefilledMessage}
focus={chatInputFocus}
sendMessage={(message) => setMessage(message)}
sendImage={(image) => setImages((prevImages) => [...prevImages, image])}
sendDisabled={processingMessage}
@@ -327,43 +345,74 @@ function ChatBodyData(props: ChatBodyDataProps) {
</div>
)}
<div
className={`${styles.suggestions} w-full ${props.isMobileWidth ? "grid" : "flex flex-row"} justify-center items-center`}
className={`${styles.suggestions} w-full ${props.isMobileWidth ? "grid" : "grid grid-cols-3"} justify-center items-center`}
>
{shuffledOptions.map((suggestion, index) => (
<div
key={`${suggestion.type} ${suggestion.description}`}
onClick={(event) => {
if (props.isLoggedIn) {
fillArea(
suggestion.link,
suggestion.type,
suggestion.description,
);
} else {
event.preventDefault();
event.stopPropagation();
setShowLoginPrompt(true);
}
}}
>
<SuggestionCard
key={suggestion.type + Math.random()}
title={suggestion.type}
body={suggestion.description}
link={suggestion.link}
color={suggestion.color}
/>
{stepTwoSuggestionOptions.length == 0 &&
stepOneSuggestionOptions.map((suggestion, index) => (
<div
key={`${suggestion.type} ${suggestion.description}`}
onClick={(event) => {
if (props.isLoggedIn) {
clickStepOneSuggestion(suggestion);
} else {
event.preventDefault();
event.stopPropagation();
setShowLoginPrompt(true);
}
}}
>
<StepOneSuggestionCard
key={suggestion.type + Math.random()}
title={suggestion.type}
body={suggestion.description}
color={suggestion.color}
/>
</div>
))}
</div>
{stepTwoSuggestionOptions.length == 0 &&
stepOneSuggestionOptions.length < stepOneSuggestions.length && (
<div className="flex items-center justify-center margin-auto">
<button
onClick={showAllSuggestionsCards}
className="m-2 p-1.5 rounded-lg dark:hover:bg-[var(--background-color)] hover:bg-stone-100 border border-stone-100 text-sm text-stone-500 dark:text-stone-300 dark:border-neutral-700"
>
More Actions <ArrowCounterClockwise className="h-4 w-4 inline" />
</button>
</div>
))}
</div>
<div className="flex items-center justify-center margin-auto">
<button
onClick={shuffleSuggestionsCards}
className="m-2 p-1.5 rounded-lg dark:hover:bg-[var(--background-color)] hover:bg-stone-100 border border-stone-100 text-sm text-stone-500 dark:text-stone-300 dark:border-neutral-700"
)}
{selectedStepOneSuggestion && (
<StepOneSuggestionRevertCard
title={selectedStepOneSuggestion.type}
body={selectedStepOneSuggestion.description}
color={selectedStepOneSuggestion.color}
onClick={() => {
setSelectedStepOneSuggestion(null);
setStepTwoSuggestionOptions([]);
setChatInputFocus(ChatInputFocus.MESSAGE);
}}
/>
)}
{stepTwoSuggestionOptions.length > 0 && (
<div
className={`w-full ${props.isMobileWidth ? "grid" : "grid grid-cols-1"} justify-center items-center gap-2 pt-2`}
>
More Ideas <ArrowCounterClockwise className="h-4 w-4 inline" />
</button>
</div>
{stepTwoSuggestionOptions.map((suggestion, index) => (
<div
key={`${suggestion.prompt} ${index}`}
className={`w-full cursor-pointer animate-fade-in-up`}
onClick={(event) => {
setMessage(suggestion.prompt);
}}
>
<StepTwoSuggestionCard
key={suggestion.prompt}
prompt={suggestion.prompt}
/>
</div>
))}
</div>
)}
</div>
{props.isMobileWidth && (
<>