mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-12 12:13:58 +02:00
Allow embedding of roadmaps
This commit is contained in:
@@ -53,14 +53,20 @@ export type GetRoadmapResponse = RoadmapDocument & {
|
|||||||
|
|
||||||
export function hideRoadmapLoader() {
|
export function hideRoadmapLoader() {
|
||||||
const loaderEl = document.querySelector(
|
const loaderEl = document.querySelector(
|
||||||
'[data-roadmap-loader]'
|
'[data-roadmap-loader]',
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
if (loaderEl) {
|
if (loaderEl) {
|
||||||
loaderEl.remove();
|
loaderEl.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CustomRoadmap() {
|
type CustomRoadmapProps = {
|
||||||
|
isEmbed?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function CustomRoadmap(props: CustomRoadmapProps) {
|
||||||
|
const { isEmbed = false } = props;
|
||||||
|
|
||||||
const { id, secret } = getUrlParams() as { id: string; secret: string };
|
const { id, secret } = getUrlParams() as { id: string; secret: string };
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
@@ -71,14 +77,15 @@ export function CustomRoadmap() {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const roadmapUrl = new URL(
|
const roadmapUrl = new URL(
|
||||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap/${id}`
|
`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap/${id}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (secret) {
|
if (secret) {
|
||||||
roadmapUrl.searchParams.set('secret', secret);
|
roadmapUrl.searchParams.set('secret', secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { response, error } = await httpGet<GetRoadmapResponse>(
|
const { response, error } = await httpGet<GetRoadmapResponse>(
|
||||||
roadmapUrl.toString()
|
roadmapUrl.toString(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error || !response) {
|
if (error || !response) {
|
||||||
@@ -95,7 +102,10 @@ export function CustomRoadmap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function trackVisit() {
|
async function trackVisit() {
|
||||||
if (!isLoggedIn()) return;
|
if (!isLoggedIn() || isEmbed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await httpPost(`${import.meta.env.PUBLIC_API_URL}/v1-visit`, {
|
await httpPost(`${import.meta.env.PUBLIC_API_URL}/v1-visit`, {
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
resourceType: 'roadmap',
|
resourceType: 'roadmap',
|
||||||
@@ -119,9 +129,9 @@ export function CustomRoadmap() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RoadmapHeader />
|
{!isEmbed && <RoadmapHeader />}
|
||||||
<FlowRoadmapRenderer roadmap={roadmap!} />
|
<FlowRoadmapRenderer isEmbed={isEmbed} roadmap={roadmap!} />
|
||||||
<TopicDetail canSubmitContribution={false} />
|
<TopicDetail isEmbed={isEmbed} canSubmitContribution={false} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +1,27 @@
|
|||||||
import { ReadonlyEditor } from '../../../editor/readonly-editor';
|
import { ReadonlyEditor } from '../../../editor/readonly-editor';
|
||||||
import type { RoadmapDocument } from './CreateRoadmap/CreateRoadmapModal';
|
import type { RoadmapDocument } from './CreateRoadmap/CreateRoadmapModal';
|
||||||
import {
|
import {
|
||||||
renderResourceProgress,
|
|
||||||
updateResourceProgress,
|
|
||||||
type ResourceProgressType,
|
|
||||||
renderTopicProgress,
|
|
||||||
refreshProgressCounters,
|
refreshProgressCounters,
|
||||||
|
renderResourceProgress,
|
||||||
|
renderTopicProgress,
|
||||||
|
type ResourceProgressType,
|
||||||
|
updateResourceProgress,
|
||||||
} from '../../lib/resource-progress';
|
} from '../../lib/resource-progress';
|
||||||
import { pageProgressMessage } from '../../stores/page';
|
import { pageProgressMessage } from '../../stores/page';
|
||||||
import { useToast } from '../../hooks/use-toast';
|
import { useToast } from '../../hooks/use-toast';
|
||||||
import type { Node } from 'reactflow';
|
import type { Node } from 'reactflow';
|
||||||
import { useCallback, type MouseEvent, useMemo, useState, useRef } from 'react';
|
import { type MouseEvent, useCallback, useRef, useState } from 'react';
|
||||||
import { EmptyRoadmap } from './EmptyRoadmap';
|
import { EmptyRoadmap } from './EmptyRoadmap';
|
||||||
import { cn } from '../../lib/classname';
|
import { cn } from '../../lib/classname';
|
||||||
import { totalRoadmapNodes } from '../../stores/roadmap.ts';
|
import { totalRoadmapNodes } from '../../stores/roadmap.ts';
|
||||||
|
|
||||||
type FlowRoadmapRendererProps = {
|
type FlowRoadmapRendererProps = {
|
||||||
|
isEmbed?: boolean;
|
||||||
roadmap: RoadmapDocument;
|
roadmap: RoadmapDocument;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) {
|
export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) {
|
||||||
const { roadmap } = props;
|
const { roadmap, isEmbed = false } = props;
|
||||||
const roadmapId = String(roadmap._id!);
|
const roadmapId = String(roadmap._id!);
|
||||||
|
|
||||||
const [hideRenderer, setHideRenderer] = useState(false);
|
const [hideRenderer, setHideRenderer] = useState(false);
|
||||||
@@ -32,6 +33,10 @@ export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) {
|
|||||||
topicId: string,
|
topicId: string,
|
||||||
newStatus: ResourceProgressType,
|
newStatus: ResourceProgressType,
|
||||||
) {
|
) {
|
||||||
|
if (isEmbed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pageProgressMessage.set('Updating progress');
|
pageProgressMessage.set('Updating progress');
|
||||||
updateResourceProgress(
|
updateResourceProgress(
|
||||||
{
|
{
|
||||||
|
@@ -29,6 +29,7 @@ import { Spinner } from '../ReactIcons/Spinner';
|
|||||||
import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx';
|
import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx';
|
||||||
|
|
||||||
type TopicDetailProps = {
|
type TopicDetailProps = {
|
||||||
|
isEmbed?: boolean;
|
||||||
canSubmitContribution: boolean;
|
canSubmitContribution: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ const linkTypes: Record<AllowedLinkTypes, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function TopicDetail(props: TopicDetailProps) {
|
export function TopicDetail(props: TopicDetailProps) {
|
||||||
const { canSubmitContribution } = props;
|
const { canSubmitContribution, isEmbed = false } = props;
|
||||||
|
|
||||||
const [hasEnoughLinks, setHasEnoughLinks] = useState(false);
|
const [hasEnoughLinks, setHasEnoughLinks] = useState(false);
|
||||||
const [contributionUrl, setContributionUrl] = useState('');
|
const [contributionUrl, setContributionUrl] = useState('');
|
||||||
@@ -163,9 +164,9 @@ export function TopicDetail(props: TopicDetailProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const links = topicDom.querySelectorAll('a');
|
const links = topicDom.querySelectorAll('a');
|
||||||
const contributionUrl =
|
const urlElem: HTMLElement =
|
||||||
topicDom.querySelector('[data-github-url]')?.dataset?.githubUrl ||
|
topicDom.querySelector('[data-github-url]')!;
|
||||||
'';
|
const contributionUrl = urlElem?.dataset?.githubUrl || '';
|
||||||
|
|
||||||
setContributionUrl(contributionUrl);
|
setContributionUrl(contributionUrl);
|
||||||
setHasEnoughLinks(links.length >= 3);
|
setHasEnoughLinks(links.length >= 3);
|
||||||
@@ -218,6 +219,7 @@ export function TopicDetail(props: TopicDetailProps) {
|
|||||||
<>
|
<>
|
||||||
{/* Actions for the topic */}
|
{/* Actions for the topic */}
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
|
{!isEmbed && (
|
||||||
<TopicProgressButton
|
<TopicProgressButton
|
||||||
topicId={topicId}
|
topicId={topicId}
|
||||||
resourceId={resourceId}
|
resourceId={resourceId}
|
||||||
@@ -226,6 +228,7 @@ export function TopicDetail(props: TopicDetailProps) {
|
|||||||
setIsActive(false);
|
setIsActive(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
29
src/pages/r/embed.astro
Normal file
29
src/pages/r/embed.astro
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||||
|
import { CustomRoadmap } from '../../components/CustomRoadmap/CustomRoadmap';
|
||||||
|
import { SkeletonRoadmapHeader } from '../../components/CustomRoadmap/SkeletonRoadmapHeader';
|
||||||
|
import Loader from '../../components/Loader.astro';
|
||||||
|
import ProgressHelpPopup from '../../components/ProgressHelpPopup.astro';
|
||||||
|
import SkeletonLayout from '../../layouts/SkeletonLayout.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
<SkeletonLayout title='Roadmaps' noIndex={true}>
|
||||||
|
<div class='relative flex min-h-[550px] flex-col'>
|
||||||
|
<div data-roadmap-loader class='flex w-full grow flex-col'>
|
||||||
|
<div class='flex grow items-center justify-center'>
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CustomRoadmap isEmbed={true} client:only='react' />
|
||||||
|
|
||||||
|
<div class='fixed bottom-5 right-4'>
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
class='rounded-md bg-gray-600 p-2 text-white hover:bg-black'
|
||||||
|
href='https://roadmap.sh'
|
||||||
|
>
|
||||||
|
roadmap.sh
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SkeletonLayout>
|
Reference in New Issue
Block a user