computer-scienceangular-roadmapbackend-roadmapblockchain-roadmapdba-roadmapdeveloper-roadmapdevops-roadmapfrontend-roadmapgo-roadmaphactoberfestjava-roadmapjavascript-roadmapnodejs-roadmappython-roadmapqa-roadmapreact-roadmaproadmapstudy-planvue-roadmapweb3-roadmap
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
2.8 KiB
121 lines
2.8 KiB
import { useEffect, useState } from 'react'; |
|
import { getUrlParams } from '../../lib/browser'; |
|
import { |
|
type AppError, |
|
type FetchError, |
|
httpGet, |
|
httpPost, |
|
} from '../../lib/http'; |
|
import { RoadmapHeader } from './RoadmapHeader'; |
|
import { RoadmapRenderer } from './RoadmapRenderer'; |
|
import { TopicDetail } from '../TopicDetail/TopicDetail'; |
|
import type { RoadmapDocument } from './CreateRoadmap/CreateRoadmapModal'; |
|
import { currentRoadmap } from '../../stores/roadmap'; |
|
import { UserProgressModal } from '../UserProgress/UserProgressModal'; |
|
import { RestrictedPage } from './RestrictedPage'; |
|
import { isLoggedIn } from '../../lib/jwt'; |
|
|
|
export const allowedLinkTypes = [ |
|
'video', |
|
'article', |
|
'opensource', |
|
'course', |
|
'website', |
|
'podcast', |
|
] as const; |
|
|
|
export type AllowedLinkTypes = (typeof allowedLinkTypes)[number]; |
|
|
|
export interface RoadmapContentDocument { |
|
_id?: string; |
|
roadmapId: string; |
|
nodeId: string; |
|
title: string; |
|
description: string; |
|
links: { |
|
id: string; |
|
type: AllowedLinkTypes; |
|
title: string; |
|
url: string; |
|
}[]; |
|
} |
|
|
|
export function hideRoadmapLoader() { |
|
const loaderEl = document.querySelector( |
|
'[data-roadmap-loader]' |
|
) as HTMLElement; |
|
if (loaderEl) { |
|
loaderEl.remove(); |
|
} |
|
} |
|
|
|
export function CustomRoadmap() { |
|
const { id, secret } = getUrlParams() as { id: string; secret: string }; |
|
|
|
const [isLoading, setIsLoading] = useState(true); |
|
const [roadmap, setRoadmap] = useState<RoadmapDocument | null>(null); |
|
const [error, setError] = useState<AppError | FetchError | undefined>(); |
|
|
|
async function getRoadmap() { |
|
setIsLoading(true); |
|
|
|
const roadmapUrl = new URL( |
|
`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap/${id}` |
|
); |
|
if (secret) { |
|
roadmapUrl.searchParams.set('secret', secret); |
|
} |
|
|
|
const { response, error } = await httpGet<RoadmapDocument>( |
|
roadmapUrl.toString() |
|
); |
|
|
|
if (error || !response) { |
|
setError(error); |
|
setIsLoading(false); |
|
return; |
|
} |
|
|
|
document.title = `${response.title} - roadmap.sh`; |
|
|
|
setRoadmap(response); |
|
currentRoadmap.set(response); |
|
setIsLoading(false); |
|
} |
|
|
|
async function trackVisit() { |
|
if (!isLoggedIn()) return; |
|
await httpPost(`${import.meta.env.PUBLIC_API_URL}/v1-visit`, { |
|
resourceId: id, |
|
resourceType: 'roadmap', |
|
}); |
|
} |
|
|
|
useEffect(() => { |
|
getRoadmap().finally(() => { |
|
hideRoadmapLoader(); |
|
}); |
|
trackVisit().then(); |
|
}, []); |
|
|
|
if (isLoading) { |
|
return null; |
|
} |
|
|
|
if (error) { |
|
return <RestrictedPage error={error} />; |
|
} |
|
|
|
return ( |
|
<> |
|
<RoadmapHeader /> |
|
<RoadmapRenderer roadmap={roadmap!} /> |
|
<TopicDetail canSubmitContribution={false} /> |
|
<UserProgressModal |
|
resourceId={roadmap?._id!} |
|
resourceType="roadmap" |
|
isCustomResource={true} |
|
/> |
|
</> |
|
); |
|
}
|
|
|