ui: fetch suggestions for kai prompts

* update display logic

* don't copy array when unnecessary

* only update prompt ideas when array size changes

---------

Co-authored-by: Jonathan Griffin <jonathangriffin@Jonathans-MacBook-Air.local>
This commit is contained in:
jonathan-caleb-griffin 2025-05-26 17:58:08 +02:00 committed by GitHub
parent 7fc744c273
commit 59c10cdbea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 45 additions and 7 deletions

View file

@ -119,7 +119,7 @@ function KaiChat() {
}}
>
{section === 'intro' ? (
<IntroSection onAsk={onCreate} />
<IntroSection onAsk={onCreate} projectId={activeSiteId} />
) : (
<ChatLog
threadId={threadId}

View file

@ -123,4 +123,15 @@ export default class KaiService extends AiService {
const data = await r.json();
return data;
};
getPromptSuggestions = async (
projectId: string,
): Promise<string[]> => {
const r = await this.client.get(`/kai/${projectId}/prompt-suggestions`);
if (!r.ok) {
throw new Error('Failed to fetch prompt suggestions');
}
const data = await r.json();
return data;
};
}

View file

@ -1,16 +1,43 @@
import React from 'react';
import { Lightbulb, MoveRight } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';
import { kaiService } from 'App/services';
import { useTranslation } from 'react-i18next';
function Ideas({ onClick }: { onClick: (query: string) => void }) {
function Ideas({ onClick, projectId }: { onClick: (query: string) => void, projectId: string }) {
const { t } = useTranslation();
const {
data: suggestedPromptIdeas = [],
isPending,
} = useQuery({
queryKey: ['kai', 'prompt-suggestions', projectId],
queryFn: () => kaiService.getPromptSuggestions(projectId),
staleTime: 1000 * 60,
});
const ideas = React.useMemo(() => {
const defaultPromptIdeas = [
'Top user journeys',
'Where do users drop off',
'Failed network requests today',
];
const result = suggestedPromptIdeas;
const targetSize = 3;
while (result.length < targetSize && defaultPromptIdeas.length) {
result.push(defaultPromptIdeas.pop())
}
return result;
}, [suggestedPromptIdeas.length]);
return (
<>
<div className={'flex items-center gap-2 mb-1 text-gray-dark'}>
<Lightbulb size={16} />
<b>Ideas:</b>
</div>
<IdeaItem onClick={onClick} title={'Top user journeys'} />
<IdeaItem onClick={onClick} title={'Where do users drop off'} />
<IdeaItem onClick={onClick} title={'Failed network requests today'} />
{
isPending ?
(<div className="animate-pulse text-disabled-text">{t('Generating ideas')}...</div>) :
(<div>{ideas.map(title => (<IdeaItem key={title} onClick={onClick} title={title} />))}</div>)
}
</>
);
}

View file

@ -2,7 +2,7 @@ import React from 'react';
import ChatInput from './ChatInput';
import Ideas from './Ideas';
function IntroSection({ onAsk }: { onAsk: (query: string) => void }) {
function IntroSection({ onAsk, projectId }: { onAsk: (query: string) => void, projectId: string }) {
const isLoading = false;
return (
<>
@ -14,7 +14,7 @@ function IntroSection({ onAsk }: { onAsk: (query: string) => void }) {
{/*<GradientBorderInput placeholder={'Ask anything about your product and users...'} onButtonClick={() => null} />*/}
<ChatInput isLoading={isLoading} onSubmit={onAsk} />
<div className={'absolute top-full flex flex-col gap-2 mt-4'}>
<Ideas onClick={(query) => onAsk(query)} />
<Ideas onClick={(query) => onAsk(query)} projectId={projectId} />
</div>
</div>
<div className={'text-disabled-text absolute bottom-4'}>