mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-25 08:35:42 +02:00
wip: topic resources rendering
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { useMemo } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { TopicResourcesModal } from './TopicResourcesModal';
|
||||
|
||||
type TopicListType = {
|
||||
topicId: string;
|
||||
@@ -40,6 +41,7 @@ export function RoadmapTopicList(props: RoadmapTopicListProps) {
|
||||
const { roadmapId, content } = props;
|
||||
|
||||
const topicListItems = parseTopicList(content);
|
||||
const [selectedTopicId, setSelectedTopicId] = useState<string | null>(null);
|
||||
|
||||
const { data: roadmapTreeData } = useQuery(
|
||||
roadmapTreeMappingOptions(roadmapId),
|
||||
@@ -63,15 +65,26 @@ export function RoadmapTopicList(props: RoadmapTopicListProps) {
|
||||
}, [topicListItems, roadmapTreeData]);
|
||||
|
||||
return (
|
||||
<div className="relative my-6 flex flex-wrap gap-1 first:mt-0 last:mb-0">
|
||||
{progressItemWithText.map((item) => (
|
||||
<button
|
||||
key={item.topicId}
|
||||
className="rounded-lg border border-gray-200 bg-white p-1 px-1.5 text-left text-sm"
|
||||
>
|
||||
{item.text}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<>
|
||||
{selectedTopicId && (
|
||||
<TopicResourcesModal
|
||||
roadmapId={roadmapId}
|
||||
topicId={selectedTopicId}
|
||||
onClose={() => setSelectedTopicId(null)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="relative my-6 flex flex-wrap gap-1 first:mt-0 last:mb-0">
|
||||
{progressItemWithText.map((item) => (
|
||||
<button
|
||||
key={item.topicId}
|
||||
className="rounded-lg border border-gray-200 bg-white p-1 px-1.5 text-left text-sm"
|
||||
onClick={() => setSelectedTopicId(item.topicId)}
|
||||
>
|
||||
{item.text}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
57
src/components/RoadmapAIChat/TopicResourcesModal.tsx
Normal file
57
src/components/RoadmapAIChat/TopicResourcesModal.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Modal } from '../Modal';
|
||||
import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { roadmapContentOptions } from '../../queries/roadmap';
|
||||
import { ModalLoader } from '../UserProgress/ModalLoader';
|
||||
import { TopicDetailLink } from '../TopicDetail/TopicDetailLink';
|
||||
|
||||
type TopicResourcesModalProps = {
|
||||
roadmapId: string;
|
||||
topicId: string;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export function TopicResourcesModal(props: TopicResourcesModalProps) {
|
||||
const { roadmapId, topicId, onClose } = props;
|
||||
|
||||
const {
|
||||
data: roadmapContentData,
|
||||
isLoading: isLoadingRoadmapContent,
|
||||
error,
|
||||
} = useQuery(roadmapContentOptions(roadmapId), queryClient);
|
||||
|
||||
const topicContent = roadmapContentData?.[topicId];
|
||||
const links = topicContent?.links || [];
|
||||
|
||||
if (isLoadingRoadmapContent || error) {
|
||||
return (
|
||||
<ModalLoader
|
||||
text="Loading Topic Resources..."
|
||||
isLoading={isLoadingRoadmapContent}
|
||||
error={error?.message}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal onClose={onClose}>
|
||||
<div className="p-4">
|
||||
<h2 className="text-xl font-bold">{topicContent?.title}</h2>
|
||||
<ul className="mt-4 space-y-1">
|
||||
{links.map((link, index) => {
|
||||
return (
|
||||
<li key={`${link.url}-${index}`}>
|
||||
<TopicDetailLink
|
||||
url={link.url}
|
||||
type={link.type}
|
||||
title={link.title}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@@ -19,6 +19,7 @@ export function roadmapJSONOptions(roadmapId: string) {
|
||||
svg,
|
||||
};
|
||||
},
|
||||
refetchOnMount: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,5 +58,40 @@ export function roadmapDetailsOptions(roadmapId: string) {
|
||||
|
||||
return roadmapDetails;
|
||||
},
|
||||
refetchOnMount: false,
|
||||
});
|
||||
}
|
||||
|
||||
export const allowedLinkTypes = [
|
||||
'video',
|
||||
'article',
|
||||
'opensource',
|
||||
'course',
|
||||
'website',
|
||||
'podcast',
|
||||
] as const;
|
||||
export type AllowedLinkTypes = (typeof allowedLinkTypes)[number];
|
||||
|
||||
export function roadmapContentOptions(roadmapId: string) {
|
||||
return queryOptions({
|
||||
queryKey: ['roadmap-content', { roadmapId }],
|
||||
queryFn: async () => {
|
||||
const baseUrl = import.meta.env.PUBLIC_APP_URL;
|
||||
return httpGet<
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
title: string;
|
||||
description: string;
|
||||
links: {
|
||||
title: string;
|
||||
url: string;
|
||||
type: AllowedLinkTypes;
|
||||
}[];
|
||||
}
|
||||
>
|
||||
>(`${baseUrl}/roadmap-content/${roadmapId}.json`);
|
||||
},
|
||||
refetchOnMount: false,
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user