mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-25 00:21:28 +02:00
Chat history improvements
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
MessageCirclePlus,
|
||||
PauseCircleIcon,
|
||||
PersonStanding,
|
||||
Plus,
|
||||
SendIcon,
|
||||
SquareArrowOutUpRight,
|
||||
Trash2,
|
||||
@@ -18,24 +19,23 @@ import { useKeydown } from '../../hooks/use-keydown';
|
||||
import {
|
||||
roadmapAIChatRenderer,
|
||||
useRoadmapAIChat,
|
||||
type RoadmapAIChatHistoryType,
|
||||
} from '../../hooks/use-roadmap-ai-chat';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { lockBodyScroll } from '../../lib/dom';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { slugify } from '../../lib/slugger';
|
||||
import { getAiCourseLimitOptions } from '../../queries/ai-course';
|
||||
import { billingDetailsOptions } from '../../queries/billing';
|
||||
import { chatHistoryOptions } from '../../queries/chat-history';
|
||||
import { roadmapJSONOptions } from '../../queries/roadmap';
|
||||
import { roadmapQuestionsOptions } from '../../queries/roadmap-questions';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
||||
import { RoadmapAIChatCard } from '../RoadmapAIChat/RoadmapAIChatCard';
|
||||
import { RoadmapAIChatHistory } from '../RoadmapAIChatHistory/RoadmapAIChatHistory';
|
||||
import { CLOSE_TOPIC_DETAIL_EVENT } from '../TopicDetail/TopicDetail';
|
||||
import { UpdatePersonaModal } from '../UserPersona/UpdatePersonaModal';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { chatHistoryOptions } from '../../queries/chat-history';
|
||||
import { RoadmapAIChatHistory } from '../RoadmapAIChatHistory/RoadmapAIChatHistory';
|
||||
|
||||
type ChatHeaderButtonProps = {
|
||||
onClick?: () => void;
|
||||
@@ -381,21 +381,22 @@ export function RoadmapFloatingChat(props: RoadmapChatProps) {
|
||||
<div className="flex">
|
||||
<ChatHeaderButton
|
||||
icon={<BookOpen className="h-3.5 w-3.5" />}
|
||||
className="text-sm"
|
||||
className="pointer-events-none text-sm"
|
||||
>
|
||||
AI Tutor
|
||||
{chatHistory?.title || 'AI Tutor'}
|
||||
</ChatHeaderButton>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-1.5">
|
||||
<ChatHeaderButton
|
||||
href={newTabUrl}
|
||||
target="_blank"
|
||||
icon={<SquareArrowOutUpRight className="h-3.5 w-3.5" />}
|
||||
className="hidden rounded-md py-1 pr-2 pl-1.5 text-gray-500 hover:bg-gray-300 sm:flex"
|
||||
>
|
||||
Open in new tab
|
||||
</ChatHeaderButton>
|
||||
{isPaidUser && activeChatHistoryId && (
|
||||
<ChatHeaderButton
|
||||
onClick={() => {
|
||||
setActiveChatHistoryId(undefined);
|
||||
}}
|
||||
icon={<Plus className="h-3.5 w-3.5" />}
|
||||
className="justify-center rounded-md bg-gray-200 px-2 py-1 text-xs text-black hover:bg-gray-300"
|
||||
/>
|
||||
)}
|
||||
|
||||
<RoadmapAIChatHistory
|
||||
roadmapId={roadmapId}
|
||||
@@ -403,20 +404,25 @@ export function RoadmapFloatingChat(props: RoadmapChatProps) {
|
||||
onChatHistoryClick={(chatHistoryId) => {
|
||||
setIsChatHistoryLoading(true);
|
||||
setActiveChatHistoryId(chatHistoryId);
|
||||
setShowScrollToBottom(false);
|
||||
}}
|
||||
onDelete={(chatHistoryId) => {
|
||||
if (activeChatHistoryId === chatHistoryId) {
|
||||
setActiveChatHistoryId(undefined);
|
||||
}
|
||||
}}
|
||||
onNewChat={() => {
|
||||
setActiveChatHistoryId(undefined);
|
||||
}}
|
||||
onUpgrade={() => {
|
||||
setShowUpgradeModal(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
<ChatHeaderButton
|
||||
href={newTabUrl}
|
||||
target="_blank"
|
||||
icon={<SquareArrowOutUpRight className="h-3.5 w-3.5" />}
|
||||
className="hidden justify-center rounded-md bg-gray-200 px-1 py-1 text-gray-500 hover:bg-gray-300 sm:flex"
|
||||
/>
|
||||
|
||||
<ChatHeaderButton
|
||||
onClick={() => setIsOpen(false)}
|
||||
icon={<X className="h-3.5 w-3.5" />}
|
||||
|
@@ -3,7 +3,7 @@ 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, XIcon } from 'lucide-react';
|
||||
import { BookIcon, BotIcon, GiftIcon, PlusIcon, XIcon } from 'lucide-react';
|
||||
import type { RoadmapAIChatTab } from './RoadmapAIChat';
|
||||
import { useState } from 'react';
|
||||
import { getPercentage } from '../../lib/number';
|
||||
@@ -158,15 +158,18 @@ export function RoadmapAIChatHeader(props: RoadmapAIChatHeaderProps) {
|
||||
|
||||
{!isDataLoading && isLoggedIn() && (
|
||||
<div className="flex gap-1.5 pr-4">
|
||||
{isPaidUser && (
|
||||
<button
|
||||
className="flex items-center gap-1 rounded-md bg-gray-200 px-2 py-1 text-xs text-black hover:bg-gray-300"
|
||||
onClick={onNewChat}
|
||||
>
|
||||
<PlusIcon className="size-4" />
|
||||
New Chat
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!isPaidUser && (
|
||||
<>
|
||||
<button
|
||||
className="hidden rounded-md bg-gray-200 px-2 py-1 text-sm hover:bg-gray-300 2xl:block"
|
||||
onClick={handleCreditsClick}
|
||||
>
|
||||
<span className="font-medium">{usagePercentage}%</span> limit
|
||||
used
|
||||
</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}
|
||||
@@ -174,23 +177,21 @@ export function RoadmapAIChatHeader(props: RoadmapAIChatHeaderProps) {
|
||||
<GiftIcon className="size-4" />
|
||||
Upgrade
|
||||
</button>
|
||||
<button
|
||||
className="hidden items-center gap-1 rounded-md bg-gray-200 px-2 py-1 text-sm text-black hover:bg-gray-300 max-xl:flex"
|
||||
onClick={onCloseChat}
|
||||
>
|
||||
<XIcon className="size-3.5" strokeWidth={2.5} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
<RoadmapAIChatHistory
|
||||
roadmapId={roadmapId}
|
||||
onChatHistoryClick={onChatHistoryClick}
|
||||
activeChatHistoryId={activeChatHistoryId}
|
||||
onNewChat={onNewChat}
|
||||
onDelete={onDeleteChatHistory}
|
||||
onUpgrade={onUpgrade}
|
||||
/>
|
||||
<button
|
||||
className="hidden items-center gap-1 rounded-md bg-gray-200 px-2 py-1 text-sm text-black hover:bg-gray-300 max-xl:flex"
|
||||
onClick={onCloseChat}
|
||||
>
|
||||
<XIcon className="size-3.5" strokeWidth={2.5} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { HistoryIcon, Loader2Icon, PlusIcon } from 'lucide-react';
|
||||
import { HistoryIcon, Loader2Icon } from 'lucide-react';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../Popover';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
|
||||
@@ -15,9 +15,9 @@ import { showLoginPopup } from '../../lib/popup';
|
||||
type RoadmapAIChatHistoryProps = {
|
||||
roadmapId: string;
|
||||
activeChatHistoryId?: string;
|
||||
activeChatHistoryTitle?: string;
|
||||
onChatHistoryClick: (id: string) => void;
|
||||
onDelete?: (id: string) => void;
|
||||
onNewChat?: () => void;
|
||||
onUpgrade?: () => void;
|
||||
};
|
||||
|
||||
@@ -25,9 +25,9 @@ export function RoadmapAIChatHistory(props: RoadmapAIChatHistoryProps) {
|
||||
const {
|
||||
roadmapId,
|
||||
activeChatHistoryId,
|
||||
activeChatHistoryTitle,
|
||||
onChatHistoryClick,
|
||||
onDelete,
|
||||
onNewChat,
|
||||
onUpgrade,
|
||||
} = props;
|
||||
|
||||
@@ -50,11 +50,19 @@ export function RoadmapAIChatHistory(props: RoadmapAIChatHistoryProps) {
|
||||
roadmapId,
|
||||
query,
|
||||
}),
|
||||
enabled: !!roadmapId && isLoggedIn() && isOpen,
|
||||
enabled: !!roadmapId && isLoggedIn() && isOpen && isPaidUser,
|
||||
},
|
||||
queryClient,
|
||||
);
|
||||
|
||||
// no initial spinner if not paid user
|
||||
// because we won't fetch the data
|
||||
useEffect(() => {
|
||||
if (!isPaidUser) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [isPaidUser]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!chatHistory || isBillingDetailsLoading) {
|
||||
return;
|
||||
@@ -83,18 +91,18 @@ export function RoadmapAIChatHistory(props: RoadmapAIChatHistoryProps) {
|
||||
setIsOpen(open);
|
||||
}}
|
||||
>
|
||||
<PopoverTrigger className="flex h-8 items-center justify-center gap-2 rounded-md px-2 text-xs text-gray-500 hover:bg-gray-200 hover:text-black">
|
||||
<PopoverTrigger className="flex items-center justify-center gap-2 rounded-md bg-gray-200 px-3 py-1.5 text-xs text-gray-900 hover:bg-gray-300 hover:text-black">
|
||||
<HistoryIcon className="size-3.5" />
|
||||
Chat History
|
||||
{activeChatHistoryTitle || 'Chat History'}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="z-[999] flex max-h-[400px] w-80 flex-col overflow-hidden p-0"
|
||||
className="z-[999] flex max-h-[400px] w-80 flex-col overflow-hidden p-0 shadow-lg"
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
>
|
||||
{isLoading && (
|
||||
<div className="flex items-center justify-center py-10">
|
||||
<Loader2Icon className="size-6 animate-spin stroke-[2.5]" />
|
||||
<Loader2Icon className="size-6 animate-spin stroke-[2.5] text-gray-400" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -167,19 +175,6 @@ export function RoadmapAIChatHistory(props: RoadmapAIChatHistoryProps) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center border-t border-gray-200">
|
||||
<button
|
||||
className="flex w-full items-center justify-center gap-2 p-2 text-sm text-gray-500 hover:bg-gray-200 hover:text-black"
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
onNewChat?.();
|
||||
}}
|
||||
>
|
||||
<PlusIcon className="size-4" />
|
||||
New Chat
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</PopoverContent>
|
||||
|
Reference in New Issue
Block a user