From e6f171e2bdbea83952d25b344e06086852e40222 Mon Sep 17 00:00:00 2001 From: Santthosh Selvadurai Date: Wed, 26 Jun 2024 21:49:35 -0700 Subject: [PATCH] #93 Adding placeholders for conversation starters --- .../assistants/[id]/chat/useChatContext.ts | 36 +++--- .../[id]/customize/DebounceInput.tsx | 2 +- .../customize/DebounceInputWithActions.tsx | 62 ++++++++++ .../customize/EditConversationStarters.tsx | 111 ++++++++++++++++++ src/app/assistants/[id]/customize/page.tsx | 6 + src/app/types/assistant.ts | 1 + src/app/utils/assistant.ts | 8 ++ 7 files changed, 211 insertions(+), 15 deletions(-) create mode 100644 src/app/assistants/[id]/customize/DebounceInputWithActions.tsx create mode 100644 src/app/assistants/[id]/customize/EditConversationStarters.tsx diff --git a/src/app/assistants/[id]/chat/useChatContext.ts b/src/app/assistants/[id]/chat/useChatContext.ts index 12c2fcc..fc1ef4c 100644 --- a/src/app/assistants/[id]/chat/useChatContext.ts +++ b/src/app/assistants/[id]/chat/useChatContext.ts @@ -31,21 +31,29 @@ export const useChatContext = () => { useEffect(() => { if (reset) { - setMessages([ - { - created_at: Date.now() / 1000, - role: 'assistant', - content: [ - { - type: 'text', - text: { - value: getInitialPrompt(assistant), - annotations: [], + let initialPrompt = getInitialPrompt(assistant); + let initialMessages:any = []; + + // Hide the initial prompt if there is none set + if (initialPrompt && initialPrompt.trim()) { + initialMessages = [ + { + created_at: Date.now() / 1000, + role: 'assistant', + content: [ + { + type: 'text', + text: { + value: getInitialPrompt(assistant), + annotations: [], + }, }, - }, - ], - }, - ]); + ], + }, + ]; + } + + setMessages(initialMessages); setReset(false); } }, [assistant, reset]); diff --git a/src/app/assistants/[id]/customize/DebounceInput.tsx b/src/app/assistants/[id]/customize/DebounceInput.tsx index 41134c1..fc58446 100644 --- a/src/app/assistants/[id]/customize/DebounceInput.tsx +++ b/src/app/assistants/[id]/customize/DebounceInput.tsx @@ -31,7 +31,7 @@ export const DebouncedInput: React.FC = (
) => setText(e.target.value) diff --git a/src/app/assistants/[id]/customize/DebounceInputWithActions.tsx b/src/app/assistants/[id]/customize/DebounceInputWithActions.tsx new file mode 100644 index 0000000..b5c8cc3 --- /dev/null +++ b/src/app/assistants/[id]/customize/DebounceInputWithActions.tsx @@ -0,0 +1,62 @@ +import React, { useState, useEffect } from 'react'; +import { useDebounce } from '@/app/utils/useDebounce'; +import { Button, Spinner, TextInput } from 'flowbite-react'; +import { HiCheck, HiTrash } from 'react-icons/hi'; + +export interface DebouncedInputWithActionsProps { + value?: string; + placeholder?: string; + onDebounceTextChange?: (text: string) => void; + onDebounceTextDelete?: (text: string) => void; +} + +export const DebouncedInputWithActions: React.FC< + DebouncedInputWithActionsProps +> = (props: DebouncedInputWithActionsProps) => { + const [text, setText] = useState(props.value ? props.value : ''); + const [loading, setLoading] = useState(false); + const debouncedText = useDebounce(text, 300); // 300ms delay + + useEffect(() => { + // This function will run after debouncedText changes and the 300ms delay passes + if (debouncedText && debouncedText !== props.value) { + setLoading(true); + props.onDebounceTextChange + ? props.onDebounceTextChange(debouncedText) + : null; + setLoading(false); + } + }, [debouncedText]); // Only re-run if debouncedText changes + + return ( +
+ ) => + setText(e.target.value) + } + placeholder={props.placeholder ? props.placeholder : ''} + /> +
+ {loading ? ( + + ) : ( + + )} +
+ +
+ ); +}; + +export default DebouncedInputWithActions; diff --git a/src/app/assistants/[id]/customize/EditConversationStarters.tsx b/src/app/assistants/[id]/customize/EditConversationStarters.tsx new file mode 100644 index 0000000..3021e18 --- /dev/null +++ b/src/app/assistants/[id]/customize/EditConversationStarters.tsx @@ -0,0 +1,111 @@ +import { Button, Label, Modal, TextInput } from 'flowbite-react'; +import React, { useContext, useEffect, useState } from 'react'; +import DebouncedInput from '@/app/assistants/[id]/customize/DebounceInput'; +import { getInitialConversationStarter } from '@/app/utils/assistant'; +import AssistantContext from '@/app/assistants/[id]/AssistantContext'; +import { ulid } from 'ulidx'; +import { HiCheck, HiPlus, HiTrash } from 'react-icons/hi'; +import DebouncedInputWithActions from '@/app/assistants/[id]/customize/DebounceInputWithActions'; + +export interface ConversationStarter { + id: string; + prompt: string; +} + +export const EditConversationStarters: React.FC = () => { + const { assistant, setAssistant } = useContext(AssistantContext); + + const [conversationStarters, setConversationStarters] = useState< + ConversationStarter[] + >([]); + const [saveConversationStarter, setSaveConversationStarter] = + useState(null); + const [dirtyConversationStarter, setdirtyConversationStarter] = + useState(null); + const [selectedConversationStarter, setSelectedConversationStarter] = + useState(null); + const [deleteConversationStarter, setDeleteConversationStarter] = + useState(null); + + useEffect(() => { + console.log('changed'); + console.log(conversationStarters); + }, [conversationStarters]); + + useEffect(() => {}, [dirtyConversationStarter]); + + useEffect(() => {}, [saveConversationStarter]); + + useEffect(() => { + if (deleteConversationStarter) { + // TODO: Remove through API + const indexToRemove = conversationStarters.findIndex( + (key) => key.id === deleteConversationStarter.id + ); + if (indexToRemove !== -1) { + conversationStarters.splice(indexToRemove, 1); + } + setConversationStarters(conversationStarters); + setDeleteConversationStarter(null); + } + }, [deleteConversationStarter]); + + const handleAddConversationStarter = function () { + setConversationStarters([ + ...conversationStarters, + { id: ulid(), prompt: '' }, + ]); + }; + + const handleSaveConversationStarter = function () {}; + + const onConversationStarterChange = function (event: any) { + conversationStarters.forEach((iterationConversationStarter) => {}); + }; + + const handleDeleteConversationStarter = function (id: string) { + conversationStarters.forEach((iterationConversationStarter) => { + if (iterationConversationStarter.id === id) { + console.log(iterationConversationStarter); + setDeleteConversationStarter(iterationConversationStarter); + } + }); + }; + + return ( +
+
+
+
+ {conversationStarters && conversationStarters.length ? ( + conversationStarters.map((starter: any, index: number) => ( +
+
+ { + handleDeleteConversationStarter(starter.id); + }} + value={starter.prompt} + /> +
+
+ )) + ) : ( + <> + )} +
+ +
+
+
+ ); +}; diff --git a/src/app/assistants/[id]/customize/page.tsx b/src/app/assistants/[id]/customize/page.tsx index 2347c40..e9b3ed3 100644 --- a/src/app/assistants/[id]/customize/page.tsx +++ b/src/app/assistants/[id]/customize/page.tsx @@ -13,6 +13,7 @@ import { EditInitialPrompt } from '@/app/assistants/[id]/customize/EditInitialPr import { EditMessageLabel } from '@/app/assistants/[id]/customize/EditMessageLabel'; import ChatPopupFrame from '@/app/assistants/[id]/chat/ChatPopupFrame'; import ChatPage from '@/app/assistants/[id]/chat/ChatPage'; +import { EditConversationStarters } from '@/app/assistants/[id]/customize/EditConversationStarters'; export default function Customize() { const { assistant, setAssistant } = useContext(AssistantContext); @@ -43,6 +44,11 @@ export default function Customize() { + + + + + diff --git a/src/app/types/assistant.ts b/src/app/types/assistant.ts index eb7838b..64943ce 100644 --- a/src/app/types/assistant.ts +++ b/src/app/types/assistant.ts @@ -9,6 +9,7 @@ export interface AssistantTheme { secondaryTextColor?: string; initialPrompt?: string; messageLabel?: string; + conversationStarters?: string[]; } export interface Assistant { diff --git a/src/app/utils/assistant.ts b/src/app/utils/assistant.ts index a8407cd..97496ea 100644 --- a/src/app/utils/assistant.ts +++ b/src/app/utils/assistant.ts @@ -86,3 +86,11 @@ export function getInputMessageLabel(assistant: Assistant) { return 'Your message...'; } + +export function getInitialConversationStarter(assistant: Assistant) { + if (assistant.theme && assistant.theme.conversationStarters) { + return assistant.theme.conversationStarters; + } + + return []; +}