mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-25 16:39:02 +02:00
wip
This commit is contained in:
@@ -4,7 +4,7 @@ import {
|
|||||||
SparklesIcon,
|
SparklesIcon,
|
||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useEffect, useId, useState, type FormEvent } from 'react';
|
import { useEffect, useId, useState } from 'react';
|
||||||
import { FormatItem } from './FormatItem';
|
import { FormatItem } from './FormatItem';
|
||||||
import { isLoggedIn } from '../../lib/jwt';
|
import { isLoggedIn } from '../../lib/jwt';
|
||||||
import { showLoginPopup } from '../../lib/popup';
|
import { showLoginPopup } from '../../lib/popup';
|
||||||
@@ -57,8 +57,7 @@ export function ContentGenerator() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
const handleSubmit = () => {
|
||||||
e.preventDefault();
|
|
||||||
if (!isLoggedIn()) {
|
if (!isLoggedIn()) {
|
||||||
showLoginPopup();
|
showLoginPopup();
|
||||||
return;
|
return;
|
||||||
@@ -113,7 +112,13 @@ export function ContentGenerator() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form className="mt-10 space-y-4" onSubmit={handleSubmit}>
|
<form
|
||||||
|
className="mt-10 space-y-4"
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleSubmit();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<label htmlFor={titleFieldId} className="inline-block text-gray-500">
|
<label htmlFor={titleFieldId} className="inline-block text-gray-500">
|
||||||
What can I help you learn?
|
What can I help you learn?
|
||||||
@@ -184,6 +189,9 @@ export function ContentGenerator() {
|
|||||||
format={selectedFormat}
|
format={selectedFormat}
|
||||||
questionAnswerChatMessages={questionAnswerChatMessages}
|
questionAnswerChatMessages={questionAnswerChatMessages}
|
||||||
setQuestionAnswerChatMessages={setQuestionAnswerChatMessages}
|
setQuestionAnswerChatMessages={setQuestionAnswerChatMessages}
|
||||||
|
onGenerateNow={() => {
|
||||||
|
handleSubmit();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { queryClient } from '../../stores/query-client';
|
import { queryClient } from '../../stores/query-client';
|
||||||
import { aiQuestionSuggestionsOptions } from '../../queries/user-ai-session';
|
import { aiQuestionSuggestionsOptions } from '../../queries/user-ai-session';
|
||||||
import type { AllowedFormat } from './ContentGenerator';
|
import type { AllowedFormat } from './ContentGenerator';
|
||||||
import { Loader2Icon, SendIcon } from 'lucide-react';
|
import { Loader2Icon, RotateCwIcon, SendIcon } from 'lucide-react';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { cn } from '../../lib/classname';
|
import { cn } from '../../lib/classname';
|
||||||
import { flushSync } from 'react-dom';
|
import { flushSync } from 'react-dom';
|
||||||
@@ -23,6 +23,7 @@ type QuestionAnswerChatProps = {
|
|||||||
setQuestionAnswerChatMessages: (
|
setQuestionAnswerChatMessages: (
|
||||||
messages: QuestionAnswerChatMessage[],
|
messages: QuestionAnswerChatMessage[],
|
||||||
) => void;
|
) => void;
|
||||||
|
onGenerateNow: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
||||||
@@ -31,6 +32,7 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
format,
|
format,
|
||||||
questionAnswerChatMessages,
|
questionAnswerChatMessages,
|
||||||
setQuestionAnswerChatMessages,
|
setQuestionAnswerChatMessages,
|
||||||
|
onGenerateNow,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [activeMessageIndex, setActiveMessageIndex] = useState(0);
|
const [activeMessageIndex, setActiveMessageIndex] = useState(0);
|
||||||
@@ -94,11 +96,18 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
|
|
||||||
const canGenerateNow =
|
const canGenerateNow =
|
||||||
// user can generate after answering 5 questions -> 5 * 2 messages (user and assistant)
|
// user can generate after answering 5 questions -> 5 * 2 messages (user and assistant)
|
||||||
!isLoadingAiQuestionSuggestions && questionAnswerChatMessages.length > 10;
|
!isLoadingAiQuestionSuggestions && questionAnswerChatMessages.length >= 10;
|
||||||
|
|
||||||
|
const canReset = questionAnswerChatMessages.length >= 2;
|
||||||
|
const handleReset = () => {
|
||||||
|
setQuestionAnswerChatMessages([]);
|
||||||
|
setActiveMessageIndex(0);
|
||||||
|
setStatus('answering');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="relative h-[300px] w-full overflow-hidden rounded-xl border border-gray-200 bg-white">
|
<div className="relative h-[420px] w-full overflow-hidden rounded-xl border border-gray-200 bg-white">
|
||||||
{isLoadingAiQuestionSuggestions && (
|
{isLoadingAiQuestionSuggestions && (
|
||||||
<div className="absolute inset-0 flex items-center justify-center bg-white">
|
<div className="absolute inset-0 flex items-center justify-center bg-white">
|
||||||
<div className="flex animate-pulse items-center gap-2 rounded-full border border-gray-200 bg-gray-50 p-2 px-4 text-sm">
|
<div className="flex animate-pulse items-center gap-2 rounded-full border border-gray-200 bg-gray-50 p-2 px-4 text-sm">
|
||||||
@@ -121,6 +130,19 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{!isLoadingAiQuestionSuggestions && status === 'answering' && (
|
{!isLoadingAiQuestionSuggestions && status === 'answering' && (
|
||||||
|
<>
|
||||||
|
{canReset && (
|
||||||
|
<div className="absolute top-2 left-2 z-10">
|
||||||
|
<button
|
||||||
|
className="flex cursor-pointer items-center gap-1.5 rounded-lg bg-gray-50 px-2 py-1 text-xs text-gray-500 hover:bg-gray-200 focus:outline-none"
|
||||||
|
onClick={handleReset}
|
||||||
|
>
|
||||||
|
<RotateCwIcon className="size-3" />
|
||||||
|
Reset and Restart Asking
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex h-full w-full flex-col bg-white">
|
<div className="flex h-full w-full flex-col bg-white">
|
||||||
<div
|
<div
|
||||||
ref={scrollAreaRef}
|
ref={scrollAreaRef}
|
||||||
@@ -147,9 +169,14 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
role="assistant"
|
role="assistant"
|
||||||
question={activeMessage?.question ?? ''}
|
question={activeMessage?.question ?? ''}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-2">
|
||||||
|
<div className="rounded-lg border border-gray-200 bg-white">
|
||||||
{activeMessage && (
|
{activeMessage && (
|
||||||
<div>
|
<div className="border-b border-gray-200 p-2">
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
Pick an answer from these or write it below
|
Pick an answer from these or write it below
|
||||||
</p>
|
</p>
|
||||||
@@ -158,7 +185,7 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
key={answer}
|
key={answer}
|
||||||
className="cursor-pointer rounded-lg bg-gray-100 p-1 px-2 hover:bg-gray-200"
|
className="cursor-pointer rounded-lg bg-gray-100 p-1 px-2 hover:bg-gray-200 focus:outline-none"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleAnswerSelect(answer);
|
handleAnswerSelect(answer);
|
||||||
}}
|
}}
|
||||||
@@ -169,13 +196,9 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="p-2">
|
|
||||||
<div
|
<div
|
||||||
className="flex w-full items-center justify-between gap-2 rounded-lg border border-gray-200 bg-white p-2"
|
className="flex w-full items-center justify-between gap-2 p-2"
|
||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleAnswerSelect(message);
|
handleAnswerSelect(message);
|
||||||
@@ -198,13 +221,15 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex size-7 shrink-0 items-center justify-center rounded-md hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50"
|
className="flex size-7 shrink-0 items-center justify-center rounded-md hover:bg-gray-100 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<SendIcon className="size-4" />
|
<SendIcon className="size-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -212,7 +237,10 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
<div className="flex w-full items-center rounded-lg border border-gray-200 bg-white p-2">
|
<div className="flex w-full items-center rounded-lg border border-gray-200 bg-white p-2">
|
||||||
<p className="text-sm">
|
<p className="text-sm">
|
||||||
Keep answering for better output or{' '}
|
Keep answering for better output or{' '}
|
||||||
<button className="text-blue-500 underline underline-offset-2 hover:no-underline focus:outline-none">
|
<button
|
||||||
|
className="text-blue-500 underline underline-offset-2 hover:no-underline focus:outline-none"
|
||||||
|
onClick={onGenerateNow}
|
||||||
|
>
|
||||||
Generate now.
|
Generate now.
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
|
Reference in New Issue
Block a user