mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-09-24 15:00:31 +08:00
feat: create new course (#8605)
* feat: create new course * seo: update latest roadmap keywords (#8590) * seo: update roadmap keywords * Update src/lib/config.ts --------- Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com> * Fix non-logged in user course access * feat: create new course * Update UI --------- Co-authored-by: Vedansh <superuser.ntsystems@outlook.com> Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
This commit is contained in:
co-authored by
Kamran Ahmed
Vedansh
parent
30d3a86784
commit
b7b96b7d7b
@@ -0,0 +1,60 @@
|
||||
import { WandSparkles } from 'lucide-react';
|
||||
import { Modal } from '../Modal';
|
||||
import { useState } from 'react';
|
||||
|
||||
type CreateCourseModalProps = {
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export function CreateCourseModal(props: CreateCourseModalProps) {
|
||||
const { onClose } = props;
|
||||
|
||||
const [subject, setSubject] = useState('');
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onClose={onClose}
|
||||
wrapperClassName="h-auto mt-20"
|
||||
overlayClassName="items-start"
|
||||
bodyClassName="p-1.5"
|
||||
>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target as HTMLFormElement);
|
||||
const subject = formData.get('subject');
|
||||
|
||||
window.location.href = `/ai/search?term=${subject}&difficulty=beginner&src=topic`;
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<label
|
||||
className="mb-2.5 ml-1 inline-block text-sm leading-none"
|
||||
htmlFor="subject"
|
||||
>
|
||||
Ask AI to Teach You
|
||||
</label>
|
||||
<div className="relative flex items-center gap-2 overflow-hidden">
|
||||
<input
|
||||
id="subject"
|
||||
type="text"
|
||||
className="w-full bg-white p-2.5 pr-8 text-sm focus:outline-hidden"
|
||||
placeholder="Enter a topic to learn"
|
||||
name="subject"
|
||||
autoFocus={true}
|
||||
value={subject}
|
||||
onChange={(e) => setSubject(e.target.value)}
|
||||
/>
|
||||
|
||||
<button
|
||||
disabled={!subject.trim()}
|
||||
className="flex h-full disabled:opacity-40 items-center justify-center gap-2 rounded-md bg-black px-3 py-1 text-sm text-white hover:opacity-80"
|
||||
>
|
||||
Generate
|
||||
<WandSparkles className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import { cn } from '../../lib/classname.ts';
|
||||
import type { AIChatHistoryType } from '../GenerateCourse/AICourseLessonChat.tsx';
|
||||
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal.tsx';
|
||||
import { TopicProgressButton } from './TopicProgressButton.tsx';
|
||||
import { CreateCourseModal } from './CreateCourseModal.tsx';
|
||||
|
||||
type TopicDetailProps = {
|
||||
resourceId?: string;
|
||||
@@ -119,6 +120,8 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
||||
const [isCustomResource, setIsCustomResource] = useState(false);
|
||||
|
||||
const [showSubjectSearchModal, setShowSubjectSearchModal] = useState(false);
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const [showPaidResourceDisclaimer, setShowPaidResourceDisclaimer] =
|
||||
@@ -139,6 +142,7 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
setShowUpgradeModal(false);
|
||||
setAiChatHistory(defaultChatHistory);
|
||||
setActiveTab('content');
|
||||
setShowSubjectSearchModal(false);
|
||||
};
|
||||
|
||||
// Close the topic detail when user clicks outside the topic detail
|
||||
@@ -349,10 +353,6 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const resourceTitleForSearch = resourceTitle
|
||||
?.toLowerCase()
|
||||
?.replace(/\s+?roadmap/gi, '');
|
||||
|
||||
const tnsLink =
|
||||
'https://thenewstack.io/devops/?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Topic';
|
||||
|
||||
@@ -362,10 +362,6 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
return resource.topicIds.includes(normalizedTopicId);
|
||||
});
|
||||
|
||||
const hasPaidScrimbaLinks = paidResourcesForTopic.some(
|
||||
(resource) => resource?.url?.toLowerCase().indexOf('scrimba') !== -1,
|
||||
);
|
||||
|
||||
const shouldShowAiTab = !isCustomResource && resourceType === 'roadmap';
|
||||
|
||||
return (
|
||||
@@ -379,6 +375,10 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
<UpgradeAccountModal onClose={() => setShowUpgradeModal(false)} />
|
||||
)}
|
||||
|
||||
{showSubjectSearchModal && (
|
||||
<CreateCourseModal onClose={() => setShowSubjectSearchModal(false)} />
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<div className="flex h-full w-full justify-center">
|
||||
<Spinner
|
||||
@@ -448,6 +448,14 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
handleClose();
|
||||
showLoginPopup();
|
||||
}}
|
||||
onShowSubjectSearchModal={() => {
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
setShowSubjectSearchModal(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -10,9 +10,8 @@ import {
|
||||
ChevronRightIcon,
|
||||
Gift,
|
||||
Loader2Icon,
|
||||
LockIcon,
|
||||
SendIcon,
|
||||
Trash2,
|
||||
LockIcon, SendIcon, Trash2,
|
||||
WandSparkles
|
||||
} from 'lucide-react';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { cn } from '../../lib/classname';
|
||||
@@ -42,6 +41,8 @@ type TopicDetailAIProps = {
|
||||
|
||||
onUpgrade: () => void;
|
||||
onLogin: () => void;
|
||||
|
||||
onShowSubjectSearchModal: () => void;
|
||||
};
|
||||
|
||||
export function TopicDetailAI(props: TopicDetailAIProps) {
|
||||
@@ -53,6 +54,7 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
|
||||
topicId,
|
||||
onUpgrade,
|
||||
onLogin,
|
||||
onShowSubjectSearchModal,
|
||||
} = props;
|
||||
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
@@ -298,6 +300,14 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
|
||||
))}
|
||||
</a>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={onShowSubjectSearchModal}
|
||||
className="flex items-center gap-1.5 rounded-md border border-gray-300 bg-transparent px-2 py-1 hover:bg-gray-200 hover:text-black"
|
||||
>
|
||||
<WandSparkles className="h-3 w-3" />
|
||||
Custom Topic
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user