mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-12 17:51:53 +08:00
Merge branch 'master' into content/spring-boot
This commit is contained in:
BIN
public/images/devops-ebook.png
Normal file
BIN
public/images/devops-ebook.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
@@ -2,7 +2,8 @@
|
||||
---
|
||||
|
||||
<script src='./analytics.js'></script>
|
||||
<script async src='https://www.googletagmanager.com/gtag/js?id=UA-139582634-1'></script>
|
||||
<script async src='https://www.googletagmanager.com/gtag/js?id=UA-139582634-1'
|
||||
></script>
|
||||
<script is:inline>
|
||||
// @ts-nocheck
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
@@ -12,4 +13,29 @@
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'UA-139582634-1');
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
let trackEl = e.target;
|
||||
if (!trackEl.getAttribute('ga-category')) {
|
||||
trackEl = trackEl.closest('[ga-category]');
|
||||
}
|
||||
|
||||
if (!trackEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const category = trackEl.getAttribute('ga-category');
|
||||
const action = trackEl.getAttribute('ga-action');
|
||||
const label = trackEl.getAttribute('ga-label');
|
||||
|
||||
if (!category) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.fireEvent({
|
||||
category,
|
||||
action,
|
||||
label,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -34,9 +34,21 @@ import CaptchaFields from './Captcha/CaptchaFields.astro';
|
||||
type='submit'
|
||||
name='submit'
|
||||
class='text-white bg-gradient-to-r from-amber-700 to-blue-800 hover:from-amber-800 hover:to-blue-900 font-regular rounded-md text-md px-5 py-2.5 w-full text-center mr-2'
|
||||
onclick="window.fireEvent({ category: 'Subscription', action: 'Submitted Popup Form', label: 'Download Roadmap Popup' })"
|
||||
submit-download-form
|
||||
>
|
||||
Send Link
|
||||
</button>
|
||||
</form>
|
||||
</Popup>
|
||||
|
||||
<script>
|
||||
document
|
||||
.querySelector('[submit-download-form]')
|
||||
?.addEventListener('click', () => {
|
||||
window.fireEvent({
|
||||
category: 'Subscription',
|
||||
action: 'Submitted Popup Form',
|
||||
label: 'Download Roadmap Popup',
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<div class='leading-relaxed text-left p-4 text-md text-gray-800 border-t border-t-gray-300 bg-gray-100 rounded-bl-md rounded-br-md'>
|
||||
<div class='text-sm sm:text-base leading-relaxed text-left p-2 sm:p-4 text-md text-gray-800 border-t border-t-gray-300 bg-gray-100 rounded-bl-md rounded-br-md'>
|
||||
<slot />
|
||||
</div>
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class='border-t bg-gray-100'>
|
||||
<div class='container'>
|
||||
<div class='flex justify-between relative -top-5'>
|
||||
<h1 class='text-md font-medium py-1 px-3 border bg-white rounded-md'>
|
||||
<h1 class='text-sm sm:text-base font-medium py-1 px-3 border bg-white rounded-md'>
|
||||
Frequently Asked Questions
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -14,9 +14,9 @@ const { question, isActive = false } = Astro.props;
|
||||
>
|
||||
<button
|
||||
faq-question
|
||||
class='flex flex-row justify-between items-center p-3 w-full'
|
||||
class='flex flex-row justify-between items-center p-2 sm:p-3 w-full'
|
||||
>
|
||||
<span class='text-md text-left font-medium'>{question}</span>
|
||||
<span class='text-sm sm:text-base text-left font-medium'>{question}</span>
|
||||
<Icon icon='down' class='h-6 hidden sm:block text-gray-400' />
|
||||
</button>
|
||||
<div class:list={['answer', { hidden: !isActive }]} faq-answer>
|
||||
|
||||
@@ -84,23 +84,29 @@ import Icon from './Icon.astro';
|
||||
<div class='text-gray-400 text-sm'>
|
||||
<p>
|
||||
<a
|
||||
href='https://thenewstack.io/category/devops/'
|
||||
href='https://thenewstack.io/category/devops?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Footer'
|
||||
target='_blank'
|
||||
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Footer` })"
|
||||
ga-category='PartnerClick'
|
||||
ga-action='TNS Referral'
|
||||
ga-label='TNS Referral - Footer'
|
||||
class='text-gray-400 hover:text-white'>DevOps</a
|
||||
>
|
||||
<span class='mx-1.5'>·</span>
|
||||
<a
|
||||
href='https://thenewstack.io/category/kubernetes/'
|
||||
href='https://thenewstack.io/category/kubernetes?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Footer'
|
||||
target='_blank'
|
||||
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Footer` })"
|
||||
ga-category='PartnerClick'
|
||||
ga-action='TNS Referral'
|
||||
ga-label='TNS Referral - Footer'
|
||||
class='text-gray-400 hover:text-white'>Kubernetes</a
|
||||
>
|
||||
<span class='mx-1.5'>·</span>
|
||||
<a
|
||||
href='https://thenewstack.io/category/cloud-native/'
|
||||
href='https://thenewstack.io/category/cloud-native?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Footer'
|
||||
target='_blank'
|
||||
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Footer` })"
|
||||
ga-category='PartnerClick'
|
||||
ga-action='TNS Referral'
|
||||
ga-label='TNS Referral - Footer'
|
||||
class='text-gray-400 hover:text-white'>Cloud-Native</a
|
||||
>
|
||||
</p>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
import DownloadPopup from "../DownloadPopup.astro";
|
||||
import Loader from "../Loader.astro";
|
||||
import ShareIcons from "../ShareIcons.astro";
|
||||
import SubscribePopup from "../SubscribePopup.astro";
|
||||
import TopicOverlay from "../TopicOverlay.astro";
|
||||
import "./InteractiveRoadmap.css";
|
||||
import DownloadPopup from '../DownloadPopup.astro';
|
||||
import Loader from '../Loader.astro';
|
||||
import ShareIcons from '../ShareIcons.astro';
|
||||
import SubscribePopup from '../SubscribePopup.astro';
|
||||
import TopicOverlay from '../TopicOverlay.astro';
|
||||
import './InteractiveRoadmap.css';
|
||||
|
||||
export interface Props {
|
||||
roadmapId: string;
|
||||
@@ -16,31 +16,32 @@ export interface Props {
|
||||
};
|
||||
}
|
||||
|
||||
const { roadmapId, jsonUrl, dimensions = null, description } =
|
||||
Astro.props;
|
||||
const { roadmapId, jsonUrl, dimensions = null, description } = Astro.props;
|
||||
---
|
||||
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/balsamiq.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
rel='preload'
|
||||
href='/fonts/balsamiq.woff2'
|
||||
as='font'
|
||||
type='font/woff2'
|
||||
crossorigin
|
||||
slot="after-header"
|
||||
slot='after-header'
|
||||
/>
|
||||
|
||||
<div class="bg-gray-50 py-4 sm:py-12">
|
||||
<div class="max-w-[1000px] container relative">
|
||||
<div class='bg-gray-50 py-4 sm:py-12'>
|
||||
<div class='max-w-[1000px] container relative'>
|
||||
<ShareIcons
|
||||
description={description}
|
||||
pageUrl={`https://roadmap.sh/${roadmapId}`}
|
||||
/>
|
||||
<DownloadPopup />
|
||||
<SubscribePopup />
|
||||
<TopicOverlay />
|
||||
<TopicOverlay roadmapId={roadmapId} />
|
||||
<div
|
||||
id="roadmap-svg"
|
||||
style={dimensions ? `--aspect-ratio:${dimensions.width}/${dimensions.height}` : null}
|
||||
id='roadmap-svg'
|
||||
style={dimensions
|
||||
? `--aspect-ratio:${dimensions.width}/${dimensions.height}`
|
||||
: null}
|
||||
data-roadmap-id={roadmapId}
|
||||
data-json-url={jsonUrl}
|
||||
>
|
||||
@@ -49,4 +50,4 @@ const { roadmapId, jsonUrl, dimensions = null, description } =
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="./roadmap.js"></script>
|
||||
<script src='./roadmap.js'></script>
|
||||
|
||||
@@ -1,204 +1,219 @@
|
||||
export class Topic {
|
||||
constructor() {
|
||||
this.overlayId = 'topic-overlay';
|
||||
this.contentId = 'topic-content';
|
||||
this.loaderId = 'topic-loader';
|
||||
this.topicBodyId = 'topic-body';
|
||||
this.topicActionsId = 'topic-actions';
|
||||
this.markTopicDoneId = 'mark-topic-done';
|
||||
this.markTopicPendingId = 'mark-topic-pending';
|
||||
this.closeTopicId = 'close-topic';
|
||||
|
||||
this.activeRoadmapId = null;
|
||||
this.activeTopicId = null;
|
||||
|
||||
this.handleTopicClick = this.handleTopicClick.bind(this);
|
||||
|
||||
this.close = this.close.bind(this);
|
||||
this.resetDOM = this.resetDOM.bind(this);
|
||||
this.populate = this.populate.bind(this);
|
||||
this.handleOverlayClick = this.handleOverlayClick.bind(this);
|
||||
this.markAsDone = this.markAsDone.bind(this);
|
||||
this.markAsPending = this.markAsPending.bind(this);
|
||||
this.queryRoadmapElementsByTopicId = this.queryRoadmapElementsByTopicId.bind(this);
|
||||
|
||||
this.init = this.init.bind(this);
|
||||
constructor() {
|
||||
this.overlayId = 'topic-overlay';
|
||||
this.contentId = 'topic-content';
|
||||
this.loaderId = 'topic-loader';
|
||||
this.topicBodyId = 'topic-body';
|
||||
this.topicActionsId = 'topic-actions';
|
||||
this.markTopicDoneId = 'mark-topic-done';
|
||||
this.markTopicPendingId = 'mark-topic-pending';
|
||||
this.closeTopicId = 'close-topic';
|
||||
this.contributionTextId = 'contrib-meta';
|
||||
|
||||
this.activeRoadmapId = null;
|
||||
this.activeTopicId = null;
|
||||
|
||||
this.handleTopicClick = this.handleTopicClick.bind(this);
|
||||
|
||||
this.close = this.close.bind(this);
|
||||
this.resetDOM = this.resetDOM.bind(this);
|
||||
this.populate = this.populate.bind(this);
|
||||
this.handleOverlayClick = this.handleOverlayClick.bind(this);
|
||||
this.markAsDone = this.markAsDone.bind(this);
|
||||
this.markAsPending = this.markAsPending.bind(this);
|
||||
this.queryRoadmapElementsByTopicId =
|
||||
this.queryRoadmapElementsByTopicId.bind(this);
|
||||
|
||||
this.init = this.init.bind(this);
|
||||
}
|
||||
|
||||
get loaderEl() {
|
||||
return document.getElementById(this.loaderId);
|
||||
}
|
||||
|
||||
get markTopicDoneEl() {
|
||||
return document.getElementById(this.markTopicDoneId);
|
||||
}
|
||||
|
||||
get markTopicPendingEl() {
|
||||
return document.getElementById(this.markTopicPendingId);
|
||||
}
|
||||
|
||||
get topicActionsEl() {
|
||||
return document.getElementById(this.topicActionsId);
|
||||
}
|
||||
|
||||
get contributionTextEl() {
|
||||
return document.getElementById(this.contributionTextId);
|
||||
}
|
||||
|
||||
get contentEl() {
|
||||
return document.getElementById(this.contentId);
|
||||
}
|
||||
|
||||
get overlayEl() {
|
||||
return document.getElementById(this.overlayId);
|
||||
}
|
||||
|
||||
resetDOM(hideOverlay = false) {
|
||||
if (hideOverlay) {
|
||||
this.overlayEl.classList.add('hidden');
|
||||
} else {
|
||||
this.overlayEl.classList.remove('hidden');
|
||||
}
|
||||
|
||||
get loaderEl() {
|
||||
return document.getElementById(this.loaderId);
|
||||
}
|
||||
|
||||
get markTopicDoneEl() {
|
||||
return document.getElementById(this.markTopicDoneId);
|
||||
}
|
||||
|
||||
get markTopicPendingEl() {
|
||||
return document.getElementById(this.markTopicPendingId);
|
||||
}
|
||||
|
||||
get topicActionsEl() {
|
||||
return document.getElementById(this.topicActionsId);
|
||||
}
|
||||
|
||||
get contentEl() {
|
||||
return document.getElementById(this.contentId);
|
||||
}
|
||||
|
||||
get overlayEl() {
|
||||
return document.getElementById(this.overlayId);
|
||||
}
|
||||
|
||||
resetDOM(hideOverlay = false) {
|
||||
if (hideOverlay) {
|
||||
this.overlayEl.classList.add('hidden');
|
||||
} else {
|
||||
this.overlayEl.classList.remove('hidden');
|
||||
}
|
||||
|
||||
this.loaderEl.classList.remove('hidden'); // Show loader
|
||||
this.topicActionsEl.classList.add('hidden'); // Hide Actions
|
||||
this.contentEl.replaceChildren(''); // Remove content
|
||||
}
|
||||
|
||||
close() {
|
||||
this.resetDOM(true);
|
||||
|
||||
this.activeRoadmapId = null;
|
||||
this.activeTopicId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | HTMLElement} html
|
||||
*/
|
||||
populate(html) {
|
||||
this.contentEl.replaceChildren(html);
|
||||
this.loaderEl.classList.add('hidden');
|
||||
this.topicActionsEl.classList.remove('hidden');
|
||||
|
||||
const normalizedGroup = (this.activeTopicId || '').replace(/^\d+-/, '');
|
||||
const isDone = localStorage.getItem(normalizedGroup) === 'done';
|
||||
|
||||
if (isDone) {
|
||||
this.markTopicDoneEl.classList.add('hidden');
|
||||
this.markTopicPendingEl.classList.remove('hidden');
|
||||
} else {
|
||||
this.markTopicDoneEl.classList.remove('hidden');
|
||||
this.markTopicPendingEl.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
fetchTopicHtml(roadmapId, topicId) {
|
||||
const topicPartial = topicId.replace(/^\d+-/, '').replaceAll(/:/g, '/');
|
||||
const fullUrl = `/${roadmapId}/${topicPartial}/`;
|
||||
|
||||
return fetch(fullUrl)
|
||||
.then((res) => {
|
||||
return res.text();
|
||||
})
|
||||
.then((topicHtml) => {
|
||||
// It's full HTML with page body, head etc.
|
||||
// We only need the inner HTML of the #main-content
|
||||
const node = new DOMParser().parseFromString(topicHtml, 'text/html');
|
||||
|
||||
return node.getElementById('main-content');
|
||||
});
|
||||
}
|
||||
|
||||
handleTopicClick(e) {
|
||||
const { roadmapId, topicId } = e.detail;
|
||||
if (!topicId || !roadmapId) {
|
||||
console.log('Missing topic or roadmap: ', e.detail);
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeRoadmapId = roadmapId;
|
||||
this.activeTopicId = topicId;
|
||||
|
||||
if (/^ext_link/.test(topicId)) {
|
||||
window.open(`https://${topicId.replace('ext_link:', '')}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.resetDOM();
|
||||
this.fetchTopicHtml(roadmapId, topicId)
|
||||
.then((content) => {
|
||||
this.populate(content);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
this.populate('Error loading the content!');
|
||||
});
|
||||
}
|
||||
|
||||
queryRoadmapElementsByTopicId(topicId) {
|
||||
const elements = document.querySelectorAll(`[data-group-id$="-${topicId}"]`);
|
||||
const matchingElements = [];
|
||||
|
||||
elements.forEach((element) => {
|
||||
const foundGroupId = element?.dataset?.groupId || '';
|
||||
const validGroupRegex = new RegExp(`^\\d+-${topicId}$`);
|
||||
|
||||
if (validGroupRegex.test(foundGroupId)) {
|
||||
matchingElements.push(element);
|
||||
}
|
||||
});
|
||||
|
||||
return matchingElements;
|
||||
}
|
||||
|
||||
markAsDone(topicId) {
|
||||
const updatedTopicId = topicId.replace(/^\d+-/, '');
|
||||
localStorage.setItem(updatedTopicId, 'done');
|
||||
|
||||
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
|
||||
item?.classList?.add('done');
|
||||
});
|
||||
}
|
||||
|
||||
markAsPending(topicId) {
|
||||
const updatedTopicId = topicId.replace(/^\d+-/, '');
|
||||
|
||||
localStorage.removeItem(updatedTopicId);
|
||||
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
|
||||
item?.classList?.remove('done');
|
||||
});
|
||||
}
|
||||
|
||||
handleOverlayClick(e) {
|
||||
const isClickedInsideTopic = e.target.closest(`#${this.topicBodyId}`);
|
||||
|
||||
if (!isClickedInsideTopic) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
const isClickedDone = e.target.id === this.markTopicDoneId || e.target.closest(`#${this.markTopicDoneId}`);
|
||||
if (isClickedDone) {
|
||||
this.markAsDone(this.activeTopicId);
|
||||
this.close();
|
||||
}
|
||||
|
||||
const isClickedPending = e.target.id === this.markTopicPendingId || e.target.closest(`#${this.markTopicPendingId}`);
|
||||
if (isClickedPending) {
|
||||
this.markAsPending(this.activeTopicId);
|
||||
this.close();
|
||||
}
|
||||
|
||||
const isClickedClose = e.target.id === this.closeTopicId || e.target.closest(`#${this.closeTopicId}`);
|
||||
if (isClickedClose) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
window.addEventListener('topic.click', this.handleTopicClick);
|
||||
window.addEventListener('click', this.handleOverlayClick);
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (e.key.toLowerCase() === 'escape') {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
this.loaderEl.classList.remove('hidden'); // Show loader
|
||||
this.topicActionsEl.classList.add('hidden'); // Hide Actions
|
||||
this.contributionTextEl.classList.add('hidden'); // Hide contribution text
|
||||
this.contentEl.replaceChildren(''); // Remove content
|
||||
}
|
||||
|
||||
close() {
|
||||
this.resetDOM(true);
|
||||
|
||||
this.activeRoadmapId = null;
|
||||
this.activeTopicId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | HTMLElement} html
|
||||
*/
|
||||
populate(html) {
|
||||
this.contentEl.replaceChildren(html);
|
||||
this.loaderEl.classList.add('hidden');
|
||||
this.topicActionsEl.classList.remove('hidden');
|
||||
this.contributionTextEl.classList.remove('hidden');
|
||||
|
||||
const normalizedGroup = (this.activeTopicId || '').replace(/^\d+-/, '');
|
||||
const isDone = localStorage.getItem(normalizedGroup) === 'done';
|
||||
|
||||
if (isDone) {
|
||||
this.markTopicDoneEl.classList.add('hidden');
|
||||
this.markTopicPendingEl.classList.remove('hidden');
|
||||
} else {
|
||||
this.markTopicDoneEl.classList.remove('hidden');
|
||||
this.markTopicPendingEl.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fetchTopicHtml(roadmapId, topicId) {
|
||||
const topicPartial = topicId.replace(/^\d+-/, '').replaceAll(/:/g, '/');
|
||||
const fullUrl = `/${roadmapId}/${topicPartial}/`;
|
||||
|
||||
return fetch(fullUrl)
|
||||
.then((res) => {
|
||||
return res.text();
|
||||
})
|
||||
.then((topicHtml) => {
|
||||
// It's full HTML with page body, head etc.
|
||||
// We only need the inner HTML of the #main-content
|
||||
const node = new DOMParser().parseFromString(topicHtml, 'text/html');
|
||||
|
||||
return node.getElementById('main-content');
|
||||
});
|
||||
}
|
||||
|
||||
handleTopicClick(e) {
|
||||
const { roadmapId, topicId } = e.detail;
|
||||
if (!topicId || !roadmapId) {
|
||||
console.log('Missing topic or roadmap: ', e.detail);
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeRoadmapId = roadmapId;
|
||||
this.activeTopicId = topicId;
|
||||
|
||||
if (/^ext_link/.test(topicId)) {
|
||||
window.open(`https://${topicId.replace('ext_link:', '')}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.resetDOM();
|
||||
this.fetchTopicHtml(roadmapId, topicId)
|
||||
.then((content) => {
|
||||
this.populate(content);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
this.populate('Error loading the content!');
|
||||
});
|
||||
}
|
||||
|
||||
queryRoadmapElementsByTopicId(topicId) {
|
||||
const elements = document.querySelectorAll(
|
||||
`[data-group-id$="-${topicId}"]`
|
||||
);
|
||||
const matchingElements = [];
|
||||
|
||||
elements.forEach((element) => {
|
||||
const foundGroupId = element?.dataset?.groupId || '';
|
||||
const validGroupRegex = new RegExp(`^\\d+-${topicId}$`);
|
||||
|
||||
if (validGroupRegex.test(foundGroupId)) {
|
||||
matchingElements.push(element);
|
||||
}
|
||||
});
|
||||
|
||||
return matchingElements;
|
||||
}
|
||||
|
||||
markAsDone(topicId) {
|
||||
const updatedTopicId = topicId.replace(/^\d+-/, '');
|
||||
localStorage.setItem(updatedTopicId, 'done');
|
||||
|
||||
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
|
||||
item?.classList?.add('done');
|
||||
});
|
||||
}
|
||||
|
||||
markAsPending(topicId) {
|
||||
const updatedTopicId = topicId.replace(/^\d+-/, '');
|
||||
|
||||
localStorage.removeItem(updatedTopicId);
|
||||
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
|
||||
item?.classList?.remove('done');
|
||||
});
|
||||
}
|
||||
|
||||
handleOverlayClick(e) {
|
||||
const isClickedInsideTopic = e.target.closest(`#${this.topicBodyId}`);
|
||||
|
||||
if (!isClickedInsideTopic) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
const isClickedDone =
|
||||
e.target.id === this.markTopicDoneId ||
|
||||
e.target.closest(`#${this.markTopicDoneId}`);
|
||||
if (isClickedDone) {
|
||||
this.markAsDone(this.activeTopicId);
|
||||
this.close();
|
||||
}
|
||||
|
||||
const isClickedPending =
|
||||
e.target.id === this.markTopicPendingId ||
|
||||
e.target.closest(`#${this.markTopicPendingId}`);
|
||||
if (isClickedPending) {
|
||||
this.markAsPending(this.activeTopicId);
|
||||
this.close();
|
||||
}
|
||||
|
||||
const isClickedClose =
|
||||
e.target.id === this.closeTopicId ||
|
||||
e.target.closest(`#${this.closeTopicId}`);
|
||||
if (isClickedClose) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
window.addEventListener('topic.click', this.handleTopicClick);
|
||||
window.addEventListener('click', this.handleOverlayClick);
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (e.key.toLowerCase() === 'escape') {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,24 +32,22 @@ import Icon from './Icon.astro';
|
||||
|
||||
<!-- Mobile Navigation Button -->
|
||||
<button
|
||||
id='show-mobile-navigation'
|
||||
class='text-gray-400 hover:text-gray-50 block sm:hidden cursor-pointer'
|
||||
aria-label='Menu'
|
||||
onclick="document.getElementById('mobile-navigation').classList.remove('hidden');"
|
||||
show-mobile-nav
|
||||
>
|
||||
<Icon icon='hamburger' />
|
||||
</button>
|
||||
|
||||
<!-- Mobile Navigation Items -->
|
||||
<div
|
||||
id='mobile-navigation'
|
||||
class='fixed top-0 bottom-0 left-0 right-0 z-40 bg-slate-900 items-center flex hidden'
|
||||
mobile-nav
|
||||
>
|
||||
<button
|
||||
id='close-mobile-navigation'
|
||||
close-mobile-nav
|
||||
class='text-gray-400 hover:text-gray-50 block cursor-pointer absolute top-6 right-6'
|
||||
aria-label='Close Menu'
|
||||
onclick="document.getElementById('mobile-navigation').classList.add('hidden');"
|
||||
>
|
||||
<Icon icon='close' />
|
||||
</button>
|
||||
@@ -80,3 +78,15 @@ import Icon from './Icon.astro';
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.querySelector('[show-mobile-nav]')?.addEventListener('click', () => {
|
||||
document.querySelector('[mobile-nav]')?.classList.remove('hidden');
|
||||
});
|
||||
|
||||
document
|
||||
.querySelector('[close-mobile-nav]')
|
||||
?.addEventListener('click', () => {
|
||||
document.querySelector('[mobile-nav]')?.classList.add('hidden');
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -7,7 +7,7 @@ const starCount = await getFormattedStars('kamranahmedse/developer-roadmap');
|
||||
|
||||
<div class='py-6 sm:py-16 border-b border-t text-left sm:text-center bg-white'>
|
||||
<div class='max-w-[600px] container'>
|
||||
<h2 class='text-3xl sm:text-5xl font-bold'>Open Source</h1>
|
||||
<h2 class='text-2xl sm:text-5xl font-bold'>Open Source</h2>
|
||||
<p class='text-gray-600 text-sm sm:text-lg leading-relaxed my-2.5 sm:my-5'>
|
||||
The project is OpenSource, <a
|
||||
href='https://github.com/search?o=desc&q=stars%3A%3E100000&s=stars&type=Repositories'
|
||||
|
||||
@@ -21,8 +21,7 @@ const { id, title, subtitle } = Astro.props;
|
||||
<div class='relative bg-white rounded-lg shadow popup-body'>
|
||||
<button
|
||||
type='button'
|
||||
class='absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center'
|
||||
onclick='this.closest(".popup").classList.add("hidden")'
|
||||
class='absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center popup-close'
|
||||
>
|
||||
<Icon icon='close' />
|
||||
<span class='sr-only'>Close popup</span>
|
||||
|
||||
@@ -31,8 +31,9 @@ export class Popup {
|
||||
const target = e.target;
|
||||
const popupBody = target.closest('.popup-body');
|
||||
const closestPopup = target.closest('.popup');
|
||||
const closeBtn = target.closest('.popup-close');
|
||||
|
||||
if (popupBody) {
|
||||
if (!closeBtn && popupBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const roadmapTitle =
|
||||
|
||||
<div
|
||||
class:list={[
|
||||
'mt-4 sm:mt-7 border rounded-md mb-0',
|
||||
'mt-4 sm:mt-7 border-0 sm:border rounded-md mb-0',
|
||||
{
|
||||
'sm:-mb-[82px]': hasTNSBanner,
|
||||
'sm:-mb-[65px]': !hasTNSBanner,
|
||||
@@ -29,10 +29,12 @@ const roadmapTitle =
|
||||
<p class='text-sm'>
|
||||
Get the latest {roadmapTitle} news from our sister site{' '}
|
||||
<a
|
||||
href='https://thenewstack.io'
|
||||
href='https://thenewstack.io?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Alert'
|
||||
target='_blank'
|
||||
class='font-semibold underline'
|
||||
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Roadmap` })"
|
||||
ga-category='PartnerClick'
|
||||
ga-action='TNS Referral'
|
||||
ga-label='TNS Referral - Roadmap'
|
||||
>
|
||||
TheNewStack.io
|
||||
</a>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
import Icon from "./Icon.astro";
|
||||
import ResourcesAlert from "./ResourcesAlert.astro";
|
||||
import TopicSearch from "./TopicSearch/TopicSearch.astro";
|
||||
import YouTubeAlert from "./YouTubeAlert.astro";
|
||||
import Icon from './Icon.astro';
|
||||
import ResourcesAlert from './ResourcesAlert.astro';
|
||||
import TopicSearch from './TopicSearch/TopicSearch.astro';
|
||||
import YouTubeAlert from './YouTubeAlert.astro';
|
||||
|
||||
export interface Props {
|
||||
title: string;
|
||||
@@ -22,50 +22,57 @@ const {
|
||||
hasTopics = false,
|
||||
} = Astro.props;
|
||||
|
||||
|
||||
const isRoadmapReady = !isUpcoming;
|
||||
---
|
||||
|
||||
<div class="border-b">
|
||||
<div class="py-5 sm:py-12 container relative">
|
||||
<div class='border-b'>
|
||||
<div class='py-5 sm:py-12 container relative'>
|
||||
<YouTubeAlert />
|
||||
|
||||
<div class="mt-0 mb-3 sm:mb-6 sm:mt-4">
|
||||
<h1 class="text-2xl sm:text-4xl mb-0.5 sm:mb-2 font-bold">
|
||||
<div class='mt-0 mb-3 sm:mb-6 sm:mt-4'>
|
||||
<h1 class='text-2xl sm:text-4xl mb-0.5 sm:mb-2 font-bold'>
|
||||
{title}
|
||||
</h1>
|
||||
<p class="text-gray-500 text-sm sm:text-lg">{description}</p>
|
||||
<p class='text-gray-500 text-sm sm:text-lg'>{description}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between">
|
||||
<div class="flex gap-1 sm:gap-2">
|
||||
<div class='flex justify-between'>
|
||||
<div class='flex gap-1 sm:gap-2'>
|
||||
{
|
||||
!hasSearch && (
|
||||
<>
|
||||
<a href='/roadmaps/' class='bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600' aria-label="Back to All Roadmaps">
|
||||
←<span class='hidden sm:inline'> All Roadmaps</span>
|
||||
</a>
|
||||
<a
|
||||
href='/roadmaps/'
|
||||
class='bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600'
|
||||
aria-label='Back to All Roadmaps'
|
||||
>
|
||||
←<span class='hidden sm:inline'> All Roadmaps</span>
|
||||
</a>
|
||||
|
||||
{isRoadmapReady && (
|
||||
<button
|
||||
data-popup="download-popup"
|
||||
class="inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500"
|
||||
aria-label="Download Roadmap"
|
||||
onclick="window.fireEvent({ category: 'Subscription', action: 'Clicked Popup Opener', label: 'Download Roadmap Popup' })"
|
||||
data-popup='download-popup'
|
||||
class='inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500'
|
||||
aria-label='Download Roadmap'
|
||||
ga-category='Subscription'
|
||||
ga-action='Clicked Popup Opener'
|
||||
ga-label='Download Roadmap Popup'
|
||||
>
|
||||
<Icon icon="download" />
|
||||
<span class="hidden sm:inline ml-2">Download</span>
|
||||
<Icon icon='download' />
|
||||
<span class='hidden sm:inline ml-2'>Download</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
data-popup="subscribe-popup"
|
||||
class="inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500"
|
||||
aria-label="Subscribe for Updates"
|
||||
onclick="window.fireEvent({ category: 'Subscription', action: 'Clicked Popup Opener', label: 'Subscribe Roadmap Popup' })"
|
||||
data-popup='subscribe-popup'
|
||||
class='inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500'
|
||||
aria-label='Subscribe for Updates'
|
||||
ga-category='Subscription'
|
||||
ga-action='Clicked Popup Opener'
|
||||
ga-label='Subscribe Roadmap Popup'
|
||||
>
|
||||
<Icon icon="email" />
|
||||
<span class="ml-2">Subscribe</span>
|
||||
<Icon icon='email' />
|
||||
<span class='ml-2'>Subscribe</span>
|
||||
</button>
|
||||
</>
|
||||
)
|
||||
@@ -75,11 +82,11 @@ const isRoadmapReady = !isUpcoming;
|
||||
hasSearch && (
|
||||
<a
|
||||
href={`/${roadmapId}/`}
|
||||
class="bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600"
|
||||
aria-label="Back to Visual Roadmap"
|
||||
class='bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600'
|
||||
aria-label='Back to Visual Roadmap'
|
||||
>
|
||||
←
|
||||
<span class="inline"> Visual Roadmap</span>
|
||||
<span class='inline'> Visual Roadmap</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
@@ -89,13 +96,13 @@ const isRoadmapReady = !isUpcoming;
|
||||
isRoadmapReady && (
|
||||
<a
|
||||
href={`https://github.com/kamranahmedse/developer-roadmap/issues/new?title=[Suggestion] ${title}`}
|
||||
target="_blank"
|
||||
class="inline-flex items-center justify-center bg-gray-500 text-white py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-gray-600"
|
||||
aria-label="Suggest Changes"
|
||||
target='_blank'
|
||||
class='inline-flex items-center justify-center bg-gray-500 text-white py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-gray-600'
|
||||
aria-label='Suggest Changes'
|
||||
>
|
||||
<Icon icon="comment" class="h-3 w-3" />
|
||||
<span class="ml-2 hidden sm:inline">Suggest Changes</span>
|
||||
<span class="ml-2 inline sm:hidden">Suggest</span>
|
||||
<Icon icon='comment' class='h-3 w-3' />
|
||||
<span class='ml-2 hidden sm:inline'>Suggest Changes</span>
|
||||
<span class='ml-2 inline sm:hidden'>Suggest</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
import type { GAEventType } from '../Analytics/analytics';
|
||||
import Icon from '../Icon.astro';
|
||||
|
||||
export type SponsorType = {
|
||||
url: string;
|
||||
@@ -25,10 +26,19 @@ const {
|
||||
id='sponsor-ad'
|
||||
target='_blank'
|
||||
rel='noopener sponsored'
|
||||
onclick={event ? `window.fireEvent(${JSON.stringify(event)})` : ''}
|
||||
ga-category={event?.category}
|
||||
ga-action={event?.action}
|
||||
ga-label={event?.label}
|
||||
class='fixed bottom-[15px] right-[20px] outline-transparent z-50 bg-white max-w-[330px] shadow-lg outline-0 hidden'
|
||||
>
|
||||
<img src={imageUrl} class='w-[100px] lg:w-[130px]' alt='Sponsor Banner' />
|
||||
<button
|
||||
class='absolute top-1.5 right-1.5 text-gray-300 hover:text-gray-800'
|
||||
aria-label='Close'
|
||||
close-sponsor
|
||||
>
|
||||
<Icon icon='close' class='h-4' />
|
||||
</button>
|
||||
<img src={imageUrl} class='h-[150px] lg:h-[169px]' alt='Sponsor Banner' />
|
||||
<span class='text-sm flex flex-col justify-between'>
|
||||
<span class='p-[10px]'>
|
||||
<span class='font-semibold mb-0.5 block'>{title}</span>
|
||||
@@ -37,3 +47,11 @@ const {
|
||||
<span class='sponsor-footer'>Partner Content</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<script>
|
||||
document.querySelector('[close-sponsor]')?.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
document.getElementById('sponsor-ad')?.classList.add('hidden');
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -33,7 +33,9 @@ import CaptchaFields from './Captcha/CaptchaFields.astro';
|
||||
type='submit'
|
||||
name='submit'
|
||||
class='text-white bg-gradient-to-r from-amber-700 to-blue-800 hover:from-amber-800 hover:to-blue-900 font-regular rounded-md text-md px-5 py-2.5 w-full text-center mr-2'
|
||||
onclick="window.fireEvent({ category: 'Subscription', action: 'Submitted Popup Form', label: 'Subscribe Roadmap Popup' })"
|
||||
ga-category='Subscription'
|
||||
ga-action='Submitted Popup Form'
|
||||
ga-label='Subscribe Roadmap Popup'
|
||||
>
|
||||
Subscribe
|
||||
</button>
|
||||
|
||||
@@ -1,31 +1,69 @@
|
||||
---
|
||||
import Icon from "./Icon.astro";
|
||||
import Loader from "./Loader.astro";
|
||||
import Icon from './Icon.astro';
|
||||
import Loader from './Loader.astro';
|
||||
|
||||
export interface Props {
|
||||
roadmapId: string;
|
||||
}
|
||||
|
||||
const { roadmapId } = Astro.props;
|
||||
const githubLink = `https://github.com/kamranahmedse/developer-roadmap/tree/master/src/roadmaps/${roadmapId}/content`;
|
||||
---
|
||||
|
||||
<div id='topic-overlay' class='hidden'>
|
||||
<div class="fixed top-0 right-0 z-40 h-screen p-4 sm:p-6 overflow-y-auto bg-white w-full sm:max-w-[600px]" tabindex="-1" id='topic-body'>
|
||||
<div id='topic-loader' class='hidden'>
|
||||
<Loader />
|
||||
</div>
|
||||
|
||||
<div id='topic-actions' class='hidden mb-2'>
|
||||
<button id='mark-topic-done' class='bg-green-600 text-white p-1 px-2 text-sm rounded-md hover:bg-green-700 inline-flex items-center'>
|
||||
<Icon icon="check" /> <span class='ml-2'>Mark as Done</span>
|
||||
</button>
|
||||
|
||||
<button id='mark-topic-pending' class='hidden bg-red-600 text-white p-1 px-2 text-sm rounded-md hover:bg-red-700 inline-flex items-center'>
|
||||
<Icon icon="reset" /> <span class='ml-2'>Mark as Pending</span>
|
||||
</button>
|
||||
|
||||
<button type="button" id='close-topic' class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 right-2.5 inline-flex items-center">
|
||||
<Icon icon="close" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id='topic-content' class='prose prose-h1:mt-7 prose-h1:mb-2.5 prose-p:mt-0 prose-p:mb-2 prose-li:m-0 prose-li:mb-0.5 prose-h2:mb-3 prose-h2:mt-0'></div>
|
||||
<div
|
||||
class='fixed top-0 right-0 z-40 h-screen p-4 sm:p-6 overflow-y-auto bg-white w-full sm:max-w-[600px]'
|
||||
tabindex='-1'
|
||||
id='topic-body'
|
||||
>
|
||||
<div id='topic-loader' class='hidden'>
|
||||
<Loader />
|
||||
</div>
|
||||
<div class="bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-30"></div>
|
||||
|
||||
<div id='topic-actions' class='hidden mb-2'>
|
||||
<button
|
||||
id='mark-topic-done'
|
||||
class='bg-green-600 text-white p-1 px-2 text-sm rounded-md hover:bg-green-700 inline-flex items-center'
|
||||
>
|
||||
<Icon icon='check' />
|
||||
<span class='ml-2'>Mark as Done</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
id='mark-topic-pending'
|
||||
class='hidden bg-red-600 text-white p-1 px-2 text-sm rounded-md hover:bg-red-700 inline-flex items-center'
|
||||
>
|
||||
<Icon icon='reset' />
|
||||
<span class='ml-2'>Mark as Pending</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type='button'
|
||||
id='close-topic'
|
||||
class='text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 right-2.5 inline-flex items-center'
|
||||
>
|
||||
<Icon icon='close' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id='topic-content'
|
||||
class='prose prose-h1:mt-7 prose-h1:mb-2.5 prose-p:mt-0 prose-p:mb-2 prose-li:m-0 prose-li:mb-0.5 prose-h2:mb-3 prose-h2:mt-0'
|
||||
>
|
||||
</div>
|
||||
|
||||
<p
|
||||
id='contrib-meta'
|
||||
class='text-gray-400 text-sm border-t pt-3 mt-10 hidden'
|
||||
>
|
||||
We are still working on this page. You can contribute by submitting a
|
||||
brief description and a few links to learn more about this topic <a
|
||||
target='_blank'
|
||||
class='underline text-blue-700'
|
||||
href={githubLink}>on GitHub repository.</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class='bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-30'>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,33 @@
|
||||
---
|
||||
import Icon from "./Icon.astro";
|
||||
import Icon from './Icon.astro';
|
||||
---
|
||||
|
||||
<div class='sticky top-0 border-b border-b-yellow-300 z-10 flex h-[37px]' id='sticky-youtube-banner'>
|
||||
<a href='https://youtube.com/theroadmap?sub_confirmation=1' target='_blank' class='flex bg-yellow-200 text-center flex-1 items-center justify-center text-sm hover:bg-yellow-300 outline-0 '>
|
||||
<Icon icon="youtube" class="mr-2" /> We now have a YouTube Channel. <span class='hidden sm:inline'>Subscribe for the video content.</span>
|
||||
</a>
|
||||
<button class='text-yellow-500 bg-yellow-200 hover:text-yellow-900 hover:bg-yellow-400 outline-0 px-2' onclick='this.parentElement.classList.add("hidden")' aria-label="Close">
|
||||
<Icon icon="close" />
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class='sticky top-0 border-b border-b-yellow-300 z-10 flex h-[37px]'
|
||||
youtube-banner
|
||||
>
|
||||
<a
|
||||
href='https://youtube.com/theroadmap?sub_confirmation=1'
|
||||
target='_blank'
|
||||
class='flex bg-yellow-200 text-center flex-1 items-center justify-center text-sm hover:bg-yellow-300 outline-0'
|
||||
>
|
||||
<Icon icon='youtube' class='mr-2' /> We now have a YouTube Channel. <span
|
||||
class='hidden sm:inline'>Subscribe for the video content.</span
|
||||
>
|
||||
</a>
|
||||
<button
|
||||
class='text-yellow-500 bg-yellow-200 hover:text-yellow-900 hover:bg-yellow-400 outline-0 px-2'
|
||||
aria-label='Close'
|
||||
close-youtube-banner
|
||||
>
|
||||
<Icon icon='close' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document
|
||||
.querySelector('[close-youtube-banner]')
|
||||
?.addEventListener('click', () => {
|
||||
document.querySelector('[youtube-banner]').remove();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -28,10 +28,7 @@ const {
|
||||
} = Astro.props;
|
||||
|
||||
// Remove trailing slashes to consider the page as canonical
|
||||
const currentPageAbsoluteUrl = `https://roadmap.sh${permalink.replace(
|
||||
/\/$/,
|
||||
''
|
||||
)}`;
|
||||
const currentPageAbsoluteUrl = `https://roadmap.sh${permalink}`;
|
||||
|
||||
const commitUrl = `https://github.com/kamranahmedse/developer-roadmap/commit/${
|
||||
import.meta.env.GITHUB_SHA
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
---
|
||||
import Answer from '../../components/FAQs/Answer.astro';
|
||||
import FAQs from '../../components/FAQs/FAQs.astro';
|
||||
import Question from '../../components/FAQs/Question.astro';
|
||||
|
||||
const salaryLink =
|
||||
'https://www.glassdoor.com/Salaries/united-states-backend-developer-salary-SRCH_IL.0,13_IN1_KO14,31.htm';
|
||||
---
|
||||
|
||||
<FAQs>
|
||||
<Question isActive question='What is Backend Development?'>
|
||||
<Answer>
|
||||
<p class='mb-3'>
|
||||
Backend web development is the part of web development that deals with
|
||||
the server-side of a web application. This includes creating and
|
||||
managing the server-side logic, connecting the application to a
|
||||
database, creating server-side APIs, handling user authentication and
|
||||
authorization, and processing and responding to user requests. It often
|
||||
involves the use of programming languages such as Python, Java, Ruby,
|
||||
PHP, JavaScript (Node.js), and .NET languages.
|
||||
</p>
|
||||
<p>
|
||||
A backend developer is responsible for the development of server-side
|
||||
components of a web application i.e. working with databases, handling
|
||||
requests, creating server-side APIs that can be consumed by frontend
|
||||
developers to retrieve and manipulate data, ensuring the scalability of
|
||||
the systems i.e. making sure that the backend can handle a high volume
|
||||
of traffic and is performant, integrating external services like payment
|
||||
gateways, message queues, cloud services, etc.
|
||||
</p>
|
||||
</Answer>
|
||||
</Question>
|
||||
|
||||
<Question question='How to become a Backend Developer?'>
|
||||
<Answer>
|
||||
<p class='mb-3'>
|
||||
If you are a complete beginner who is just getting started, you can
|
||||
start by learning <span class='bg-yellow-100'
|
||||
>a backend programming language</span
|
||||
> such as Python, Ruby, Java, Go etc. Once you have got the basic to intermediate
|
||||
understanding of the language, learn about <span class='bg-yellow-100'
|
||||
>the package manager</span
|
||||
> for that language and learn how to install and use external packages into
|
||||
your applications. Learn the basics of some <span class='bg-yellow-100'
|
||||
>relational database</span
|
||||
> e.g. PostgreSQL and learn how to run simple CRUD operations. Optionally,
|
||||
you can pick up and learn a <span class='bg-yellow-100'
|
||||
>web framework</span
|
||||
> for the language of your choice as well. Learn how to build a <span
|
||||
class='bg-yellow-100'>simple RESTful API</span
|
||||
> and implement simple Authentication/Authorization into it. While you are
|
||||
learning all the items mentioned above, don't forget to learn about <span
|
||||
class='bg-yellow-100'>Git and GitHub</span
|
||||
> as well.
|
||||
</p>
|
||||
|
||||
<p class='mb-3'>
|
||||
After following all the instructions above, you can start applying for
|
||||
the entry level backend developer jobs. Also, look at the backend
|
||||
developer roadmap above to get an idea about the landscape and see what
|
||||
else you are missing. A degree in computer science or related field is
|
||||
not always necessary but networking, building a portfolio and actively
|
||||
seeking internships, junior developer positions or consulting can help
|
||||
to start and advance a career as a backend developer.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note: remember to make a lot of projects while you are learning to
|
||||
solidify your understanding of the concepts. Also, it's important to
|
||||
have the attitude of continuous learning to improve your skills and be
|
||||
prepared for the fast-paced technology evolution in the industry.
|
||||
</p>
|
||||
</Answer>
|
||||
</Question>
|
||||
<Question question='How long does it take to become a Backend Developer?'>
|
||||
<Answer>
|
||||
<p class='mb-3'>
|
||||
The amount of time it takes to become a backend developer can vary
|
||||
depending on several factors, such as your learning pace, previous
|
||||
experience and the amount of time you are able to dedicate to learning.
|
||||
</p>
|
||||
<p>
|
||||
If you have a background in computer science or a related field, and
|
||||
have experience with programming, you may be able to become a backend
|
||||
developer relatively quickly, potentially within a few months. However,
|
||||
if you are starting with little or no prior experience or education in
|
||||
computer science, it may take longer to develop the necessary skills and
|
||||
gain the experience needed to be a proficient backend developer. It
|
||||
could take anywhere from 6 months to 2 years.
|
||||
</p>
|
||||
</Answer>
|
||||
</Question>
|
||||
<Question question='What are the Backend Developer salaries?'>
|
||||
<Answer>
|
||||
<p class='mb-3'>
|
||||
Backend developer salaries can vary depending on factors such as
|
||||
location, experience, and company size. According to data from
|
||||
Glassdoor, the average base salary for a backend developer in the United
|
||||
States is around $92,000 per year. However, this number can vary greatly
|
||||
depending on location, with the highest-paying cities such as San
|
||||
Francisco, Seattle and New York having an average salary of $120,000 to
|
||||
$135,000 per year.
|
||||
</p>
|
||||
|
||||
<p class='mb-3'>
|
||||
It's important to keep in mind that these are just averages, and
|
||||
salaries can vary greatly depending on factors such as experience level,
|
||||
specific skills, and the company you work for. With more experience and
|
||||
specific skills relevant to the job you are applying for you can expect
|
||||
to earn more.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
It is worth looking at a range of resources, including salary surveys,
|
||||
and job boards to get a general understanding of the current market in
|
||||
your location and experience level. Also try reaching out to other
|
||||
professionals in the field and getting an understanding of their own
|
||||
experience and salary ranges.
|
||||
</p>
|
||||
</Answer>
|
||||
</Question>
|
||||
|
||||
<Question question='Should I learn everything listed on the Backend Roadmap?'>
|
||||
<Answer>
|
||||
<p class='mb-3'>
|
||||
This roadmap contains everything that you might encounter while working
|
||||
as a Backend Developer. You may not need everything listed on this
|
||||
roadmap to get into the industry; every job is different and most of the
|
||||
jobs will require a subset of the items on the roadmap. However, knowing
|
||||
what you don't know is as important as knowing things, so you can use
|
||||
this roadmap to get an idea of what you are missing as well.
|
||||
</p>
|
||||
<p>
|
||||
If you are a beginner who is just getting started, don't feel
|
||||
overwhelmed by looking at this roadmap. Look at the answer to the FAQ
|
||||
"How to become a Backend Developer?"
|
||||
</p>
|
||||
</Answer>
|
||||
</Question>
|
||||
</FAQs>
|
||||
|
||||
@@ -11,14 +11,14 @@ dimensions:
|
||||
width: 968
|
||||
height: 2527.46
|
||||
sponsor:
|
||||
url: "https://www.getambassador.io/edge-stack-guide-v4?utm_source=roadmap.sh&utm_medium=ebook&utm_campaign=edgestack-guide"
|
||||
url: "https://www.tigera.io/lp/kubernetes-security-and-observability-ebook/?utm_campaign=O%27Reilly-book-final-release&utm_medium=web&utm_source=Insight-Partners-popup"
|
||||
title: "Free eBook"
|
||||
imageUrl: "https://i.imgur.com/0bH1Vl6.png"
|
||||
description: "Learn about API Gateways, Microservices, Load Balancing, and more with this free eBook."
|
||||
imageUrl: "/images/devops-ebook.png"
|
||||
description: "Learn how to secure and troubleshoot your cloud-native applications with this free eBook."
|
||||
event:
|
||||
category: "SponsorClick"
|
||||
action: "Ambassador EBook Redirect"
|
||||
label: "Clicked Ambassador EBook Link"
|
||||
action: "Tigera EBook Redirect"
|
||||
label: "Clicked Tigera EBook Link"
|
||||
seo:
|
||||
title: "DevOps Roadmap: Learn to become a DevOps Engineer or SRE"
|
||||
description: "Community driven, articles, resources, guides, interview questions, quizzes for DevOps. Learn to become a modern DevOps engineer by following the steps, skills, resources and guides listed in this roadmap."
|
||||
|
||||
@@ -11,7 +11,7 @@ const salaryLink =
|
||||
<Question isActive question='What is Frontend Development?'>
|
||||
<Answer>
|
||||
<p class='mb-3'>
|
||||
Front-end development is the devleopment of the visual and interactive
|
||||
Front-end development is the devleopment of visual and interactive
|
||||
elements of a website that users interact with directly. It's a
|
||||
combination of HTML, CSS and JavaScript, where HTML provides the
|
||||
structure, CSS the styling and layout, and JavaScript the dynamic
|
||||
@@ -77,20 +77,23 @@ const salaryLink =
|
||||
</p>
|
||||
</Answer>
|
||||
</Question>
|
||||
<Question question='What are the Frontend Developer Salaries?'>
|
||||
<Question question='What are the Frontend Developer salaries?'>
|
||||
<Answer>
|
||||
<p class='mb-3'>
|
||||
Frontend developer salaries can vary depending on factors such as
|
||||
location, experience, and company size. According to data from the
|
||||
website
|
||||
<a
|
||||
class='underline text-blue-700'
|
||||
rel='nofollow'
|
||||
target='_blank'
|
||||
href={salaryLink}>Glassdoor</a
|
||||
>, the average base salary for a frontend developer in the United States
|
||||
is around $84,454 per year, although this can range from around $55,000
|
||||
to $131,000 or more depending on the individual factors mentioned above.
|
||||
location, experience, and company size. According to data from
|
||||
Glassdoor, the average base salary for a frontend developer in the
|
||||
United States is around $80,000 per year. However, this number can vary
|
||||
greatly depending on location, with the highest-paying cities such as
|
||||
San Francisco, Seattle, and New York having an average salary of
|
||||
$110,000 to $130,000.
|
||||
</p>
|
||||
|
||||
<p class='mb-3'>
|
||||
It's important to keep in mind that these are just averages, and
|
||||
salaries can vary greatly depending on factors such as experience level,
|
||||
specific skills, and the company you work for. With more experience and
|
||||
specific skills you can expect to earn more.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
jsonUrl: "/jsons/software-design-architecture.json"
|
||||
pdfUrl: "/pdfs/software-design-architecture.pdf"
|
||||
order: 11
|
||||
featuredTitle: "Software Design"
|
||||
featuredTitle: "Design and Architecture"
|
||||
featuredDescription: "Guide to learn software design and architecture"
|
||||
title: "Software Design and Architecture"
|
||||
description: "Step by step guide to learn software design and architecture"
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
# Terminology
|
||||
|
||||
Spring Boot is a framework for building Spring-based applications. It provides a set of features and tools to help developers quickly and easily create stand-alone, production-grade Spring-based applications.
|
||||
Spring Core is the foundation of the Spring Framework, which provides a comprehensive programming and configuration model for Java-based applications. Here are some key terms and concepts related to Spring Core:
|
||||
|
||||
Here are a few key terms and concepts that are commonly used when working with Spring Boot:
|
||||
- Beans: In Spring, a "bean" is a Java object that is managed by the Spring container. Beans are typically defined using configuration metadata, which can be specified in XML, Java annotations, or Java code.
|
||||
- Inversion of Control (IoC): One of the main principles of Spring is Inversion of Control (IoC), which means that the Spring container is responsible for managing the lifecycle of beans and injecting their dependencies.
|
||||
- Dependency Injection (DI): Spring uses Dependency Injection (DI) to manage the dependencies between beans. In DI, an object's dependencies are provided to it by the container, rather than the object creating or looking up its own dependencies.
|
||||
- Container: The Spring container is the core part of the Spring Framework, which creates and manages beans and their dependencies.
|
||||
- ApplicationContext: An ApplicationContext is an implementation of the Spring container. It is responsible for loading and managing the configuration metadata and creating the beans defined in that metadata.
|
||||
- Aspect-Oriented Programming (AOP): Spring supports Aspect-Oriented Programming (AOP), which allows you to separate cross-cutting concerns, such as logging or security, from the business logic of your application.
|
||||
- Events: Spring provides an event model that allows beans to send and receive events. This is used to decouple the beans from each other, making the application more loosely coupled.
|
||||
- ApplicationEvent and listener: Spring support publish subscribe model for event handling, ApplicationEvent defines event object, and the listener is a class that implements ApplicationListener interface, listening for the specific event and take the necessary action.
|
||||
- Data Access: Spring provides a consistent, high-level abstraction for data access using various frameworks like JDBC, Hibernate, JPA.
|
||||
- Transactions: Spring provides a flexible, consistent and easy way to declaratively manage transactions with different underlying technologies such as JPA, JDBC, and Hibernate.
|
||||
- Task Execution and Scheduling: Spring provides a TaskExecutor and TaskScheduler, providing a convenient way to run tasks concurrently, on a scheduled basis or asynchronously.
|
||||
|
||||
- Starters: Spring Boot starters are a set of convenient dependency descriptors that you can include in your application to add useful libraries and frameworks to your project. For example, if you want to add Spring MVC to your project, you can include the "spring-boot-starter-web" dependency in your pom.xml file, and Spring Boot will automatically add all of the necessary dependencies to your project.
|
||||
This list is not exhaustive, it covers common terms and concepts used. Visit the following links to learn more about Spring:
|
||||
|
||||
- Auto-configuration: One of the key features of Spring Boot is its ability to automatically configure your application based on the dependencies that you have included in your project. This can include things like setting up an embedded web server, configuring a database connection, or enabling security features.
|
||||
|
||||
- Properties: Spring Boot applications can be configured using a variety of properties. These properties can be defined in a application.properties or application.yml file in the project classpath, and they can be used to configure things like the server port, database connection details, and various other aspects of the application.
|
||||
|
||||
- Spring Initializer: Spring Initializer is a web-based tool that allows you to quickly generate a basic structure for a new Spring Boot project, including a pom.xml file with the appropriate dependencies and a simple "Hello, World" application.
|
||||
|
||||
For more information visit the following links:
|
||||
|
||||
- [Spring Boot - Introduction](https://www.tutorialspoint.com/spring_boot/spring_boot_introduction.htm)
|
||||
- [Spring Boot Tutorial](https://www.digitalocean.com/community/tutorials/spring-boot-tutorial)
|
||||
- [Spring Boot - Official Website](https://spring.io/projects/spring-boot)
|
||||
- [Spring Boot - Starter Guide](https://spring.io/quickstart)
|
||||
Reference in New Issue
Block a user