1
0
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:
Arik Chakma
2025-05-21 19:10:39 +06:00
parent 57c440a8d7
commit d757db0735
3 changed files with 117 additions and 11 deletions

View File

@@ -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>
</>
);
}

View 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>
);
}

View File

@@ -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,
});
}