1
0
mirror of https://github.com/kamranahmedse/developer-roadmap.git synced 2025-09-03 06:12:53 +02:00

Upgrade flow in floating chat

This commit is contained in:
Kamran Ahmed
2025-06-10 17:30:44 +01:00
parent c4043cc77a
commit 474dd14631
3 changed files with 36 additions and 6 deletions

View File

@@ -185,6 +185,7 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
bodyClassName="p-4 sm:p-6 bg-white" bodyClassName="p-4 sm:p-6 bg-white"
wrapperClassName="h-auto rounded-xl max-w-3xl w-full min-h-[540px] mx-2 sm:mx-4" wrapperClassName="h-auto rounded-xl max-w-3xl w-full min-h-[540px] mx-2 sm:mx-4"
overlayClassName="items-start md:items-center" overlayClassName="items-start md:items-center"
hasCloseButton={true}
> >
<div onClick={(e) => e.stopPropagation()}> <div onClick={(e) => e.stopPropagation()}>
{errorContent} {errorContent}

View File

@@ -22,14 +22,15 @@ import {
import { cn } from '../../lib/classname'; import { cn } from '../../lib/classname';
import { lockBodyScroll } from '../../lib/dom'; import { lockBodyScroll } from '../../lib/dom';
import { slugify } from '../../lib/slugger'; import { slugify } from '../../lib/slugger';
import { getAiCourseLimitOptions } from '../../queries/ai-course';
import { billingDetailsOptions } from '../../queries/billing';
import { roadmapJSONOptions } from '../../queries/roadmap'; import { roadmapJSONOptions } from '../../queries/roadmap';
import { roadmapQuestionsOptions } from '../../queries/roadmap-questions'; import { roadmapQuestionsOptions } from '../../queries/roadmap-questions';
import { queryClient } from '../../stores/query-client'; import { queryClient } from '../../stores/query-client';
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
import { RoadmapAIChatCard } from '../RoadmapAIChat/RoadmapAIChatCard'; import { RoadmapAIChatCard } from '../RoadmapAIChat/RoadmapAIChatCard';
import { UpdatePersonaModal } from '../UserPersona/UpdatePersonaModal';
import { CLOSE_TOPIC_DETAIL_EVENT } from '../TopicDetail/TopicDetail'; import { CLOSE_TOPIC_DETAIL_EVENT } from '../TopicDetail/TopicDetail';
import { billingDetailsOptions } from '../../queries/billing'; import { UpdatePersonaModal } from '../UserPersona/UpdatePersonaModal';
import { getAiCourseLimitOptions } from '../../queries/ai-course';
type ChatHeaderButtonProps = { type ChatHeaderButtonProps = {
onClick?: () => void; onClick?: () => void;
@@ -156,6 +157,7 @@ export function RoadmapFloatingChat(props: RoadmapChatProps) {
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const [isPersonalizeOpen, setIsPersonalizeOpen] = useState(false); const [isPersonalizeOpen, setIsPersonalizeOpen] = useState(false);
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
// Fetch questions from API // Fetch questions from API
const { data: questionsData } = useQuery( const { data: questionsData } = useQuery(
@@ -292,6 +294,14 @@ export function RoadmapFloatingChat(props: RoadmapChatProps) {
></div> ></div>
)} )}
{showUpgradeModal && (
<UpgradeAccountModal
onClose={() => {
setShowUpgradeModal(false);
}}
/>
)}
{isPersonalizeOpen && ( {isPersonalizeOpen && (
<UpdatePersonaModal <UpdatePersonaModal
roadmapId={roadmapId} roadmapId={roadmapId}
@@ -366,6 +376,12 @@ export function RoadmapFloatingChat(props: RoadmapChatProps) {
key={`default-question-${index}`} key={`default-question-${index}`}
className="flex h-full self-start rounded-md bg-yellow-500/10 px-3 py-2 text-left text-sm text-black hover:bg-yellow-500/20" className="flex h-full self-start rounded-md bg-yellow-500/10 px-3 py-2 text-left text-sm text-black hover:bg-yellow-500/20"
onClick={() => { onClick={() => {
if (isLimitExceeded) {
setShowUpgradeModal(true);
setIsOpen(false);
return;
}
handleChatSubmit( handleChatSubmit(
textToJSON(question), textToJSON(question),
isRoadmapDetailLoading, isRoadmapDetailLoading,
@@ -415,7 +431,8 @@ export function RoadmapFloatingChat(props: RoadmapChatProps) {
{isLimitExceeded && ( {isLimitExceeded && (
<UpgradeMessage <UpgradeMessage
onUpgradeClick={() => { onUpgradeClick={() => {
window.open('/premium', '_blank'); setShowUpgradeModal(true);
setIsOpen(false);
}} }}
/> />
)} )}
@@ -436,7 +453,8 @@ export function RoadmapFloatingChat(props: RoadmapChatProps) {
<UsageButton <UsageButton
percentageUsed={percentageUsed} percentageUsed={percentageUsed}
onUpgradeClick={() => { onUpgradeClick={() => {
window.open('/premium', '_blank'); setShowUpgradeModal(true);
setIsOpen(false);
}} }}
/> />
)} )}

View File

@@ -2,6 +2,7 @@ import { type ReactNode, useRef } from 'react';
import { useOutsideClick } from '../hooks/use-outside-click'; import { useOutsideClick } from '../hooks/use-outside-click';
import { useKeydown } from '../hooks/use-keydown'; import { useKeydown } from '../hooks/use-keydown';
import { cn } from '../lib/classname'; import { cn } from '../lib/classname';
import { X } from 'lucide-react';
type ModalProps = { type ModalProps = {
onClose: () => void; onClose: () => void;
@@ -9,6 +10,7 @@ type ModalProps = {
overlayClassName?: string; overlayClassName?: string;
bodyClassName?: string; bodyClassName?: string;
wrapperClassName?: string; wrapperClassName?: string;
hasCloseButton?: boolean;
}; };
export function Modal(props: ModalProps) { export function Modal(props: ModalProps) {
@@ -18,6 +20,7 @@ export function Modal(props: ModalProps) {
bodyClassName, bodyClassName,
wrapperClassName, wrapperClassName,
overlayClassName, overlayClassName,
hasCloseButton = true,
} = props; } = props;
const popupBodyEl = useRef<HTMLDivElement>(null); const popupBodyEl = useRef<HTMLDivElement>(null);
@@ -33,7 +36,7 @@ export function Modal(props: ModalProps) {
return ( return (
<div <div
className={cn( className={cn(
'fixed left-0 right-0 top-0 z-99 flex h-full items-center justify-center overflow-y-auto overflow-x-hidden bg-black/50', 'fixed top-0 right-0 left-0 z-99 flex h-full items-center justify-center overflow-x-hidden overflow-y-auto bg-black/50',
overlayClassName, overlayClassName,
)} )}
> >
@@ -50,6 +53,14 @@ export function Modal(props: ModalProps) {
bodyClassName, bodyClassName,
)} )}
> >
{hasCloseButton && (
<button
onClick={onClose}
className="absolute top-4 right-4 text-gray-300 hover:text-gray-700"
>
<X className="h-5 w-5" />
</button>
)}
{children} {children}
</div> </div>
</div> </div>