fix: add suggestion for roadmap

feat/ai-autocomplete
Arik Chakma 8 months ago
parent 6c65e023a5
commit 10700a5e9d
  1. 21
      src/components/GenerateRoadmap/AITermSuggestionInput.tsx
  2. 22
      src/components/GenerateRoadmap/GenerateRoadmap.tsx

@ -10,6 +10,7 @@ import { useOutsideClick } from '../../hooks/use-outside-click';
import { useDebounceValue } from '../../hooks/use-debounce'; import { useDebounceValue } from '../../hooks/use-debounce';
import { httpGet } from '../../lib/http'; import { httpGet } from '../../lib/http';
import { useToast } from '../../hooks/use-toast'; import { useToast } from '../../hooks/use-toast';
import { Loader2 } from 'lucide-react';
type GetTopAIRoadmapTermResponse = { type GetTopAIRoadmapTermResponse = {
_id: string; _id: string;
@ -24,7 +25,10 @@ type AITermSuggestionInputProps = {
inputClassName?: string; inputClassName?: string;
wrapperClassName?: string; wrapperClassName?: string;
placeholder?: string; placeholder?: string;
} & Omit<InputHTMLAttributes<HTMLInputElement>, 'onSelect' | 'onChange'>; } & Omit<
InputHTMLAttributes<HTMLInputElement>,
'onSelect' | 'onChange' | 'className'
>;
export function AITermSuggestionInput(props: AITermSuggestionInputProps) { export function AITermSuggestionInput(props: AITermSuggestionInputProps) {
const { const {
@ -47,6 +51,7 @@ export function AITermSuggestionInput(props: AITermSuggestionInputProps) {
const dropdownRef = useRef<HTMLDivElement>(null); const dropdownRef = useRef<HTMLDivElement>(null);
const [isActive, setIsActive] = useState(false); const [isActive, setIsActive] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [searchResults, setSearchResults] = const [searchResults, setSearchResults] =
useState<GetTopAIRoadmapTermResponse>([]); useState<GetTopAIRoadmapTermResponse>([]);
const [searchedText, setSearchedText] = useState(defaultValue); const [searchedText, setSearchedText] = useState(defaultValue);
@ -87,9 +92,11 @@ export function AITermSuggestionInput(props: AITermSuggestionInputProps) {
} }
setIsActive(true); setIsActive(true);
setIsLoading(true);
loadTopAIRoadmapTerm().then((results) => { loadTopAIRoadmapTerm().then((results) => {
setSearchResults(results?.slice(0, 5) || []); setSearchResults(results?.slice(0, 5) || []);
setActiveCounter(0); setActiveCounter(0);
setIsLoading(false);
}); });
}, [debouncedSearchValue]); }, [debouncedSearchValue]);
@ -103,9 +110,9 @@ export function AITermSuggestionInput(props: AITermSuggestionInputProps) {
{...inputProps} {...inputProps}
ref={searchInputRef} ref={searchInputRef}
type="text" type="text"
value={searchedText} value={defaultValue}
className={cn( className={cn(
'w-full rounded-md border border-gray-400 px-3 py-2.5 transition-colors focus:border-black focus:outline-none', 'w-full rounded-md border border-gray-400 px-3 py-2.5 pr-8 transition-colors focus:border-black focus:outline-none',
inputClassName, inputClassName,
)} )}
placeholder={placeholder} placeholder={placeholder}
@ -146,7 +153,13 @@ export function AITermSuggestionInput(props: AITermSuggestionInputProps) {
}} }}
/> />
{isActive && searchResults.length > 0 && ( {isLoading && (
<div className="absolute right-2 top-0 flex h-full items-center">
<Loader2 className="h-5 w-5 animate-spin stroke-[2.5]" />
</div>
)}
{isActive && searchResults.length > 0 && searchedText.length > 0 && (
<div <div
className="absolute top-full z-50 mt-1 w-full rounded-md border bg-white px-2 py-2 shadow" className="absolute top-full z-50 mt-1 w-full rounded-md border bg-white px-2 py-2 shadow"
ref={dropdownRef} ref={dropdownRef}

@ -36,6 +36,7 @@ import { RoadmapTopicDetail } from './RoadmapTopicDetail.tsx';
import { AIRoadmapAlert } from './AIRoadmapAlert.tsx'; import { AIRoadmapAlert } from './AIRoadmapAlert.tsx';
import { OpenAISettings } from './OpenAISettings.tsx'; import { OpenAISettings } from './OpenAISettings.tsx';
import { IS_KEY_ONLY_ROADMAP_GENERATION } from '../../lib/ai.ts'; import { IS_KEY_ONLY_ROADMAP_GENERATION } from '../../lib/ai.ts';
import { AITermSuggestionInput } from './AITermSuggestionInput.tsx';
export type GetAIRoadmapLimitResponse = { export type GetAIRoadmapLimitResponse = {
used: number; used: number;
@ -197,7 +198,7 @@ export function GenerateRoadmap() {
return; return;
} }
if (roadmapTerm === currentRoadmap?.topic) { if (roadmapTerm === currentRoadmap?.term) {
return; return;
} }
@ -293,7 +294,8 @@ export function GenerateRoadmap() {
pageProgressMessage.set('Loading Roadmap'); pageProgressMessage.set('Loading Roadmap');
const { response, error } = await httpGet<{ const { response, error } = await httpGet<{
topic: string; term: string;
title: string;
data: string; data: string;
}>(`${import.meta.env.PUBLIC_API_URL}/v1-get-ai-roadmap/${roadmapId}`); }>(`${import.meta.env.PUBLIC_API_URL}/v1-get-ai-roadmap/${roadmapId}`);
@ -516,15 +518,15 @@ export function GenerateRoadmap() {
onSubmit={handleSubmit} onSubmit={handleSubmit}
className="my-3 flex w-full flex-col gap-2 sm:flex-row sm:items-center sm:justify-center" className="my-3 flex w-full flex-col gap-2 sm:flex-row sm:items-center sm:justify-center"
> >
<input <AITermSuggestionInput
type="text" autoFocus={true}
autoFocus
placeholder="e.g. Try searching for Ansible or DevOps"
className="flex-grow rounded-md border border-gray-400 px-3 py-2 transition-colors focus:border-black focus:outline-none"
value={roadmapTerm} value={roadmapTerm}
onInput={(e) => onValueChange={(value) => setRoadmapTerm(value)}
setRoadmapTerm((e.target as HTMLInputElement).value) placeholder="e.g. Try searching for Ansible or DevOps"
} wrapperClassName="grow"
onSelect={(id) => {
setUrlParams({ id });
}}
/> />
<button <button
type={'submit'} type={'submit'}

Loading…
Cancel
Save