mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-12 17:51:53 +08:00
* Update * Add stats and health endpoints * Add pre-render * fix: redirect to the error page * Fix generate-renderer issue * Rename * Fix best practice topics not loading * Handle SSR for static pages * Refactor faqs * Refactor best practices * Fix absolute import * Fix stats * Add custom roadmap page * Minor UI change * feat: custom roadmap slug routes (#4987) * feat: replace roadmap slug * fix: remove roadmap slug * feat: username route * fix: user public page * feat: show roadmap progress * feat: update public profile * fix: replace with toast * feat: user public profile page * feat: implement profile form * feat: implement user profile roadmap page * refactor: remove logs * fix: increase progress gap * fix: remove title margin * fix: breakpoint for roadmaps * Update dependencies * Upgrade dependencies * fix: improper avatars * fix: heatmap focus * wip: remove `getStaticPaths` * fix: add disable props * wip * feat: add email icon * fix: update pnpm lock * fix: implement author page * Fix beginner roadmaps not working * Changes to form * Refactor profile and form * Refactor public profile form * Rearrange sidebar items * Update UI for public form * Minor text update * Refactor public profile form * Error page for user * Revamp UI for profile page * Add public profile page * Fix vite warnings * Add private profile banner * feat: on blur check username * Update fetch depth * Add error detail * Use hybrid mode of rendering * Do not pre-render stats pages * Update deployment workflow * Update deployment workflow --------- Co-authored-by: Arik Chakma <arikchangma@gmail.com>
123 lines
2.8 KiB
TypeScript
123 lines
2.8 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { getUrlParams } from '../../lib/browser';
|
|
import { type AppError, type FetchError, httpGet } from '../../lib/http';
|
|
import { RoadmapHeader } from './RoadmapHeader';
|
|
import { TopicDetail } from '../TopicDetail/TopicDetail';
|
|
import type { RoadmapDocument } from './CreateRoadmap/CreateRoadmapModal';
|
|
import { currentRoadmap } from '../../stores/roadmap';
|
|
import { RestrictedPage } from './RestrictedPage';
|
|
import { FlowRoadmapRenderer } from './FlowRoadmapRenderer';
|
|
|
|
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 type CreatorType = {
|
|
id: string;
|
|
name: string;
|
|
avatar: string;
|
|
};
|
|
|
|
export type GetRoadmapResponse = RoadmapDocument & {
|
|
canManage: boolean;
|
|
creator?: CreatorType;
|
|
team?: CreatorType;
|
|
};
|
|
|
|
export function hideRoadmapLoader() {
|
|
const loaderEl = document.querySelector(
|
|
'[data-roadmap-loader]',
|
|
) as HTMLElement;
|
|
if (loaderEl) {
|
|
loaderEl.remove();
|
|
}
|
|
}
|
|
|
|
type CustomRoadmapProps = {
|
|
isEmbed?: boolean;
|
|
slug?: string;
|
|
};
|
|
|
|
export function CustomRoadmap(props: CustomRoadmapProps) {
|
|
const { isEmbed = false, slug } = props;
|
|
|
|
const { id, secret } = getUrlParams() as { id: string; secret: string };
|
|
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [roadmap, setRoadmap] = useState<GetRoadmapResponse | null>(null);
|
|
const [error, setError] = useState<AppError | FetchError | undefined>();
|
|
|
|
async function getRoadmap() {
|
|
setIsLoading(true);
|
|
|
|
const roadmapUrl = slug
|
|
? new URL(
|
|
`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap-by-slug/${slug}`,
|
|
)
|
|
: new URL(`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap/${id}`);
|
|
|
|
if (secret) {
|
|
roadmapUrl.searchParams.set('secret', secret);
|
|
}
|
|
|
|
const { response, error } = await httpGet<GetRoadmapResponse>(
|
|
roadmapUrl.toString(),
|
|
);
|
|
|
|
if (error || !response) {
|
|
setError(error);
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
document.title = `${response.title} - roadmap.sh`;
|
|
|
|
setRoadmap(response);
|
|
currentRoadmap.set(response);
|
|
setIsLoading(false);
|
|
}
|
|
|
|
useEffect(() => {
|
|
getRoadmap().finally(() => {
|
|
hideRoadmapLoader();
|
|
});
|
|
}, []);
|
|
|
|
if (isLoading) {
|
|
return null;
|
|
}
|
|
|
|
if (error) {
|
|
return <RestrictedPage error={error} />;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{!isEmbed && <RoadmapHeader />}
|
|
<FlowRoadmapRenderer isEmbed={isEmbed} roadmap={roadmap!} />
|
|
<TopicDetail isEmbed={isEmbed} canSubmitContribution={false} />
|
|
</>
|
|
);
|
|
}
|