feat: implement ai roadmap download

ai/roadmap
Arik Chakma 8 months ago committed by Kamran Ahmed
parent dfbb6a6dba
commit 33e4e4b3cd
  1. 2
      package.json
  2. 14
      pnpm-lock.yaml
  3. 5
      src/components/GenerateRoadmap/GenerateRoadmap.css
  4. 85
      src/components/GenerateRoadmap/GenerateRoadmap.tsx

@ -32,6 +32,7 @@
"astro": "^4.4.0",
"astro-compress": "^2.2.10",
"clsx": "^2.1.0",
"dom-to-image": "^2.6.0",
"dracula-prism": "^2.1.16",
"jose": "^5.2.2",
"js-cookie": "^3.0.5",
@ -57,6 +58,7 @@
"devDependencies": {
"@playwright/test": "^1.41.2",
"@tailwindcss/typography": "^0.5.10",
"@types/dom-to-image": "^2.6.7",
"@types/js-cookie": "^3.0.6",
"@types/prismjs": "^1.26.3",
"csv-parser": "^3.0.0",

@ -35,6 +35,9 @@ dependencies:
clsx:
specifier: ^2.1.0
version: 2.1.0
dom-to-image:
specifier: ^2.6.0
version: 2.6.0
dracula-prism:
specifier: ^2.1.16
version: 2.1.16
@ -106,6 +109,9 @@ devDependencies:
'@tailwindcss/typography':
specifier: ^0.5.10
version: 0.5.10(tailwindcss@3.4.1)
'@types/dom-to-image':
specifier: ^2.6.7
version: 2.6.7
'@types/js-cookie':
specifier: ^3.0.6
version: 3.0.6
@ -1624,6 +1630,10 @@ packages:
'@types/ms': 0.7.34
dev: false
/@types/dom-to-image@2.6.7:
resolution: {integrity: sha512-me5VbCv+fcXozblWwG13krNBvuEOm6kA5xoa4RrjDJCNFOZSWR3/QLtOXimBHk1Fisq69Gx3JtOoXtg1N1tijg==}
dev: true
/@types/estree@1.0.5:
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
dev: false
@ -2703,6 +2713,10 @@ packages:
entities: 4.5.0
dev: false
/dom-to-image@2.6.0:
resolution: {integrity: sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA==}
dev: false
/domelementtype@2.3.0:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
dev: false

@ -1,3 +1,8 @@
@font-face {
font-family: 'balsamiq';
src: url('/fonts/balsamiq.woff2');
}
svg text tspan {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;

@ -22,8 +22,7 @@ export function GenerateRoadmap() {
const [hasSubmitted, setHasSubmitted] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState(false);
const [roadmapTopic, setRoadmapTopic] = useState('');
const [nodes, setNodes] = useState<any[]>([]);
const [edges, setEdges] = useState<any[]>([]);
const [generatedRoadmap, setGeneratedRoadmap] = useState('');
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
@ -79,23 +78,7 @@ export function GenerateRoadmap() {
}
},
onStreamEnd: async (result) => {
const { nodes, edges } = generateAIRoadmapFromText(result);
setNodes(
nodes.map((node) => ({
...node,
// To reset the width and height of the node
// so that it can be calculated based on the content in the editor
width: undefined,
height: undefined,
style: {
...node.style,
width: undefined,
height: undefined,
},
})),
);
setEdges(edges);
setGeneratedRoadmap(result);
},
});
@ -105,11 +88,25 @@ export function GenerateRoadmap() {
const editGeneratedRoadmap = async () => {
pageProgressMessage.set('Redirecting to Editor');
const { nodes, edges } = generateAIRoadmapFromText(generatedRoadmap);
const { response, error } = await httpPost<{
roadmapId: string;
}>(`${import.meta.env.PUBLIC_API_URL}/v1-edit-ai-generated-roadmap`, {
title: roadmapTopic,
nodes,
nodes: nodes.map((node) => ({
...node,
// To reset the width and height of the node
// so that it can be calculated based on the content in the editor
width: undefined,
height: undefined,
style: {
...node.style,
width: undefined,
height: undefined,
},
})),
edges,
});
@ -122,6 +119,46 @@ export function GenerateRoadmap() {
window.location.href = `${import.meta.env.PUBLIC_EDITOR_APP_URL}/${response.roadmapId}`;
};
const downloadGeneratedRoadmap = async () => {
pageProgressMessage.set('Downloading Roadmap');
const node = document.getElementById('roadmap-container');
if (!node) {
toast.error('Something went wrong');
return;
}
// Append a watermark to the bottom right of the image
const watermark = document.createElement('div');
watermark.className = 'flex justify-end absolute bottom-4 right-4 gap-2';
watermark.innerHTML = `
<span
class='rounded-md bg-black py-2 px-2 text-white'
>
roadmap.sh
</span>
`;
node.insertAdjacentElement('afterbegin', watermark);
try {
const domtoimage = (await import('dom-to-image')).default;
const dataUrl = await domtoimage.toJpeg(node, {
bgcolor: 'white',
quality: 1,
});
node?.removeChild(watermark);
const link = document.createElement('a');
link.download = `${roadmapTopic}-roadmap.jpg`;
link.href = dataUrl;
link.click();
pageProgressMessage.set('');
} catch (error) {
console.error(error);
toast.error('Something went wrong');
}
};
if (!hasSubmitted) {
return (
<RoadmapSearch
@ -175,7 +212,10 @@ export function GenerateRoadmap() {
</form>
<div className="flex w-full items-center justify-between gap-2">
<div className="flex items-center justify-between gap-2">
<button className="inline-flex items-center justify-center gap-2 rounded-md bg-yellow-400 py-1.5 pl-2.5 pr-3 text-xs font-medium transition-opacity duration-300 hover:bg-yellow-500 sm:text-sm">
<button
className="inline-flex items-center justify-center gap-2 rounded-md bg-yellow-400 py-1.5 pl-2.5 pr-3 text-xs font-medium transition-opacity duration-300 hover:bg-yellow-500 sm:text-sm"
onClick={downloadGeneratedRoadmap}
>
<Download size={15} />
Download
</button>
@ -198,7 +238,8 @@ export function GenerateRoadmap() {
</div>
<div
ref={roadmapContainerRef}
className="px-4 py-5 [&>svg]:mx-auto [&>svg]:max-w-[1300px] "
id="roadmap-container"
className="relative px-4 py-5 [&>svg]:mx-auto [&>svg]:max-w-[1300px]"
/>
</section>
);

Loading…
Cancel
Save