mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-17 20:31:54 +08:00
Compare commits
2 Commits
feat/roadm
...
feat/devop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c0adf22b6 | ||
|
|
6a4481a3d0 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,3 @@
|
||||
.idea
|
||||
|
||||
# build output
|
||||
dist/
|
||||
.output/
|
||||
|
||||
@@ -5,7 +5,6 @@ import tailwind from '@astrojs/tailwind';
|
||||
import compress from 'astro-compress';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import rehypeExternalLinks from 'rehype-external-links';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { serializeSitemap, shouldIndexPage } from './sitemap.mjs';
|
||||
|
||||
// https://astro.build/config
|
||||
@@ -46,22 +45,6 @@ export default defineConfig({
|
||||
format: 'file',
|
||||
},
|
||||
integrations: [
|
||||
{
|
||||
name: 'client-authenticated',
|
||||
hooks: {
|
||||
'astro:config:setup'(options) {
|
||||
options.addClientDirective({
|
||||
name: 'authenticated',
|
||||
entrypoint: fileURLToPath(
|
||||
new URL(
|
||||
'./src/directives/client-authenticated.mjs',
|
||||
import.meta.url
|
||||
)
|
||||
),
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
tailwind({
|
||||
config: {
|
||||
applyBaseStyles: false,
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
"@astrojs/tailwind": "^3.1.3",
|
||||
"@fingerprintjs/fingerprintjs": "^3.4.1",
|
||||
"@nanostores/preact": "^0.5.0",
|
||||
"astro": "^2.6.3",
|
||||
"astro-compress": "^1.1.47",
|
||||
"astro": "^2.5.7",
|
||||
"astro-compress": "^1.1.46",
|
||||
"jose": "^4.14.4",
|
||||
"js-cookie": "^3.0.5",
|
||||
"nanostores": "^0.9.1",
|
||||
@@ -39,7 +39,7 @@
|
||||
"tailwindcss": "^3.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.35.0",
|
||||
"@playwright/test": "^1.34.3",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/js-cookie": "^3.0.3",
|
||||
"csv-parser": "^3.0.0",
|
||||
|
||||
1808
pnpm-lock.yaml
generated
1808
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -30,9 +30,9 @@ Roadmaps are now interactive, you can click the nodes to read more about the top
|
||||
|
||||
Here is the list of available roadmaps with more being actively worked upon.
|
||||
|
||||
- [Frontend Roadmap](https://roadmap.sh/frontend) / [Frontend Beginner Roadmap](https://roadmap.sh/frontend?r=frontend-beginner)
|
||||
- [Frontend Roadmap](https://roadmap.sh/frontend)
|
||||
- [Backend Roadmap](https://roadmap.sh/backend)
|
||||
- [DevOps Roadmap](https://roadmap.sh/devops) / [DevOps Beginner Roadmap](https://roadmap.sh/devops?r=devops-beginner)
|
||||
- [DevOps Roadmap](https://roadmap.sh/devops)
|
||||
- [Full Stack Roadmap](https://roadmap.sh/full-stack)
|
||||
- [Computer Science Roadmap](https://roadmap.sh/computer-science)
|
||||
- [QA Roadmap](https://roadmap.sh/qa)
|
||||
|
||||
@@ -9,52 +9,39 @@ export interface Props {
|
||||
}
|
||||
|
||||
const sidebarLinks = [
|
||||
{
|
||||
href: '/account',
|
||||
title: 'Activity',
|
||||
id: 'activity',
|
||||
isNew: false,
|
||||
icon: {
|
||||
glyph: 'analytics',
|
||||
classes: 'h-3 w-4',
|
||||
{
|
||||
href: '/account',
|
||||
title: 'Activity',
|
||||
id: 'activity',
|
||||
icon: {
|
||||
glyph: 'analytics',
|
||||
classes: 'h-3 w-4',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
href: '/account/road-card',
|
||||
title: 'Card',
|
||||
id: 'road-card',
|
||||
isNew: true,
|
||||
icon: {
|
||||
glyph: 'badge',
|
||||
classes: 'h-4 w-4',
|
||||
{
|
||||
href: '/account/update-profile',
|
||||
title: 'Profile',
|
||||
id: 'profile',
|
||||
icon: {
|
||||
glyph: 'user',
|
||||
classes: 'h-4 w-4',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
href: '/account/update-profile',
|
||||
title: 'Profile',
|
||||
id: 'profile',
|
||||
isNew: false,
|
||||
icon: {
|
||||
glyph: 'user',
|
||||
classes: 'h-4 w-4',
|
||||
{
|
||||
href: '/account/update-password',
|
||||
title: 'Security',
|
||||
id: 'change-password',
|
||||
icon: {
|
||||
glyph: 'security',
|
||||
classes: 'h-4 w-4'
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
href: '/account/update-password',
|
||||
title: 'Security',
|
||||
id: 'change-password',
|
||||
isNew: false,
|
||||
icon: {
|
||||
glyph: 'security',
|
||||
classes: 'h-4 w-4',
|
||||
},
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
<div class='relative mb-5 block border-b p-4 shadow-inner md:hidden'>
|
||||
<button
|
||||
class='flex h-10 w-full items-center justify-between rounded-md border bg-white px-2 text-center text-sm font-medium text-gray-900'
|
||||
class='flex h-10 w-full items-center justify-between rounded-md border bg-white px-2 text-center text-gray-900 text-sm font-medium'
|
||||
id='settings-menu'
|
||||
>
|
||||
{activePageTitle}
|
||||
@@ -65,67 +52,42 @@ const sidebarLinks = [
|
||||
class='absolute left-0 right-0 z-10 mt-1 hidden space-y-1.5 bg-white p-2 shadow-lg'
|
||||
>
|
||||
{
|
||||
sidebarLinks.map((sidebarLink) => {
|
||||
const isActive = activePageId === sidebarLink.id;
|
||||
|
||||
return (
|
||||
<li>
|
||||
<a
|
||||
href={sidebarLink.href}
|
||||
class={`flex w-full items-center rounded px-3 py-1.5 text-sm text-slate-900 hover:bg-slate-200 ${
|
||||
isActive ? 'bg-slate-100' : ''
|
||||
}`}
|
||||
>
|
||||
<AstroIcon
|
||||
icon={sidebarLink.icon.glyph}
|
||||
class={`${sidebarLink.icon.classes} mr-2`}
|
||||
/>
|
||||
sidebarLinks.map((sidebarLink) => (
|
||||
<li>
|
||||
<a
|
||||
href={sidebarLink.href}
|
||||
class={`flex items-center w-full rounded px-3 py-1.5 text-slate-900 hover:bg-slate-200 text-sm ${
|
||||
activePageId === sidebarLink.id ? 'bg-slate-100' : ''
|
||||
}`}
|
||||
>
|
||||
<AstroIcon icon={sidebarLink.icon.glyph} class={`${sidebarLink.icon.classes} mr-2`} />
|
||||
{sidebarLink.title}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class='container flex min-h-screen items-stretch'>
|
||||
<!-- Start Desktop Sidebar -->
|
||||
<aside class='hidden w-44 shrink-0 border-r border-slate-200 py-10 md:block'>
|
||||
<aside class='hidden shrink-0 w-44 border-r border-slate-200 py-10 md:block'>
|
||||
<nav>
|
||||
<ul class='space-y-1'>
|
||||
{
|
||||
sidebarLinks.map((sidebarLink) => {
|
||||
const isActive = activePageId === sidebarLink.id;
|
||||
|
||||
return (
|
||||
<li>
|
||||
<a
|
||||
href={sidebarLink.href}
|
||||
class={`font-regular flex w-full items-center border-r-2 px-2 py-1.5 text-sm ${
|
||||
isActive
|
||||
? 'border-r-black bg-gray-100 text-black'
|
||||
: 'border-r-transparent text-gray-500 hover:border-r-gray-300'
|
||||
}`}
|
||||
>
|
||||
<span class='flex flex-grow items-center'>
|
||||
<AstroIcon
|
||||
icon={sidebarLink.icon.glyph}
|
||||
class={`${sidebarLink.icon.classes} mr-2`}
|
||||
/>
|
||||
{sidebarLink.title}
|
||||
</span>
|
||||
|
||||
{sidebarLink.isNew && !isActive && (
|
||||
<span class='relative mr-1 flex items-center'>
|
||||
<span class='relative rounded-full bg-gray-200 p-1 text-xs' />
|
||||
<span class='absolute bottom-0 left-0 right-0 top-0 animate-ping rounded-full bg-gray-400 p-1 text-xs' />
|
||||
</span>
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})
|
||||
sidebarLinks.map((sidebarLink) => (
|
||||
<li>
|
||||
<a
|
||||
href={sidebarLink.href}
|
||||
class={`font-regular flex w-full items-center gap-2 px-2 py-1.5 text-sm border-r-2 ${
|
||||
activePageId === sidebarLink.id ? 'text-black border-r-black bg-gray-100' : 'text-gray-500 border-r-transparent hover:border-r-gray-300'
|
||||
}`}
|
||||
>
|
||||
<AstroIcon icon={sidebarLink.icon.glyph} class={`${sidebarLink.icon.classes} mr-0`} />
|
||||
{sidebarLink.title}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import RoadmapIcon from '../../icons/roadmap.svg';
|
||||
import CheckIcon from '../../icons/roadmap.svg';
|
||||
|
||||
export function EmptyActivity() {
|
||||
return (
|
||||
@@ -6,7 +6,7 @@ export function EmptyActivity() {
|
||||
<div class="flex flex-col items-center p-7 text-center">
|
||||
<img
|
||||
alt="no roadmaps"
|
||||
src={RoadmapIcon}
|
||||
src={CheckIcon}
|
||||
class="mb-2 w-[60px] h-[60px] sm:h-[120px] sm:w-[120px] opacity-10"
|
||||
/>
|
||||
<h2 class="text-lg sm:text-xl font-bold">No Progress</h2>
|
||||
|
||||
@@ -34,7 +34,6 @@ function handleGuest() {
|
||||
const authenticatedRoutes = [
|
||||
'/account/update-profile',
|
||||
'/account/update-password',
|
||||
'/account/road-card',
|
||||
'/account',
|
||||
];
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ const { isUpcoming = false, isNew = false, text, url } = Astro.props;
|
||||
|
||||
<a
|
||||
class:list={[
|
||||
'group border border-slate-800 bg-slate-900 p-2.5 sm:p-3.5 block no-underline rounded-lg relative text-slate-400 font-regular text-md hover:border-slate-600 hover:text-slate-100 overflow-hidden',
|
||||
'group border border-slate-800 bg-slate-900 p-2.5 sm:p-3.5 block no-underline rounded-lg relative text-slate-400 font-regular text-md hover:border-slate-600 hover:text-slate-100',
|
||||
{
|
||||
'opacity-50': isUpcoming,
|
||||
},
|
||||
]}
|
||||
href={url}
|
||||
>
|
||||
<span class='text-slate-400 relative z-20'>
|
||||
<span class='text-slate-400'>
|
||||
{text}
|
||||
</span>
|
||||
|
||||
@@ -47,5 +47,4 @@ const { isUpcoming = false, isNew = false, text, url } = Astro.props;
|
||||
</span>
|
||||
)
|
||||
}
|
||||
<span data-progress class="z-10 bg-[#172a3a] absolute top-0 left-0 bottom-0 duration-300 transition-[width] w-0"></span>
|
||||
</a>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
type CheckIconProps = {
|
||||
additionalClasses?: string;
|
||||
};
|
||||
|
||||
export function CheckIcon(props: CheckIconProps) {
|
||||
const { additionalClasses = 'mr-2 top-[0.5px] w-[20px] h-[20px]' } = props;
|
||||
|
||||
return (
|
||||
<svg
|
||||
className={`relative ${additionalClasses}]`}
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
stroke-width="0"
|
||||
viewBox="0 0 16 16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { CheckIcon } from './CheckIcon';
|
||||
|
||||
type EmptyProgressProps = {
|
||||
title?: string;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export function EmptyProgress(props: EmptyProgressProps) {
|
||||
const {
|
||||
title = 'Start learning ..',
|
||||
message = 'Your progress and favorite roadmaps will show up here.',
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className="relative flex min-h-full flex-col items-start sm:items-center justify-center py-6">
|
||||
<h2 className={'mb-1 flex items-center text-lg sm:text-2xl text-gray-200'}>
|
||||
<CheckIcon additionalClasses='mr-2 top-[0.5px] w-[16px] h-[16px] sm:w-[20px] sm:h-[20px]' />
|
||||
Start learning ..
|
||||
</h2>
|
||||
<p className={'text-gray-400 text-sm sm:text-base'}>{message}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
import { EmptyProgress } from './EmptyProgress';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import { ProgressList } from './ProgressList';
|
||||
|
||||
export type UserProgressResponse = {
|
||||
resourceId: string;
|
||||
resourceType: 'roadmap' | 'best-practice';
|
||||
resourceTitle: string;
|
||||
done: number;
|
||||
learning: number;
|
||||
skipped: number;
|
||||
total: number;
|
||||
updatedAt: Date;
|
||||
}[];
|
||||
|
||||
function renderProgress(progressList: UserProgressResponse) {
|
||||
progressList.forEach((progress) => {
|
||||
const href =
|
||||
progress.resourceType === 'best-practice'
|
||||
? `/best-practices/${progress.resourceId}`
|
||||
: `/${progress.resourceId}`;
|
||||
const element = document.querySelector(`a[href="${href}"]`);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const totalDone = progress.done + progress.skipped;
|
||||
const percentageDone = (totalDone / progress.total) * 100;
|
||||
|
||||
const progressBar: HTMLElement | null =
|
||||
element.querySelector('[data-progress]');
|
||||
if (progressBar) {
|
||||
progressBar.style.width = `${percentageDone}%`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function FavoriteRoadmaps() {
|
||||
const [isPreparing, setIsPreparing] = useState(true);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [progress, setProgress] = useState<UserProgressResponse>([]);
|
||||
const [containerOpacity, setContainerOpacity] = useState(0);
|
||||
|
||||
function showProgressContainer() {
|
||||
const heroEl = document.getElementById('hero-text')!;
|
||||
if (!heroEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
heroEl.classList.add('opacity-0');
|
||||
setTimeout(() => {
|
||||
heroEl.parentElement?.removeChild(heroEl);
|
||||
setIsPreparing(false);
|
||||
|
||||
setTimeout(() => {
|
||||
setContainerOpacity(100);
|
||||
}, 50);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
async function loadProgress() {
|
||||
setIsLoading(true);
|
||||
const { response: progressList, error } =
|
||||
await httpGet<UserProgressResponse>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-all-progress`
|
||||
);
|
||||
|
||||
if (error || !progressList) {
|
||||
return;
|
||||
}
|
||||
|
||||
setProgress(progressList);
|
||||
setIsLoading(false);
|
||||
showProgressContainer();
|
||||
|
||||
// render progress on featured items
|
||||
renderProgress(progressList);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadProgress().finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (isPreparing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasProgress = progress.length > 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
class={`flex min-h-[192px] bg-gradient-to-b transition-opacity duration-500 sm:min-h-[280px] opacity-${containerOpacity} ${
|
||||
hasProgress && `border-t border-t-[#1e293c]`
|
||||
}`}
|
||||
>
|
||||
<div className="container min-h-full">
|
||||
{isLoading && <EmptyProgress title="Loading progress .." />}
|
||||
{!isLoading && progress.length == 0 && <EmptyProgress />}
|
||||
{!isLoading && progress.length > 0 && (
|
||||
<ProgressList progress={progress} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
import { FavoriteRoadmaps } from './FavoriteRoadmaps';
|
||||
---
|
||||
|
||||
<div class='relative min-h-auto min-h-[192px] sm:min-h-[281px] border-b border-b-[#1e293c]'>
|
||||
<div
|
||||
class='container px-6 py-6 pb-14 text-left sm:px-0 sm:py-20 sm:text-center transition-opacity duration-300'
|
||||
id='hero-text'
|
||||
>
|
||||
<h1
|
||||
class='mb-2 bg-gradient-to-b from-amber-50 to-purple-500 bg-clip-text text-2xl font-bold text-transparent sm:mb-4 sm:text-5xl'
|
||||
>
|
||||
Developer Roadmaps
|
||||
</h1>
|
||||
|
||||
<p class='hidden px-4 text-lg text-gray-400 sm:block'>
|
||||
<span class='font-medium text-gray-400'>roadmap.sh</span> is a community effort
|
||||
to create roadmaps, guides and other educational content to help guide developers
|
||||
in picking up a path and guide their learnings.
|
||||
</p>
|
||||
|
||||
<p class='text-md block px-0 text-gray-400 sm:hidden'>
|
||||
Community created roadmaps, guides and articles to help developers grow in
|
||||
their career.
|
||||
</p>
|
||||
</div>
|
||||
<FavoriteRoadmaps client:authenticated />
|
||||
</div>
|
||||
@@ -1,46 +0,0 @@
|
||||
import type { UserProgressResponse } from './FavoriteRoadmaps';
|
||||
import { CheckIcon } from './CheckIcon';
|
||||
|
||||
type ProgressListProps = {
|
||||
progress: UserProgressResponse;
|
||||
};
|
||||
|
||||
export function ProgressList(props: ProgressListProps) {
|
||||
const { progress } = props;
|
||||
|
||||
return (
|
||||
<div className="relative pt-4 sm:pt-7 pb-12">
|
||||
<p className="mb-4 flex items-center text-sm text-gray-400">
|
||||
<CheckIcon additionalClasses={'mr-1.5 w-[14px] h-[14px]'} />
|
||||
<span className='hidden sm:inline'>Your progress and favorite roadmaps.</span>
|
||||
<span className='inline sm:hidden'>Your progress and favorite roadmaps.</span>
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-2">
|
||||
{progress.map((resource) => {
|
||||
const url =
|
||||
resource.resourceType === 'roadmap'
|
||||
? `/${resource.resourceId}`
|
||||
: `/best-practices/${resource.resourceId}`;
|
||||
|
||||
const percentageDone =
|
||||
((resource.skipped + resource.done) / resource.total) * 100;
|
||||
|
||||
return (
|
||||
<a
|
||||
href={url}
|
||||
className="relative flex flex-col rounded-md border border-slate-800 bg-slate-900 p-3 text-sm text-slate-400 hover:border-slate-600 hover:text-slate-300 overflow-hidden"
|
||||
>
|
||||
<span className='relative z-20'>{resource.resourceTitle}</span>
|
||||
|
||||
<span
|
||||
class="absolute bottom-0 left-0 top-0 z-10 bg-[#172a3a]"
|
||||
style={{ width: `${percentageDone}%` }}
|
||||
></span>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -5,11 +5,7 @@ import AccountDropdown from './AccountDropdown.astro';
|
||||
|
||||
<div class='bg-slate-900 py-5 text-white sm:py-8'>
|
||||
<nav class='container flex items-center justify-between'>
|
||||
<a
|
||||
class='flex items-center text-lg font-medium text-white'
|
||||
href='/'
|
||||
aria-label='roadmap.sh'
|
||||
>
|
||||
<a class='flex items-center text-lg font-medium text-white' href='/' aria-label="roadmap.sh">
|
||||
<Icon icon='logo' />
|
||||
</a>
|
||||
|
||||
@@ -30,12 +26,9 @@ import AccountDropdown from './AccountDropdown.astro';
|
||||
<a href='/videos' class='text-gray-400 hover:text-white'>Videos</a>
|
||||
</li>
|
||||
<li>
|
||||
<kbd
|
||||
data-command-menu
|
||||
class='hidden items-center rounded-md border border-gray-800 px-2.5 py-1 text-sm text-gray-400 hover:cursor-pointer hover:bg-gray-800 sm:flex'
|
||||
>
|
||||
<Icon icon='search' class='mr-2 h-3 w-3' />
|
||||
<kbd class='mr-1 font-sans'>⌘</kbd><kbd class='font-sans'>K</kbd>
|
||||
<kbd data-command-menu class="hidden sm:flex items-center text-gray-400 border border-gray-800 rounded-md px-2.5 py-1 text-sm hover:bg-gray-800 hover:cursor-pointer">
|
||||
<Icon icon='search' class='h-3 w-3 mr-2' />
|
||||
<kbd class='font-sans mr-1'>⌘</kbd><kbd class='font-sans'>K</kbd>
|
||||
</kbd>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -104,7 +97,10 @@ import AccountDropdown from './AccountDropdown.astro';
|
||||
|
||||
<!-- Links for logged in users -->
|
||||
<li data-auth-required class='hidden'>
|
||||
<a href='/account' class='text-xl hover:text-blue-300 md:text-lg'>
|
||||
<a
|
||||
href='/account'
|
||||
class='text-xl hover:text-blue-300 md:text-lg'
|
||||
>
|
||||
Account
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -29,18 +29,6 @@ export function PageSponsor(props: PageSponsorProps) {
|
||||
const [sponsor, setSponsor] = useState<PageSponsorType>();
|
||||
|
||||
const loadSponsor = async () => {
|
||||
const currentPath = window.location.pathname;
|
||||
if (
|
||||
currentPath === '/' ||
|
||||
currentPath === '/best-practices' ||
|
||||
currentPath === '/roadmaps' ||
|
||||
currentPath.startsWith('/guides') ||
|
||||
currentPath.startsWith('/videos') ||
|
||||
currentPath.startsWith('/account')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { response, error } = await httpGet<V1GetSponsorResponse>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-sponsor`,
|
||||
{
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { useCopyText } from '../../hooks/use-copy-text';
|
||||
import CopyIcon from '../../icons/copy.svg';
|
||||
|
||||
type EditorProps = {
|
||||
title: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export function Editor(props: EditorProps) {
|
||||
const { text, title } = props;
|
||||
|
||||
const { isCopied, copyText } = useCopyText();
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-grow flex-col overflow-hidden rounded border border-gray-300 bg-gray-50">
|
||||
<div className="flex items-center justify-between gap-2 border-b border-gray-300 px-3 py-2">
|
||||
<span className="text-xs uppercase leading-none text-gray-400">
|
||||
{title}
|
||||
</span>
|
||||
<button className="flex items-center" onClick={() => copyText(text)}>
|
||||
{isCopied && (
|
||||
<span className="mr-1 text-xs leading-none text-gray-700">
|
||||
Copied!
|
||||
</span>
|
||||
)}
|
||||
|
||||
<img src={CopyIcon} alt="Copy" className="inline-block h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
className="no-scrollbar block h-12 w-full overflow-x-auto whitespace-nowrap bg-gray-200/70 p-3 text-sm text-gray-900 focus:bg-gray-50 focus:outline-0"
|
||||
readOnly
|
||||
onClick={(e: any) => {
|
||||
e.target.select();
|
||||
copyText(e.target.value);
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</textarea>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
export function GitHubReadmeBanner() {
|
||||
return (
|
||||
<p className="mt-3 rounded-md border p-2 text-sm w-full bg-yellow-100 border-yellow-400 text-yellow-900">
|
||||
Add this badge to your{' '}
|
||||
<a
|
||||
href="https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 underline hover:text-blue-800"
|
||||
>
|
||||
GitHub profile readme.
|
||||
</a>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
import { useState } from 'preact/hooks';
|
||||
|
||||
import { useCopyText } from '../../hooks/use-copy-text';
|
||||
import { useAuth } from '../../hooks/use-auth';
|
||||
import CopyIcon from '../../icons/copy.svg';
|
||||
import { RoadmapSelect } from './RoadmapSelect';
|
||||
import { GitHubReadmeBanner } from './GitHubReadmeBanner';
|
||||
import { downloadImage } from '../../helper/download-image';
|
||||
import { SelectionButton } from './SelectionButton';
|
||||
import { StepCounter } from './StepCounter';
|
||||
import { Editor } from './Editor';
|
||||
|
||||
type StepLabelProps = {
|
||||
label: string;
|
||||
};
|
||||
function StepLabel(props: StepLabelProps) {
|
||||
const { label } = props;
|
||||
|
||||
return (
|
||||
<span className="mb-3 flex items-center gap-2 text-sm leading-none text-gray-400">
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function RoadCardPage() {
|
||||
const { isCopied, copyText } = useCopyText();
|
||||
const [roadmaps, setRoadmaps] = useState<string[]>([]);
|
||||
const [version, setVersion] = useState<'tall' | 'wide'>('tall');
|
||||
const [variant, setVariant] = useState<'dark' | 'light'>('dark');
|
||||
const user = useAuth();
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const badgeUrl = new URL(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-badge/${version}/${user?.id}`
|
||||
);
|
||||
|
||||
badgeUrl.searchParams.set('variant', variant);
|
||||
if (roadmaps.length > 0) {
|
||||
badgeUrl.searchParams.set('roadmaps', roadmaps.join(','));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-start gap-4 mx-0 sm:-mx-10 px-0 sm:px-10 border-b pt-2 pb-4">
|
||||
<StepCounter step={1} />
|
||||
<div>
|
||||
<StepLabel label="Pick progress to show (Max. 4)" />
|
||||
|
||||
<div className="flex flex-wrap">
|
||||
<RoadmapSelect
|
||||
selectedRoadmaps={roadmaps}
|
||||
setSelectedRoadmaps={setRoadmaps}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4 mx-0 sm:-mx-10 px-0 sm:px-10 border-b py-4">
|
||||
<StepCounter step={2} />
|
||||
<div>
|
||||
<StepLabel label="Select Mode (Dark vs Light)" />
|
||||
|
||||
<div className="flex gap-2">
|
||||
<SelectionButton
|
||||
text={'Dark'}
|
||||
isDisabled={false}
|
||||
isSelected={variant === 'dark'}
|
||||
onClick={() => {
|
||||
setVariant('dark');
|
||||
}}
|
||||
/>
|
||||
|
||||
<SelectionButton
|
||||
text={'Light'}
|
||||
isDisabled={false}
|
||||
isSelected={variant === 'light'}
|
||||
onClick={() => {
|
||||
setVariant('light');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4 mx-0 sm:-mx-10 px-0 sm:px-10 border-b py-4">
|
||||
<StepCounter step={3} />
|
||||
<div>
|
||||
<StepLabel label="Select Version" />
|
||||
|
||||
<div className="flex gap-2">
|
||||
<SelectionButton
|
||||
text={'Tall'}
|
||||
isDisabled={false}
|
||||
isSelected={version === 'tall'}
|
||||
onClick={() => {
|
||||
setVersion('tall');
|
||||
}}
|
||||
/>
|
||||
<SelectionButton
|
||||
text={'Wide'}
|
||||
isDisabled={false}
|
||||
isSelected={version === 'wide'}
|
||||
onClick={() => {
|
||||
setVersion('wide');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4 mx-0 sm:-mx-10 px-0 sm:px-10 border-b py-4">
|
||||
<StepCounter step={4} />
|
||||
<div class="flex-grow">
|
||||
<StepLabel label="Share your #RoadCard with others" />
|
||||
<div className={'rounded-md border bg-gray-50 p-2 text-center'}>
|
||||
<a
|
||||
href={badgeUrl.toString()}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`relative block hover:cursor-pointer ${
|
||||
version === 'tall' ? ' max-w-[270px] ' : ' w-full '
|
||||
}`}
|
||||
>
|
||||
<img src={badgeUrl.toString()} alt="RoadCard" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 grid grid-cols-2 gap-2">
|
||||
<button
|
||||
className="flex items-center justify-center rounded border border-gray-300 p-1.5 px-2 text-sm font-medium"
|
||||
onClick={() =>
|
||||
downloadImage({
|
||||
url: badgeUrl.toString(),
|
||||
name: 'road-card',
|
||||
scale: 4,
|
||||
})
|
||||
}
|
||||
>
|
||||
Download
|
||||
</button>
|
||||
<button
|
||||
disabled={isCopied}
|
||||
className="flex cursor-pointer items-center justify-center rounded border border-gray-300 p-1.5 px-2 text-sm font-medium disabled:bg-blue-50"
|
||||
onClick={() => copyText(badgeUrl.toString())}
|
||||
>
|
||||
<img alt="Copy" src={CopyIcon} className="mr-1" />
|
||||
|
||||
{isCopied ? 'Copied!' : 'Copy Link'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex flex-col gap-3">
|
||||
<Editor
|
||||
title={'HTML'}
|
||||
text={`<a href="https://roadmap.sh"><img src="${badgeUrl}" alt="roadmap.sh"/></a>`.trim()}
|
||||
/>
|
||||
|
||||
<Editor
|
||||
title={'Markdown'}
|
||||
text={`[](https://roadmap.sh)`.trim()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<GitHubReadmeBanner />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
import { httpGet } from '../../lib/http';
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
import { pageProgressMessage } from '../../stores/page';
|
||||
import type { UserProgressResponse } from '../HeroSection/FavoriteRoadmaps';
|
||||
import { SelectionButton } from './SelectionButton';
|
||||
|
||||
type RoadmapSelectProps = {
|
||||
selectedRoadmaps: string[];
|
||||
setSelectedRoadmaps: (updatedRoadmaps: string[]) => void;
|
||||
};
|
||||
|
||||
export function RoadmapSelect(props: RoadmapSelectProps) {
|
||||
const { selectedRoadmaps, setSelectedRoadmaps } = props;
|
||||
|
||||
const [progressList, setProgressList] = useState<UserProgressResponse>();
|
||||
|
||||
const fetchProgress = async () => {
|
||||
const { response, error } = await httpGet<UserProgressResponse>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-all-progress`
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
return;
|
||||
}
|
||||
|
||||
setProgressList(response);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchProgress().finally(() => {
|
||||
pageProgressMessage.set('');
|
||||
});
|
||||
}, []);
|
||||
|
||||
const canSelectMore = selectedRoadmaps.length < 4;
|
||||
const allProgress = progressList?.filter(
|
||||
(progress) => progress.resourceType === 'roadmap'
|
||||
) || [];
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{allProgress?.length === 0 && <p className={'text-sm text-gray-400 italic'}>No progress tracked so far.</p>}
|
||||
{allProgress?.map((progress) => {
|
||||
const isSelected = selectedRoadmaps.includes(progress.resourceId);
|
||||
const canSelect = isSelected || canSelectMore;
|
||||
|
||||
return (
|
||||
<SelectionButton
|
||||
text={progress.resourceTitle}
|
||||
isDisabled={!canSelect}
|
||||
isSelected={isSelected}
|
||||
onClick={() => {
|
||||
if (isSelected) {
|
||||
setSelectedRoadmaps(
|
||||
selectedRoadmaps.filter(
|
||||
(roadmap) => roadmap !== progress.resourceId
|
||||
)
|
||||
);
|
||||
} else if (selectedRoadmaps.length < 4) {
|
||||
setSelectedRoadmaps([...selectedRoadmaps, progress.resourceId]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
type SelectionButtonProps = {
|
||||
text: string;
|
||||
isDisabled: boolean;
|
||||
isSelected: boolean;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
export function SelectionButton(props: SelectionButtonProps) {
|
||||
const { text, isDisabled, isSelected, onClick } = props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`rounded-md border p-1 px-2 text-sm ${
|
||||
isSelected ? ' border-gray-500 bg-gray-300 ' : ''
|
||||
} ${
|
||||
!isDisabled ? ' cursor-pointer ' : ' cursor-not-allowed opacity-40 '
|
||||
}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
type StepCounterProps = {
|
||||
step: number;
|
||||
};
|
||||
|
||||
export function StepCounter(props: StepCounterProps) {
|
||||
const { step } = props;
|
||||
|
||||
return (
|
||||
<span
|
||||
className={
|
||||
'flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-300 text-white'
|
||||
}
|
||||
>
|
||||
{step}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
---
|
||||
import { ClearProgress } from './Activity/ClearProgress';
|
||||
import AstroIcon from './AstroIcon.astro';
|
||||
import Icon from './AstroIcon.astro';
|
||||
|
||||
export interface Props {
|
||||
@@ -44,25 +42,30 @@ const roadmapTitle =
|
||||
}
|
||||
|
||||
<!-- Desktop: Roadmap Resources - Alert -->
|
||||
<div id="progress-nums-container"
|
||||
<div
|
||||
class:list={[
|
||||
'hidden sm:flex justify-between px-2 bg-white items-center py-1.5 relative striped-loader bg-white',
|
||||
'hidden sm:flex justify-between px-2 bg-white items-center',
|
||||
{
|
||||
'rounded-bl-md rounded-br-md': hasTNSBanner,
|
||||
'rounded-md': !hasTNSBanner,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<p class='text-sm flex opacity-0 transition-opacity duration-300' id="progress-nums">
|
||||
<span class="font-medium py-0.5 rounded-sm text-xs uppercase bg-yellow-200 px-1 text-yellow-900 mr-2.5">
|
||||
<span class="progress-percentage">0</span>% Done
|
||||
</span>
|
||||
|
||||
<span><span class="progress-done">0</span> completed</span><span class="mx-1.5 text-gray-400">·</span>
|
||||
<span><span class="progress-learning">0</span> learning</span><span class="mx-1.5 text-gray-400">·</span>
|
||||
<span><span class="progress-skipped">0</span> skipped</span><span class="mx-1.5 text-gray-400">·</span>
|
||||
<span><span class="progress-total">0</span> Total</span>
|
||||
<p class='text-sm'>
|
||||
<span
|
||||
class='mr-0.5 rounded-sm bg-yellow-200 px-1 py-0.5 text-xs font-medium uppercase text-yellow-900'
|
||||
>New</span
|
||||
>
|
||||
Track your progress and learn by clicking roadmap items.
|
||||
</p>
|
||||
|
||||
<a
|
||||
href={`/${roadmapId}/topics`}
|
||||
class='inline-flex items-center justify-center rounded-md px-1 py-1.5 text-sm font-medium text-gray-500 hover:text-black'
|
||||
>
|
||||
<Icon icon='search' />
|
||||
<span class='ml-2'>Search Topics</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Mobile - Roadmap resources alert -->
|
||||
@@ -71,4 +74,4 @@ const roadmapTitle =
|
||||
>
|
||||
Track your progress and learn about the topics by clicking the roadmap items.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
ResourceProgressType,
|
||||
ResourceType,
|
||||
getTopicStatus,
|
||||
refreshProgressCounters,
|
||||
renderTopicProgress,
|
||||
updateResourceProgress,
|
||||
} from '../../lib/resource-progress';
|
||||
@@ -136,7 +135,6 @@ export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
setProgress(progress);
|
||||
onClose();
|
||||
renderTopicProgress(topicId, progress);
|
||||
refreshProgressCounters();
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err.message);
|
||||
|
||||
@@ -80,7 +80,7 @@ export default function UpdatePasswordForm() {
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div class="hidden md:block mb-8">
|
||||
<h2 className="text-3xl font-bold sm:text-4xl">Password</h2>
|
||||
<p className="mt-2 text-gray-400">Use the form below to update your password.</p>
|
||||
<p className="mt-2">Use the form below to update your password.</p>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
{authProvider === 'email' && (
|
||||
|
||||
@@ -83,7 +83,7 @@ export function UpdateProfileForm() {
|
||||
<div>
|
||||
<div className="mb-8 hidden md:block">
|
||||
<h2 className="text-3xl font-bold sm:text-4xl">Profile</h2>
|
||||
<p className="mt-2 text-gray-400">Update your profile details below.</p>
|
||||
<p className="mt-2">Update your profile details below.</p>
|
||||
</div>
|
||||
<UploadProfilePicture
|
||||
avatarUrl={
|
||||
|
||||
@@ -6,4 +6,3 @@ Visit the following resources to learn more:
|
||||
|
||||
- [Union Types - typescriptlang](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html)
|
||||
- [Union Type video for Beginners](https://www.youtube.com/watch?v=uxjpm4W5pCo)
|
||||
- [Union Types - typescriptlang](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types)
|
||||
@@ -9,4 +9,3 @@ Type guards are typically used for narrowing a type and are pretty similar to fe
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Types Guards - Blog](https://blog.logrocket.com/how-to-use-type-guards-typescript/)
|
||||
- [TypeScript Type Guards Explained](https://www.youtube.com/watch?v=feeeitmtdwg)
|
||||
@@ -6,4 +6,3 @@ Visit the following resources to learn more:
|
||||
|
||||
- [C# official website?](https://learn.microsoft.com/en-us/dotnet/csharp//)
|
||||
- [The Beginners Guide to C#](https://www.w3schools.com/CS/index.php)
|
||||
- [C# Tutorial](https://www.w3schools.com/cs/index.php)
|
||||
@@ -10,4 +10,3 @@ Visit the following resources to learn more:
|
||||
- [Codeacademy - Free Course](https://www.codecademy.com/learn/learn-java)
|
||||
- [W3 Schools Tutorials](https://www.w3schools.com/java/)
|
||||
- [Java Crash Course](https://www.youtube.com/watch?v=eIrMbAQSU34)
|
||||
- [Complete Java course](https://www.youtube.com/watch?v=xk4_1vdrzzo)
|
||||
@@ -11,4 +11,3 @@ Visit the following resources to learn more:
|
||||
- [Git vs. GitHub: Whats the difference?](https://www.youtube.com/watch?v=wpISo9TNjfU)
|
||||
- [Git and GitHub for Beginners](https://www.youtube.com/watch?v=RGOj5yH7evk)
|
||||
- [Git and GitHub - CS50 Beyond 2019](https://www.youtube.com/watch?v=eulnSXkhE7I)
|
||||
- [Learn Git Branching](https://learngitbranching.js.org/?locale=en_us)
|
||||
@@ -7,5 +7,4 @@ Visit the following resources to learn more:
|
||||
- [MySQL website](https://www.mysql.com/)
|
||||
- [W3Schools - MySQL tutorial ](https://www.w3schools.com/mySQl/default.asp)
|
||||
- [MySQL tutorial for beginners](https://www.youtube.com/watch?v=7S_tz1z_5bA)
|
||||
- [MySQL for Developers](https://planetscale.com/courses/mysql-for-developers/introduction/course-introduction)
|
||||
- [MySQL Tutorial](https://www.mysqltutorial.org/)
|
||||
- [MySQL for Developers](https://planetscale.com/courses/mysql-for-developers/introduction/course-introduction)
|
||||
@@ -9,4 +9,3 @@ Visit the following resources to learn more:
|
||||
- [What is OpenID](https://openid.net/connect/)
|
||||
- [OAuth vs OpenID](https://securew2.com/blog/oauth-vs-openid-which-is-better)
|
||||
- [An Illustrated Guide to OAuth and OpenID Connect](https://www.youtube.com/watch?v=t18YB3xDfXI)
|
||||
- [OAuth 2.0 and OpenID Connect (in plain English)](https://www.youtube.com/watch?v=996oiexhze0)
|
||||
@@ -8,4 +8,3 @@ Visit the following resources to learn more:
|
||||
- [Blockchain Explained](https://www.investopedia.com/terms/b/blockchain.asp)
|
||||
- [How does a blockchain work?](https://youtu.be/SSo_EIwHSd4)
|
||||
- [What Is a Blockchain? | Blockchain Basics for Developers](https://youtu.be/4ff9esY_4aU)
|
||||
- [An Elementary and Slightly Distilled Introduction to Blockchain](https://markpetherbridge.co.uk/blog/an-elementary-and-slightly-distilled-introduction-to-blockchain/)
|
||||
@@ -6,5 +6,3 @@ Visit the following resources to learn more:
|
||||
|
||||
- [Learn Cpp](https://learncpp.com/)
|
||||
- [C++ Reference](https://en.cppreference.com/)
|
||||
- [C++ TutorialsPoint](https://www.tutorialspoint.com/cplusplus/index.htm)
|
||||
- [W3Schools C++](https://www.w3schools.com/cpp/default.asp)
|
||||
@@ -6,4 +6,3 @@ Visit the following resources to learn more:
|
||||
|
||||
- [Breadth First Search or BFS for a Graph](https://www.geeksforgeeks.org/breadth-first-search-or-bfs-for-a-graph/)
|
||||
- [Graph Algorithms II - DFS, BFS, Kruskals Algorithm, Union Find Data Structure - Lecture 7](https://www.youtube.com/watch?v=ufj5_bppBsA&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=7)
|
||||
- [Breadth-first search in 4 minutes](https://www.youtube.com/watch?v=hz5ytanv5qe)
|
||||
@@ -45,4 +45,4 @@ int main() {
|
||||
|
||||
In the above program, we define a simple function `add` and a class `Calculator` with a member function `multiply`. The `main` function demonstrates how to use these to perform basic arithmetic.
|
||||
|
||||
- [C++ Tutorial for Beginners - Full Course](https://www.youtube.com/watch?v=vlnpwxzdw4y)
|
||||
- [C++ Tutorial for Beginners - Full Course](https://youtu.be/vlnpwxzdw4y)
|
||||
@@ -10,6 +10,4 @@ Code editors are programs specifically designed for editing, managing and writin
|
||||
|
||||
These are just a few examples, and there are many other code editors available, including Atom, Notepad++, and Geany. They all have their features and may suit different developers' needs. Finding the right code editor is often a matter of personal preference and workflow.
|
||||
|
||||
To work with C++ in your chosen code editor, you often need to install some additional tools and add-ons, such as compilers, linters, and debugger support. Make sure to follow the instructions provided by the editor's documentation to set up C++ correctly.
|
||||
|
||||
- [Using C++ on Linux in VSCode](https://code.visualstudio.com/docs/cpp/config-linux)
|
||||
To work with C++ in your chosen code editor, you often need to install some additional tools and add-ons, such as compilers, linters, and debugger support. Make sure to follow the instructions provided by the editor's documentation to set up C++ correctly.
|
||||
@@ -59,7 +59,4 @@ auto updateDays = [&expiresInDays](int newDays) {
|
||||
updateDays(30); // expiresInDays = 30
|
||||
```
|
||||
|
||||
Note that, when using the capture by reference, any change made to the captured variable *inside* the lambda function will affect its value in the surrounding scope.
|
||||
|
||||
- [Lambdas in C++](https://youtu.be/mwgmbbz0y8c)
|
||||
- [Lambda Expressions](https://en.cppreference.com/w/cpp/language/lambda)
|
||||
Note that, when using the capture by reference, any change made to the captured variable *inside* the lambda function will affect its value in the surrounding scope.
|
||||
@@ -45,6 +45,4 @@ int main() {
|
||||
}
|
||||
```
|
||||
|
||||
In this example, we create a `shared_ptr` named `shared` that manages a `MyClass` object. By assigning it to a `weak_ptr` named `weak`, we store a non-owning reference to the object. Inside the inner scope, we create a new `shared_ptr` named `sharedFromWeak` using `weak.lock()` to safely use the object. After the inner scope, the `MyClass` object is destroyed since `shared` goes out of scope, and any further attempt to create a `shared_ptr` from `weak` will fail as the object is already destroyed.
|
||||
|
||||
- [CPP Reference](https://en.cppreference.com/w/cpp/memory/weak_ptr)
|
||||
In this example, we create a `shared_ptr` named `shared` that manages a `MyClass` object. By assigning it to a `weak_ptr` named `weak`, we store a non-owning reference to the object. Inside the inner scope, we create a new `shared_ptr` named `sharedFromWeak` using `weak.lock()` to safely use the object. After the inner scope, the `MyClass` object is destroyed since `shared` goes out of scope, and any further attempt to create a `shared_ptr` from `weak` will fail as the object is already destroyed.
|
||||
@@ -39,10 +39,10 @@ The third stage is converting the compiler's intermediate representation into as
|
||||
|
||||
**Code Example (x86 Assembly):**
|
||||
|
||||
```
|
||||
mov eax, 10
|
||||
mov ebx, 20
|
||||
add eax, ebx
|
||||
```assembly
|
||||
mov eax, 10
|
||||
mov ebx, 20
|
||||
add eax, ebx
|
||||
```
|
||||
|
||||
## Linking
|
||||
@@ -55,4 +55,4 @@ The final stage is the linking of the object code with the necessary libraries a
|
||||
$ g++ main.o -o main -lm
|
||||
```
|
||||
|
||||
In summary, the compilation process in C++ involves four primary stages: preprocessing, compilation, assembly, and linking. Each stage plays a crucial role in transforming the source code into an executable program.
|
||||
In summary, the compilation process in C++ involves four primary stages: preprocessing, compilation, assembly, and linking. Each stage plays a crucial role in transforming the source code into an executable program.
|
||||
@@ -68,6 +68,4 @@ int main()
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, Poco is used to send an HTTP GET request and process the response. It manages tasks like connecting to the server, handling exceptions, and managing HTTP headers.
|
||||
|
||||
- [Official Docs for Poco Library](https://docs.pocoproject.org/)
|
||||
In the above example, Poco is used to send an HTTP GET request and process the response. It manages tasks like connecting to the server, handling exceptions, and managing HTTP headers.
|
||||
@@ -33,6 +33,3 @@ To minimize the risks associated with NFC, follow these best practices:
|
||||
- Ensure you're using trusted and secure apps to handle your NFC transactions.
|
||||
|
||||
In conclusion, understanding the basics of NFC and adhering to security best practices will help ensure that you can safely and effectively use this innovative technology.
|
||||
|
||||
- [The Beginner's Guide to NFCs](https://www.spiceworks.com/tech/networking/articles/what-is-near-field-communication/)
|
||||
- [NFC Guide: All You Need to Know About Near Field Communication](https://squareup.com/us/en/the-bottom-line/managing-your-finances/nfc)
|
||||
@@ -39,5 +39,3 @@ To protect yourself and your devices, follow these best practices:
|
||||
- **Use a Virtual Private Network (VPN)**: Connect to the internet using a VPN, which provides a secure, encrypted tunnel for data transmission.
|
||||
|
||||
By understanding the potential security risks associated with WiFi connections and following these best practices, you can enjoy the convenience, flexibility, and mobility of WiFi while ensuring a secure browsing experience.
|
||||
|
||||
- [Wireless Networks - Howstuffworks](https://computer.howstuffworks.com/wireless-network.htm)
|
||||
@@ -41,6 +41,3 @@ Logs are records of system events, application behavior, and user activity, whic
|
||||
- **Leverage log-analysis tools**: Utilize specialized tools or scripts to help parse, filter, and analyze large or complex log files.
|
||||
|
||||
In conclusion, developing OS-independent troubleshooting skills allows you to effectively diagnose and resolve issues on any system. By following a structured approach, understanding common symptoms, and utilizing the appropriate tools, you can minimize downtime and maintain the security and efficiency of your organization's IT systems.
|
||||
|
||||
- [How to identify 9 signs of Operating System.](https://bro4u.com/blog/how-to-identify-9-signs-of-operating-system)
|
||||
- [Trouble shooting guide](https://cdnsm5-ss6.sharpschool.com/userfiles/servers/server_20856499/file/teacher%20pages/lindsay%20dolezal/it%20essentials/5.6.pdf)
|
||||
@@ -64,5 +64,3 @@ As the digital world is constantly evolving, so too are cyber threats. Therefore
|
||||
- Regularly backing up data
|
||||
|
||||
By honing these basic IT skills, you will be better prepared to navigate and protect your digital life, as well as making the most of the technology at your fingertips.
|
||||
|
||||
- [IT skills Training for beginners | Complete Course](https://youtu.be/on6dsip5yw0)
|
||||
@@ -31,5 +31,3 @@ Proper key management is crucial to maintain the security of encrypted data. Key
|
||||
Cryptanalysis is the process of attempting to break cryptographic systems, often by exploiting weaknesses in the algorithms, protocols, or key management processes. The strength of a cryptographic system lies in its resistance to cryptanalysis. As a cyber security professional, understanding cryptanalysis techniques can help you identify and protect against potential vulnerabilities in your organization's cryptographic infrastructure.
|
||||
|
||||
In conclusion, cryptography is a fundamental aspect of cyber security, offering a layer of protection for sensitive data in digital networks. To effectively implement cryptography in your organization, you should be familiar with the various types of cryptography, cryptographic protocols, and key management best practices, and understand the potential threats posed by cryptanalysis.
|
||||
|
||||
- [Cryptography for Dummies (TryHackMe)](https://tryhackme.com/room/cryptographyfordummies)
|
||||
@@ -40,5 +40,3 @@ The Purple Team bridges the gap between the Blue Team and Red Team, helping to c
|
||||
- Foster a culture of continuous improvement and collaboration
|
||||
|
||||
By investing in Blue, Red, and Purple Team efforts, organizations can achieve a more robust and resilient security posture, capable of withstanding and adapting to ever-evolving threats.
|
||||
|
||||
- [Red Team Fundamentals (TryHackMe)](https://tryhackme.com/room/redteamfundamentals)
|
||||
@@ -18,5 +18,3 @@ Professionals working in digital forensics need a solid understanding of various
|
||||
- Effective communication abilities to convey technical findings to non-technical stakeholders
|
||||
|
||||
Overall, digital forensics is a crucial component of cybersecurity as it helps organizations respond effectively to cyber attacks, identify vulnerabilities, and take appropriate steps to safeguard their digital assets.
|
||||
|
||||
- [Introduction to Digital Forensics (TryHackMe)](https://tryhackme.com/room/introdigitalforensics)
|
||||
@@ -47,6 +47,3 @@ Smishing, or SMS phishing, is the act of using text messages to deceive victims
|
||||
- Install mobile security software to protect your device from potential threats
|
||||
|
||||
By staying informed about these various attack types, you can better protect yourself and your organization from falling victim to cyber threats. Remain vigilant and ensure you have proper security measures in place to minimize the risk of these attacks.
|
||||
|
||||
- [What is Phishing?](https://www.phishing.org/what-is-phishing)
|
||||
- [Phishing Examples](https://www.phishing.org/phishing-examples)
|
||||
@@ -17,5 +17,3 @@ Parrot OS, also known as Parrot Security OS, is a powerful Linux-based distribut
|
||||
- **Reverse Engineering**: The OS also includes tools for reverse engineering, assisting security professionals in examining and analyzing software or malware designs.
|
||||
|
||||
Overall, Parrot OS is a reliable, versatile, and user-friendly cyber security distribution, ideal for both beginners and advanced users engaged in ethical hacking, penetration testing, and digital forensics.
|
||||
|
||||
- [Link to Download Parrot OS ](https://www.parrotsec.org/download/)
|
||||
@@ -35,5 +35,3 @@ Achieving a CompTIA A+ certification can offer several benefits, such as:
|
||||
- Serving as a prerequisite for more advanced certifications, such as CompTIA Network+ and CompTIA Security+
|
||||
|
||||
Overall, if you're an aspiring IT professional, the CompTIA A+ certification is a great starting point to kick off your IT career and begin acquiring the skills and knowledge needed to thrive in this ever-evolving industry.
|
||||
|
||||
- [CompTIA A+ 220-1101 - Professor Messer](https://www.youtube.com/watch?v=87t6p5zhtp0&list=plg49s3nxzannomvg5ugvenb_qqgsh01uc&index=1)
|
||||
@@ -1,3 +1 @@
|
||||
# Bash scripting
|
||||
|
||||
- [Bash Scripting Tutorial](https://www.freecodecamp.org/news/bash-scripting-tutorial-linux-shell-script-and-command-line-for-beginners/)
|
||||
# Bash scripting
|
||||
@@ -1,3 +1 @@
|
||||
# Powershell
|
||||
|
||||
- [PowerShell Documentation](https://learn.microsoft.com/en-us/powershell/)
|
||||
# Powershell
|
||||
@@ -1,6 +1 @@
|
||||
# Nexus
|
||||
|
||||
- [Repository Management Basics](https://learn.sonatype.com/courses/nxrm-admin-100/)
|
||||
- [Nexus Installation and Configuration](https://learn.sonatype.com/courses/nxrm-config-100/)
|
||||
- [Nexus Repository Security Essentials](https://learn.sonatype.com/courses/nxrm-sec-100/)
|
||||
- [Nexus Best Practices](https://help.sonatype.com/repomanager3/nexus-repository-best-practices)
|
||||
# Nexus
|
||||
@@ -1551,7 +1551,7 @@
|
||||
"y": "0",
|
||||
"properties": {
|
||||
"size": "17",
|
||||
"text": "KodeKloud DevOps Courses"
|
||||
"text": "KodeCloud DevOps Courses"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ schema:
|
||||
description: 'Learn to become a DevOps, SRE or get any other operations role with this interactive step by step guide in 2023. We also have resources and short descriptions attached to the roadmap items so you can get everything you want to learn in one place.'
|
||||
imageUrl: 'https://roadmap.sh/roadmaps/devops.png'
|
||||
datePublished: '2023-01-05'
|
||||
dateModified: '2023-06-10'
|
||||
dateModified: '2023-01-20'
|
||||
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.'
|
||||
|
||||
@@ -13,6 +13,4 @@ Unlike traditional virtualization, which emulates a complete operating system wi
|
||||
|
||||
## Containers and Docker
|
||||
|
||||
Docker is a platform that simplifies the process of creating, deploying, and managing containers. It provides developers and administrators with a set of tools and APIs to manage containerized applications. With Docker, you can build and package application code, libraries, and dependencies into a container image, which can be distributed and run consistently in any environment that supports Docker.
|
||||
|
||||
- [What is a container?](https://www.docker.com/resources/what-container/)
|
||||
Docker is a platform that simplifies the process of creating, deploying, and managing containers. It provides developers and administrators with a set of tools and APIs to manage containerized applications. With Docker, you can build and package application code, libraries, and dependencies into a container image, which can be distributed and run consistently in any environment that supports Docker.
|
||||
@@ -1,3 +1 @@
|
||||
# Inherited Widgets
|
||||
|
||||
- [InheritedWidget Official Guide](https://api.flutter.dev/flutter/widgets/inheritedwidget-class.html)
|
||||
@@ -18,4 +18,3 @@ Learn more from the following links:
|
||||
- [Flutter - Design](https://dart.dev/guides/language/effective-dart/design)
|
||||
- [Design Patterns Explained in 10 Minutes](https://www.youtube.com/watch?v=tv-_1er1mWI)
|
||||
- [Cookbook Designs in Flutter](https://docs.flutter.dev/cookbook/design)
|
||||
- [Flutter Design Patterns](https://www.youtube.com/watch?v=sk5hwzfndqs&list=pllzmawv2ytgb-1ldoo-9vctgre-1dywkp&index=1)
|
||||
@@ -10,4 +10,3 @@ Visit the following resources to learn more:
|
||||
- [The guide to responsive web design in 2022](https://webflow.com/blog/responsive-web-design)
|
||||
- [5 simple tips to making responsive layouts the easy way](https://www.youtube.com/watch?v=VQraviuwbzU)
|
||||
- [Introduction To Responsive Web Design](https://www.youtube.com/watch?v=srvUrASNj0s)
|
||||
- [Useful & Responsive Layouts, no Media Queries required](https://youtu.be/p3_xn2zp1ty)
|
||||
@@ -7,4 +7,3 @@ Visit the following resources to learn more:
|
||||
- [Go Structs](https://go.dev/tour/moretypes/2)
|
||||
- [Go by Example: Structs](https://gobyexample.com/structs)
|
||||
- [Structs in Go](https://www.youtube.com/watch?v=NMTN543WVQY)
|
||||
- [Structs, Struct tags](https://www.youtube.com/watch?v=0m6ifd9n_cy&list=ploilbko9rg3skrcj37kn5zj803hhiurk6&index=13)
|
||||
@@ -6,4 +6,3 @@ Visit the following resources to learn more:
|
||||
|
||||
- [Java Memory Management](https://www.geeksforgeeks.org/java-memory-management/)
|
||||
- [Memory Management in Java](https://www.javatpoint.com/memory-management-in-java)
|
||||
- [Memory Management Tutorial in Java](https://www.youtube.com/watch?v=fm8yj93x80s)
|
||||
@@ -7,4 +7,3 @@ Visit the following resources to learn more:
|
||||
- [Java Garbage Collection](https://stackify.com/what-is-java-garbage-collection/)
|
||||
- [Garbage Collection in Java](https://www.geeksforgeeks.org/garbage-collection-java/)
|
||||
- [Java Garbage Collection](https://www.javatpoint.com/Garbage-Collection)
|
||||
- [Garbage Collection in Java - Geekific](https://www.youtube.com/watch?v=xxoacv5xm9s)
|
||||
@@ -8,4 +8,3 @@ Visit the following resources to learn more:
|
||||
|
||||
- [JavaScript MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label)
|
||||
- [Tutorialspoint – JavaScript Tutorial](https://www.tutorialspoint.com/What-are-label-statements-in-JavaScript)
|
||||
- [Labels for break/continue](https://javascript.info/while-for#labels-for-break-continue)
|
||||
@@ -3,6 +3,3 @@
|
||||
The throw statement throws a user-defined exception. Execution of the current function will stop (the statements after throw won't be executed), and control will be passed to the first catch block in the call stack. If no catch block exists among caller functions, the program will terminate. (excerpt from MDN)
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [throw statement - w3schools](https://www.w3schools.com/jsref/jsref_throw.asp)
|
||||
- [JavaScript MDN Docs](https://developer.mozilla.org/en-us/docs/web/javascript/reference/statements/throw)
|
||||
@@ -5,4 +5,3 @@ In JavaScript, all exceptions are simply objects. While the majority of exceptio
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Throwing Exceptions in JavaScript](https://rollbar.com/guides/javascript/how-to-throw-exceptions-in-javascript)
|
||||
- [try, catch, finally, throw (video)](https://youtu.be/cftftueq-10)
|
||||
@@ -5,4 +5,3 @@ The callback hell is when we try to write asynchronous JavaScript in a way where
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Callback Hell](http://callbackhell.com/)
|
||||
- [Callbacks in Callbacks - Pyramid of Doom](https://javascript.info/callbacks#pyramid-of-doom)
|
||||
@@ -6,4 +6,3 @@ Visit the following resources to learn more:
|
||||
|
||||
- [Callbacks in JavaScript](https://javascript.info/callbacks)
|
||||
- [Callback Functions](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function)
|
||||
- [W3School CallBack Function](https://www.w3schools.com/js/js_callback.asp)
|
||||
@@ -1,6 +1,6 @@
|
||||
# Installing a Local Cluster
|
||||
|
||||
To install and configure a Kubernetes cluster on CentOS 7 or Ubuntu, you would need to setup the prerequisites and requirements for setting up a Kubernetes cluster after which you would be installing the Kubernetes components, including Kubeadm, Kubelet, and Kubectl and then you'll need to connect the master and the worker nodes. Once the connection is established you can check it by deploying application on the cluster.
|
||||
To install and configure a Kubernetes cluster on CentOS 7 or Ubunto, you would need to setup the prerequisites and requirements for setting up a Kubernetes cluster after which you would be installing the Kubernetes components, including Kubeadm, Kubelet, and Kubectl and then you'll need to connect the master and the worker nodes. Once the connection is established you can check it by deploying application on the cluster.
|
||||
|
||||
Learn more from the following links:
|
||||
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
# Deployments
|
||||
|
||||
- [Deployments Documentation](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)
|
||||
@@ -27,6 +27,4 @@ Relational databases offer several advantages in terms of efficiency, flexibilit
|
||||
- **Scalability**: Relational databases can handle large amounts of structured data and can be scaled to accommodate growing data requirements.
|
||||
- **Querying**: The SQL (Structured Query Language) is used for querying, updating, and managing relational databases, providing a powerful and standardized way to access and manipulate the data.
|
||||
|
||||
In summary, relational databases are a powerful and versatile tool for storing and managing structured data. Their ability to represent relationships among data and to ensure data integrity make them the backbone of many applications and services.
|
||||
|
||||
- [Relational Databases: concept and history](https://www.ibm.com/topics/relational-databases)
|
||||
In summary, relational databases are a powerful and versatile tool for storing and managing structured data. Their ability to represent relationships among data and to ensure data integrity make them the backbone of many applications and services.
|
||||
@@ -22,6 +22,4 @@ Translate the following to French:
|
||||
Hello, how are you?
|
||||
```
|
||||
|
||||
But it's one of the best practices to be clear and use delimiters to separate the content in prompt from the instructions. You will learn more about it in the "Best Practices" nodes of the roadmap.
|
||||
|
||||
- [Basic Prompting](https://learnprompting.org/docs/basics/intro)
|
||||
But it's one of the best practices to be clear and use delimiters to separate the content in prompt from the instructions. You will learn more about it in the "Best Practices" nodes of the roadmap.
|
||||
@@ -10,4 +10,3 @@ Visit the following resources to learn more:
|
||||
- [Python Data Types](https://www.w3schools.com/python/python_datatypes.asp)
|
||||
- [Basic Data Types in Python](https://realpython.com/python-data-types/)
|
||||
- [Python for Beginners: Data Types](https://thenewstack.io/python-for-beginners-data-types/)
|
||||
- [Python Variables - Python Tutorial for Beginners with Examples | Mosh](https://www.youtube.com/watch?v=cqt33yu9py8)
|
||||
@@ -9,4 +9,3 @@ Visit the following resources to learn more:
|
||||
- [Python Wikipedia](<https://en.wikipedia.org/wiki/Python_(programming_language)>)
|
||||
- [Googles Python Class](https://developers.google.com/edu/python)
|
||||
- [W3Schools - Python Tutorial](https://www.w3schools.com/python)
|
||||
- [Python](https://www.kaggle.com/learn/python)
|
||||
@@ -7,4 +7,3 @@ Visit the following resources to learn more:
|
||||
- [Classes in Python](https://docs.python.org/3/tutorial/classes.html)
|
||||
- [Python Classes and Objects](https://www.geeksforgeeks.org/python-classes-and-objects/)
|
||||
- [Python Classes and Objects](https://www.w3schools.com/python/python_classes.asp)
|
||||
- [Python OOP Tutorial](https://www.youtube.com/watch?v=zda-z5jzlym&list=pl-osie80tetsqhiuoqkhwlxsibidseytc)
|
||||
@@ -7,4 +7,3 @@ Visit the following resources to learn more:
|
||||
- [Python Decorators](https://www.datacamp.com/tutorial/decorators-python)
|
||||
- [Decorators in Python](https://www.geeksforgeeks.org/decorators-in-python/)
|
||||
- [Decorators in Python](https://www.youtube.com/watch?v=FXUUSfJO_J4)
|
||||
- [Python Decorators in 1 Minute](https://www.youtube.com/watch?v=be-l7xu8po4)
|
||||
@@ -5,5 +5,4 @@ Python Lambda Functions are anonymous function means that the function is withou
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Lambdas in Python](https://www.w3schools.com/python/python_lambda.asp)
|
||||
- [How to use Lambda functions](https://realpython.com/python-lambda/)
|
||||
- [ Python Lambda Functions](https://www.youtube.com/watch?v=kr22jigjlok)
|
||||
- [How to use Lambda functions](https://realpython.com/python-lambda/)
|
||||
@@ -4,7 +4,7 @@ When you render lists in React, you can use the `key` prop to specify a unique k
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Lists and Keys](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key)
|
||||
- [Lists and Keys](https://reactjs.org/docs/lists-and-keys.html)
|
||||
- [Rendering Lists](https://react.dev/learn/rendering-lists)
|
||||
- [List components in React by Example](https://www.robinwieruch.de/react-list-component/)
|
||||
- [Why do we need the key prop in React?](https://www.robinwieruch.de/react-list-key/)
|
||||
- [Why do we need the key prop in React?](https://www.robinwieruch.de/react-list-key/)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Render Props
|
||||
|
||||
The term 'render props' refers to a technique for sharing code between React components using a prop whose value is a function.
|
||||
The term “render prop” refers to a technique for sharing code between React components using a prop whose value is a function.
|
||||
|
||||
A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Render Props in React](https://react.dev/learn/passing-props-to-a-component)
|
||||
- [Render Props in React](https://reactjs.org/docs/render-props.html)
|
||||
- [How to create a Render Prop Component](https://www.robinwieruch.de/react-render-props/)
|
||||
- [Render Props Pattern](https://www.patterns.dev/posts/render-props-pattern/)
|
||||
- [Render Props Pattern](https://www.patterns.dev/posts/render-props-pattern/)
|
||||
|
||||
@@ -6,9 +6,9 @@ In the typical React dataflow, props are the only way that parent components int
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Refs and DOM](https://react.dev/learn/referencing-values-with-refs)
|
||||
- [Refs and DOM](https://reactjs.org/docs/refs-and-the-dom.html)
|
||||
- [Referencing Values with Refs](https://react.dev/learn/referencing-values-with-refs)
|
||||
- [Manipulating the DOM with Refs](https://react.dev/learn/manipulating-the-dom-with-refs)
|
||||
- [Examples of using refs in React](https://www.robinwieruch.de/react-ref/)
|
||||
- [The Complete Guide to useRef() and Refs in React](https://dmitripavlutin.com/react-useref-guide/)
|
||||
- [Learn useRef in 11 Minutes - Web Dev Simplified](https://www.youtube.com/watch?v=t2ypzz6gJm0)
|
||||
- [Learn useRef in 11 Minutes - Web Dev Simplified](https://www.youtube.com/watch?v=t2ypzz6gJm0)
|
||||
|
||||
@@ -23,4 +23,3 @@ Visit the following resources to learn more:
|
||||
- [useContext Hook by Example](https://www.robinwieruch.de/react-usecontext-hook/)
|
||||
- [useReducer Hook by Example](https://www.robinwieruch.de/react-usereducer-hook/)
|
||||
- [useReducer vs useState Hook](https://www.robinwieruch.de/react-usereducer-vs-usestate/)
|
||||
- [useDefferedValue Hook video](https://www.youtube.com/watch?v=jcgmedd6iwa)
|
||||
@@ -11,4 +11,3 @@ To learn more, visit the following links:
|
||||
|
||||
- [Scalability, Availability & Stability Patterns](https://www.slideshare.net/jboner/scalability-availability-stability-patterns/)
|
||||
- [A Word on Scalability](https://www.allthingsdistributed.com/2006/03/a_word_on_scalability.html)
|
||||
- [Performance vs Scalability](https://blog.professorbeekums.com/performance-vs-scalability/)
|
||||
@@ -8,4 +8,3 @@ Learn more from the following links:
|
||||
|
||||
- [System Design: Latency vs Throughput](https://cs.fyi/guide/latency-vs-throughput/)
|
||||
- [Understanding Latency versus Throughput](https://community.cadence.com/cadence_blogs_8/b/fv/posts/understanding-latency-vs-throughput)
|
||||
- [Latency and Throughput - MIT Video](https://www.youtube.com/watch?v=3hiv4mnlgcw)
|
||||
@@ -12,5 +12,3 @@ let str = num as string;
|
||||
```
|
||||
|
||||
It's important to note that type assertions do not change the runtime type of a value, and do not cause any type of conversion. They simply provide a way for the programmer to override the type inference performed by the compiler.
|
||||
|
||||
- [Type assertions](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions)
|
||||
@@ -1,9 +0,0 @@
|
||||
export default async (load, opts) => {
|
||||
const isAuthenticated = document.cookie.toString().indexOf('__roadmapsh_jt__') !== -1;
|
||||
if (isAuthenticated) {
|
||||
console.log("loading");
|
||||
|
||||
const hydrate = await load();
|
||||
await hydrate();
|
||||
}
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
type DownloadImageProps = {
|
||||
url: string;
|
||||
name: string;
|
||||
extension?: 'png' | 'jpg';
|
||||
scale?: number;
|
||||
};
|
||||
|
||||
export async function downloadImage({
|
||||
url,
|
||||
name,
|
||||
extension = 'png',
|
||||
scale = 1,
|
||||
}: DownloadImageProps) {
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
const svg = await res.text();
|
||||
|
||||
const image = `data:image/svg+xml;base64,${window.btoa(svg)}`;
|
||||
const img = new Image();
|
||||
img.src = image;
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.width * scale;
|
||||
canvas.height = img.height * scale;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx?.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
const png = canvas.toDataURL('image/png', 1.0); // Increase the quality by setting a higher value (0.0 - 1.0)
|
||||
const a = document.createElement('a');
|
||||
a.href = png;
|
||||
a.download = `${name}.${extension}`;
|
||||
a.click();
|
||||
};
|
||||
} catch (error) {
|
||||
alert('Error downloading image');
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import Cookies from 'js-cookie';
|
||||
import { TOKEN_COOKIE_NAME, decodeToken } from '../lib/jwt';
|
||||
|
||||
export function useAuth() {
|
||||
const token = Cookies.get(TOKEN_COOKIE_NAME);
|
||||
if (!token) {
|
||||
return null;
|
||||
}
|
||||
const user = decodeToken(token);
|
||||
|
||||
return user;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
|
||||
export function useCopyText() {
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
|
||||
const copyText = (text: string) => {
|
||||
navigator.clipboard.writeText(text).then();
|
||||
setIsCopied(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
if (isCopied) {
|
||||
timeout = setTimeout(() => {
|
||||
setIsCopied(false);
|
||||
}, 2000);
|
||||
}
|
||||
return () => clearTimeout(timeout);
|
||||
}, [isCopied]);
|
||||
|
||||
return { isCopied, copyText };
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 384 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M256 48V64c0 17.7-14.3 32-32 32H160c-17.7 0-32-14.3-32-32V48H64c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320c8.8 0 16-7.2 16-16V64c0-8.8-7.2-16-16-16H256zM0 64C0 28.7 28.7 0 64 0H320c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zM160 320h64c44.2 0 80 35.8 80 80c0 8.8-7.2 16-16 16H96c-8.8 0-16-7.2-16-16c0-44.2 35.8-80 80-80zm-32-96a64 64 0 1 1 128 0 64 64 0 1 1 -128 0z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 571 B |
@@ -1 +0,0 @@
|
||||
<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg>
|
||||
|
Before Width: | Height: | Size: 419 B |
@@ -1,3 +1,4 @@
|
||||
<svg viewBox="0 0 14 14" focusable="false" class='h-3 w-3' aria-hidden="true">
|
||||
<path fill="currentColor" d="M11.2857,6.05714 L10.08571,4.85714 L7.85714,7.14786 L7.85714,1 L6.14286,1 L6.14286,7.14786 L3.91429,4.85714 L2.71429,6.05714 L7,10.42857 L11.2857,6.05714 Z M1,11.2857 L1,13 L13,13 L13,11.2857 L1,11.2857 Z"></path>
|
||||
<path fill="currentColor"
|
||||
d="M11.2857,6.05714 L10.08571,4.85714 L7.85714,7.14786 L7.85714,1 L6.14286,1 L6.14286,7.14786 L3.91429,4.85714 L2.71429,6.05714 L7,10.42857 L11.2857,6.05714 Z M1,11.2857 L1,13 L13,13 L13,11.2857 L1,11.2857 Z"></path>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 339 B |
@@ -149,8 +149,8 @@ const gaPageIdentifier = Astro.url.pathname
|
||||
</slot>
|
||||
|
||||
<Authenticator />
|
||||
<CommandMenu client:idle />
|
||||
<PageProgress initialMessage={initialLoadingMessage} client:idle />
|
||||
<CommandMenu client:idle />
|
||||
<PageSponsor
|
||||
gaPageIdentifier={briefTitle || gaPageIdentifier}
|
||||
client:load
|
||||
|
||||
@@ -223,61 +223,4 @@ export async function renderResourceProgress(
|
||||
skipped.forEach((topicId) => {
|
||||
renderTopicProgress(topicId, 'skipped');
|
||||
});
|
||||
|
||||
refreshProgressCounters();
|
||||
}
|
||||
|
||||
export function refreshProgressCounters() {
|
||||
const progressNumsContainer = document.getElementById('progress-nums-container');
|
||||
const progressNums = document.getElementById('progress-nums');
|
||||
if (!progressNumsContainer || !progressNums) {
|
||||
return;
|
||||
}
|
||||
|
||||
const totalClickable = document.querySelectorAll('.clickable-group').length;
|
||||
const externalLinks = document.querySelectorAll(
|
||||
'[data-group-id^="ext_link:"]'
|
||||
).length;
|
||||
const roadmapSwitchers = document.querySelectorAll(
|
||||
'[data-group-id^="json:"]'
|
||||
).length;
|
||||
|
||||
const totalItems = totalClickable - externalLinks - roadmapSwitchers;
|
||||
const totalDone = document.querySelectorAll('.clickable-group.done').length;
|
||||
const totalLearning = document.querySelectorAll(
|
||||
'.clickable-group.learning'
|
||||
).length;
|
||||
const totalSkipped = document.querySelectorAll(
|
||||
'.clickable-group.skipped'
|
||||
).length;
|
||||
|
||||
const doneCountEl = document.querySelector('.progress-done');
|
||||
if (doneCountEl) {
|
||||
doneCountEl.innerHTML = `${totalDone}`;
|
||||
}
|
||||
|
||||
const learningCountEl = document.querySelector('.progress-learning');
|
||||
if (learningCountEl) {
|
||||
learningCountEl.innerHTML = `${totalLearning}`;
|
||||
}
|
||||
|
||||
const skippedCountEl = document.querySelector('.progress-skipped');
|
||||
if (skippedCountEl) {
|
||||
skippedCountEl.innerHTML = `${totalSkipped}`;
|
||||
}
|
||||
|
||||
const totalCountEl = document.querySelector('.progress-total');
|
||||
if (totalCountEl) {
|
||||
totalCountEl.innerHTML = `${totalItems}`;
|
||||
}
|
||||
|
||||
const progressPercentage = Math.round(((totalDone + totalSkipped) / totalItems) * 100);
|
||||
const progressPercentageEl = document.querySelector('.progress-percentage');
|
||||
if (progressPercentageEl) {
|
||||
progressPercentageEl.innerHTML = `${progressPercentage}`;
|
||||
}
|
||||
|
||||
progressNumsContainer.classList.remove('striped-loader')
|
||||
progressNums.classList.remove('opacity-0');
|
||||
progressNums.classList.remove('opacity-100');
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ const legacyRoadmapUrls = [...roadmapIds.map((id) => `/${id}/`), '/roadmaps/'];
|
||||
---
|
||||
|
||||
<BaseLayout title='Page not found' permalink={'/404'} noIndex={true}>
|
||||
<!-- Legacy roadmap pages handling -->
|
||||
<!-- Legacy roadmp pages handling -->
|
||||
<script slot='after-header' define:vars={{ legacyRoadmapUrls }}>
|
||||
// If it's a roadmap page and it ends with a slash
|
||||
// redirect to the same page without the slash
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
import AccountSidebar from '../../components/AccountSidebar.astro';
|
||||
import AccountLayout from '../../layouts/AccountLayout.astro';
|
||||
import { RoadCardPage } from '../../components/RoadCard/RoadCardPage';
|
||||
---
|
||||
|
||||
<AccountLayout
|
||||
title='Road Card'
|
||||
noIndex={true}
|
||||
initialLoadingMessage='Preparing card'
|
||||
>
|
||||
<AccountSidebar activePageId='road-card' activePageTitle='Road Card'>
|
||||
<RoadCardPage client:load />
|
||||
</AccountSidebar>
|
||||
</AccountLayout>
|
||||
@@ -1,8 +1,7 @@
|
||||
---
|
||||
import FeaturedVideos from '../components/FeaturedVideos.astro';
|
||||
import FeaturedGuides from '../components/FeaturedGuides.astro';
|
||||
import FeaturedItems from '../components/FeaturedItems/FeaturedItems.astro';
|
||||
import HeroSection from '../components/HeroSection/HeroSection.astro';
|
||||
import FeaturedVideos from '../components/FeaturedVideos.astro';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { getAllBestPractices } from '../lib/best-pratice';
|
||||
import { getAllGuides } from '../lib/guide';
|
||||
@@ -23,7 +22,24 @@ const videos = await getAllVideos();
|
||||
permalink={'/'}
|
||||
>
|
||||
<div class='bg-gradient-to-b from-slate-900 to-black'>
|
||||
<HeroSection />
|
||||
<div class='border-b border-b-[#1e293c]'>
|
||||
<div class='container text-left sm:text-center py-6 pb-14 sm:py-20 px-6 sm:px-0'>
|
||||
<h1
|
||||
class='text-2xl sm:text-5xl mb-2 sm:mb-4 font-bold bg-gradient-to-b from-amber-50 to-purple-500 text-transparent bg-clip-text'
|
||||
>
|
||||
Developer Roadmaps
|
||||
</h1>
|
||||
|
||||
<p class='hidden sm:block text-gray-400 text-lg px-4'>
|
||||
<span class='font-medium text-gray-400'>roadmap.sh</span> is a community effort to create roadmaps, guides and
|
||||
other educational content to help guide developers in picking up the path and guide their learnings.
|
||||
</p>
|
||||
|
||||
<p class='block sm:hidden text-gray-400 text-md px-0'>
|
||||
Community created roadmaps, guides and articles to help developers grow in their career.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FeaturedItems
|
||||
heading='Role based Roadmaps'
|
||||
@@ -38,10 +54,7 @@ const videos = await getAllVideos();
|
||||
<FeaturedItems
|
||||
heading='Skill based Roadmaps'
|
||||
featuredItems={skillRoadmaps.map((roadmapItem) => ({
|
||||
text:
|
||||
roadmapItem.frontmatter.briefTitle === 'Go'
|
||||
? 'Go Roadmap'
|
||||
: roadmapItem.frontmatter.briefTitle,
|
||||
text: roadmapItem.frontmatter.briefTitle === 'Go' ? 'Go Roadmap' : roadmapItem.frontmatter.briefTitle,
|
||||
url: `/${roadmapItem.id}`,
|
||||
isNew: roadmapItem.frontmatter.isNew,
|
||||
isUpcoming: roadmapItem.frontmatter.isUpcoming,
|
||||
@@ -58,7 +71,7 @@ const videos = await getAllVideos();
|
||||
}))}
|
||||
/>
|
||||
|
||||
<div class='grid grid-cols-1 gap-7 bg-gray-50 py-7 sm:gap-16 sm:py-16'>
|
||||
<div class='grid grid-cols-1 gap-7 sm:gap-16 bg-gray-50 py-7 sm:py-16'>
|
||||
<FeaturedGuides heading='Guides' guides={guides.slice(0, 7)} />
|
||||
<FeaturedVideos heading='Videos' videos={videos.slice(0, 7)} />
|
||||
</div>
|
||||
|
||||
@@ -6,16 +6,6 @@
|
||||
.container {
|
||||
@apply mx-auto max-w-[830px] px-4;
|
||||
}
|
||||
|
||||
/* Chrome, Safari and Opera */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
}
|
||||
|
||||
blockquote p:before {
|
||||
@@ -63,21 +53,3 @@ a > code:before {
|
||||
)
|
||||
hsla(203, 11%, 95%, 0.4);
|
||||
}
|
||||
|
||||
.striped-loader {
|
||||
background-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
transparent,
|
||||
transparent 5px,
|
||||
hsla(0, 0%, 0%, 0.025) 5px,
|
||||
hsla(0, 0%, 0%, 0.025) 10px
|
||||
);
|
||||
background-size: 200% 200%;
|
||||
animation: barberpole 15s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes barberpole {
|
||||
100% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user