mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-08 16:20:40 +02:00
Changes to ai chat
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
"enabled": false
|
||||
},
|
||||
"_variables": {
|
||||
"lastUpdateCheck": 1747060270496
|
||||
"lastUpdateCheck": 1748277554631
|
||||
}
|
||||
}
|
1
.astro/types.d.ts
vendored
1
.astro/types.d.ts
vendored
@@ -1,2 +1 @@
|
||||
/// <reference types="astro/client" />
|
||||
/// <reference path="content.d.ts" />
|
@@ -56,7 +56,7 @@ export function ChatEditor(props: ChatEditorProps) {
|
||||
content,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: 'focus:outline-none w-full p-2',
|
||||
class: 'focus:outline-none w-full px-4 py-2 min-h-[40px]',
|
||||
},
|
||||
handleKeyDown(_, event) {
|
||||
if (!editor) {
|
||||
@@ -115,7 +115,7 @@ export function ChatEditor(props: ChatEditorProps) {
|
||||
}, [editor, roadmapTreeData, roadmapDetailsData]);
|
||||
|
||||
return (
|
||||
<div className="chat-editor w-full px-2 py-1.5">
|
||||
<div className="chat-editor w-full py-1.5">
|
||||
<EditorContent editor={editor} />
|
||||
</div>
|
||||
);
|
||||
|
45
src/components/RoadmapAIChat/AIChatActionButtons.tsx
Normal file
45
src/components/RoadmapAIChat/AIChatActionButtons.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { SettingsIcon, Trash2, type LucideIcon } from 'lucide-react';
|
||||
|
||||
type AIChatActionButtonProps = {
|
||||
icon: LucideIcon;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
function AIChatActionButton(props: AIChatActionButtonProps) {
|
||||
const { icon: Icon, label, onClick } = props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className="flex hover:bg-gray-100 items-center gap-1 rounded-md border border-gray-200 px-2 py-1.5 text-xs"
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon className="size-3" />
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
type AIChatActionButtonsProps = {
|
||||
onTellUsAboutYourSelf: () => void;
|
||||
onClearChat: () => void;
|
||||
};
|
||||
|
||||
export function AIChatActionButtons(props: AIChatActionButtonsProps) {
|
||||
const { onTellUsAboutYourSelf, onClearChat } = props;
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 px-4 pt-2">
|
||||
<AIChatActionButton
|
||||
icon={SettingsIcon}
|
||||
label="Tell us about your self"
|
||||
onClick={onTellUsAboutYourSelf}
|
||||
/>
|
||||
<AIChatActionButton
|
||||
icon={Trash2}
|
||||
label="Clear chat"
|
||||
onClick={onClearChat}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -45,6 +45,7 @@ import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
||||
import { billingDetailsOptions } from '../../queries/billing';
|
||||
import { TopicDetail } from '../TopicDetail/TopicDetail';
|
||||
import { slugify } from '../../lib/slugger';
|
||||
import { AIChatActionButtons } from './AIChatActionButtons';
|
||||
|
||||
export type RoamdapAIChatHistoryType = {
|
||||
role: AllowedAIChatRole;
|
||||
@@ -483,91 +484,102 @@ export function RoadmapAIChat(props: RoadmapAIChatProps) {
|
||||
</div>
|
||||
|
||||
{!isLoading && (
|
||||
<div className="relative flex items-start border-t border-gray-200 text-sm">
|
||||
<ChatEditor
|
||||
editorRef={editorRef}
|
||||
roadmapId={roadmapId}
|
||||
onSubmit={(content) => {
|
||||
if (
|
||||
isStreamingMessage ||
|
||||
abortControllerRef.current ||
|
||||
!isLoggedIn() ||
|
||||
isDataLoading ||
|
||||
isEmptyContent(content)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
<div className="flex flex-col border-t border-gray-200">
|
||||
{!isLimitExceeded && (
|
||||
<AIChatActionButtons
|
||||
onTellUsAboutYourSelf={() => {}}
|
||||
onClearChat={() => {
|
||||
setAiChatHistory([]);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
handleChatSubmit(content);
|
||||
}}
|
||||
/>
|
||||
<div className="relative flex items-start text-sm">
|
||||
<ChatEditor
|
||||
editorRef={editorRef}
|
||||
roadmapId={roadmapId}
|
||||
onSubmit={(content) => {
|
||||
if (
|
||||
isStreamingMessage ||
|
||||
abortControllerRef.current ||
|
||||
!isLoggedIn() ||
|
||||
isDataLoading ||
|
||||
isEmptyContent(content)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
{isLimitExceeded && isLoggedIn() && (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center gap-2 bg-black text-white">
|
||||
<LockIcon
|
||||
className="size-4 cursor-not-allowed"
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
<p className="cursor-not-allowed">
|
||||
Limit reached for today
|
||||
{isPaidUser ? '. Please wait until tomorrow.' : ''}
|
||||
</p>
|
||||
{!isPaidUser && (
|
||||
handleChatSubmit(content);
|
||||
}}
|
||||
/>
|
||||
|
||||
{isLimitExceeded && isLoggedIn() && (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center gap-2 bg-black text-white">
|
||||
<LockIcon
|
||||
className="size-4 cursor-not-allowed"
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
<p className="cursor-not-allowed">
|
||||
Limit reached for today
|
||||
{isPaidUser ? '. Please wait until tomorrow.' : ''}
|
||||
</p>
|
||||
{!isPaidUser && (
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowUpgradeModal(true);
|
||||
}}
|
||||
className="rounded-md bg-white px-2 py-1 text-xs font-medium text-black hover:bg-gray-300"
|
||||
>
|
||||
Upgrade for more
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoggedIn() && (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center gap-2 bg-black text-white">
|
||||
<LockIcon
|
||||
className="size-4 cursor-not-allowed"
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
<p className="cursor-not-allowed">
|
||||
Please login to continue
|
||||
</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowUpgradeModal(true);
|
||||
showLoginPopup();
|
||||
}}
|
||||
className="rounded-md bg-white px-2 py-1 text-xs font-medium text-black hover:bg-gray-300"
|
||||
>
|
||||
Upgrade for more
|
||||
Login / Register
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoggedIn() && (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center gap-2 bg-black text-white">
|
||||
<LockIcon
|
||||
className="size-4 cursor-not-allowed"
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
<p className="cursor-not-allowed">
|
||||
Please login to continue
|
||||
</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
showLoginPopup();
|
||||
}}
|
||||
className="rounded-md bg-white px-2 py-1 text-xs font-medium text-black hover:bg-gray-300"
|
||||
>
|
||||
Login / Register
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
className="flex aspect-square size-[36px] items-center justify-center p-2 text-zinc-500 hover:text-black disabled:cursor-not-allowed disabled:opacity-50"
|
||||
onClick={(e) => {
|
||||
if (isStreamingMessage || abortControllerRef.current) {
|
||||
handleAbort();
|
||||
return;
|
||||
}
|
||||
|
||||
const json = editorRef.current?.getJSON();
|
||||
if (!json || isEmptyContent(json)) {
|
||||
toast.error('Please enter a message');
|
||||
return;
|
||||
}
|
||||
|
||||
handleChatSubmit(json);
|
||||
}}
|
||||
>
|
||||
{isStreamingMessage ? (
|
||||
<PauseCircleIcon className="size-4 stroke-[2.5]" />
|
||||
) : (
|
||||
<SendIcon className="size-4 stroke-[2.5]" />
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="flex aspect-square size-[36px] items-center justify-center p-2 text-zinc-500 hover:text-black disabled:cursor-not-allowed disabled:opacity-50"
|
||||
onClick={(e) => {
|
||||
if (isStreamingMessage || abortControllerRef.current) {
|
||||
handleAbort();
|
||||
return;
|
||||
}
|
||||
|
||||
const json = editorRef.current?.getJSON();
|
||||
if (!json || isEmptyContent(json)) {
|
||||
toast.error('Please enter a message');
|
||||
return;
|
||||
}
|
||||
|
||||
handleChatSubmit(json);
|
||||
}}
|
||||
>
|
||||
{isStreamingMessage ? (
|
||||
<PauseCircleIcon className="size-4 stroke-[2.5]" />
|
||||
) : (
|
||||
<SendIcon className="size-4 stroke-[2.5]" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
@@ -3,10 +3,9 @@ import { getAiCourseLimitOptions } from '../../queries/ai-course';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { billingDetailsOptions } from '../../queries/billing';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { BookIcon, BotIcon, GiftIcon, Trash2Icon, XIcon } from 'lucide-react';
|
||||
import { BookIcon, BotIcon, GiftIcon, XIcon } from 'lucide-react';
|
||||
import type {
|
||||
RoadmapAIChatTab,
|
||||
RoamdapAIChatHistoryType,
|
||||
RoadmapAIChatTab
|
||||
} from './RoadmapAIChat';
|
||||
import { useState } from 'react';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
@@ -17,9 +16,6 @@ import { cn } from '../../lib/classname';
|
||||
type RoadmapAIChatHeaderProps = {
|
||||
isLoading: boolean;
|
||||
|
||||
hasChatHistory: boolean;
|
||||
setAiChatHistory: (history: RoamdapAIChatHistoryType[]) => void;
|
||||
|
||||
onLogin: () => void;
|
||||
onUpgrade: () => void;
|
||||
|
||||
@@ -70,8 +66,6 @@ function TabButton(props: TabButtonProps) {
|
||||
|
||||
export function RoadmapAIChatHeader(props: RoadmapAIChatHeaderProps) {
|
||||
const {
|
||||
hasChatHistory,
|
||||
setAiChatHistory,
|
||||
onLogin,
|
||||
onUpgrade,
|
||||
isLoading: isDataLoading,
|
||||
@@ -127,7 +121,7 @@ export function RoadmapAIChatHeader(props: RoadmapAIChatHeaderProps) {
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex h-[46px] flex-shrink-0 items-center justify-between border-b border-gray-200 text-sm">
|
||||
<div className="flex h-[46px] items-center justify-between border-b border-gray-200 text-sm">
|
||||
<div className="flex h-full items-center">
|
||||
<TabButton
|
||||
icon={<BotIcon className="size-4 shrink-0 text-black" />}
|
||||
@@ -149,24 +143,15 @@ export function RoadmapAIChatHeader(props: RoadmapAIChatHeaderProps) {
|
||||
|
||||
{!isDataLoading && (
|
||||
<div className="flex gap-1.5 pr-4">
|
||||
{hasChatHistory && (
|
||||
<button
|
||||
className="rounded-md bg-white px-2 py-2 text-xs font-medium text-black hover:bg-gray-200"
|
||||
onClick={() => setAiChatHistory([])}
|
||||
>
|
||||
<Trash2Icon className="size-3.5" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!isPaidUser && (
|
||||
<>
|
||||
{/* <button
|
||||
<button
|
||||
className="hidden rounded-md bg-gray-200 px-2 py-1 text-sm hover:bg-gray-300 sm:block"
|
||||
onClick={handleCreditsClick}
|
||||
>
|
||||
<span className="font-medium">{usagePercentage}%</span>{' '}
|
||||
credits used
|
||||
</button> */}
|
||||
</button>
|
||||
<button
|
||||
className="flex items-center gap-1 rounded-md bg-yellow-400 px-2 py-1 text-sm text-black hover:bg-yellow-500"
|
||||
onClick={handleUpgradeClick}
|
||||
|
Reference in New Issue
Block a user