|
|
|
@ -1,9 +1,18 @@ |
|
|
|
|
import { wireframeJSONToSVG } from 'roadmap-renderer'; |
|
|
|
|
import Cookies from 'js-cookie'; |
|
|
|
|
import { TOKEN_COOKIE_NAME } from '../../lib/jwt.ts'; |
|
|
|
|
import { httpGet } from '../../lib/http.ts'; |
|
|
|
|
import { |
|
|
|
|
renderResourceProgress, |
|
|
|
|
ResourceType, |
|
|
|
|
} from '../../lib/resource-progress'; |
|
|
|
|
|
|
|
|
|
export class Renderer { |
|
|
|
|
resourceId: string; |
|
|
|
|
resourceType: string; |
|
|
|
|
jsonUrl: string; |
|
|
|
|
loaderHTML: string | null; |
|
|
|
|
|
|
|
|
|
containerId: string; |
|
|
|
|
loaderId: string; |
|
|
|
|
|
|
|
|
|
constructor() { |
|
|
|
|
this.resourceId = ''; |
|
|
|
|
this.resourceType = ''; |
|
|
|
@ -35,83 +44,64 @@ export class Renderer { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Clone it so we can use it later
|
|
|
|
|
this.loaderHTML = this.loaderEl.innerHTML; |
|
|
|
|
this.loaderHTML = this.loaderEl!.innerHTML; |
|
|
|
|
const dataset = this.containerEl.dataset; |
|
|
|
|
|
|
|
|
|
this.resourceType = dataset.resourceType; |
|
|
|
|
this.resourceId = dataset.resourceId; |
|
|
|
|
this.jsonUrl = dataset.jsonUrl; |
|
|
|
|
this.resourceType = dataset.resourceType!; |
|
|
|
|
this.resourceId = dataset.resourceId!; |
|
|
|
|
this.jsonUrl = dataset.jsonUrl!; |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async loadProgress() { |
|
|
|
|
const token = Cookies.get(TOKEN_COOKIE_NAME); |
|
|
|
|
if (!token) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const { response, error } = await httpGet( |
|
|
|
|
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-resource-progress`, |
|
|
|
|
{ |
|
|
|
|
resourceId: this.resourceId, |
|
|
|
|
resourceType: this.resourceType, |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (!response) { |
|
|
|
|
console.error(error); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const { done } = response; |
|
|
|
|
done.forEach((topic) => { |
|
|
|
|
const topicEl = document.querySelector(`[data-group-id$="-${topic}"]`); |
|
|
|
|
|
|
|
|
|
if (topicEl) { |
|
|
|
|
topicEl.classList.add('done'); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param { string } jsonUrl |
|
|
|
|
* @returns {Promise<SVGElement>} |
|
|
|
|
*/ |
|
|
|
|
jsonToSvg(jsonUrl) { |
|
|
|
|
jsonToSvg(jsonUrl: string) { |
|
|
|
|
if (!jsonUrl) { |
|
|
|
|
console.error('jsonUrl not defined in frontmatter'); |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.containerEl.innerHTML = this.loaderHTML; |
|
|
|
|
return Promise.all([ |
|
|
|
|
fetch(jsonUrl) |
|
|
|
|
.then((res) => { |
|
|
|
|
return res.json(); |
|
|
|
|
}) |
|
|
|
|
.then((json) => { |
|
|
|
|
return wireframeJSONToSVG(json, { |
|
|
|
|
fontURL: '/fonts/balsamiq.woff2', |
|
|
|
|
}); |
|
|
|
|
}) |
|
|
|
|
.then((svg) => { |
|
|
|
|
this.containerEl.replaceChildren(svg); |
|
|
|
|
}) |
|
|
|
|
.catch((error) => { |
|
|
|
|
const message = ` |
|
|
|
|
if (!this.containerEl) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.containerEl.innerHTML = this.loaderHTML!; |
|
|
|
|
|
|
|
|
|
return fetch(jsonUrl) |
|
|
|
|
.then((res) => { |
|
|
|
|
return res.json(); |
|
|
|
|
}) |
|
|
|
|
.then((json) => { |
|
|
|
|
return wireframeJSONToSVG(json, { |
|
|
|
|
fontURL: '/fonts/balsamiq.woff2', |
|
|
|
|
}); |
|
|
|
|
}) |
|
|
|
|
.then((svg) => { |
|
|
|
|
this.containerEl?.replaceChildren(svg); |
|
|
|
|
}) |
|
|
|
|
.then(() => { |
|
|
|
|
return renderResourceProgress( |
|
|
|
|
this.resourceType as ResourceType, |
|
|
|
|
this.resourceId |
|
|
|
|
); |
|
|
|
|
}) |
|
|
|
|
.catch((error) => { |
|
|
|
|
if (!this.containerEl) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const message = ` |
|
|
|
|
<strong>There was an error.</strong><br> |
|
|
|
|
|
|
|
|
|
Try loading the page again. or submit an issue on GitHub with following:<br><br> |
|
|
|
|
|
|
|
|
|
${error.message} <br /> ${error.stack} |
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
this.containerEl.innerHTML = `<div class="error py-5 text-center text-red-600 mx-auto">${message}</div>`; |
|
|
|
|
}), |
|
|
|
|
|
|
|
|
|
this.loadProgress(), |
|
|
|
|
]); |
|
|
|
|
this.containerEl.innerHTML = `<div class="error py-5 text-center text-red-600 mx-auto">${message}</div>`; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
onDOMLoaded() { |
|
|
|
@ -129,16 +119,16 @@ export class Renderer { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switchRoadmap(newJsonUrl) { |
|
|
|
|
const newJsonFileSlug = newJsonUrl.split('/').pop().replace('.json', ''); |
|
|
|
|
switchRoadmap(newJsonUrl: string) { |
|
|
|
|
const newJsonFileSlug = newJsonUrl.split('/').pop()?.replace('.json', ''); |
|
|
|
|
|
|
|
|
|
// Update the URL and attach the new roadmap type
|
|
|
|
|
if (window?.history?.pushState) { |
|
|
|
|
const url = new URL(window.location); |
|
|
|
|
const url = new URL(window.location.href); |
|
|
|
|
const type = this.resourceType[0]; // r for roadmap, b for best-practices
|
|
|
|
|
|
|
|
|
|
url.searchParams.delete(type); |
|
|
|
|
url.searchParams.set(type, newJsonFileSlug); |
|
|
|
|
url.searchParams.set(type, newJsonFileSlug!); |
|
|
|
|
|
|
|
|
|
window.history.pushState(null, '', url.toString()); |
|
|
|
|
} |
|
|
|
@ -154,13 +144,13 @@ export class Renderer { |
|
|
|
|
label: `${newJsonFileSlug}`, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.jsonToSvg(newJsonUrl).then(() => { |
|
|
|
|
this.containerEl.setAttribute('style', ''); |
|
|
|
|
this.jsonToSvg(newJsonUrl)?.then(() => { |
|
|
|
|
this.containerEl?.setAttribute('style', ''); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
handleSvgClick(e) { |
|
|
|
|
const targetGroup = e.target.closest('g') || {}; |
|
|
|
|
handleSvgClick(e: any) { |
|
|
|
|
const targetGroup = e.target?.closest('g') || {}; |
|
|
|
|
const groupId = targetGroup.dataset ? targetGroup.dataset.groupId : ''; |
|
|
|
|
if (!groupId) { |
|
|
|
|
return; |