mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-12 17:51:53 +08:00
Migrate best practices to editor
This commit is contained in:
@@ -22,32 +22,27 @@ export default defineConfig({
|
||||
},
|
||||
'/best-practices': {
|
||||
status: 301,
|
||||
destination:'/roadmaps'
|
||||
destination: '/roadmaps',
|
||||
},
|
||||
'/best-practices/aws': {
|
||||
status: 301,
|
||||
destination:'/aws-best-practices'
|
||||
destination: '/aws-best-practices',
|
||||
},
|
||||
'/best-practices/backend-performance': {
|
||||
status: 301,
|
||||
destination:'/backend-performance-best-practices'
|
||||
destination: '/backend-performance-best-practices',
|
||||
},
|
||||
'/best-practices/frontend-performance': {
|
||||
status: 301,
|
||||
destination:'/frontend-performance-best-practices'
|
||||
destination: '/frontend-performance-best-practices',
|
||||
},
|
||||
'/best-practices/api-security': {
|
||||
status: 301,
|
||||
destination:'/api-security-best-practices'
|
||||
destination: '/api-security-best-practices',
|
||||
},
|
||||
'/best-practices/code-review': {
|
||||
status: 301,
|
||||
destination:'/code-review-best-practices'
|
||||
},
|
||||
},
|
||||
vite: {
|
||||
server: {
|
||||
allowedHosts: ['roadmap.sh', 'port3k.kamranahmed.info'],
|
||||
destination: '/code-review-best-practices',
|
||||
},
|
||||
},
|
||||
markdown: {
|
||||
@@ -96,5 +91,8 @@ export default defineConfig({
|
||||
ssr: {
|
||||
noExternal: [/^@roadmapsh\/editor.*$/],
|
||||
},
|
||||
server: {
|
||||
allowedHosts: ['roadmap.sh', 'port3k.kamranahmed.info'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -78,13 +78,6 @@ const defaultPages: PageType[] = [
|
||||
icon: <RoadmapIcon className="mr-2 h-4 w-4 stroke-2" />,
|
||||
isProtected: true,
|
||||
},
|
||||
{
|
||||
id: 'best-practices',
|
||||
url: '/best-practices',
|
||||
title: 'Best Practices',
|
||||
group: 'Pages',
|
||||
icon: <BestPracticesIcon className="mr-2 h-4 w-4 stroke-2" />,
|
||||
},
|
||||
{
|
||||
id: 'questions',
|
||||
url: '/questions',
|
||||
|
||||
@@ -453,12 +453,12 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
|
||||
<HeroRoadmap
|
||||
key={roadmap.id}
|
||||
resourceId={roadmap.id}
|
||||
resourceType="best-practice"
|
||||
resourceType="roadmap"
|
||||
resourceTitle={roadmap.title}
|
||||
isFavorite={roadmap.isFavorite}
|
||||
percentageDone={percentageDone}
|
||||
isNew={roadmap.isNew}
|
||||
url={`/best-practices/${roadmap.id}`}
|
||||
url={`/${roadmap.id}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -176,6 +176,51 @@ export function EditorRoadmapRenderer(props: RoadmapRendererProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodeType === 'checklist-item' && (target.tagName === 'text' || target.tagName === 'tspan')) {
|
||||
e.preventDefault();
|
||||
|
||||
const textElement = target.tagName === 'tspan' ? (target.closest('text') as SVGTextElement) : target;
|
||||
const clickedText = textElement?.textContent?.trim();
|
||||
if (!clickedText) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentChecklistId = targetGroup?.dataset?.parentId;
|
||||
if (!parentChecklistId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentChecklistGroup = roadmapRef.current?.querySelector(
|
||||
`g[data-node-id="${parentChecklistId}"][data-type="checklist"]`
|
||||
);
|
||||
if (!parentChecklistGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
const labelGroup = parentChecklistGroup.querySelector(
|
||||
'g[data-type="checklist-label"]'
|
||||
);
|
||||
if (!labelGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
const labelText = labelGroup.querySelector('text')?.textContent?.trim();
|
||||
if (!labelText) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('roadmap.checklist.click', {
|
||||
detail: {
|
||||
roadmapId: resourceId,
|
||||
labelText,
|
||||
clickedText,
|
||||
},
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// we don't have the topic popup for checklist-item
|
||||
if (nodeType === 'checklist-item') {
|
||||
return;
|
||||
|
||||
@@ -306,6 +306,53 @@ export function RoadmapFloatingChat(props: RoadmapChatProps) {
|
||||
};
|
||||
}, [isOpen, isPersonalizeOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleChecklistClick = (e: CustomEvent) => {
|
||||
const { roadmapId: eventRoadmapId, labelText, clickedText } = e.detail;
|
||||
|
||||
if (eventRoadmapId !== roadmapId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLoggedIn()) {
|
||||
setIsOpen(false);
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
const roadmapTitle = roadmapDetail?.json?.title?.page || roadmapDetail?.json?.title?.card || 'this roadmap';
|
||||
const message = `Explain the '${roadmapTitle}' best practice '${labelText} > ${clickedText}'`;
|
||||
|
||||
flushSync(() => {
|
||||
setIsOpen(true);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
sendMessage(
|
||||
{ text: message, metadata: { json: textToJSON(message) } },
|
||||
{
|
||||
body: {
|
||||
roadmapId,
|
||||
...(activeChatHistoryId
|
||||
? { chatHistoryId: activeChatHistoryId }
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
scrollToBottom('smooth');
|
||||
inputRef.current?.focus();
|
||||
}, 0);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
window.addEventListener('roadmap.checklist.click', handleChecklistClick as EventListener);
|
||||
return () => {
|
||||
window.removeEventListener('roadmap.checklist.click', handleChecklistClick as EventListener);
|
||||
};
|
||||
}, [roadmapId, roadmapDetail, sendMessage, activeChatHistoryId, scrollToBottom]);
|
||||
|
||||
function textToJSON(text: string): JSONContent {
|
||||
return {
|
||||
type: 'doc',
|
||||
|
||||
@@ -81,9 +81,6 @@ export function AccountDropdownList(props: AccountDropdownListProps) {
|
||||
<SquareUserRound className="h-4 w-4 stroke-[2.5px] text-slate-400 group-hover:text-white" />
|
||||
My Profile
|
||||
</span>
|
||||
<span className="rounded-xs bg-yellow-300 px-1 text-xs tracking-wide text-black uppercase">
|
||||
New
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li className="px-1">
|
||||
|
||||
@@ -15,7 +15,7 @@ import type { BuiltInRoadmap } from '../components/Dashboard/PersonalDashboard';
|
||||
const roadmaps = await listOfficialRoadmaps();
|
||||
const roleRoadmaps = roadmaps.filter((roadmap) => roadmap.type === 'role');
|
||||
const skillRoadmaps = roadmaps.filter((roadmap) => roadmap.type === 'skill');
|
||||
const bestPractices = await getAllBestPractices();
|
||||
const bestPractices = roadmaps.filter(roadmap => roadmap.type === 'best-practice');
|
||||
const questionGroups = await getAllQuestionGroups();
|
||||
const guides = await listOfficialGuides();
|
||||
const videos = await getAllVideos();
|
||||
@@ -48,15 +48,18 @@ const enrichedSkillRoadmaps: BuiltInRoadmap[] = skillRoadmaps.map((roadmap) => {
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const enrichedBestPractices = bestPractices.map((bestPractice) => {
|
||||
const { frontmatter } = bestPractice;
|
||||
|
||||
const enrichedBestPracticeRoadmaps: BuiltInRoadmap[] = bestPractices.map((roadmap) => {
|
||||
return {
|
||||
id: bestPractice.id,
|
||||
url: `/best-practices/${bestPractice.id}`,
|
||||
title: frontmatter.briefTitle,
|
||||
description: frontmatter.briefDescription,
|
||||
id: roadmap.slug,
|
||||
url: `/${roadmap.slug}`,
|
||||
title: roadmap.title.card,
|
||||
description: roadmap.description,
|
||||
relatedRoadmapIds: roadmap.relatedRoadmaps,
|
||||
renderer: 'editor',
|
||||
isNew: isNewRoadmap(roadmap.createdAt),
|
||||
metadata: {
|
||||
tags: ['best-practice-roadmap'],
|
||||
},
|
||||
};
|
||||
});
|
||||
---
|
||||
@@ -65,7 +68,7 @@ const enrichedBestPractices = bestPractices.map((bestPractice) => {
|
||||
<DashboardPage
|
||||
builtInRoleRoadmaps={enrichedRoleRoadmaps}
|
||||
builtInSkillRoadmaps={enrichedSkillRoadmaps}
|
||||
builtInBestPractices={enrichedBestPractices}
|
||||
builtInBestPractices={enrichedBestPracticeRoadmaps}
|
||||
questionGroups={questionGroups}
|
||||
guides={guides.slice(0, 10)}
|
||||
videos={videos.slice(0, 10)}
|
||||
|
||||
Reference in New Issue
Block a user