mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-15 11:21:46 +08:00
Compare commits
58 Commits
fix/authen
...
ai/roadmap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87a3de3302 | ||
|
|
1a7bbdde18 | ||
|
|
90b8563413 | ||
|
|
359cc332ec | ||
|
|
e04d93101d | ||
|
|
5e67914d7d | ||
|
|
377d5d763e | ||
|
|
610aae2a07 | ||
|
|
bb9dcfb678 | ||
|
|
33e4e4b3cd | ||
|
|
dfbb6a6dba | ||
|
|
59bb3fc2da | ||
|
|
38951a5dd2 | ||
|
|
90df14c545 | ||
|
|
58f12af2d7 | ||
|
|
44d3724880 | ||
|
|
9d4aae10b5 | ||
|
|
070d04334b | ||
|
|
a06f813f77 | ||
|
|
7040b6637c | ||
|
|
cfa8d2a986 | ||
|
|
d2f372fd6f | ||
|
|
b5303e88e4 | ||
|
|
b8bdbcb1c5 | ||
|
|
1aa54662d6 | ||
|
|
6e0f170680 | ||
|
|
f0b2434126 | ||
|
|
f6cd6419be | ||
|
|
dcef07d7c6 | ||
|
|
08a315c341 | ||
|
|
bd3fd8bfe2 | ||
|
|
797b1e6dca | ||
|
|
44b62c2b2d | ||
|
|
d958a29862 | ||
|
|
69df6d906b | ||
|
|
37ffc2cc62 | ||
|
|
dd3a46e972 | ||
|
|
9f8fcb8265 | ||
|
|
6c99127fc4 | ||
|
|
9157e18ec7 | ||
|
|
84093e3525 | ||
|
|
04d0f7c0b1 | ||
|
|
f1f56408d5 | ||
|
|
d847eb0685 | ||
|
|
d697707384 | ||
|
|
fdee813a0b | ||
|
|
707f0097dc | ||
|
|
27e98b0eba | ||
|
|
8d677f3a22 | ||
|
|
a9734c7eeb | ||
|
|
7ba48523da | ||
|
|
c1ebe9ae47 | ||
|
|
415a9c0fd0 | ||
|
|
d3b8cbceaa | ||
|
|
c390f4428e | ||
|
|
2e890b1b25 | ||
|
|
7b9be9377b | ||
|
|
4863f08a4c |
38
package.json
38
package.json
@@ -22,48 +22,52 @@
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/react": "^3.0.9",
|
||||
"@astrojs/react": "^3.0.10",
|
||||
"@astrojs/sitemap": "^3.0.5",
|
||||
"@astrojs/tailwind": "^5.1.0",
|
||||
"@fingerprintjs/fingerprintjs": "^4.2.1",
|
||||
"@fingerprintjs/fingerprintjs": "^4.2.2",
|
||||
"@nanostores/react": "^0.7.1",
|
||||
"@types/react": "^18.2.48",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"astro": "^4.2.1",
|
||||
"astro-compress": "^2.2.8",
|
||||
"@types/react": "^18.2.56",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"astro": "^4.4.0",
|
||||
"astro-compress": "^2.2.10",
|
||||
"clsx": "^2.1.0",
|
||||
"dracula-prism": "^2.1.13",
|
||||
"jose": "^5.2.0",
|
||||
"dom-to-image": "^2.6.0",
|
||||
"dracula-prism": "^2.1.16",
|
||||
"jose": "^5.2.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.300.0",
|
||||
"nanoid": "^5.0.4",
|
||||
"lucide-react": "^0.334.0",
|
||||
"nanoid": "^5.0.5",
|
||||
"nanostores": "^0.9.5",
|
||||
"node-html-parser": "^6.1.12",
|
||||
"npm-check-updates": "^16.14.12",
|
||||
"npm-check-updates": "^16.14.15",
|
||||
"prismjs": "^1.29.0",
|
||||
"react": "^18.2.0",
|
||||
"react-confetti": "^6.1.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"reactflow": "^11.10.2",
|
||||
"reactflow": "^11.10.4",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"remark-parse": "^11.0.0",
|
||||
"roadmap-renderer": "^1.0.6",
|
||||
"slugify": "^1.6.6",
|
||||
"tailwind-merge": "^2.2.1",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"zustand": "^4.5.0"
|
||||
"unified": "^11.0.4",
|
||||
"zustand": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.41.1",
|
||||
"@playwright/test": "^1.41.2",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/dom-to-image": "^2.6.7",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/prismjs": "^1.26.3",
|
||||
"csv-parser": "^3.0.0",
|
||||
"gh-pages": "^6.1.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"markdown-it": "^14.0.0",
|
||||
"openai": "^4.25.0",
|
||||
"prettier": "^3.2.4",
|
||||
"prettier-plugin-astro": "^0.12.3",
|
||||
"openai": "^4.28.0",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-astro": "^0.13.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.11"
|
||||
}
|
||||
}
|
||||
|
||||
389
pnpm-lock.yaml
generated
389
pnpm-lock.yaml
generated
@@ -6,50 +6,53 @@ settings:
|
||||
|
||||
dependencies:
|
||||
'@astrojs/react':
|
||||
specifier: ^3.0.9
|
||||
version: 3.0.9(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)(vite@5.0.12)
|
||||
specifier: ^3.0.10
|
||||
version: 3.0.10(@types/react-dom@18.2.19)(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)(vite@5.1.3)
|
||||
'@astrojs/sitemap':
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
'@astrojs/tailwind':
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0(astro@4.2.1)(tailwindcss@3.4.1)
|
||||
version: 5.1.0(astro@4.4.0)(tailwindcss@3.4.1)
|
||||
'@fingerprintjs/fingerprintjs':
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1
|
||||
specifier: ^4.2.2
|
||||
version: 4.2.2
|
||||
'@nanostores/react':
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1(nanostores@0.9.5)(react@18.2.0)
|
||||
'@types/react':
|
||||
specifier: ^18.2.48
|
||||
version: 18.2.48
|
||||
specifier: ^18.2.56
|
||||
version: 18.2.59
|
||||
'@types/react-dom':
|
||||
specifier: ^18.2.18
|
||||
version: 18.2.18
|
||||
specifier: ^18.2.19
|
||||
version: 18.2.19
|
||||
astro:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1
|
||||
specifier: ^4.4.0
|
||||
version: 4.4.0
|
||||
astro-compress:
|
||||
specifier: ^2.2.8
|
||||
version: 2.2.8
|
||||
specifier: ^2.2.10
|
||||
version: 2.2.10
|
||||
clsx:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0
|
||||
dom-to-image:
|
||||
specifier: ^2.6.0
|
||||
version: 2.6.0
|
||||
dracula-prism:
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13
|
||||
specifier: ^2.1.16
|
||||
version: 2.1.16
|
||||
jose:
|
||||
specifier: ^5.2.0
|
||||
version: 5.2.0
|
||||
specifier: ^5.2.2
|
||||
version: 5.2.2
|
||||
js-cookie:
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
lucide-react:
|
||||
specifier: ^0.300.0
|
||||
version: 0.300.0(react@18.2.0)
|
||||
specifier: ^0.334.0
|
||||
version: 0.334.0(react@18.2.0)
|
||||
nanoid:
|
||||
specifier: ^5.0.4
|
||||
version: 5.0.4
|
||||
specifier: ^5.0.5
|
||||
version: 5.0.5
|
||||
nanostores:
|
||||
specifier: ^0.9.5
|
||||
version: 0.9.5
|
||||
@@ -57,8 +60,8 @@ dependencies:
|
||||
specifier: ^6.1.12
|
||||
version: 6.1.12
|
||||
npm-check-updates:
|
||||
specifier: ^16.14.12
|
||||
version: 16.14.12
|
||||
specifier: ^16.14.15
|
||||
version: 16.14.15
|
||||
prismjs:
|
||||
specifier: ^1.29.0
|
||||
version: 1.29.0
|
||||
@@ -72,11 +75,14 @@ dependencies:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
reactflow:
|
||||
specifier: ^11.10.2
|
||||
version: 11.10.2(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
specifier: ^11.10.4
|
||||
version: 11.10.4(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
rehype-external-links:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
remark-parse:
|
||||
specifier: ^11.0.0
|
||||
version: 11.0.0
|
||||
roadmap-renderer:
|
||||
specifier: ^1.0.6
|
||||
version: 1.0.6
|
||||
@@ -89,17 +95,23 @@ dependencies:
|
||||
tailwindcss:
|
||||
specifier: ^3.4.1
|
||||
version: 3.4.1
|
||||
unified:
|
||||
specifier: ^11.0.4
|
||||
version: 11.0.4
|
||||
zustand:
|
||||
specifier: ^4.5.0
|
||||
version: 4.5.0(@types/react@18.2.48)(react@18.2.0)
|
||||
specifier: ^4.5.1
|
||||
version: 4.5.1(@types/react@18.2.59)(react@18.2.0)
|
||||
|
||||
devDependencies:
|
||||
'@playwright/test':
|
||||
specifier: ^1.41.1
|
||||
version: 1.41.1
|
||||
specifier: ^1.41.2
|
||||
version: 1.41.2
|
||||
'@tailwindcss/typography':
|
||||
specifier: ^0.5.10
|
||||
version: 0.5.10(tailwindcss@3.4.1)
|
||||
'@types/dom-to-image':
|
||||
specifier: ^2.6.7
|
||||
version: 2.6.7
|
||||
'@types/js-cookie':
|
||||
specifier: ^3.0.6
|
||||
version: 3.0.6
|
||||
@@ -119,17 +131,17 @@ devDependencies:
|
||||
specifier: ^14.0.0
|
||||
version: 14.0.0
|
||||
openai:
|
||||
specifier: ^4.25.0
|
||||
version: 4.25.0
|
||||
specifier: ^4.28.0
|
||||
version: 4.28.0
|
||||
prettier:
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4
|
||||
specifier: ^3.2.5
|
||||
version: 3.2.5
|
||||
prettier-plugin-astro:
|
||||
specifier: ^0.12.3
|
||||
version: 0.12.3
|
||||
specifier: ^0.13.0
|
||||
version: 0.13.0
|
||||
prettier-plugin-tailwindcss:
|
||||
specifier: ^0.5.11
|
||||
version: 0.5.11(prettier-plugin-astro@0.12.3)(prettier@3.2.4)
|
||||
version: 0.5.11(prettier-plugin-astro@0.13.0)(prettier@3.2.5)
|
||||
|
||||
packages:
|
||||
|
||||
@@ -149,16 +161,16 @@ packages:
|
||||
resolution: {integrity: sha512-o/ObKgtMzl8SlpIdzaxFnt7SATKPxu4oIP/1NL+HDJRzxfJcAkOTAb/ZKMRyULbz4q+1t2/DAebs2Z1QairkZw==}
|
||||
dev: true
|
||||
|
||||
/@astrojs/compiler@2.5.0:
|
||||
resolution: {integrity: sha512-ZDluNgMIJT+z+HJcZ6QEJ/KqaFkTkrb+Za6c6VZs8G/nb1LBErL14/iU5EVJ9yu25i4QCLweuBJ3m5df34gZJg==}
|
||||
/@astrojs/compiler@2.5.3:
|
||||
resolution: {integrity: sha512-jzj01BRv/fmo+9Mr2FhocywGzEYiyiP2GVHje1ziGNU6c97kwhYGsnvwMkHrncAy9T9Vi54cjaMK7UE4ClX4vA==}
|
||||
dev: false
|
||||
|
||||
/@astrojs/internal-helpers@0.2.1:
|
||||
resolution: {integrity: sha512-06DD2ZnItMwUnH81LBLco3tWjcZ1lGU9rLCCBaeUCGYe9cI0wKyY2W3kDyoW1I6GmcWgt1fu+D1CTvz+FIKf8A==}
|
||||
dev: false
|
||||
|
||||
/@astrojs/markdown-remark@4.1.0:
|
||||
resolution: {integrity: sha512-JnIy6zk+6f/SgSVMZecZFxQt0faT1uBckwYCuBxKH1hYYJsal8OOe+tx6JxfnyaV+xViyjUvQ28mmn+p/F5LkQ==}
|
||||
/@astrojs/markdown-remark@4.2.1:
|
||||
resolution: {integrity: sha512-2RQBIwrq+2qPYtp99bH+eL5hfbK0BoxXla85lHsRpIX/IsGqFrPX6pXI2cbWPihBwGbKCdxS6uZNX2QerZWwpQ==}
|
||||
dependencies:
|
||||
'@astrojs/prism': 3.0.0
|
||||
github-slugger: 2.0.0
|
||||
@@ -185,8 +197,8 @@ packages:
|
||||
prismjs: 1.29.0
|
||||
dev: false
|
||||
|
||||
/@astrojs/react@3.0.9(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)(vite@5.0.12):
|
||||
resolution: {integrity: sha512-31J5yF5p9yBFV1axBooLA9PB4B2+MYm7swWhtlezSsJiUNXRFo5Is9qI3teJ7cKgmaCv+ZA593Smskko0NGaDQ==}
|
||||
/@astrojs/react@3.0.10(@types/react-dom@18.2.19)(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)(vite@5.1.3):
|
||||
resolution: {integrity: sha512-uGRIwKMAn7tva2vxXMyoVIGxWFr0rjZ8ZWIlkTG/vIpnAjD2nM8Cz6B8j7yzj176jvl6gZ6xTbTVPm09aeK0Yw==}
|
||||
engines: {node: '>=18.14.1'}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.50 || ^18.0.21
|
||||
@@ -194,9 +206,9 @@ packages:
|
||||
react: ^17.0.2 || ^18.0.0
|
||||
react-dom: ^17.0.2 || ^18.0.0
|
||||
dependencies:
|
||||
'@types/react': 18.2.48
|
||||
'@types/react-dom': 18.2.18
|
||||
'@vitejs/plugin-react': 4.2.1(vite@5.0.12)
|
||||
'@types/react': 18.2.59
|
||||
'@types/react-dom': 18.2.19
|
||||
'@vitejs/plugin-react': 4.2.1(vite@5.1.3)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
ultrahtml: 1.5.2
|
||||
@@ -212,13 +224,13 @@ packages:
|
||||
zod: 3.22.4
|
||||
dev: false
|
||||
|
||||
/@astrojs/tailwind@5.1.0(astro@4.2.1)(tailwindcss@3.4.1):
|
||||
/@astrojs/tailwind@5.1.0(astro@4.4.0)(tailwindcss@3.4.1):
|
||||
resolution: {integrity: sha512-BJoCDKuWhU9FT2qYg+fr6Nfb3qP4ShtyjXGHKA/4mHN94z7BGcmauQK23iy+YH5qWvTnhqkd6mQPQ1yTZTe9Ig==}
|
||||
peerDependencies:
|
||||
astro: ^3.0.0 || ^4.0.0
|
||||
tailwindcss: ^3.0.24
|
||||
dependencies:
|
||||
astro: 4.2.1
|
||||
astro: 4.4.0
|
||||
autoprefixer: 10.4.17(postcss@8.4.33)
|
||||
postcss: 8.4.33
|
||||
postcss-load-config: 4.0.2(postcss@8.4.33)
|
||||
@@ -718,8 +730,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@fingerprintjs/fingerprintjs@4.2.1:
|
||||
resolution: {integrity: sha512-uW+GVUNTgCXbVPEbgnbf5Aor22e1dyYR0JRwdUiZBaikfxr7KlhV9y0aahA1FB99fEeQVvhCEvTcPIFSYTy9Pw==}
|
||||
/@fingerprintjs/fingerprintjs@4.2.2:
|
||||
resolution: {integrity: sha512-scD+pDgNZW78LuFAr7ms2yxmDx2NWC4+K5iiOjPT2ZlTlHFbLsORUzLJI2rcKicxxLtHbvf3A7BU1drVr4iHGg==}
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
@@ -959,6 +971,10 @@ packages:
|
||||
'@jridgewell/resolve-uri': 3.1.1
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
|
||||
/@medv/finder@3.1.0:
|
||||
resolution: {integrity: sha512-ojkXjR3K0Zz3jnCR80tqPL+0yvbZk/lEodb6RIVjLz7W8RVA2wrw8ym/CzCpXO9SYVUIKHFUpc7jvf8UKfIM3w==}
|
||||
dev: false
|
||||
|
||||
/@nanostores/react@0.7.1(nanostores@0.9.5)(react@18.2.0):
|
||||
resolution: {integrity: sha512-EXQg9N4MdI4eJQz/AZLIx3hxQ6BuBmV4Q55bCd5YCSgEOAW7tGTsIZxpRXxvxLXzflNvHTBvfrDNY38TlSVBkQ==}
|
||||
engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
|
||||
@@ -1069,12 +1085,12 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@playwright/test@1.41.1:
|
||||
resolution: {integrity: sha512-9g8EWTjiQ9yFBXc6HjCWe41msLpxEX0KhmfmPl9RPLJdfzL4F0lg2BdJ91O9azFdl11y1pmpwdjBiSxvqc+btw==}
|
||||
/@playwright/test@1.41.2:
|
||||
resolution: {integrity: sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
playwright: 1.41.1
|
||||
playwright: 1.41.2
|
||||
dev: true
|
||||
|
||||
/@pnpm/config.env-replace@1.1.0:
|
||||
@@ -1098,40 +1114,40 @@ packages:
|
||||
config-chain: 1.1.13
|
||||
dev: false
|
||||
|
||||
/@reactflow/background@11.3.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-PhkvoFtO/NXJgFtBvfbPwdR/6/dl25egQlFhKWS3T4aYa7rh80dvf6dF3t6+JXJS4q5ToYJizD2/n8/qylo1yQ==}
|
||||
/@reactflow/background@11.3.9(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-byj/G9pEC8tN0wT/ptcl/LkEP/BBfa33/SvBkqE4XwyofckqF87lKp573qGlisfnsijwAbpDlf81PuFL41So4Q==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/core': 11.10.2(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/core': 11.10.4(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
classcat: 5.0.4
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.5.0(@types/react@18.2.48)(react@18.2.0)
|
||||
zustand: 4.5.1(@types/react@18.2.59)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@reactflow/controls@11.2.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-mugzVALH/SuKlVKk+JCRm1OXQ+p8e9+k8PCTIaqL+nBl+lPF8KA4uMm8ApsOvhuSAb2A80ezewpyvYHr0qSYVA==}
|
||||
/@reactflow/controls@11.2.9(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-e8nWplbYfOn83KN1BrxTXS17+enLyFnjZPbyDgHSRLtI5ZGPKF/8iRXV+VXb2LFVzlu4Wh3la/pkxtfP/0aguA==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/core': 11.10.2(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/core': 11.10.4(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
classcat: 5.0.4
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.5.0(@types/react@18.2.48)(react@18.2.0)
|
||||
zustand: 4.5.1(@types/react@18.2.59)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@reactflow/core@11.10.2(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-/cbTxtFpfkIGReSVkcnQhS4Jx4VFY2AhPlJ5n0sbPtnR7OWowF9zodh5Yyzr4j1NOUoBgJ9h+UqGEwwY2dbAlw==}
|
||||
/@reactflow/core@11.10.4(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-j3i9b2fsTX/sBbOm+RmNzYEFWbNx4jGWGuGooh2r1jQaE2eV+TLJgiG/VNOp0q5mBl9f6g1IXs3Gm86S9JfcGw==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
@@ -1146,19 +1162,19 @@ packages:
|
||||
d3-zoom: 3.0.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.5.0(@types/react@18.2.48)(react@18.2.0)
|
||||
zustand: 4.5.1(@types/react@18.2.59)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@reactflow/minimap@11.7.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Pwqw31tJ663cJur6ypqyJU33nPckvTepmz96erdQZoHsfOyLmFj4nXT7afC30DJ48lp0nfNsw+028mlf7f/h4g==}
|
||||
/@reactflow/minimap@11.7.9(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-le95jyTtt3TEtJ1qa7tZ5hyM4S7gaEQkW43cixcMOZLu33VAdc2aCpJg/fXcRrrf7moN2Mbl9WIMNXUKsp5ILA==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/core': 11.10.2(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/core': 11.10.4(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@types/d3-selection': 3.0.10
|
||||
'@types/d3-zoom': 3.0.8
|
||||
classcat: 5.0.4
|
||||
@@ -1166,41 +1182,41 @@ packages:
|
||||
d3-zoom: 3.0.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.5.0(@types/react@18.2.48)(react@18.2.0)
|
||||
zustand: 4.5.1(@types/react@18.2.59)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@reactflow/node-resizer@2.2.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-BMBstmWNiklHnnAjHu8irkiPQ8/k8nnjzqlTql4acbVhD6Tsdxx/t/saOkELmfQODqGZNiPw9+pHcAHgtE6oNQ==}
|
||||
/@reactflow/node-resizer@2.2.9(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-HfickMm0hPDIHt9qH997nLdgLt0kayQyslKE0RS/GZvZ4UMQJlx/NRRyj5y47Qyg0NnC66KYOQWDM9LLzRTnUg==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/core': 11.10.2(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/core': 11.10.4(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
classcat: 5.0.4
|
||||
d3-drag: 3.0.0
|
||||
d3-selection: 3.0.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.5.0(@types/react@18.2.48)(react@18.2.0)
|
||||
zustand: 4.5.1(@types/react@18.2.59)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@reactflow/node-toolbar@1.3.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-75moEQKg23YKA3A2DNSFhq719ZPmby5mpwOD+NO7ZffJ88oMS/2eY8l8qpA3hvb1PTBHDxyKazhJirW+f4t0Wg==}
|
||||
/@reactflow/node-toolbar@1.3.9(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-VmgxKmToax4sX1biZ9LXA7cj/TBJ+E5cklLGwquCCVVxh+lxpZGTBF3a5FJGVHiUNBBtFsC8ldcSZIK4cAlQww==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/core': 11.10.2(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/core': 11.10.4(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
classcat: 5.0.4
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.5.0(@types/react@18.2.48)(react@18.2.0)
|
||||
zustand: 4.5.1(@types/react@18.2.59)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
@@ -1614,6 +1630,10 @@ packages:
|
||||
'@types/ms': 0.7.34
|
||||
dev: false
|
||||
|
||||
/@types/dom-to-image@2.6.7:
|
||||
resolution: {integrity: sha512-me5VbCv+fcXozblWwG13krNBvuEOm6kA5xoa4RrjDJCNFOZSWR3/QLtOXimBHk1Fisq69Gx3JtOoXtg1N1tijg==}
|
||||
dev: true
|
||||
|
||||
/@types/estree@1.0.5:
|
||||
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
|
||||
dev: false
|
||||
@@ -1659,7 +1679,7 @@ packages:
|
||||
/@types/node-fetch@2.6.11:
|
||||
resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
|
||||
dependencies:
|
||||
'@types/node': 18.19.8
|
||||
'@types/node': 20.11.0
|
||||
form-data: 4.0.0
|
||||
dev: true
|
||||
|
||||
@@ -1675,6 +1695,12 @@ packages:
|
||||
|
||||
/@types/node@20.11.0:
|
||||
resolution: {integrity: sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==}
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
dev: true
|
||||
|
||||
/@types/node@20.11.17:
|
||||
resolution: {integrity: sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==}
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
dev: false
|
||||
@@ -1687,14 +1713,14 @@ packages:
|
||||
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
||||
dev: false
|
||||
|
||||
/@types/react-dom@18.2.18:
|
||||
resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==}
|
||||
/@types/react-dom@18.2.19:
|
||||
resolution: {integrity: sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==}
|
||||
dependencies:
|
||||
'@types/react': 18.2.48
|
||||
'@types/react': 18.2.59
|
||||
dev: false
|
||||
|
||||
/@types/react@18.2.48:
|
||||
resolution: {integrity: sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==}
|
||||
/@types/react@18.2.59:
|
||||
resolution: {integrity: sha512-DE+F6BYEC8VtajY85Qr7mmhTd/79rJKIHCg99MU9SWPB4xvLb6D1za2vYflgZfmPqQVEr6UqJTnLXEwzpVPuOg==}
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.11
|
||||
'@types/scheduler': 0.16.8
|
||||
@@ -1723,7 +1749,7 @@ packages:
|
||||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||
dev: false
|
||||
|
||||
/@vitejs/plugin-react@4.2.1(vite@5.0.12):
|
||||
/@vitejs/plugin-react@4.2.1(vite@5.1.3):
|
||||
resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@@ -1734,7 +1760,7 @@ packages:
|
||||
'@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.7)
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.14.0
|
||||
vite: 5.0.12
|
||||
vite: 5.1.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
@@ -1871,29 +1897,29 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/astro-compress@2.2.8:
|
||||
resolution: {integrity: sha512-nLV/htvhPt8t4iM7IdxCfLA/+69+4UN+cknOSTFJs6sKg31Dik0tRZQyHBLdAf7ZRVoSUV6M+49vcwg06FGE2g==}
|
||||
/astro-compress@2.2.10:
|
||||
resolution: {integrity: sha512-JjoEQ5mSrsskoccb81XaAp0Z/J6uYlw9jyVnLHmX4sslyO0a3SKYmLzTIyb+ayGmDar2R8yEKolm4oXtaKC3+w==}
|
||||
dependencies:
|
||||
'@types/csso': 5.0.4
|
||||
'@types/html-minifier-terser': 7.0.2
|
||||
csso: 5.0.5
|
||||
files-pipe: 2.1.6
|
||||
files-pipe: 2.1.8
|
||||
html-minifier-terser: 7.2.0
|
||||
kleur: 4.1.5
|
||||
lightningcss: 1.23.0
|
||||
sharp: 0.33.2
|
||||
svgo: 3.2.0
|
||||
terser: 5.26.0
|
||||
terser: 5.27.0
|
||||
dev: false
|
||||
|
||||
/astro@4.2.1:
|
||||
resolution: {integrity: sha512-TcrveW2/lohmljrbTUgcDxajEdF1yK+zBvb7SXroloGix/d4jkegO6GANFgvyy0zprMyajW7qgJEFyhWUX86Vw==}
|
||||
/astro@4.4.0:
|
||||
resolution: {integrity: sha512-JAsMrm1Z6W4Iqg9Q7LW/6lCXrqa4jndEOlR/yu7NGNP0BrPwSM8i4+yzya6hxgsNvyyVK8ywthaNhFmqd8Z+cg==}
|
||||
engines: {node: '>=18.14.1', npm: '>=6.14.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@astrojs/compiler': 2.5.0
|
||||
'@astrojs/compiler': 2.5.3
|
||||
'@astrojs/internal-helpers': 0.2.1
|
||||
'@astrojs/markdown-remark': 4.1.0
|
||||
'@astrojs/markdown-remark': 4.2.1
|
||||
'@astrojs/telemetry': 3.0.4
|
||||
'@babel/core': 7.23.7
|
||||
'@babel/generator': 7.23.6
|
||||
@@ -1901,6 +1927,7 @@ packages:
|
||||
'@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.23.7)
|
||||
'@babel/traverse': 7.23.7
|
||||
'@babel/types': 7.23.6
|
||||
'@medv/finder': 3.1.0
|
||||
'@types/babel__core': 7.20.5
|
||||
acorn: 8.11.3
|
||||
aria-query: 5.3.0
|
||||
@@ -1911,6 +1938,7 @@ packages:
|
||||
clsx: 2.1.0
|
||||
common-ancestor-path: 1.0.1
|
||||
cookie: 0.6.0
|
||||
cssesc: 3.0.0
|
||||
debug: 4.3.4
|
||||
deterministic-object-hash: 2.0.2
|
||||
devalue: 4.3.2
|
||||
@@ -1937,20 +1965,20 @@ packages:
|
||||
p-queue: 8.0.1
|
||||
path-to-regexp: 6.2.1
|
||||
preferred-pm: 3.1.2
|
||||
probe-image-size: 7.2.3
|
||||
prompts: 2.4.2
|
||||
rehype: 13.0.1
|
||||
resolve: 1.22.8
|
||||
semver: 7.5.4
|
||||
server-destroy: 1.0.1
|
||||
shikiji: 0.9.19
|
||||
shikiji-core: 0.9.19
|
||||
string-width: 7.1.0
|
||||
strip-ansi: 7.1.0
|
||||
tsconfck: 3.0.1
|
||||
unist-util-visit: 5.0.0
|
||||
vfile: 6.0.1
|
||||
vite: 5.0.12
|
||||
vitefu: 0.2.5(vite@5.0.12)
|
||||
vite: 5.1.3
|
||||
vitefu: 0.2.5(vite@5.1.3)
|
||||
which-pm: 2.1.1
|
||||
yargs-parser: 21.1.1
|
||||
zod: 3.22.4
|
||||
@@ -2569,28 +2597,6 @@ packages:
|
||||
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||
dev: false
|
||||
|
||||
/debug@2.6.9:
|
||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
dependencies:
|
||||
ms: 2.0.0
|
||||
dev: false
|
||||
|
||||
/debug@3.2.7:
|
||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
dev: false
|
||||
|
||||
/debug@4.3.4:
|
||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
@@ -2707,6 +2713,10 @@ packages:
|
||||
entities: 4.5.0
|
||||
dev: false
|
||||
|
||||
/dom-to-image@2.6.0:
|
||||
resolution: {integrity: sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA==}
|
||||
dev: false
|
||||
|
||||
/domelementtype@2.3.0:
|
||||
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
|
||||
dev: false
|
||||
@@ -2740,8 +2750,8 @@ packages:
|
||||
is-obj: 2.0.0
|
||||
dev: false
|
||||
|
||||
/dracula-prism@2.1.13:
|
||||
resolution: {integrity: sha512-mgm8Nr/X0RGUz/wpha88dKN/xw0QIGK8BQmWXrzgtOp9be+qcKiFEa2J5SQ3+/WNvL5sPPtNQXPL+Vy//Q8+dg==}
|
||||
/dracula-prism@2.1.16:
|
||||
resolution: {integrity: sha512-fNZU8sMYOFYq/K8WFtsVUJEHemYlQJy7E3wm+Lndp3hTWG+Hp3+sCcbQdWVvQTfw+xIJeI+mIrjfUWHb9Q/s2Q==}
|
||||
dev: false
|
||||
|
||||
/dset@3.1.3:
|
||||
@@ -2950,10 +2960,10 @@ packages:
|
||||
trim-repeated: 1.0.0
|
||||
dev: true
|
||||
|
||||
/files-pipe@2.1.6:
|
||||
resolution: {integrity: sha512-M5z8ff6PQHb4IE5FwOdfFveye9PAyX69g6er7IaHVnYV9xfxR6HSAaXI/2kzIn8Fbe0M8uXlqJ6phKDqQwtF6A==}
|
||||
/files-pipe@2.1.8:
|
||||
resolution: {integrity: sha512-2qebFhQ/Vnw7HygoeKpLjnd6ClzTt9y8qSNXjcP+5DHAFEwDq1T+d6sW8PLSkDujHMsN6Lq2ufFoWZCT8YraXw==}
|
||||
dependencies:
|
||||
'@types/node': 20.11.0
|
||||
'@types/node': 20.11.17
|
||||
deepmerge-ts: 5.1.0
|
||||
fast-glob: 3.3.2
|
||||
dev: false
|
||||
@@ -3421,7 +3431,7 @@ packages:
|
||||
entities: 4.5.0
|
||||
param-case: 3.0.4
|
||||
relateurl: 0.2.7
|
||||
terser: 5.26.0
|
||||
terser: 5.27.0
|
||||
dev: false
|
||||
|
||||
/html-void-elements@3.0.0:
|
||||
@@ -3471,13 +3481,6 @@ packages:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
/iconv-lite@0.4.24:
|
||||
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
dev: false
|
||||
|
||||
/iconv-lite@0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3708,8 +3711,8 @@ packages:
|
||||
resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==}
|
||||
dev: false
|
||||
|
||||
/jose@5.2.0:
|
||||
resolution: {integrity: sha512-oW3PCnvyrcm1HMvGTzqjxxfnEs9EoFOFWi2HsEGhlFVOXxTE3K9GKWVMFoFw06yPUqwpvEWic1BmtUZBI/tIjw==}
|
||||
/jose@5.2.2:
|
||||
resolution: {integrity: sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg==}
|
||||
dev: false
|
||||
|
||||
/js-cookie@3.0.5:
|
||||
@@ -3955,6 +3958,7 @@ packages:
|
||||
|
||||
/lodash.merge@4.6.2:
|
||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||
dev: true
|
||||
|
||||
/lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
@@ -4012,8 +4016,8 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/lucide-react@0.300.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-rQxUUCmWAvNLoAsMZ5j04b2+OJv6UuNLYMY7VK0eVlm4aTwUEjEEHc09/DipkNIlhXUSDn2xoyIzVT0uh7dRsg==}
|
||||
/lucide-react@0.334.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-y0Rv/Xx6qAq4FutZ3L/efl3O9vl6NC/1p0YOg6mBfRbQ4k1JCE2rz0rnV7WC8Moxq1RY99vLATvjcqUegGJTvA==}
|
||||
peerDependencies:
|
||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
@@ -4677,10 +4681,6 @@ packages:
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/ms@2.0.0:
|
||||
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
||||
dev: false
|
||||
|
||||
/ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
dev: false
|
||||
@@ -4700,8 +4700,8 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
/nanoid@5.0.4:
|
||||
resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
|
||||
/nanoid@5.0.5:
|
||||
resolution: {integrity: sha512-/Veqm+QKsyMY3kqi4faWplnY1u+VuKO3dD2binyPIybP31DRO29bPF+1mszgLnrR2KqSLceFLBNw0zmvDzN1QQ==}
|
||||
engines: {node: ^18 || >=20}
|
||||
hasBin: true
|
||||
dev: false
|
||||
@@ -4717,18 +4717,6 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/needle@2.9.1:
|
||||
resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==}
|
||||
engines: {node: '>= 4.4.x'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
iconv-lite: 0.4.24
|
||||
sax: 1.3.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/negotiator@0.6.3:
|
||||
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -4850,8 +4838,8 @@ packages:
|
||||
npm-normalize-package-bin: 3.0.1
|
||||
dev: false
|
||||
|
||||
/npm-check-updates@16.14.12:
|
||||
resolution: {integrity: sha512-5FvqaDX8AqWWTDQFbBllgLwoRXTvzlqVIRSKl9Kg8bYZTfNwMnrp1Zlmb5e/ocf11UjPTc+ShBFjYQ7kg6FL0w==}
|
||||
/npm-check-updates@16.14.15:
|
||||
resolution: {integrity: sha512-WH0wJ9j6CP7Azl+LLCxWAYqroT2IX02kRIzgK/fg0rPpMbETgHITWBdOPtrv521xmA3JMgeNsQ62zvVtS/nCmQ==}
|
||||
engines: {node: '>=14.14'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
@@ -4996,8 +4984,8 @@ packages:
|
||||
mimic-fn: 4.0.0
|
||||
dev: false
|
||||
|
||||
/openai@4.25.0:
|
||||
resolution: {integrity: sha512-qLMFOizjxKuDfQkBrczZPYo6XVL4bdcuz9MR11Q+M91kGcs8dQw+O90nRcC+qWuhaGphQkfXQJMn4cd7Yew3Kg==}
|
||||
/openai@4.28.0:
|
||||
resolution: {integrity: sha512-JM8fhcpmpGN0vrUwGquYIzdcEQHtFuom6sRCbbCM6CfzZXNuRk33G7KfeRAIfnaCxSpzrP5iHtwJzIm6biUZ2Q==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@types/node': 18.19.8
|
||||
@@ -5236,18 +5224,18 @@ packages:
|
||||
dependencies:
|
||||
find-up: 4.1.0
|
||||
|
||||
/playwright-core@1.41.1:
|
||||
resolution: {integrity: sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg==}
|
||||
/playwright-core@1.41.2:
|
||||
resolution: {integrity: sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/playwright@1.41.1:
|
||||
resolution: {integrity: sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ==}
|
||||
/playwright@1.41.2:
|
||||
resolution: {integrity: sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
playwright-core: 1.41.1
|
||||
playwright-core: 1.41.2
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
dev: true
|
||||
@@ -5323,6 +5311,15 @@ packages:
|
||||
picocolors: 1.0.0
|
||||
source-map-js: 1.0.2
|
||||
|
||||
/postcss@8.4.35:
|
||||
resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
dependencies:
|
||||
nanoid: 3.3.7
|
||||
picocolors: 1.0.0
|
||||
source-map-js: 1.0.2
|
||||
dev: false
|
||||
|
||||
/prebuild-install@7.1.1:
|
||||
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -5354,16 +5351,16 @@ packages:
|
||||
which-pm: 2.0.0
|
||||
dev: false
|
||||
|
||||
/prettier-plugin-astro@0.12.3:
|
||||
resolution: {integrity: sha512-GthUSu3zCvmtVyqlArosez0xE08vSJ0R1sWurxIWpABaCkNGYFANoUdFkqmIo54EV2uPLGcVJzOucWvCjPBWvg==}
|
||||
/prettier-plugin-astro@0.13.0:
|
||||
resolution: {integrity: sha512-5HrJNnPmZqTUNoA97zn4gNQv9BgVhv+et03314WpQ9H9N8m2L9OSV798olwmG2YLXPl1iSstlJCR1zB3x5xG4g==}
|
||||
engines: {node: ^14.15.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@astrojs/compiler': 1.8.2
|
||||
prettier: 3.2.4
|
||||
prettier: 3.2.5
|
||||
sass-formatter: 0.7.8
|
||||
dev: true
|
||||
|
||||
/prettier-plugin-tailwindcss@0.5.11(prettier-plugin-astro@0.12.3)(prettier@3.2.4):
|
||||
/prettier-plugin-tailwindcss@0.5.11(prettier-plugin-astro@0.13.0)(prettier@3.2.5):
|
||||
resolution: {integrity: sha512-AvI/DNyMctyyxGOjyePgi/gqj5hJYClZ1avtQvLlqMT3uDZkRbi4HhGUpok3DRzv9z7Lti85Kdj3s3/1CeNI0w==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
peerDependencies:
|
||||
@@ -5412,12 +5409,12 @@ packages:
|
||||
prettier-plugin-twig-melody:
|
||||
optional: true
|
||||
dependencies:
|
||||
prettier: 3.2.4
|
||||
prettier-plugin-astro: 0.12.3
|
||||
prettier: 3.2.5
|
||||
prettier-plugin-astro: 0.13.0
|
||||
dev: true
|
||||
|
||||
/prettier@3.2.4:
|
||||
resolution: {integrity: sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==}
|
||||
/prettier@3.2.5:
|
||||
resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
@@ -5427,16 +5424,6 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/probe-image-size@7.2.3:
|
||||
resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==}
|
||||
dependencies:
|
||||
lodash.merge: 4.6.2
|
||||
needle: 2.9.1
|
||||
stream-parser: 0.3.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/proc-log@3.0.0:
|
||||
resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
@@ -5576,18 +5563,18 @@ packages:
|
||||
loose-envify: 1.4.0
|
||||
dev: false
|
||||
|
||||
/reactflow@11.10.2(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-tqQJfPEiIkXonT3piVYf+F9CvABI5e28t5I6rpaLTnO8YVCAOh1h0f+ziDKz0Bx9Y2B/mFgyz+H7LZeUp/+lhQ==}
|
||||
/reactflow@11.10.4(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-0CApYhtYicXEDg/x2kvUHiUk26Qur8lAtTtiSlptNKuyEuGti6P1y5cS32YGaUoDMoCqkm/m+jcKkfMOvSCVRA==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/background': 11.3.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/controls': 11.2.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/core': 11.10.2(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/minimap': 11.7.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/node-resizer': 2.2.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/node-toolbar': 1.3.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/background': 11.3.9(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/controls': 11.2.9(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/core': 11.10.4(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/minimap': 11.7.9(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/node-resizer': 2.2.9(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/node-toolbar': 1.3.9(@types/react@18.2.59)(react-dom@18.2.0)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
@@ -5889,7 +5876,9 @@ packages:
|
||||
|
||||
/safer-buffer@2.1.2:
|
||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/sass-formatter@0.7.8:
|
||||
resolution: {integrity: sha512-7fI2a8THglflhhYis7k06eUf92VQuJoXzEs2KRP0r1bluFxKFvLx0Ns7c478oYGM0fPfrr846ZRWVi2MAgHt9Q==}
|
||||
@@ -6179,14 +6168,6 @@ packages:
|
||||
bl: 5.1.0
|
||||
dev: false
|
||||
|
||||
/stream-parser@0.3.1:
|
||||
resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==}
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/streamx@2.15.6:
|
||||
resolution: {integrity: sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==}
|
||||
requiresBuild: true
|
||||
@@ -6424,8 +6405,8 @@ packages:
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/terser@5.26.0:
|
||||
resolution: {integrity: sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==}
|
||||
/terser@5.27.0:
|
||||
resolution: {integrity: sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
@@ -6784,8 +6765,8 @@ packages:
|
||||
vfile-message: 4.0.2
|
||||
dev: false
|
||||
|
||||
/vite@5.0.12:
|
||||
resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==}
|
||||
/vite@5.1.3:
|
||||
resolution: {integrity: sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -6813,13 +6794,13 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
esbuild: 0.19.11
|
||||
postcss: 8.4.33
|
||||
postcss: 8.4.35
|
||||
rollup: 4.9.6
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
dev: false
|
||||
|
||||
/vitefu@0.2.5(vite@5.0.12):
|
||||
/vitefu@0.2.5(vite@5.1.3):
|
||||
resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==}
|
||||
peerDependencies:
|
||||
vite: ^3.0.0 || ^4.0.0 || ^5.0.0
|
||||
@@ -6827,7 +6808,7 @@ packages:
|
||||
vite:
|
||||
optional: true
|
||||
dependencies:
|
||||
vite: 5.0.12
|
||||
vite: 5.1.3
|
||||
dev: false
|
||||
|
||||
/web-namespaces@2.0.1:
|
||||
@@ -6968,8 +6949,8 @@ packages:
|
||||
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
|
||||
dev: false
|
||||
|
||||
/zustand@4.5.0(@types/react@18.2.48)(react@18.2.0):
|
||||
resolution: {integrity: sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==}
|
||||
/zustand@4.5.1(@types/react@18.2.59)(react@18.2.0):
|
||||
resolution: {integrity: sha512-XlauQmH64xXSC1qGYNv00ODaQ3B+tNPoy22jv2diYiP4eoDKr9LA+Bh5Bc3gplTrFdb6JVI+N4kc1DZ/tbtfPg==}
|
||||
engines: {node: '>=12.7.0'}
|
||||
peerDependencies:
|
||||
'@types/react': '>=16.8'
|
||||
@@ -6983,7 +6964,7 @@ packages:
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 18.2.48
|
||||
'@types/react': 18.2.59
|
||||
react: 18.2.0
|
||||
use-sync-external-store: 1.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
BIN
public/authors/peter-thaleikis.png
Normal file
BIN
public/authors/peter-thaleikis.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 150 KiB |
BIN
public/images/icons8-wand.gif
Normal file
BIN
public/images/icons8-wand.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
public/pdfs/roadmaps/datastructures-and-algorithms.pdf
Normal file
BIN
public/pdfs/roadmaps/datastructures-and-algorithms.pdf
Normal file
Binary file not shown.
@@ -31,11 +31,13 @@ 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)
|
||||
- [Backend Roadmap](https://roadmap.sh/backend)
|
||||
- [Backend Roadmap](https://roadmap.sh/backend) / [Backend Beginner Roadmap](https://roadmap.sh/backend?r=backend-beginner)
|
||||
- [DevOps Roadmap](https://roadmap.sh/devops) / [DevOps Beginner Roadmap](https://roadmap.sh/devops?r=devops-beginner)
|
||||
- [Full Stack Roadmap](https://roadmap.sh/full-stack)
|
||||
- [Computer Science Roadmap](https://roadmap.sh/computer-science)
|
||||
- [Data Structures and Algorithms Roadmap](https://roadmap.sh/datastructures-and-algorithms)
|
||||
- [AI and Data Scientist Roadmap](https://roadmap.sh/ai-data-scientist)
|
||||
- [MLOps Roadmap](https://roadmap.sh/mlops)
|
||||
- [QA Roadmap](https://roadmap.sh/qa)
|
||||
- [Python Roadmap](https://roadmap.sh/python)
|
||||
- [Software Architect Roadmap](https://roadmap.sh/software-architect)
|
||||
|
||||
@@ -11,15 +11,16 @@ async function getSVG(name: string) {
|
||||
const filepath = `/src/icons/${name}.svg`;
|
||||
|
||||
const files = import.meta.glob<string>('/src/icons/**/*.svg', {
|
||||
query: '?raw',
|
||||
eager: true,
|
||||
as: 'raw',
|
||||
});
|
||||
|
||||
|
||||
if (!(filepath in files)) {
|
||||
throw new Error(`${filepath} not found`);
|
||||
}
|
||||
|
||||
const root = parse(files[filepath]);
|
||||
const root = parse(files[filepath].default as string);
|
||||
|
||||
const svg = root.querySelector('svg');
|
||||
|
||||
@@ -35,4 +36,4 @@ const { attributes: baseAttributes, innerHTML } = await getSVG(icon);
|
||||
const svgAttributes = { ...baseAttributes, ...attributes };
|
||||
---
|
||||
|
||||
<svg {...svgAttributes} set:html={innerHTML}></svg>
|
||||
<svg {...svgAttributes} set:html={innerHTML} />
|
||||
|
||||
41
src/components/AuthenticationFlow/AuthenticationForm.tsx
Normal file
41
src/components/AuthenticationFlow/AuthenticationForm.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useState } from 'react';
|
||||
import { GitHubButton } from './GitHubButton';
|
||||
import { GoogleButton } from './GoogleButton';
|
||||
import { LinkedInButton } from './LinkedInButton';
|
||||
import { EmailLoginForm } from './EmailLoginForm';
|
||||
import { EmailSignupForm } from './EmailSignupForm';
|
||||
|
||||
type AuthenticationFormProps = {
|
||||
type?: 'login' | 'signup';
|
||||
};
|
||||
|
||||
export function AuthenticationForm(props: AuthenticationFormProps) {
|
||||
const { type = 'login' } = props;
|
||||
|
||||
const [isDisabled, setIsDisabled] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<GitHubButton isDisabled={isDisabled} setIsDisabled={setIsDisabled} />
|
||||
<GoogleButton isDisabled={isDisabled} setIsDisabled={setIsDisabled} />
|
||||
<LinkedInButton isDisabled={isDisabled} setIsDisabled={setIsDisabled} />
|
||||
</div>
|
||||
|
||||
<div className="flex w-full items-center gap-2 py-6 text-sm text-slate-600">
|
||||
<div className="h-px w-full bg-slate-200" />
|
||||
OR
|
||||
<div className="h-px w-full bg-slate-200" />
|
||||
</div>
|
||||
|
||||
{type === 'login' ? (
|
||||
<EmailLoginForm isDisabled={isDisabled} setIsDisabled={setIsDisabled} />
|
||||
) : (
|
||||
<EmailSignupForm
|
||||
isDisabled={isDisabled}
|
||||
setIsDisabled={setIsDisabled}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -2,9 +2,16 @@ import Cookies from 'js-cookie';
|
||||
import type { FormEvent } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { httpPost } from '../../lib/http';
|
||||
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
|
||||
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
|
||||
|
||||
type EmailLoginFormProps = {
|
||||
isDisabled?: boolean;
|
||||
setIsDisabled?: (isDisabled: boolean) => void;
|
||||
};
|
||||
|
||||
export function EmailLoginForm(props: EmailLoginFormProps) {
|
||||
const { isDisabled, setIsDisabled } = props;
|
||||
|
||||
export function EmailLoginForm() {
|
||||
const [email, setEmail] = useState<string>('');
|
||||
const [password, setPassword] = useState<string>('');
|
||||
const [error, setError] = useState('');
|
||||
@@ -14,6 +21,7 @@ export function EmailLoginForm() {
|
||||
const handleFormSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
setIsDisabled?.(true);
|
||||
setError('');
|
||||
|
||||
const { response, error } = await httpPost<{ token: string }>(
|
||||
@@ -26,11 +34,7 @@ export function EmailLoginForm() {
|
||||
|
||||
// Log the user in and reload the page
|
||||
if (response?.token) {
|
||||
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
|
||||
path: '/',
|
||||
expires: 30,
|
||||
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
|
||||
});
|
||||
setAuthToken(response.token);
|
||||
window.location.reload();
|
||||
|
||||
return;
|
||||
@@ -45,6 +49,7 @@ export function EmailLoginForm() {
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
setError(error?.message || 'Something went wrong. Please try again later.');
|
||||
};
|
||||
|
||||
@@ -92,7 +97,7 @@ export function EmailLoginForm() {
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
disabled={isLoading || isDisabled}
|
||||
className="inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-none focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
|
||||
>
|
||||
{isLoading ? 'Please wait...' : 'Continue'}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { type FormEvent, useState } from 'react';
|
||||
import { httpPost } from '../../lib/http';
|
||||
|
||||
export function EmailSignupForm() {
|
||||
type EmailSignupFormProps = {
|
||||
isDisabled?: boolean;
|
||||
setIsDisabled?: (isDisabled: boolean) => void;
|
||||
};
|
||||
|
||||
export function EmailSignupForm(props: EmailSignupFormProps) {
|
||||
const { isDisabled, setIsDisabled } = props;
|
||||
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [name, setName] = useState('');
|
||||
@@ -13,6 +20,7 @@ export function EmailSignupForm() {
|
||||
e.preventDefault();
|
||||
|
||||
setIsLoading(true);
|
||||
setIsDisabled?.(true);
|
||||
setError('');
|
||||
|
||||
const { response, error } = await httpPost<{ status: 'ok' }>(
|
||||
@@ -21,20 +29,21 @@ export function EmailSignupForm() {
|
||||
email,
|
||||
password,
|
||||
name,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (error || response?.status !== 'ok') {
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
setError(
|
||||
error?.message || 'Something went wrong. Please try again later.'
|
||||
error?.message || 'Something went wrong. Please try again later.',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = `/verification-pending?email=${encodeURIComponent(
|
||||
email
|
||||
email,
|
||||
)}`;
|
||||
};
|
||||
|
||||
@@ -90,7 +99,7 @@ export function EmailSignupForm() {
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
disabled={isLoading || isDisabled}
|
||||
className="inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-none focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
|
||||
>
|
||||
{isLoading ? 'Please wait...' : 'Continue to Verify Email'}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx';
|
||||
import Cookies from 'js-cookie';
|
||||
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
|
||||
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import { Spinner } from '../ReactIcons/Spinner.tsx';
|
||||
|
||||
type GitHubButtonProps = {};
|
||||
type GitHubButtonProps = {
|
||||
isDisabled?: boolean;
|
||||
setIsDisabled?: (isDisabled: boolean) => void;
|
||||
};
|
||||
|
||||
const GITHUB_REDIRECT_AT = 'githubRedirectAt';
|
||||
const GITHUB_LAST_PAGE = 'githubLastPage';
|
||||
|
||||
export function GitHubButton(props: GitHubButtonProps) {
|
||||
const { isDisabled, setIsDisabled } = props;
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
@@ -25,6 +30,7 @@ export function GitHubButton(props: GitHubButtonProps) {
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setIsDisabled?.(true);
|
||||
httpGet<{ token: string }>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-github-callback${
|
||||
window.location.search
|
||||
@@ -35,6 +41,7 @@ export function GitHubButton(props: GitHubButtonProps) {
|
||||
const errMessage = error?.message || 'Something went wrong.';
|
||||
setError(errMessage);
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -63,21 +70,19 @@ export function GitHubButton(props: GitHubButtonProps) {
|
||||
|
||||
localStorage.removeItem(GITHUB_REDIRECT_AT);
|
||||
localStorage.removeItem(GITHUB_LAST_PAGE);
|
||||
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
|
||||
path: '/',
|
||||
expires: 30,
|
||||
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
|
||||
});
|
||||
setAuthToken(response.token);
|
||||
window.location.href = redirectUrl;
|
||||
})
|
||||
.catch((err) => {
|
||||
setError('Something went wrong. Please try again later.');
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleClick = async () => {
|
||||
setIsLoading(true);
|
||||
setIsDisabled?.(true);
|
||||
|
||||
const { response, error } = await httpGet<{ loginUrl: string }>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-github-login`,
|
||||
@@ -89,13 +94,14 @@ export function GitHubButton(props: GitHubButtonProps) {
|
||||
);
|
||||
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// For non authentication pages, we want to redirect back to the page
|
||||
// the user was on before they clicked the social login button
|
||||
if (!['/login', '/signup'].includes(window.location.pathname)) {
|
||||
const pagePath = ['/respond-invite', '/befriend'].includes(
|
||||
const pagePath = ['/respond-invite', '/befriend', '/r', '/ai'].includes(
|
||||
window.location.pathname,
|
||||
)
|
||||
? window.location.pathname + window.location.search
|
||||
@@ -112,7 +118,7 @@ export function GitHubButton(props: GitHubButtonProps) {
|
||||
<>
|
||||
<button
|
||||
className="inline-flex h-10 w-full items-center justify-center gap-2 rounded border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-none focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60"
|
||||
disabled={isLoading}
|
||||
disabled={isLoading || isDisabled}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{isLoading ? (
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import Cookies from 'js-cookie';
|
||||
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
|
||||
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import { Spinner } from '../ReactIcons/Spinner.tsx';
|
||||
import { GoogleIcon } from '../ReactIcons/GoogleIcon.tsx';
|
||||
|
||||
type GoogleButtonProps = {};
|
||||
type GoogleButtonProps = {
|
||||
isDisabled?: boolean;
|
||||
setIsDisabled?: (isDisabled: boolean) => void;
|
||||
};
|
||||
|
||||
const GOOGLE_REDIRECT_AT = 'googleRedirectAt';
|
||||
const GOOGLE_LAST_PAGE = 'googleLastPage';
|
||||
|
||||
export function GoogleButton(props: GoogleButtonProps) {
|
||||
const { isDisabled, setIsDisabled } = props;
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
@@ -25,6 +30,7 @@ export function GoogleButton(props: GoogleButtonProps) {
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setIsDisabled?.(true);
|
||||
httpGet<{ token: string }>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-google-callback${
|
||||
window.location.search
|
||||
@@ -34,6 +40,7 @@ export function GoogleButton(props: GoogleButtonProps) {
|
||||
if (!response?.token) {
|
||||
setError(error?.message || 'Something went wrong.');
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -62,21 +69,19 @@ export function GoogleButton(props: GoogleButtonProps) {
|
||||
|
||||
localStorage.removeItem(GOOGLE_REDIRECT_AT);
|
||||
localStorage.removeItem(GOOGLE_LAST_PAGE);
|
||||
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
|
||||
path: '/',
|
||||
expires: 30,
|
||||
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
|
||||
});
|
||||
setAuthToken(response.token);
|
||||
window.location.href = redirectUrl;
|
||||
})
|
||||
.catch((err) => {
|
||||
setError('Something went wrong. Please try again later.');
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleClick = () => {
|
||||
setIsLoading(true);
|
||||
setIsDisabled?.(true);
|
||||
httpGet<{ loginUrl: string }>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-google-login`,
|
||||
)
|
||||
@@ -84,6 +89,7 @@ export function GoogleButton(props: GoogleButtonProps) {
|
||||
if (!response?.loginUrl) {
|
||||
setError(error?.message || 'Something went wrong.');
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -91,7 +97,7 @@ export function GoogleButton(props: GoogleButtonProps) {
|
||||
// For non authentication pages, we want to redirect back to the page
|
||||
// the user was on before they clicked the social login button
|
||||
if (!['/login', '/signup'].includes(window.location.pathname)) {
|
||||
const pagePath = ['/respond-invite', '/befriend'].includes(
|
||||
const pagePath = ['/respond-invite', '/befriend', '/r', '/ai'].includes(
|
||||
window.location.pathname,
|
||||
)
|
||||
? window.location.pathname + window.location.search
|
||||
@@ -106,6 +112,7 @@ export function GoogleButton(props: GoogleButtonProps) {
|
||||
.catch((err) => {
|
||||
setError('Something went wrong. Please try again later.');
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -113,7 +120,7 @@ export function GoogleButton(props: GoogleButtonProps) {
|
||||
<>
|
||||
<button
|
||||
className="inline-flex h-10 w-full items-center justify-center gap-2 rounded border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-none focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60"
|
||||
disabled={isLoading}
|
||||
disabled={isLoading || isDisabled}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{isLoading ? (
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import Cookies from 'js-cookie';
|
||||
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
|
||||
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import { Spinner } from '../ReactIcons/Spinner.tsx';
|
||||
import { LinkedInIcon } from '../ReactIcons/LinkedInIcon.tsx';
|
||||
|
||||
type LinkedInButtonProps = {};
|
||||
type LinkedInButtonProps = {
|
||||
isDisabled?: boolean;
|
||||
setIsDisabled?: (isDisabled: boolean) => void;
|
||||
};
|
||||
|
||||
const LINKEDIN_REDIRECT_AT = 'linkedInRedirectAt';
|
||||
const LINKEDIN_LAST_PAGE = 'linkedInLastPage';
|
||||
|
||||
export function LinkedInButton(props: LinkedInButtonProps) {
|
||||
const { isDisabled, setIsDisabled } = props;
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
@@ -25,6 +30,7 @@ export function LinkedInButton(props: LinkedInButtonProps) {
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setIsDisabled?.(true);
|
||||
httpGet<{ token: string }>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-linkedin-callback${
|
||||
window.location.search
|
||||
@@ -34,6 +40,7 @@ export function LinkedInButton(props: LinkedInButtonProps) {
|
||||
if (!response?.token) {
|
||||
setError(error?.message || 'Something went wrong.');
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -62,21 +69,19 @@ export function LinkedInButton(props: LinkedInButtonProps) {
|
||||
|
||||
localStorage.removeItem(LINKEDIN_REDIRECT_AT);
|
||||
localStorage.removeItem(LINKEDIN_LAST_PAGE);
|
||||
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
|
||||
path: '/',
|
||||
expires: 30,
|
||||
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
|
||||
});
|
||||
setAuthToken(response.token);
|
||||
window.location.href = redirectUrl;
|
||||
})
|
||||
.catch((err) => {
|
||||
setError('Something went wrong. Please try again later.');
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleClick = () => {
|
||||
setIsLoading(true);
|
||||
setIsDisabled?.(true);
|
||||
httpGet<{ loginUrl: string }>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-linkedin-login`,
|
||||
)
|
||||
@@ -84,6 +89,7 @@ export function LinkedInButton(props: LinkedInButtonProps) {
|
||||
if (!response?.loginUrl) {
|
||||
setError(error?.message || 'Something went wrong.');
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -91,7 +97,7 @@ export function LinkedInButton(props: LinkedInButtonProps) {
|
||||
// For non authentication pages, we want to redirect back to the page
|
||||
// the user was on before they clicked the social login button
|
||||
if (!['/login', '/signup'].includes(window.location.pathname)) {
|
||||
const pagePath = ['/respond-invite', '/befriend'].includes(
|
||||
const pagePath = ['/respond-invite', '/befriend', '/r', '/ai'].includes(
|
||||
window.location.pathname,
|
||||
)
|
||||
? window.location.pathname + window.location.search
|
||||
@@ -106,6 +112,7 @@ export function LinkedInButton(props: LinkedInButtonProps) {
|
||||
.catch((err) => {
|
||||
setError('Something went wrong. Please try again later.');
|
||||
setIsLoading(false);
|
||||
setIsDisabled?.(false);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -113,7 +120,7 @@ export function LinkedInButton(props: LinkedInButtonProps) {
|
||||
<>
|
||||
<button
|
||||
className="inline-flex h-10 w-full items-center justify-center gap-2 rounded border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-none focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60"
|
||||
disabled={isLoading}
|
||||
disabled={isLoading || isDisabled}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{isLoading ? (
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
---
|
||||
import Popup from '../Popup/Popup.astro';
|
||||
import { EmailLoginForm } from './EmailLoginForm';
|
||||
import Divider from './Divider.astro';
|
||||
import { GitHubButton } from './GitHubButton';
|
||||
import { GoogleButton } from './GoogleButton';
|
||||
import { LinkedInButton } from './LinkedInButton';
|
||||
import { AuthenticationForm } from './AuthenticationForm';
|
||||
---
|
||||
|
||||
<Popup id='login-popup' title='' subtitle=''>
|
||||
<div class='text-center'>
|
||||
<div class='mb-7 text-center'>
|
||||
<p class='mb-3 text-2xl font-semibold leading-5 text-slate-900'>
|
||||
Login to your account
|
||||
</p>
|
||||
@@ -16,19 +12,9 @@ import { LinkedInButton } from './LinkedInButton';
|
||||
You must be logged in to perform this action.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class='mt-7 flex flex-col gap-2'>
|
||||
<GitHubButton client:load />
|
||||
<GoogleButton client:load />
|
||||
<LinkedInButton client:load />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<EmailLoginForm client:load />
|
||||
|
||||
<AuthenticationForm client:load />
|
||||
<div class='mt-6 text-center text-sm text-slate-600'>
|
||||
Don't have an account?{' '}
|
||||
<a href='/signup' class='font-medium text-[#4285f4]'>Sign up</a>
|
||||
<a href='/signup' class='font-medium text-[#4285f4]'> Sign up</a>
|
||||
</div>
|
||||
</Popup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type FormEvent, useEffect, useState } from 'react';
|
||||
import { httpPost } from '../../lib/http';
|
||||
import Cookies from 'js-cookie';
|
||||
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
|
||||
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
|
||||
|
||||
export function ResetPasswordForm() {
|
||||
const [code, setCode] = useState('');
|
||||
@@ -37,7 +37,7 @@ export function ResetPasswordForm() {
|
||||
newPassword: password,
|
||||
confirmPassword: passwordConfirm,
|
||||
code,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (error?.message) {
|
||||
@@ -53,11 +53,7 @@ export function ResetPasswordForm() {
|
||||
}
|
||||
|
||||
const token = response.token;
|
||||
Cookies.set(TOKEN_COOKIE_NAME, token, {
|
||||
path: '/',
|
||||
expires: 30,
|
||||
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
|
||||
});
|
||||
setAuthToken(response.token);
|
||||
window.location.href = '/';
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import Cookies from 'js-cookie';
|
||||
import { httpPost } from '../../lib/http';
|
||||
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
|
||||
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
|
||||
import { Spinner } from '../ReactIcons/Spinner';
|
||||
import { ErrorIcon2 } from '../ReactIcons/ErrorIcon2';
|
||||
|
||||
@@ -26,11 +26,7 @@ export function TriggerVerifyAccount() {
|
||||
return;
|
||||
}
|
||||
|
||||
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
|
||||
path: '/',
|
||||
expires: 30,
|
||||
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
|
||||
});
|
||||
setAuthToken(response.token);
|
||||
window.location.href = '/';
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
55
src/components/CustomRoadmap/CustomRoadmapAlert.tsx
Normal file
55
src/components/CustomRoadmap/CustomRoadmapAlert.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { BadgeCheck, MessageCircleHeart, PencilRuler } from 'lucide-react';
|
||||
import { showLoginPopup } from '../../lib/popup.ts';
|
||||
import { isLoggedIn } from '../../lib/jwt.ts';
|
||||
import { useState } from 'react';
|
||||
import { CreateRoadmapModal } from './CreateRoadmap/CreateRoadmapModal.tsx';
|
||||
|
||||
export function CustomRoadmapAlert() {
|
||||
const [isCreatingRoadmap, setIsCreatingRoadmap] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCreatingRoadmap && (
|
||||
<CreateRoadmapModal
|
||||
onClose={() => {
|
||||
setIsCreatingRoadmap(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="relative mb-5 mt-0 rounded-md border border-yellow-500 bg-yellow-100 p-2 sm:-mt-6 sm:mb-7 sm:p-2.5">
|
||||
<h2 className="text-base font-semibold text-yellow-800 sm:text-lg">
|
||||
Community Roadmap
|
||||
</h2>
|
||||
<p className="mt-2 mb-2.5 sm:mb-1.5 sm:mt-1 text-sm text-yellow-800 sm:text-base">
|
||||
This is a custom roadmap made by community and isn't verified by{' '}
|
||||
<span className="font-semibold">roadmap.sh</span>
|
||||
</p>
|
||||
<div className="flex items-start sm:items-center flex-col sm:flex-row gap-2">
|
||||
<a
|
||||
href="/roadmaps"
|
||||
className="inline-flex items-center gap-1.5 text-sm font-semibold text-yellow-700 underline-offset-2 hover:underline"
|
||||
>
|
||||
<BadgeCheck className="h-4 w-4 stroke-[2.5]" />
|
||||
Visit Official Roadmaps
|
||||
</a>
|
||||
<span className="font-black text-yellow-700 hidden sm:block">·</span>
|
||||
<button
|
||||
className="inline-flex items-center gap-1.5 text-sm font-semibold text-yellow-700 underline-offset-2 hover:underline"
|
||||
onClick={() => {
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
} else {
|
||||
setIsCreatingRoadmap(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<PencilRuler className="h-4 w-4 stroke-[2.5]" />
|
||||
Create Your Own Roadmap
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<MessageCircleHeart className="absolute bottom-2 right-2 hidden h-12 w-12 text-yellow-500 opacity-50 sm:block" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { Lock, Shapes } from 'lucide-react';
|
||||
import { Modal } from '../Modal';
|
||||
import { ShareSuccess } from '../ShareOptions/ShareSuccess';
|
||||
import { ShareRoadmapButton } from '../ShareRoadmapButton.tsx';
|
||||
import { CustomRoadmapAlert } from './CustomRoadmapAlert.tsx';
|
||||
|
||||
type RoadmapHeaderProps = {};
|
||||
|
||||
@@ -89,6 +90,8 @@ export function RoadmapHeader(props: RoadmapHeaderProps) {
|
||||
return (
|
||||
<div className="border-b">
|
||||
<div className="container relative py-5 sm:py-12">
|
||||
{!$canManageCurrentRoadmap && <CustomRoadmapAlert />}
|
||||
|
||||
{creator?.name && (
|
||||
<div className="-mb-1 flex items-center gap-1.5 text-sm text-gray-500">
|
||||
<img
|
||||
|
||||
@@ -48,11 +48,10 @@ const {
|
||||
{
|
||||
isNew && (
|
||||
<span class='absolute bottom-1.5 right-2 flex items-center rounded-br rounded-tl text-xs font-medium text-purple-300'>
|
||||
<span class='mr-1.5 flex h-2 w-2'>
|
||||
<span class='flex h-2 w-2'>
|
||||
<span class='absolute inline-flex h-2 w-2 animate-ping rounded-full bg-purple-400 opacity-75' />
|
||||
<span class='relative inline-flex h-2 w-2 rounded-full bg-purple-500' />
|
||||
</span>
|
||||
New
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ import Icon from './AstroIcon.astro';
|
||||
class='my-1.5 mr-auto sm:ml-auto sm:mr-0'
|
||||
width='200'
|
||||
height='24.8'
|
||||
loading="lazy"
|
||||
/>
|
||||
</a>
|
||||
<p class='my-4 text-slate-300/60'>
|
||||
|
||||
58
src/components/GenerateRoadmap/GenerateRoadmap.css
Normal file
58
src/components/GenerateRoadmap/GenerateRoadmap.css
Normal file
@@ -0,0 +1,58 @@
|
||||
@font-face {
|
||||
font-family: 'balsamiq';
|
||||
src: url('/fonts/balsamiq.woff2');
|
||||
}
|
||||
|
||||
svg text tspan {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-rendering: optimizeSpeed;
|
||||
}
|
||||
|
||||
svg > g[data-type='topic'],
|
||||
svg > g[data-type='subtopic'],
|
||||
svg > g > g[data-type='link-item'],
|
||||
svg > g[data-type='button'] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg > g[data-type='topic']:hover > rect {
|
||||
fill: #d6d700;
|
||||
}
|
||||
|
||||
svg > g[data-type='subtopic']:hover > rect {
|
||||
fill: #f3c950;
|
||||
}
|
||||
svg > g[data-type='button']:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
svg .done rect {
|
||||
fill: #cbcbcb !important;
|
||||
}
|
||||
|
||||
svg .done text,
|
||||
svg .skipped text {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
svg > g[data-type='topic'].learning > rect + text,
|
||||
svg > g[data-type='topic'].done > rect + text {
|
||||
fill: black;
|
||||
}
|
||||
|
||||
svg > g[data-type='subtipic'].done > rect + text,
|
||||
svg > g[data-type='subtipic'].learning > rect + text {
|
||||
fill: #cbcbcb;
|
||||
}
|
||||
|
||||
svg .learning rect {
|
||||
fill: #dad1fd !important;
|
||||
}
|
||||
svg .learning text {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
svg .skipped rect {
|
||||
fill: #496b69 !important;
|
||||
}
|
||||
361
src/components/GenerateRoadmap/GenerateRoadmap.tsx
Normal file
361
src/components/GenerateRoadmap/GenerateRoadmap.tsx
Normal file
@@ -0,0 +1,361 @@
|
||||
import { type FormEvent, useEffect, useRef, useState } from 'react';
|
||||
import './GenerateRoadmap.css';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { generateAIRoadmapFromText } from '../../../editor/utils/roadmap-generator';
|
||||
import { renderFlowJSON } from '../../../editor/renderer/renderer';
|
||||
import { replaceChildren } from '../../lib/dom';
|
||||
import { readAIRoadmapStream } from '../../helper/read-stream';
|
||||
import { isLoggedIn, removeAuthToken } from '../../lib/jwt';
|
||||
import { RoadmapSearch } from './RoadmapSearch.tsx';
|
||||
import { Spinner } from '../ReactIcons/Spinner.tsx';
|
||||
import { Ban, Download, PenSquare, Wand } from 'lucide-react';
|
||||
import { ShareRoadmapButton } from '../ShareRoadmapButton.tsx';
|
||||
import { httpGet, httpPost } from '../../lib/http.ts';
|
||||
import { pageProgressMessage } from '../../stores/page.ts';
|
||||
import {
|
||||
deleteUrlParam,
|
||||
getUrlParams,
|
||||
setUrlParams,
|
||||
} from '../../lib/browser.ts';
|
||||
import { downloadGeneratedRoadmapImage } from '../../helper/download-image.ts';
|
||||
import { showLoginPopup } from '../../lib/popup.ts';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
|
||||
const ROADMAP_ID_REGEX = new RegExp('@ROADMAPID:(\\w+)@');
|
||||
|
||||
export function GenerateRoadmap() {
|
||||
const roadmapContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { id: roadmapId } = getUrlParams() as { id: string };
|
||||
const toast = useToast();
|
||||
|
||||
const [hasSubmitted, setHasSubmitted] = useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [roadmapTopic, setRoadmapTopic] = useState('');
|
||||
const [generatedRoadmap, setGeneratedRoadmap] = useState('');
|
||||
|
||||
const [roadmapLimit, setRoadmapLimit] = useState(0);
|
||||
const [roadmapLimitUsed, setRoadmapLimitUsed] = useState(0);
|
||||
|
||||
const renderRoadmap = async (roadmap: string) => {
|
||||
const { nodes, edges } = generateAIRoadmapFromText(roadmap);
|
||||
const svg = await renderFlowJSON({ nodes, edges });
|
||||
if (roadmapContainerRef?.current) {
|
||||
replaceChildren(roadmapContainerRef?.current, svg);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (!roadmapTopic) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setHasSubmitted(true);
|
||||
|
||||
if (roadmapLimitUsed >= roadmapLimit) {
|
||||
toast.error('You have reached your limit of generating roadmaps');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
deleteUrlParam('id');
|
||||
|
||||
const response = await fetch(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-generate-ai-roadmap`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ topic: roadmapTopic }),
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
toast.error(data?.message || 'Something went wrong');
|
||||
setIsLoading(false);
|
||||
|
||||
// Logout user if token is invalid
|
||||
if (data.status === 401) {
|
||||
removeAuthToken();
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
|
||||
if (!reader) {
|
||||
setIsLoading(false);
|
||||
toast.error('Something went wrong');
|
||||
return;
|
||||
}
|
||||
|
||||
await readAIRoadmapStream(reader, {
|
||||
onStream: async (result) => {
|
||||
if (result.includes('@ROADMAPID')) {
|
||||
// @ROADMAPID: is a special token that we use to identify the roadmap
|
||||
// @ROADMAPID:1234@ is the format, we will remove the token and the id
|
||||
// and replace it with a empty string
|
||||
const roadmapId = result.match(ROADMAP_ID_REGEX)?.[1] || '';
|
||||
setUrlParams({ id: roadmapId });
|
||||
result = result.replace(ROADMAP_ID_REGEX, '');
|
||||
}
|
||||
|
||||
await renderRoadmap(result);
|
||||
},
|
||||
onStreamEnd: async (result) => {
|
||||
result = result.replace(ROADMAP_ID_REGEX, '');
|
||||
setGeneratedRoadmap(result);
|
||||
loadAIRoadmapLimit().finally(() => {});
|
||||
},
|
||||
});
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const editGeneratedRoadmap = async () => {
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
pageProgressMessage.set('Redirecting to Editor');
|
||||
|
||||
const { nodes, edges } = generateAIRoadmapFromText(generatedRoadmap);
|
||||
|
||||
const { response, error } = await httpPost<{
|
||||
roadmapId: string;
|
||||
}>(`${import.meta.env.PUBLIC_API_URL}/v1-edit-ai-generated-roadmap`, {
|
||||
title: roadmapTopic,
|
||||
nodes: nodes.map((node) => ({
|
||||
...node,
|
||||
|
||||
// To reset the width and height of the node
|
||||
// so that it can be calculated based on the content in the editor
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
style: {
|
||||
...node.style,
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
},
|
||||
})),
|
||||
edges,
|
||||
});
|
||||
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = `${import.meta.env.PUBLIC_EDITOR_APP_URL}/${response.roadmapId}`;
|
||||
};
|
||||
|
||||
const downloadGeneratedRoadmap = async () => {
|
||||
pageProgressMessage.set('Downloading Roadmap');
|
||||
|
||||
const node = document.getElementById('roadmap-container');
|
||||
if (!node) {
|
||||
toast.error('Something went wrong');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await downloadGeneratedRoadmapImage(roadmapTopic, node);
|
||||
pageProgressMessage.set('');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Something went wrong');
|
||||
}
|
||||
};
|
||||
|
||||
const loadAIRoadmapLimit = async () => {
|
||||
const { response, error } = await httpGet<{
|
||||
limit: number;
|
||||
used: number;
|
||||
}>(`${import.meta.env.PUBLIC_API_URL}/v1-get-ai-roadmap-limit`);
|
||||
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
return;
|
||||
}
|
||||
|
||||
const { limit, used } = response;
|
||||
setRoadmapLimit(limit);
|
||||
setRoadmapLimitUsed(used);
|
||||
};
|
||||
|
||||
const loadAIRoadmap = async (roadmapId: string) => {
|
||||
pageProgressMessage.set('Loading Roadmap');
|
||||
|
||||
const { response, error } = await httpGet<{
|
||||
topic: string;
|
||||
data: string;
|
||||
}>(`${import.meta.env.PUBLIC_API_URL}/v1-get-ai-roadmap/${roadmapId}`);
|
||||
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const { topic, data } = response;
|
||||
await renderRoadmap(data);
|
||||
|
||||
setRoadmapTopic(topic);
|
||||
setGeneratedRoadmap(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadAIRoadmapLimit().finally(() => {});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!roadmapId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setHasSubmitted(true);
|
||||
loadAIRoadmap(roadmapId).finally(() => {
|
||||
pageProgressMessage.set('');
|
||||
});
|
||||
}, [roadmapId]);
|
||||
|
||||
if (!hasSubmitted) {
|
||||
return (
|
||||
<RoadmapSearch
|
||||
roadmapTopic={roadmapTopic}
|
||||
setRoadmapTopic={setRoadmapTopic}
|
||||
handleSubmit={handleSubmit}
|
||||
limit={roadmapLimit}
|
||||
limitUsed={roadmapLimitUsed}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const pageUrl = `https://roadmap.sh/ai?id=${roadmapId}`;
|
||||
const canGenerateMore = roadmapLimitUsed < roadmapLimit;
|
||||
|
||||
return (
|
||||
<section className="flex flex-grow flex-col bg-gray-100">
|
||||
<div className="flex items-center justify-center border-b bg-white py-3 sm:py-6">
|
||||
{isLoading && (
|
||||
<span className="flex items-center gap-2 rounded-full bg-black px-3 py-1 text-white">
|
||||
<Spinner isDualRing={false} innerFill={'white'} />
|
||||
Generating roadmap ..
|
||||
</span>
|
||||
)}
|
||||
{!isLoading && (
|
||||
<div className="flex max-w-[600px] flex-grow flex-col items-center px-5">
|
||||
<div className="mt-2 flex w-full items-center justify-between text-sm">
|
||||
<span className="text-gray-800">
|
||||
<span
|
||||
className={cn(
|
||||
'inline-block w-[65px] rounded-md border px-0.5 text-center text-sm tabular-nums text-gray-800',
|
||||
{
|
||||
'animate-pulse border-zinc-300 bg-zinc-300 text-zinc-300':
|
||||
!roadmapLimit,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{roadmapLimitUsed} of {roadmapLimit}
|
||||
</span>{' '}
|
||||
roadmaps generated
|
||||
{!isLoggedIn() && (
|
||||
<>
|
||||
{' '}
|
||||
<button
|
||||
className="font-medium text-black underline underline-offset-2"
|
||||
onClick={showLoginPopup}
|
||||
>
|
||||
Login to increase your limit
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="my-3 flex w-full flex-col sm:flex-row sm:items-center sm:justify-center gap-2"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
autoFocus
|
||||
placeholder="e.g. Ansible"
|
||||
className="flex-grow rounded-md border border-gray-400 px-3 py-2 transition-colors focus:border-black focus:outline-none"
|
||||
value={roadmapTopic}
|
||||
onInput={(e) =>
|
||||
setRoadmapTopic((e.target as HTMLInputElement).value)
|
||||
}
|
||||
/>
|
||||
<button
|
||||
type={'submit'}
|
||||
className={cn(
|
||||
'flex min-w-[127px] flex-shrink-0 items-center gap-2 rounded-md bg-black px-4 py-2 text-white justify-center',
|
||||
{
|
||||
'cursor-not-allowed opacity-50':
|
||||
!roadmapLimit ||
|
||||
!roadmapTopic ||
|
||||
roadmapLimitUsed >= roadmapLimit,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{roadmapLimit > 0 && canGenerateMore && (
|
||||
<>
|
||||
<Wand size={20} />
|
||||
Generate
|
||||
</>
|
||||
)}
|
||||
|
||||
{roadmapLimit === 0 && <span>Please wait..</span>}
|
||||
|
||||
{roadmapLimit > 0 && !canGenerateMore && (
|
||||
<span className="flex items-center text-sm">
|
||||
<Ban size={15} className="mr-2" />
|
||||
Limit reached
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<button
|
||||
className="inline-flex items-center justify-center gap-2 rounded-md bg-yellow-400 py-1.5 pl-2.5 pr-3 text-xs font-medium transition-opacity duration-300 hover:bg-yellow-500 sm:text-sm"
|
||||
onClick={downloadGeneratedRoadmap}
|
||||
>
|
||||
<Download size={15} />
|
||||
<span className="hidden sm:inline">Download</span>
|
||||
</button>
|
||||
{roadmapId && (
|
||||
<ShareRoadmapButton
|
||||
description={`Check out ${roadmapTopic} roadmap I generated on roadmap.sh`}
|
||||
pageUrl={pageUrl}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
className="inline-flex items-center justify-center gap-2 rounded-md bg-gray-200 py-1.5 pl-2.5 pr-3 text-xs font-medium text-black transition-colors duration-300 hover:bg-gray-300 sm:text-sm"
|
||||
onClick={editGeneratedRoadmap}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<PenSquare size={15} />
|
||||
Edit in Editor
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
ref={roadmapContainerRef}
|
||||
id="roadmap-container"
|
||||
className="relative px-4 py-5 [&>svg]:mx-auto [&>svg]:max-w-[1300px]"
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
121
src/components/GenerateRoadmap/RoadmapSearch.tsx
Normal file
121
src/components/GenerateRoadmap/RoadmapSearch.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import { Ban, Wand } from 'lucide-react';
|
||||
import type { FormEvent } from 'react';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
|
||||
type RoadmapSearchProps = {
|
||||
roadmapTopic: string;
|
||||
setRoadmapTopic: (topic: string) => void;
|
||||
handleSubmit: (e: FormEvent<HTMLFormElement>) => void;
|
||||
limit: number;
|
||||
limitUsed: number;
|
||||
};
|
||||
|
||||
export function RoadmapSearch(props: RoadmapSearchProps) {
|
||||
const {
|
||||
roadmapTopic,
|
||||
setRoadmapTopic,
|
||||
handleSubmit,
|
||||
limit = 0,
|
||||
limitUsed = 0,
|
||||
} = props;
|
||||
|
||||
const canGenerateMore = limitUsed < limit;
|
||||
|
||||
return (
|
||||
<div className="flex flex-grow flex-col items-center justify-center px-4 py-6 sm:px-6">
|
||||
<div className="flex flex-col gap-0 text-center sm:gap-2">
|
||||
<h1 className="relative text-2xl font-medium sm:text-3xl">
|
||||
<span className="hidden sm:inline">Generate roadmaps with AI</span>
|
||||
<span className="inline sm:hidden">AI Roadmap Generator</span>
|
||||
</h1>
|
||||
<p className="text-base text-gray-500 sm:text-lg">
|
||||
<span className="hidden sm:inline">
|
||||
Enter a topic and let the AI generate a roadmap for you
|
||||
</span>
|
||||
<span className="inline sm:hidden">
|
||||
Enter a topic to generate a roadmap
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
if (limit > 0 && canGenerateMore) {
|
||||
handleSubmit(e);
|
||||
} else {
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
className="my-3 flex w-full max-w-[600px] flex-col gap-2 sm:my-5 sm:flex-row"
|
||||
>
|
||||
<input
|
||||
autoFocus
|
||||
type="text"
|
||||
placeholder="e.g. Ansible"
|
||||
className="w-full rounded-md border border-gray-400 px-3 py-2.5 transition-colors focus:border-black focus:outline-none"
|
||||
value={roadmapTopic}
|
||||
onInput={(e) => setRoadmapTopic((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
<button
|
||||
className={cn(
|
||||
'flex min-w-[143px] flex-shrink-0 items-center justify-center gap-2 rounded-md bg-black px-4 py-2 text-white',
|
||||
{
|
||||
'cursor-not-allowed opacity-50':
|
||||
!limit || !roadmapTopic || limitUsed >= limit,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{limit > 0 && canGenerateMore && (
|
||||
<>
|
||||
<Wand size={20} />
|
||||
Generate
|
||||
</>
|
||||
)}
|
||||
|
||||
{limit === 0 && (
|
||||
<>
|
||||
<span>Please wait..</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
{limit > 0 && !canGenerateMore && (
|
||||
<span className="flex items-center text-base sm:text-sm">
|
||||
<Ban size={15} className="mr-2" />
|
||||
Limit reached
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
<div className="mb-36">
|
||||
<p className="text-gray-500">
|
||||
<span className="inline sm:hidden">Generated </span>
|
||||
<span className="hidden sm:inline">You have generated </span>
|
||||
<span
|
||||
className={cn(
|
||||
'inline-block w-[65px] rounded-md border px-0.5 text-center text-sm tabular-nums text-gray-800',
|
||||
{
|
||||
'animate-pulse border-zinc-300 bg-zinc-300 text-zinc-300':
|
||||
!limit,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{limitUsed} of {limit}
|
||||
</span>{' '}
|
||||
roadmaps.
|
||||
{!isLoggedIn && (
|
||||
<>
|
||||
{' '}
|
||||
<button
|
||||
className="font-semibold text-black underline underline-offset-2"
|
||||
onClick={showLoginPopup}
|
||||
>
|
||||
Log in to increase your limit
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,28 +6,32 @@ export interface Props {
|
||||
}
|
||||
|
||||
const { guide } = Astro.props;
|
||||
const { frontmatter } = guide;
|
||||
const { author } = frontmatter;
|
||||
const { frontmatter, author } = guide;
|
||||
---
|
||||
|
||||
<div class='bg-white border-b py-5 sm:py-12'>
|
||||
<div class='border-b bg-white py-5 sm:py-12'>
|
||||
<div class='container text-left sm:text-center'>
|
||||
<p
|
||||
class='text-gray-400 hidden sm:flex items-center justify-start sm:justify-center'
|
||||
class='hidden items-center justify-start text-gray-400 sm:flex sm:justify-center'
|
||||
>
|
||||
<a
|
||||
href={author.url}
|
||||
target='_blank'
|
||||
class='font-medium hover:text-gray-600 inline-flex items-center hover:underline'
|
||||
>
|
||||
<img
|
||||
alt={author.name}
|
||||
src={author.imageUrl}
|
||||
class='w-5 h-5 inline mr-2 rounded-full'
|
||||
/>
|
||||
{author.name}
|
||||
</a>
|
||||
<span class='mx-1.5'>·</span>
|
||||
{
|
||||
author?.frontmatter && (
|
||||
<>
|
||||
<a
|
||||
href={`/authors/${author.id}`}
|
||||
class='inline-flex items-center font-medium hover:text-gray-600 hover:underline'
|
||||
>
|
||||
<img
|
||||
alt={author.frontmatter.name}
|
||||
src={author.frontmatter.imageUrl}
|
||||
class='mr-2 inline h-5 w-5 rounded-full'
|
||||
/>
|
||||
{author.frontmatter.name}
|
||||
</a>
|
||||
<span class='mx-1.5'>·</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
<span class='capitalize'>{frontmatter.type} Guide</span>
|
||||
<span class='mx-1.5'>·</span>
|
||||
<a
|
||||
@@ -36,10 +40,10 @@ const { author } = frontmatter;
|
||||
target='_blank'>Improve this Guide</a
|
||||
>
|
||||
</p>
|
||||
<h1 class='text-2xl sm:text-5xl my-0 sm:my-3.5 font-bold'>
|
||||
<h1 class='my-0 text-2xl font-bold sm:my-3.5 sm:text-5xl'>
|
||||
{frontmatter.title}
|
||||
</h1>
|
||||
<p class='hidden sm:block text-gray-400 text-xl'>
|
||||
<p class='hidden text-xl text-gray-400 sm:block'>
|
||||
{frontmatter.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import type { GuideFileType } from "../lib/guide";
|
||||
import type { GuideFileType } from '../lib/guide';
|
||||
|
||||
export interface Props {
|
||||
guide: GuideFileType;
|
||||
@@ -11,30 +11,34 @@ const { frontmatter, id } = guide;
|
||||
|
||||
<a
|
||||
class:list={[
|
||||
"block no-underline py-2 group text-md items-center text-gray-600 hover:text-blue-600 flex justify-between border-b",
|
||||
'text-md group block flex items-center justify-between border-b py-2 text-gray-600 no-underline hover:text-blue-600',
|
||||
]}
|
||||
href={frontmatter.excludedBySlug ? frontmatter.excludedBySlug : `/guides/${id}`}
|
||||
href={frontmatter.excludedBySlug
|
||||
? frontmatter.excludedBySlug
|
||||
: `/guides/${id}`}
|
||||
>
|
||||
<span class="group-hover:translate-x-2 transition-transform">
|
||||
<span
|
||||
class='text-sm transition-transform group-hover:translate-x-2 md:text-base'
|
||||
>
|
||||
{frontmatter.title}
|
||||
|
||||
{
|
||||
frontmatter.isNew && (
|
||||
<span class="bg-green-300 text-green-900 text-xs font-medium px-1.5 py-0.5 rounded-sm uppercase ml-1.5">
|
||||
<span class='ml-1.5 rounded-sm bg-green-300 px-1.5 py-0.5 text-xs font-medium uppercase text-green-900'>
|
||||
New
|
||||
<span class="hidden sm:inline">
|
||||
<span class='hidden sm:inline'>
|
||||
·
|
||||
{new Date(frontmatter.date).toLocaleString("default", {
|
||||
month: "long",
|
||||
{new Date(frontmatter.date).toLocaleString('default', {
|
||||
month: 'long',
|
||||
})}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</span>
|
||||
<span class="capitalize text-gray-500 text-xs hidden sm:block">
|
||||
<span class='hidden text-xs capitalize text-gray-500 sm:block'>
|
||||
{frontmatter.type}
|
||||
</span>
|
||||
|
||||
<span class="text-gray-400 text-xs block sm:hidden"> »</span>
|
||||
<span class='block text-xs text-gray-400 sm:hidden'> »</span>
|
||||
</a>
|
||||
|
||||
@@ -16,6 +16,25 @@ import { AccountDropdown } from './AccountDropdown';
|
||||
<Icon icon='logo' />
|
||||
</a>
|
||||
|
||||
<a
|
||||
target='_blank'
|
||||
rel='noreferrer nofollow'
|
||||
href='https://boards.greenhouse.io/insightmediagroupllc/jobs/4002116008'
|
||||
class='group inline sm:hidden relative !mr-2 text-blue-300 hover:text-white'
|
||||
>
|
||||
We're Hiring
|
||||
|
||||
<span class='absolute -right-[11px] top-0'>
|
||||
<span class='relative flex h-2 w-2'>
|
||||
<span
|
||||
class='absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-400 opacity-75'
|
||||
></span>
|
||||
<span class='relative inline-flex h-2 w-2 rounded-full bg-sky-500'
|
||||
></span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<!-- Desktop navigation items -->
|
||||
<div class='hidden space-x-5 sm:flex sm:items-center'>
|
||||
<NavigationDropdown client:load />
|
||||
@@ -23,9 +42,27 @@ import { AccountDropdown } from './AccountDropdown';
|
||||
Start Here
|
||||
</a>
|
||||
<a href='/teams' class='text-gray-400 hover:text-white'> Teams</a>
|
||||
<a
|
||||
target='_blank'
|
||||
rel='noreferrer nofollow'
|
||||
href='https://boards.greenhouse.io/insightmediagroupllc/jobs/4002116008'
|
||||
class='group relative !mr-2 text-blue-300 hover:text-white'
|
||||
>
|
||||
We're Hiring
|
||||
|
||||
<span class='absolute -right-[11px] top-0'>
|
||||
<span class='relative flex h-2 w-2'>
|
||||
<span
|
||||
class='absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-400 opacity-75'
|
||||
></span>
|
||||
<span class='relative inline-flex h-2 w-2 rounded-full bg-sky-500'
|
||||
></span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
<button
|
||||
data-command-menu
|
||||
class='hidden items-center rounded-md border border-gray-800 px-2.5 py-1.5 text-sm text-gray-400 hover:cursor-pointer hover:bg-gray-800 sm:flex'
|
||||
class='hidden items-center rounded-md border border-gray-800 px-2.5 py-1.5 text-sm text-gray-400 hover:cursor-pointer hover:bg-gray-800 md:flex'
|
||||
>
|
||||
<Icon icon='search' class='h-3 w-3' />
|
||||
<span class='ml-2'>Search</span>
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import Cookies from 'js-cookie';
|
||||
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
|
||||
import { TOKEN_COOKIE_NAME, removeAuthToken } from '../../lib/jwt';
|
||||
|
||||
export function logout() {
|
||||
Cookies.remove(TOKEN_COOKIE_NAME, {
|
||||
path: '/',
|
||||
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
|
||||
});
|
||||
removeAuthToken();
|
||||
|
||||
// Reloading will automatically redirect the user if required
|
||||
window.location.reload();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
BookOpen,
|
||||
BookOpenText,
|
||||
CheckSquare,
|
||||
FileQuestion,
|
||||
|
||||
@@ -1,42 +1,34 @@
|
||||
---
|
||||
import { getFormattedStars } from '../lib/github';
|
||||
import Icon from './AstroIcon.astro';
|
||||
import { getDiscordInfo } from '../lib/discord';
|
||||
import OpenSourceStat from './OpenSourceStat.astro';
|
||||
|
||||
const starCount = await getFormattedStars('kamranahmedse/developer-roadmap');
|
||||
const discordInfo = await getDiscordInfo();
|
||||
---
|
||||
|
||||
<div class='py-6 sm:py-16 border-b border-t text-left sm:text-center bg-white'>
|
||||
<div class='!max-w-[600px] container'>
|
||||
<p class='text-2xl sm:text-5xl font-bold'>Community</p>
|
||||
<p class='text-gray-600 text-sm sm:text-lg leading-relaxed my-2.5 sm:my-5'>
|
||||
<div class='border-b border-t bg-white py-6 text-left sm:py-16 sm:text-center'>
|
||||
<div class='container !max-w-[650px]'>
|
||||
<p class='text-2xl font-bold sm:text-5xl'>Join the Community</p>
|
||||
<p class='my-2.5 text-sm leading-relaxed text-gray-600 sm:my-5 sm:text-lg'>
|
||||
roadmap.sh is the <a
|
||||
href='https://github.com/search?o=desc&q=stars%3A%3E100000&s=stars&type=Repositories'
|
||||
target='_blank'
|
||||
class='font-medium text-gray-600 hover:text-black underline underline-offset-2'
|
||||
class='font-medium text-gray-600 underline underline-offset-2 hover:text-black'
|
||||
>6th most starred project on GitHub</a
|
||||
> and is visited by hundreds of thousands of developers every month.
|
||||
</p>
|
||||
|
||||
<div class='flex justify-start flex-col sm:flex-row sm:justify-center gap-2 sm:gap-3 mb-1.5 sm:mb-0'>
|
||||
<a
|
||||
href='https://github.com/kamranahmedse/developer-roadmap'
|
||||
target='_blank'
|
||||
class='inline-flex items-center border border-black py-1.5 px-3 rounded-lg text-sm hover:text-white hover:bg-black bg-white'
|
||||
>
|
||||
<Icon icon='star' class='mr-1 -ml-1 fill-current' />
|
||||
|
||||
<span class='lowercase'>{starCount}</span>
|
||||
<span class='ml-2 hover:block'>GitHub Stars</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://discord.gg/cJpEt5Qbwa"
|
||||
target='_blank'
|
||||
class='relative pointer inline-flex items-center border border-black py-1.5 px-3 rounded-lg text-sm hover:text-white hover:bg-black bg-white group'
|
||||
>
|
||||
<Icon icon='discord' class='h-[14px] mr-2 -ml-1 fill-current' />
|
||||
Join on Discord
|
||||
</a>
|
||||
<div
|
||||
class='mt-5 grid grid-cols-1 justify-between gap-2 divide-x-0 sm:my-11 sm:grid-cols-3 sm:gap-0 sm:divide-x'
|
||||
>
|
||||
<OpenSourceStat text='GitHub Stars' value={starCount} />
|
||||
<OpenSourceStat text='Registered Users' value={'850k'} />
|
||||
<OpenSourceStat
|
||||
text='Discord Members'
|
||||
value={discordInfo.totalFormatted}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
105
src/components/OpenSourceStat.astro
Normal file
105
src/components/OpenSourceStat.astro
Normal file
@@ -0,0 +1,105 @@
|
||||
---
|
||||
import { ChevronUp } from 'lucide-react';
|
||||
import Icon from './AstroIcon.astro';
|
||||
export interface Props {
|
||||
value: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const { value, text } = Astro.props;
|
||||
|
||||
const isGitHubStars = text.toLowerCase() === 'github stars';
|
||||
const isRegistered = text.toLowerCase() === 'registered users';
|
||||
const isDiscordMembers = text.toLowerCase() === 'discord members';
|
||||
---
|
||||
|
||||
<div
|
||||
class='flex items-start sm:items-center justify-start flex-col sm:justify-center sm:gap-0 gap-2 sm:bg-transparent bg-gray-200 sm:rounded-none rounded-xl p-4'
|
||||
>
|
||||
{
|
||||
isGitHubStars && (
|
||||
<p class='flex items-center text-sm text-blue-500 sm:flex'>
|
||||
<span class='rounded-md bg-blue-500 px-1 text-white'>Rank 6th</span>
|
||||
out of 28M!
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
isRegistered && (
|
||||
<p class='flex items-center text-sm text-blue-500 sm:flex'>
|
||||
<span class='mr-1.5 rounded-md bg-blue-500 px-1 text-white'>+55k</span>
|
||||
every month
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
isDiscordMembers && (
|
||||
<p class='flex items-center text-sm text-blue-500 sm:flex'>
|
||||
<span class='mr-1.5 rounded-md bg-blue-500 px-1 text-white'>+1.5k</span>
|
||||
every month
|
||||
</p>
|
||||
)
|
||||
}
|
||||
<div class="flex flex-row items-center sm:flex-col my-1 sm:my-0">
|
||||
<p
|
||||
class='relative my-0 sm:my-4 mr-1 sm:mr-0 text-base font-bold lowercase sm:w-auto sm:text-5xl'
|
||||
>
|
||||
{value}
|
||||
</p>
|
||||
<p class='mb-0 mt-0 text-base sm:text-xs tracking-wide text-black sm:-mt-3 sm:mb-5'>
|
||||
{text}
|
||||
</p>
|
||||
</div>
|
||||
{
|
||||
isGitHubStars && (
|
||||
<a
|
||||
href='https://github.com/kamranahmedse/developer-roadmap'
|
||||
target='_blank'
|
||||
class='group mt-0 flex flex-col items-center rounded-lg border border-black bg-white px-3 py-2 text-sm hover:bg-black hover:text-white'
|
||||
>
|
||||
<div class='mb-0.5 flex items-center font-semibold'>
|
||||
<Icon icon='star' class='-ml-1 fill-current' />
|
||||
<span class='ml-1.5 hover:block'>Star us on GitHub</span>
|
||||
</div>
|
||||
<span class='text-xs text-gray-500 group-hover:text-gray-100'>
|
||||
Help us reach #1
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
{
|
||||
isRegistered && (
|
||||
<a
|
||||
href='/signup'
|
||||
class='group mt-0 flex flex-col items-center rounded-lg border border-black bg-white px-3 py-2 text-sm hover:bg-black hover:text-white'
|
||||
>
|
||||
<div class='mb-0.5 flex items-center font-semibold'>
|
||||
<Icon icon='users' class='-ml-1 h-[15px] fill-current' />
|
||||
<span class='ml-1 hover:block'>Register yourself</span>
|
||||
</div>
|
||||
<span class='text-xs text-gray-500 group-hover:text-gray-100'>
|
||||
Commit to your growth
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
{
|
||||
isDiscordMembers && (
|
||||
<a
|
||||
href='https://discord.gg/cJpEt5Qbwa'
|
||||
target='_blank'
|
||||
class='group mt-0 flex flex-col items-center rounded-lg border border-black bg-white px-3 py-2 text-sm hover:bg-black hover:text-white'
|
||||
>
|
||||
<div class='mb-0.5 flex items-center font-semibold'>
|
||||
<Icon icon='discord' class='-ml-1 h-[13px] fill-current' />
|
||||
<span class='ml-1 hover:block'>Join on Discord</span>
|
||||
</div>
|
||||
<span class='text-xs text-gray-500 group-hover:text-gray-100'>
|
||||
Join the community
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
@@ -105,7 +105,7 @@ export function PageSponsor(props: PageSponsorProps) {
|
||||
</span>
|
||||
<img
|
||||
src={imageUrl}
|
||||
className="block h-[150px] object-fill lg:h-[169px] lg:w-[118.18px]"
|
||||
className="block h-[150px] object-cover lg:h-[169px] lg:w-[118.18px]"
|
||||
alt="Sponsor Banner"
|
||||
/>
|
||||
<span className="flex flex-1 flex-col justify-between text-sm">
|
||||
|
||||
@@ -10,7 +10,7 @@ export function TeamAnnouncement(props: TeamAnnouncementProps) {
|
||||
New
|
||||
</span>{' '}
|
||||
<span className={'hidden sm:inline'}>Announcing roadmaps for teams. <span className='font-semibold'>Learn more!</span></span>
|
||||
<span className={'inline sm:hidden'}>Announcing roadmaps for teams!</span>
|
||||
<span className={'inline text-sm sm:hidden'}>Roadmaps for teams!</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Cookies from 'js-cookie';
|
||||
import { type ChangeEvent, type FormEvent, useEffect, useRef, useState } from 'react';
|
||||
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
|
||||
import { TOKEN_COOKIE_NAME, removeAuthToken } from '../../lib/jwt';
|
||||
|
||||
interface PreviewFile extends File {
|
||||
preview: string;
|
||||
@@ -128,7 +128,7 @@ export default function UploadProfilePicture(props: UploadProfilePictureProps) {
|
||||
|
||||
// Logout user if token is invalid
|
||||
if (data.status === 401) {
|
||||
Cookies.remove(TOKEN_COOKIE_NAME);
|
||||
removeAuthToken();
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,40 +1,38 @@
|
||||
---
|
||||
import type { VideoFileType } from '../lib/video';
|
||||
import YouTubeAlert from "./YouTubeAlert.astro";
|
||||
import YouTubeAlert from './YouTubeAlert.astro';
|
||||
|
||||
export interface Props {
|
||||
video: VideoFileType;
|
||||
}
|
||||
|
||||
const { video } = Astro.props;
|
||||
const { frontmatter } = video;
|
||||
const { author } = frontmatter;
|
||||
const { frontmatter, author } = video;
|
||||
---
|
||||
|
||||
<div class='bg-white border-b py-5 sm:py-12'>
|
||||
<div class='border-b bg-white py-5 sm:py-12'>
|
||||
<div class='container text-left sm:text-center'>
|
||||
<p
|
||||
class='text-gray-400 hidden sm:flex items-center justify-start sm:justify-center'
|
||||
class='hidden items-center justify-start text-gray-400 sm:flex sm:justify-center'
|
||||
>
|
||||
<a
|
||||
href={author.url}
|
||||
target='_blank'
|
||||
class='font-medium hover:text-gray-600 inline-flex items-center hover:underline'
|
||||
href={`/authors/${author.id}`}
|
||||
class='inline-flex items-center font-medium hover:text-gray-600 hover:underline'
|
||||
>
|
||||
<img
|
||||
alt={author.name}
|
||||
src={author.imageUrl}
|
||||
class='w-5 h-5 inline mr-2 rounded-full'
|
||||
alt={author.frontmatter.name}
|
||||
src={author.frontmatter.imageUrl}
|
||||
class='mr-2 inline h-5 w-5 rounded-full'
|
||||
/>
|
||||
{author.name}
|
||||
{author.frontmatter.name}
|
||||
</a>
|
||||
<span class='mx-1.5'>·</span>
|
||||
<span class='capitalize'>Illustrated Video</span>
|
||||
</p>
|
||||
<h1 class='text-2xl sm:text-5xl my-0 sm:my-3.5 font-bold'>
|
||||
<h1 class='my-0 text-2xl font-bold sm:my-3.5 sm:text-5xl'>
|
||||
{frontmatter.title}
|
||||
</h1>
|
||||
<p class='hidden sm:block text-gray-400 text-md'>
|
||||
<p class='text-md hidden text-gray-400 sm:block'>
|
||||
{frontmatter.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
8
src/data/authors/ebrahim-bharmal.md
Normal file
8
src/data/authors/ebrahim-bharmal.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
name: 'Ebrahim Bharmal'
|
||||
imageUrl: '/authors/ebrahimbharmal007.png'
|
||||
social:
|
||||
twitter: 'https://twitter.com/BharmalEbrahim'
|
||||
---
|
||||
|
||||
Full-stack developer interested in all things web development.
|
||||
11
src/data/authors/fernando.md
Normal file
11
src/data/authors/fernando.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: 'Fernando Doglio'
|
||||
imageUrl: '/authors/fernando.jpeg'
|
||||
social:
|
||||
twitter: 'https://twitter.com/deleteman123'
|
||||
linkedin: 'https://www.linkedin.com/in/fernandodoglio'
|
||||
---
|
||||
|
||||
With two decades of experience in Software Development, Fernando Doglio excels in diverse languages like Ruby, Perl, PHP, Python, and JavaScript. He's led teams in crafting scalable architectures for both in-house and cloud infrastructures.
|
||||
|
||||
An author of 8 technical books and over 250 articles, Fernando's current role as a Dev Advocate allows him to blend his passion for coding with content creation, enhancing developer experiences with products through engaging outreach.
|
||||
9
src/data/authors/jesse-li.md
Normal file
9
src/data/authors/jesse-li.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: 'Jesse Li'
|
||||
imageUrl: '/authors/jesse.png'
|
||||
social:
|
||||
twitter: 'https://twitter.com/__jesse_li'
|
||||
github: 'https://github.com/veggiedefender'
|
||||
---
|
||||
|
||||
Jesse has made several [interesting open-source projects](https://github.com/veggiedefender) and wrote some interesting [articles on his blog](https://blog.jse.li/) including the one he wrote on roadmap.sh.
|
||||
13
src/data/authors/kamran.md
Normal file
13
src/data/authors/kamran.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: 'Kamran Ahmed'
|
||||
imageUrl: '/authors/kamran.jpeg'
|
||||
social:
|
||||
linkedin: 'https://www.linkedin.com/in/kamrify'
|
||||
twitter: 'https://twitter.com/kamrify'
|
||||
github: 'https://github.com/kamranahmedse'
|
||||
website: 'https://kamranahmed.info'
|
||||
---
|
||||
|
||||
Kamran is the founder of **roadmap.sh**. He has a decade long experience working mostly with startups and scale-ups. Over the years, he has worked with a variety of technologies in a variety of domains and have worn several different hats. He is working full time on roadmap.sh at the moment.
|
||||
|
||||
He is also a Google Developer Expert and a GitHub Star. He is a huge proponent of open-source, and has authored several popular open-source projects. He is [the second most starred developer](https://twitter.com/kamrify/status/1750345095587754382) on GitHub globally.
|
||||
9
src/data/authors/peter-thaleikis.md
Normal file
9
src/data/authors/peter-thaleikis.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: 'Peter Thaleikis'
|
||||
imageUrl: '/authors/peter-thaleikis.png'
|
||||
social:
|
||||
twitter: 'https://twitter.com/spekulatius1984'
|
||||
website: 'https://peterthaleikis.com/'
|
||||
---
|
||||
|
||||
Peter Thaleikis a software engineer and business owner. He has been developing web applications since around 2000. Before he started his own software development company [Bring Your Own Ideas Ltd.](https://bringyourownideas.com/), he has been a Lead Developer for multiple organizations.
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Asymptotic Notation'
|
||||
description: 'Learn the basics of measuring the time and space complexity of algorithms'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Asymptotic Notation - roadmap.sh'
|
||||
description: 'Learn the basics of measuring the time and space complexity of algorithms'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Async and Defer Script Loading'
|
||||
description: 'Learn how to avoid render blocking JavaScript using async and defer scripts.'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Async and Defer Script Loading - roadmap.sh'
|
||||
description: 'Learn how to avoid render blocking JavaScript using async and defer scripts.'
|
||||
|
||||
270
src/data/guides/backend-developer-skills.md
Normal file
270
src/data/guides/backend-developer-skills.md
Normal file
@@ -0,0 +1,270 @@
|
||||
---
|
||||
title: '8 In-Demand Backend Developer Skills to Master'
|
||||
description: 'Learn what the essential backend skills you should master to advance in your career.'
|
||||
authorId: fernando
|
||||
excludedBySlug: '/backend/developer-skills'
|
||||
seo:
|
||||
title: '8 In-Demand Backend Developer Skills to Master'
|
||||
description: 'Learn what the essential backend developer skills are that you should learn and master to advance in your career.'
|
||||
isNew: true
|
||||
type: 'textual'
|
||||
date: 2024-02-27
|
||||
sitemap:
|
||||
priority: 0.7
|
||||
changefreq: 'weekly'
|
||||
tags:
|
||||
- 'guide'
|
||||
- 'textual-guide'
|
||||
- 'guide-sitemap'
|
||||
---
|
||||
|
||||
Whether your goal is to become a backend developer or to stay relevant as one, the goal itself requires adopting an eternal student mindset. The ever-evolving web development space demands continuous learning, regardless of the programming language you use. New frameworks, libraries, and methodologies emerge regularly, offering different solutions to old problems. To remain relevant as a [backend developer](/backend), you’ll have to stay updated by honing your core skills.
|
||||
|
||||
In this article, we’ll cover the following set of backend developer skills we recommend you aim for:
|
||||
|
||||
- Keeping an eye on core and new backend programming languages
|
||||
- Understanding the basics of software design and architecture
|
||||
- Understanding databases and how to use them
|
||||
- API development
|
||||
- The basics of version control
|
||||
- Testing and debugging
|
||||
- CI/CD and DevOps fundamentals
|
||||
- Soft skills
|
||||
|
||||
So, let's get going!
|
||||
|
||||
## Understanding Backend Development
|
||||
|
||||
Before we move on and start discussing the different backend development skills you should focus on, let’s first understand what a backend developer is. After all, if you’re looking to actually become a backend developer, you’ll need this.
|
||||
|
||||
A backend developer focuses entirely on writing business logic for an application and much of the supporting logic as well.
|
||||
|
||||
That said, there might be applications where the business logic is split into the frontend and the backend. However, while the frontend dev might have to share their time between UI code and business logic, the backend dev will focus most of their time on core business logic. That’s the main difference between the two.
|
||||
|
||||

|
||||
|
||||
In the above image, you can see how there is a lot more behind the curtain than just the UI when it comes to web applications. In this case, a “simple” log-in form needs a backend to contain its core business logic.
|
||||
|
||||
Let’s now look at the most in-demand backend developer skills you should focus on in backend development.
|
||||
|
||||
## Proficiency in Core and Emerging Programming Languages
|
||||
|
||||
One of the most basic skills you should focus on as a backend developer is on identifying key programming languages to learn (or at least keep an eye out for).
|
||||
|
||||
There are some essential backend languages that the industry has adopted as de facto standards. This means most new projects are usually coded using one (or multiple) of these programming languages.
|
||||
|
||||

|
||||
|
||||
The most common names you should look out for are:
|
||||
|
||||
- **JavaScript (or any of its variants, such as TypeScript).** This is a very common option because it’s also the language used by frontend developers, thus making it easier for developers to work on both sides of the same project.
|
||||
- **Python.** While a very common option for other types of projects (such as data processing and [data science](https://roadmap.sh/ai-data-scientist)), it’s still very popular in the web development world. Python has many good qualities and supporting frameworks that make it a very easy-to-pick-up option for coding backend systems.
|
||||
- **Go (A.K.A Golang).** This programming language was developed by Google. It was designed with simplicity, efficiency, and concurrency in mind. That’s made it gain popularity in the backend development space, making it an interesting option for projects that prioritize performance and concurrency.
|
||||
- **Java.** One of the most common alternatives for enterprise solutions, Java, has been constantly evolving since its first release back in 1995. All that time making its way into big enterprises that trust its robustness and ever-growing community of developers. While not the easiest language to learn, it’s definitely up there in the top 10 most popular [backend languages](https://roadmap.sh/backend/languages) (according to [StackOverflow’s 2023 Developer survey](https://survey.stackoverflow.co/2023/#technology-most-popular-technologies)).
|
||||
|
||||
While there are other options, the ones mentioned above, from the backend point of view, are some of the most relevant languages to pay attention to. Here are the top 10 most popular ones amongst professional developers (screenshot taken from SO’s survey of 2023):
|
||||
|
||||

|
||||
|
||||
### Keeping an eye on the rising stars
|
||||
|
||||
If working with at least one of the most common backend languages was important, understanding what are the rising technologies in the backend world is just as crucial.
|
||||
|
||||
You won’t see a new programming language being released every month. However, in the span of a few years, you might see the release of several, and out of those, some might stick long enough to become new standards.
|
||||
|
||||
For example, take a look at the period between 2012 and 2015; in just 3 years, 9 programming languages were released, out of which most of them are being used to this day.
|
||||
|
||||

|
||||
|
||||
- In 2012, we got Julia, Elm, Go, and TypeScript.
|
||||
- In 2013, we got Dart
|
||||
- In 2014, we got Swift, Hack, and Crystal
|
||||
- And in 2015, we got Rust.
|
||||
|
||||
Some of those languages are very relevant to this day, such as TypeScript and Rust, while others, such as Hack and Crystal, might be known to only a few in very niche sectors.
|
||||
|
||||
Of course, it’s impossible to predict which programming language will become a standard. However, the skill that you need to hone is that of keeping an eye on the industry to spot new and emerging trends.
|
||||
|
||||
### The importance of supporting frameworks
|
||||
|
||||
Frameworks for a specific programming language do change a lot faster than the language itself, though.
|
||||
|
||||
Frameworks are there to provide you with a simplified gateway into the functionalities that you’d normally need for common tasks. For example, in the context of backend web development, frameworks usually take care of:
|
||||
|
||||
- **Parsing HTTP requests** and turning them into objects you can easily interact with (so you don’t have to learn how the HTTP protocol works).
|
||||
- **Abstracting concepts,** such as a request or a response, into objects and functions that you can reason about at a higher level. This gives you an easier time thinking about how to solve a problem using these tools.
|
||||
- **Accessing data becomes a lot easier when there are abstractions.** Some frameworks provide what is known as an ORM (Object Relational Mapping). Through ORM, you can interact with databases without having to think about writing SQL queries or even database schemas.
|
||||
- And many more.
|
||||
|
||||
Frameworks are an essential aspect of the work you’ll do as a backend developer, which is why you should not neglect them. Of course, learning and mastering every single framework out there is impossible. Instead, learn to keep an eye out in the industry and see what are the most common frameworks, and focus on one (or two) of them.
|
||||
|
||||
## Software Design and Architecture
|
||||
|
||||
Coding is not just about writing code.
|
||||
|
||||
While that statement might be a bit confusing, the truth is there is a lot of architecture involved in software development (both in frontend and backend development). Sometimes, working on these aspects of a system is the job of a specific role called “architect.” However, for backend systems, it’s not uncommon for backend developers to also be involved in architecture conversations and decisions. You’re helping design the underlying backend infrastructure, after all.
|
||||
|
||||
The following diagram shows an example of what a very simple system’s architecture might look like:
|
||||
|
||||

|
||||
|
||||
While the example is oversimplified, it gives you an idea of what the practice of “architecting a system” is.
|
||||
|
||||
Essentially, architecting a system means coming up with concepts that represent different aspects of the solution and then deciding how you want to make them interact with each other.
|
||||
|
||||
Why is architecture so important here? Because it gives you properties such as code encapsulation, separation of concerns, reusability, and even scalability as a byproduct of the architecture itself.
|
||||
|
||||
Let’s take a quick look at some of the most common architectures used for creating backend systems.
|
||||
|
||||
### Most common backend architectures
|
||||
|
||||
There are too many different architectural styles and patterns to cover them all inside a single article, but let's just look at some of the most common ones and how they can help you while working on your backend system.
|
||||
|
||||
- **Monolithic architecture:** In a monolithic architecture, the entire application is built as a single, tightly coupled unit. All components (frontend, backend, database, etc) are part of the same codebase. This is a great first architecture because it feels very natural to develop under, and if your project is not going to grow out of proportion, then you will probably not hit any of the cons.
|
||||
- **Microservice-based architecture:** The application is divided into small, independent services, each responsible for a specific business capability. These services communicate through APIs.
|
||||
- **Service-Oriented Architecture:** Similar to microservices, a service-oriented architecture splits functionality into individual services. The main difference is that these services aren’t as granular as a microservice, so they might incorporate functionalities related to different business entities.
|
||||
- **Event-driven architecture:** With this architecture, each system (or service) responds to events (e.g., user actions and messages from other services) by triggering actions or processes. All services communicate with each other indirectly through an event bus (also known as a “message bus”), so it removes the possibility of having two or more services coupled with each other (meaning that they can’t be treated individually).
|
||||
- **Serverless Architecture:** Also known as Function as a Service (FaaS), serverless architecture allows you to focus on writing code without worrying about the server where they’ll run. Functions are executed in response to events without the need for provisioning or managing servers (this is done FOR you automatically).
|
||||
- **Microkernel architecture:** This architecture lets you build the core, essential functionality into a small microkernel and have the rest of the features built as plugins that can be added, removed or exchanged easily.
|
||||
|
||||
And if you want to know more about the patterns and principles mentioned here, please check out the [Software Design and Architecture roadmap](https://roadmap.sh/software-design-architecture).
|
||||
|
||||
## Mastery of Database Management Systems
|
||||
|
||||
As a backend developer, you will undoubtedly have to deal with database administration in your daily tasks. They are the industry standard for storing persistent data.
|
||||
|
||||
Because of that, it’s important to understand that you should be aware of two main categories of databases: SQL databases and NoSQL databases.
|
||||
|
||||
### SQL databases
|
||||
|
||||
These are the standard structured databases (A.K.A relational databases) where you need to define the schema for your data (essentially the data structures you’re dealing with), and then you’ll use a language called [SQL (Structured Query Language)](https://roadmap.sh/sql) to interact with the data inside it. Most backend developers will interact with SQL databases at some point in their career, as this is the most common type of database.
|
||||
|
||||
### NoSQL databases
|
||||
|
||||
As the name implies, these are not your standard SQL databases; in fact, within this category, there are columnar databases, document-based ones (such as MongoDB), key-value-based ones (like Redis), and more. They don’t use predefined data structures, giving you more flexibility and control over what you can store and how you store it. Backend developers will deal with only a handful of these, as there are many different sub-types, and more are created every year.
|
||||
|
||||
Some examples of these databases are:
|
||||
|
||||
- MongoDB, a document-based database (see here a mongoDB roadmap if you’re interested).
|
||||
- Redis, an in-memory key-value pair database.
|
||||
- Neo4J, a graph database.
|
||||
- ElasticSearch, a document-based search engine.
|
||||
|
||||
In the end, the decision between SQL and NoSQL is about trade-offs and figuring out what works best for your particular use case.
|
||||
|
||||
## API Development Capabilities
|
||||
|
||||
Application Programming Interfaces (APIs) are everywhere. They power the backend of almost all major systems out there (according to a [study conducted by O’Reilly in 2020](https://www.oreilly.com/pub/pr/3307), 77% of companies were using microservices/APIs).
|
||||
|
||||
That is to say, if you’re thinking about becoming a backend developer, you will be coding APIs/microservices. This is why understanding the basics of them is crucial to ensuring your relevance in the field.
|
||||
|
||||

|
||||
|
||||
The above diagram explains how APIs interact with whatever you might be building.
|
||||
|
||||
Now, if you’re inside the “**The System**” box, then you need to understand how to interact with these APIs using the right tools. If you’re inside the “**External System**” box, then you need to understand the type of standards these APIs need to follow and how to implement them.
|
||||
|
||||
Don’t worry though, for both situations, there are always frameworks and libraries you can use to simplify your task and ensure you’re following the proper industry standards.
|
||||
|
||||
### What are the most common API types?
|
||||
|
||||
The most common types of APIs used in the industry currently are REST and GraphQL.
|
||||
|
||||
As a backend developer, it’s not mandatory that you master both of these types, but it’s definitely recommended that you have some practical experience with one of them.
|
||||
|
||||
- **RESTful APIs.** These are APIs that work over HTTP and make extensive use of the HTTP Verbs to give meaning to each request. They’ve been the most popular type of API until recently, so there are still a lot of projects and teams that make use of it.
|
||||
- **GraphQL.** GraphQL APIs operate over HTTP as well, leveraging the HTTP protocol and its verbs. In contrast to the conventional RESTful APIs, GraphQL has emerged as a powerful alternative, offering a flexible and efficient approach to data querying and manipulation. GraphQL allows clients to request only the data they need, providing a more tailored and efficient interaction between clients and servers.
|
||||
|
||||
Is there one better than the other? There is no easy way to answer that question as both are capable of doing everything you’d need. It’s more about your particular requirements and the preferences of your dev team.
|
||||
|
||||
## Version Control Savvy
|
||||
|
||||
One mandatory skill that all backend developers should work on (actually, all developers, in general) is version control, or in other words, understanding and being familiar with version control systems.
|
||||
|
||||
Essentially, you’ll want to know how to use the version control tool that everyone else is using. The industry standard at the moment of writing this is [Git](https://git-scm.com/), while there might be some teams using other (older) tools, as long as you understand the current one, you’ll be in good shape.
|
||||
|
||||
### What is version control?
|
||||
|
||||
Version control references the ability for you and other developers to share code with each other while you’re working on the same files.
|
||||
|
||||
While Git is the industry standard at the moment, GitHub has created such a popular platform around Git, that it almost makes it mandatory to learn about.
|
||||
|
||||
So go ahead and create an account, browse what others are doing, and upload your own personal projects. It’s definitely a great way to learn.
|
||||
|
||||
### What should you learn about Git?
|
||||
|
||||
If you’re picking up Git as your version control system of choice, there are two main areas you should be focusing on.
|
||||
|
||||
- **The basics.** Clearly understanding how Git works and the basic commands to add, push and pull changes. You should aim to learn enough about them to feel comfortable using them on your day-to-day (because you will).
|
||||
- **Branching strategies.** Sadly, using Git alone is not enough. While through Git you can already start versioning your code, when the project is complex enough and your team big enough, the tool alone will not be enough. You’ll have to come up with [branching strategies](https://learngitbranching.js.org/?locale=es_ES) to organize the whole team’s workflow.
|
||||
|
||||
Keep in mind that Git and Git branching are not trivial topics, and they’ll take a while to master. So, while you should give yourself time to learn about them, also make sure you check with others (or use tools such as ChatGPT) to validate your commands before using them. Remember, a wrong Git command or a wrong workflow can cause major problems within a project, especially if there are many developers working on the same codebase.
|
||||
|
||||
## Testing
|
||||
|
||||
Understanding both what testing is and the importance of it within the backend development workflow is crucial for all developers, and one of the mandatory backend developer skills to focus on.
|
||||
|
||||
Testing is the development process of making sure your code works in a way that doesn’t involve you manually testing every feature but rather using tools that allow you to test and reproduce any problems that can be found programmatically.
|
||||
|
||||
This, of course, helps to remove potential human error from the equation when testing big systems and to increase the speed at which these tests can be done (think seconds vs hours of you doing it manually).
|
||||
|
||||
Testing is a far more complex discipline than I can describe here. Just know that there are many different ways to test a system, and all backend developers should be aware of the following:
|
||||
|
||||
- **Unit testing:** This is the most common way of doing code testing. You’ll write tests using a testing framework for every publicly available function/method in your code. This way you’re making sure every piece of code that can be used is tested and performs according to plan. Running these tests is usually quite fast, so you’ll be doing it before every commit (usually).
|
||||
- **Integration testing:** If you’re building a system that consists of multiple independent systems working together (think, for instance, a microservice-based architecture), then testing each individual part is not enough. You also have to make sure systems that should interact with each other do so correctly. This is where integration tests come into play.
|
||||
- **End-to-end testing (E2E):** These tests are similar to integration tests, but they also include the UI of the system. There are tools you can use to automate actions in your UI as if a real user were performing them and then checking the result. For example, clicking on a log-out button and checking if you’re later redirected to the log-in screen. This flow would involve the backend performing some actions that result in the user being logged out.
|
||||
- **Load testing:** While not exactly the same process as with the previous test types, load testing is great for backend systems because it helps you determine if your backend is ready to deal with high amounts of traffic.
|
||||
|
||||
You can think of the list in graphical format as the following diagram:
|
||||
|
||||

|
||||
|
||||
If you’re just getting started with testing, I’d recommend focusing only on unit testing for the time being. Once you have a grasp on it, start moving out following the above diagram and slowly move into the other types as you progress.
|
||||
|
||||
## CI/CD and DevOps Familiarity
|
||||
|
||||
As a backend developer, your code will be constantly deployed, either into cloud environments or perhaps even into normal, on-premise servers. The point is that what you build will run through CI/CD (Continuous Integration and Continuous Deployment) processes.
|
||||
|
||||

|
||||
|
||||
These processes will automatically test it (Continuous Integration) and automatically deploy it (if all tests go well). As a backend developer, you’re not usually expected to know and understand how to configure these processes; however, it’s important that you know about them.
|
||||
|
||||
DevOps is yet another tangential area to that of a backend developer. When teams are small enough, backend devs might be “gently pushed” into tasks such as configuring CI/CD pipelines, setting up servers, and more. These tasks are usually performed by dedicated professionals with the role of DevOps. Their specialty is automation, making the deployment process efficient and ensuring that everything runs smoothly in the real-world server environment. They play a crucial role in maintaining the reliability and performance of applications and websites.
|
||||
|
||||
So, while they’re not strictly the responsibilities of backend developers, they’re close enough to the role’s day-to-day that it would be a good idea to learn about them. If you’re interested in learning more about DevOps, check out [our DevOps roadmap](https://roadmap.sh/devops) containing all the key topics you should learn about if you want to become a DevOps engineer.
|
||||
|
||||
## Soft Skills
|
||||
|
||||
Finally, the last set of backend developer skills you should focus on are, actually, not technical skills, nor are they exclusively useful for backend developers. These are skills that every developer should work on during their career: soft skills.
|
||||
|
||||
### Improving communication
|
||||
|
||||
The ability to communicate with others, both technical and non-technical people, is crucial in any developer's career.
|
||||
|
||||
For backend developers, it’s especially important because communicating their work and the effects of it is definitely harder than other roles, such as frontend developers who can actually showcase what they’re building.
|
||||
|
||||
As a backend developer, you’ll be able to better explain problems or blockers to your colleagues, you’ll be able to perform requirement gathering much more effectively, and you’ll even improve your own problem-solving skills by being better at articulating the problems and potential solutions to yourself.
|
||||
|
||||
### Critical thinking
|
||||
|
||||
Honing your critical thinking as a backend developer will help your ability to analyze complex problems, identify patterns much faster, and come up with innovative solutions to the problems you’re facing.
|
||||
|
||||
Pushing the limits of your critical thinking skills will also foster a more systematic and strategic approach to coding and architecting robust and efficient solutions.
|
||||
|
||||
In other words, it’ll make you a better and more efficient coder. And who doesn’t want that?
|
||||
|
||||
## Conclusion
|
||||
|
||||
To summarize, if you expect to become a backend developer or to grow in the area of backend development:
|
||||
|
||||
- Keep an eye on the industry to understand what’s the status quo and what’s new and hot.
|
||||
- Understand the basics of software design and architecture.
|
||||
- Look into relational databases and NoSQL databases as well; they’re both important.
|
||||
- Learn how to build and use APIs; they’ll be part of almost every project you work on.
|
||||
- Remember, testing might look like it’s not mandatory, but it’s definitely a standard practice when it comes to backend development.
|
||||
- CI/CD and DevOps are practices you’ll be involved with, either directly or indirectly, so learn about them.
|
||||
- Soft skills are just as important as technical skills if you expect to grow in your career.
|
||||
|
||||
That said, do not take this list as the ultimate roadmap but rather as a starting point. If you’re willing to take your backend developer career to the next level, push yourself out of your comfort zone and pursue the skills listed here and the ones listed in this detailed [backend development roadmap](https://roadmap.sh/backend).
|
||||
|
||||
Remember, constant learning is the only absolute truth in the software development world (this is true for backend developers, too). If you keep your skillset updated with the latest trends, you’ll remain adaptable and effective as a backend developer.
|
||||
@@ -1,15 +1,12 @@
|
||||
---
|
||||
title: 'The 5 Best Backend Development Languages to Master (2024)'
|
||||
description: 'Discover the best backend development languages to master in 2024.'
|
||||
author:
|
||||
name: 'Fernando Doglio'
|
||||
url: 'https://twitter.com/deleteman123'
|
||||
imageUrl: '/authors/fernando.jpeg'
|
||||
authorId: fernando
|
||||
excludedBySlug: '/backend/languages'
|
||||
seo:
|
||||
title: 'The 5 Best Backend Development Languages to Master (2024)'
|
||||
description: 'Discover the best backend development languages to learn right now for career development, with practical tips from an experienced developer.'
|
||||
isNew: true
|
||||
isNew: false
|
||||
type: 'textual'
|
||||
date: 2024-01-18
|
||||
sitemap:
|
||||
@@ -116,9 +113,9 @@ What makes Python extra appealing, especially for beginners, is the fact that re
|
||||
|
||||
One of Python's standout features is its beginner-friendly syntax, making it an ideal language for those new to programming. The emphasis on readability and the absence of complex syntax (for the most part), eases the learning curve, enabling new developers to quickly grasp fundamental concepts.
|
||||
|
||||
Python's community plays a critical role in its accessibility. Abundant learning resources, tutorials, and documentation are readily available, empowering beginners to progress from basic programming principles to advanced backend development seamlessly. Online platforms like Codecademy, Coursera, realpython.com, and even Google offer comprehensive courses tailored to all skill levels.
|
||||
Python's community plays a critical role in its accessibility. Abundant learning resources, tutorials, and documentation are readily available, empowering beginners to progress from basic programming principles to advanced backend development seamlessly. Online platforms like Codecademy, Coursera, realpython.com, and even Google offer comprehensive courses tailored to all skill levels.
|
||||
|
||||
#### Practical Applications and Popular Frameworks
|
||||
#### Practical Applications and Popular Frameworks
|
||||
|
||||
Python's versatility is evident in its applicability across a spectrum of industries, from web development and data science to artificial intelligence and automation. In the context of backend development, Python shines brightly with its two standout frameworks: [Django](https://www.djangoproject.com/) and [Flask](https://github.com/pallets/flask).
|
||||
|
||||
@@ -154,7 +151,7 @@ Java has a massive presence and for good reason (according to [JetBrain’s surv
|
||||
|
||||
#### Is it worth learning Java?
|
||||
|
||||
Now, learning Java, (a strongly typed, object oriented programming language (OOP), is a journey worth taking, but it's not a walk in the park. It's a bit like climbing a mountain – you start at the bottom with the basics, and as you ascend, you get into the nitty-gritty of things like object-oriented programming. The process will force you to learn a lot, which is a great thing, by the end you’ll have a lot of understanding of mechanics and concepts around OOP that can be extrapolated into other languages. However, that can also be overwhelming to some developers who just want to learn by building mini-projects. In those situations, the learning curve of Java might be too long (not steep, but long because there is a lot more to cover than with alternatives such as Python or JavaScript).
|
||||
Now, learning Java, (a strongly typed, object oriented programming language (OOP), is a journey worth taking, but it's not a walk in the park. It's a bit like climbing a mountain – you start at the bottom with the basics, and as you ascend, you get into the nitty-gritty of things like object-oriented programming. The process will force you to learn a lot, which is a great thing, by the end you’ll have a lot of understanding of mechanics and concepts around OOP that can be extrapolated into other languages. However, that can also be overwhelming to some developers who just want to learn by building mini-projects. In those situations, the learning curve of Java might be too long (not steep, but long because there is a lot more to cover than with alternatives such as Python or JavaScript).
|
||||
|
||||
That said, the community is big and there are tons of resources, from online courses to forums, helping you navigate the Java landscape. And good reason, considering Java has been around for quite a while.
|
||||
|
||||
@@ -197,7 +194,7 @@ If you were to rank languages based on the amount of content out there to learn
|
||||
|
||||
#### Key Frameworks and Development Tools
|
||||
|
||||
Now, let's talk about frameworks. In the case of JavaScript, this topic is so varied that recommending a single option for someone just getting started is really hard.
|
||||
Now, let's talk about frameworks. In the case of JavaScript, this topic is so varied that recommending a single option for someone just getting started is really hard.
|
||||
|
||||
For example, if you want to go frontend agnostic, or in other words, you don’t care about the technology being used to develop the client side of your app, then a good starting option would be [Express.js](https://expressjs.com/). This framework used to be the industry standard. And while that’s no longer the case, it’s still a perfect first choice if you’re looking for something with the required functionality to make your life a lot easier.
|
||||
|
||||
@@ -205,7 +202,7 @@ Now, if on the other hand, you’re looking to build the frontend and the backen
|
||||
|
||||
#### Does it make sense to pick up JavaScript as a backend language?
|
||||
|
||||
The answer to this question is always going to be “yes”, whether you’re coming from the frontend and you already have JS experience or if you’re picking it up from scratch. In fact, according to [StackOverflow’s 2023 survey, JavaScript is the most used language by professionals](https://survey.stackoverflow.co/2023/#most-popular-technologies-language-prof) (with 65.85% of the votes).
|
||||
The answer to this question is always going to be “yes”, whether you’re coming from the frontend and you already have JS experience or if you’re picking it up from scratch. In fact, according to [StackOverflow’s 2023 survey, JavaScript is the most used language by professionals](https://survey.stackoverflow.co/2023/#most-popular-technologies-language-prof) (with 65.85% of the votes).
|
||||
|
||||

|
||||
|
||||
@@ -234,7 +231,7 @@ While there might not be a downside to picking JS, there is no perfect language
|
||||
|
||||
### PHP
|
||||
|
||||
Now, if you’re looking for something very well established in the web development industry, just like Java but with a shorter learning curve, then you’re probably looking for PHP.
|
||||
Now, if you’re looking for something very well established in the web development industry, just like Java but with a shorter learning curve, then you’re probably looking for PHP.
|
||||
|
||||
> As a note about PHP’s relevancy, while many developers might claim that PHP is a dying tech, according to [W3Techs, over 75% of websites with a backend use PHP](https://w3techs.com/technologies/details/pl-php).
|
||||
|
||||
@@ -242,7 +239,7 @@ It's the glue that holds a ton of websites together, and its longevity in the we
|
||||
|
||||
#### Ease of Mastery and Vast Library Support
|
||||
|
||||
If you're diving into PHP, you wouldn’t be so wrong (no matter what others might tell you). It's got a gentle learning curve, which means you can start building things pretty quickly. Getting everything set up and working will probably take you 10 minutes, and you’ll be writing your first “hello world” 5 minutes after that.
|
||||
If you're diving into PHP, you wouldn’t be so wrong (no matter what others might tell you). It's got a gentle learning curve, which means you can start building things pretty quickly. Getting everything set up and working will probably take you 10 minutes, and you’ll be writing your first “hello world” 5 minutes after that.
|
||||
|
||||
The vast community support and an ocean of online resources make mastering PHP a breeze. Plus, its library support is like having a toolkit that's always expanding – you'll find what you need, whether you're wrangling databases, handling forms, or making your website dance with dynamic content.
|
||||
|
||||
@@ -250,9 +247,9 @@ If you’re looking to pick up PHP, look for the LAMP stack, which stands for **
|
||||
|
||||
#### Modern PHP Frameworks and Their Impact
|
||||
|
||||
If we’re talking about PHP frameworks, then we gotta talk about [Laravel](https://laravel.com/) and [Symfony](https://symfony.com/). They are like the rockstars of the modern PHP world.
|
||||
If we’re talking about PHP frameworks, then we gotta talk about [Laravel](https://laravel.com/) and [Symfony](https://symfony.com/). They are like the rockstars of the modern PHP world.
|
||||
|
||||
Laravel comes with a lot of tools and features that help you speed up your development process. On the other side, Symfony has a modular architecture, making it a solid choice for projects of all sizes.
|
||||
Laravel comes with a lot of tools and features that help you speed up your development process. On the other side, Symfony has a modular architecture, making it a solid choice for projects of all sizes.
|
||||
|
||||
These frameworks showcase how PHP has evolved, staying relevant and powerful in the ever-changing landscape of web development.
|
||||
|
||||
@@ -337,7 +334,7 @@ Lucky for you, if you’re reading this, that means you’ve found the most comp
|
||||
|
||||
### Guided Learning: From Online Courses to Bootcamps
|
||||
|
||||
Online courses and bootcamps serve as invaluable companions on your learning expedition. Platforms like Udemy, Coursera, and freeCodeCamp offer comprehensive backend development courses.
|
||||
Online courses and bootcamps serve as invaluable companions on your learning expedition. Platforms like Udemy, Coursera, and freeCodeCamp offer comprehensive backend development courses.
|
||||
|
||||
These resources not only cover programming languages like Python, Java, or JavaScript but also dive deep into frameworks like Django, Express.js, or Laravel. For those seeking a more immersive experience, coding bootcamps provide intensive, hands-on training to fast-track your backend development skills.
|
||||
|
||||
@@ -345,7 +342,7 @@ Whatever choice you go for, make sure you’re not following trends or just copy
|
||||
|
||||
### Building Community Connections for Learning Support
|
||||
|
||||
Joining developer communities (there are several on Twitter for example), forums like Stack Overflow, or participating in social media groups dedicated to backend development creates a network of support.
|
||||
Joining developer communities (there are several on Twitter for example), forums like Stack Overflow, or participating in social media groups dedicated to backend development creates a network of support.
|
||||
|
||||
Engaging with experienced developers, sharing challenges, and seeking advice fosters a collaborative learning environment. Attend local meetups or virtual events if you can to connect with professionals in the field, gaining insights and building relationships that can prove invaluable throughout your journey.
|
||||
|
||||
@@ -368,7 +365,7 @@ As you accumulate skills and knowledge, showcase your journey through a well-cra
|
||||
|
||||
When it comes to deciding where to publish this portfolio, you have some options, such as directly on your GitHub profile (if you have one), or perhaps on your own personal website where you can share some design thoughts about each project along with the code.
|
||||
|
||||
In the end, the important thing is that you should be sharing your experience somewhere, especially when you don’t have working experience in the field.
|
||||
In the end, the important thing is that you should be sharing your experience somewhere, especially when you don’t have working experience in the field.
|
||||
|
||||
### Conclusion
|
||||
|
||||
@@ -383,12 +380,3 @@ In the end, there are many backend programming languages to choose from, and wha
|
||||
You’re the one who gets to decide, but just know that no matter what you choose, getting started in backend development is a one-way street. You’ll be learning from this moment on, and you’ll be jumping from one language to the other as the field evolves.
|
||||
|
||||
Remember that there is a very detailed version of a [backend roadmap here](https://roadmap.sh/backend), it might be a great place to get started! And if you’re also interested in frontend development, there is an [equally handy roadmap](https://roadmap.sh/frontend) here as well!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Basic Authentication'
|
||||
description: 'Understand what is basic authentication and how it is implemented'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Basic Authentication - roadmap.sh'
|
||||
description: 'Understand what is basic authentication and how it is implemented'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Basics of Authentication'
|
||||
description: 'Learn the basics of Authentication and Authorization'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Basics of Authentication - roadmap.sh'
|
||||
description: 'Learn the basics of Authentication and Authorization'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Big-O Notation'
|
||||
description: 'Easy to understand explanation of Big-O notation without any fancy terms'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Big-O Notation - roadmap.sh'
|
||||
description: 'Easy to understand explanation of Big-O notation without any fancy terms'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Character Encodings'
|
||||
description: 'Covers the basics of character encodings and explains ASCII vs Unicode'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Character Encodings - roadmap.sh'
|
||||
description: 'Covers the basics of character encodings and explains ASCII vs Unicode'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'What is CI and CD?'
|
||||
description: 'Learn the basics of CI/CD and how to implement that with GitHub Actions.'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'What is CI and CD? - roadmap.sh'
|
||||
description: 'Learn the basics of CI/CD and how to implement that with GitHub Actions.'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Consistency Patterns'
|
||||
description: 'Everything you need to know about Week, Strong and Eventual Consistency'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Consistency Patterns - roadmap.sh'
|
||||
description: 'Everything you need to know about Week, Strong and Eventual Consistency'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Design Patterns for Humans'
|
||||
description: 'A language agnostic, ultra-simplified explanation to design patterns'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Design Patterns for Humans - roadmap.sh'
|
||||
description: 'A language agnostic, ultra-simplified explanation to design patterns'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'DHCP in One Picture'
|
||||
description: 'Here is what happens when a new device joins the network.'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'DHCP in One Picture - roadmap.sh'
|
||||
description: 'Here is what happens when a new device joins the network.'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'DNS in One Picture'
|
||||
description: 'Quick illustrative guide on how a website is found on the internet.'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'DNS in One Picture - roadmap.sh'
|
||||
description: 'Quick illustrative guide on how a website is found on the internet.'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: '5 Free Resources to Master LLMs'
|
||||
description: 'Dive into the world of LLMs with these free resources'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: '5 Free Resources to Master Language Models (LLMs) - roadmap.sh'
|
||||
description: 'Looking to dive into the fascinating world of Language Models (LLMs)? Discover the top 5 free resources that will help you learn and excel in understanding LLMs. From comprehensive tutorials to interactive courses, this blog post provides you with the ultimate guide to sharpen your skills and unravel the potential of language models. Start your journey today and become a pro in LLMs without spending a dime!'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Brief History of JavaScript'
|
||||
description: 'How JavaScript was introduced and evolved over the years'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Brief History of JavaScript - roadmap.sh'
|
||||
description: 'How JavaScript was introduced and evolved over the years'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Jump Servers: What, Why and How'
|
||||
description: 'Learn what is a Jump Server and how to set it up for SSH access.'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Jump Servers: What, Why and How - roadmap.sh'
|
||||
description: 'Learn what is a Jump Server and how to set it up for SSH access.'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'HTTP Basic Authentication'
|
||||
description: 'Learn what is HTTP Basic Authentication and how to implement it in Node.js'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'HTTP Basic Authentication - roadmap.sh'
|
||||
description: 'Learn what is HTTP Basic Authentication and how to implement it in Node.js'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'HTTP Caching'
|
||||
description: 'Everything you need to know about web caching'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'HTTP Caching - roadmap.sh'
|
||||
description: 'Everything you need to know about web caching'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Introduction to LLMs'
|
||||
description: 'What are LLMs, how does ChatGPT and other LLMs work?'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Introduction to LLMs - roadmap.sh'
|
||||
description: 'What are LLMs, how does ChatGPT and other LLMs work?'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Journey to HTTP/2'
|
||||
description: 'The evolution of HTTP. How it all started and where we stand today'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Journey to HTTP/2 - roadmap.sh'
|
||||
description: 'The evolution of HTTP. How it all started and where we stand today'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'JWT Authentication'
|
||||
description: 'Understand what is JWT authentication and how is it implemented'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'JWT Authentication - roadmap.sh'
|
||||
description: 'Understand what is JWT authentication and how is it implemented'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Levels of Seniority'
|
||||
description: 'How to Step Up as a Junior, Mid Level or a Senior Developer?'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Levels of Seniority - roadmap.sh'
|
||||
description: 'How to Step Up as a Junior, Mid Level or a Senior Developer?'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'OAuth — Open Authorization'
|
||||
description: 'Learn and understand what is OAuth and how it works'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'OAuth — Open Authorization - roadmap.sh'
|
||||
description: 'Learn and understand what is OAuth and how it works'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Proxy Servers'
|
||||
description: 'How do proxy servers work and what are forward and reverse proxies?'
|
||||
author:
|
||||
name: 'Ebrahim Bharmal'
|
||||
url: 'https://twitter.com/BharmalEbrahim'
|
||||
imageUrl: '/authors/ebrahimbharmal007.png'
|
||||
authorId: 'ebrahim-bharmal'
|
||||
seo:
|
||||
title: 'Proxy Servers - roadmap.sh'
|
||||
description: 'How do proxy servers work and what are forward and reverse proxies?'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Random Numbers: Are they?'
|
||||
description: 'Learn how they are generated and why they may not be truly random.'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Random Numbers: Are they? - roadmap.sh'
|
||||
description: 'Learn how they are generated and why they may not be truly random.'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Scaling Databases'
|
||||
description: 'Learn the ups and downs of different database scaling strategies'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Scaling Databases - roadmap.sh'
|
||||
description: 'Learn the ups and downs of different database scaling strategies'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Session Based Authentication'
|
||||
description: 'Understand what is session based authentication and how it is implemented'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Session Based Authentication - roadmap.sh'
|
||||
description: 'Understand what is session based authentication and how it is implemented'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Session Based Authentication'
|
||||
description: 'Learn what is Session Based Authentication and how to implement it in Node.js'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Session Based Authentication - roadmap.sh'
|
||||
description: 'Learn what is Session Based Authentication and how to implement it in Node.js'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: "Guide to Let's Encrypt SSL Setup"
|
||||
description: "Learn how to protect your website using Let's Encrypt SSL Certificates."
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: "Guide to Let's Encrypt SSL Setup - roadmap.sh"
|
||||
description: "Learn how to protect your website using Let's Encrypt SSL Certificates."
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Single Command Database Setup'
|
||||
description: 'Learn how to run MySQL, PostgreSQL, or MongoDB in Docker with single Command'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Single Command Database Setup - roadmap.sh'
|
||||
description: 'Learn how to run MySQL, PostgreSQL, or MongoDB in Docker with single Command'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'SSL vs TLS vs SSH'
|
||||
description: 'Quick tidbit on the differences between SSL, TLS, HTTPS and SSH'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'SSL vs TLS vs SSH - roadmap.sh'
|
||||
description: 'Quick tidbit on the differences between SSL, TLS, HTTPS and SSH'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'SSO — Single Sign On'
|
||||
description: 'Learn the basics of SAML and understand how does Single Sign On work.'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'SSO — Single Sign On - roadmap.sh'
|
||||
description: 'Learn the basics of SAML and understand how does Single Sign On work.'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Token Based Authentication'
|
||||
description: 'Understand what is token based authentication and how it is implemented'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Token Based Authentication - roadmap.sh'
|
||||
description: 'Understand what is token based authentication and how it is implemented'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Building a BitTorrent Client'
|
||||
description: 'Learn everything you need to know about BitTorrent by writing a client in Go'
|
||||
author:
|
||||
name: 'Jesse Li'
|
||||
url: 'https://twitter.com/__jesse_li'
|
||||
imageUrl: '/authors/jesse.png'
|
||||
authorId: 'jesse-li'
|
||||
seo:
|
||||
title: 'Building a BitTorrent Client - roadmap.sh'
|
||||
description: 'Learn everything you need to know about BitTorrent by writing a client in Go'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Unfamiliar Codebase'
|
||||
description: 'Tips on getting familiar with an unfamiliar codebase'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'Unfamiliar Codebase - roadmap.sh'
|
||||
description: 'Tips on getting familiar with an unfamiliar codebase'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'What are Web Vitals?'
|
||||
description: 'Learn what are the core web vitals and how to measure them.'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'What are Web Vitals? - roadmap.sh'
|
||||
description: 'Learn what are the core web vitals and how to measure them.'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'How does the internet work?'
|
||||
description: 'Learn the basics of internet and everything involved with this short video series'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'How does the internet work? - roadmap.sh'
|
||||
description: 'Learn the basics of internet and everything involved with this short video series'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'SLIs, SLOs and SLAs'
|
||||
description: 'Learn what are different indicators for performance identification of any service.'
|
||||
author:
|
||||
name: 'Kamran Ahmed'
|
||||
url: 'https://twitter.com/kamrify'
|
||||
imageUrl: '/authors/kamranahmedse.jpeg'
|
||||
authorId: 'kamran'
|
||||
seo:
|
||||
title: 'SLIs, SLOs and SLAs - roadmap.sh'
|
||||
description: 'Learn what are different indicators for performance identification of any service.'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
title: 'Build it and they will come?'
|
||||
description: 'Why “build it and they will come” alone won’t work anymore'
|
||||
author:
|
||||
name: 'Peter Thaleikis'
|
||||
url: 'https://twitter.com/spekulatius1984'
|
||||
imageUrl: '/authors/spekulatius.jpg'
|
||||
authorId: 'peter-thaleikis'
|
||||
seo:
|
||||
title: 'Build it and they will come? - roadmap.sh'
|
||||
description: 'Why “build it and they will come” alone won’t work anymore'
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
# Hateoas
|
||||
|
||||
HATEOAS is an acronym for <b>H</b>ypermedia <b>A</b>s <b>T</b>he <b>E</b>ngine <b>O</b>f <b>A</b>pplication <b>S</b>tate, it's the concept that when sending information over a RESTful API the document received should contain everything the client needs in order to parse and use the data i.e they don't have to contact any other endpoint not explicitly mentioned within the Document
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Oktane17: Designing Beautiful REST + JSON APIs (3:56 - 5:57)](https://youtu.be/MiOSzpfP1Ww?t=236)
|
||||
HATEOAS is an acronym for <b>H</b>ypermedia <b>A</b>s <b>T</b>he <b>E</b>ngine <b>O</b>f <b>A</b>pplication <b>S</b>tate, it's the concept that when sending information over a RESTful API the document received should contain everything the client needs in order to parse and use the data i.e they don't have to contact any other endpoint not explicitly mentioned within the Document.
|
||||
|
||||
@@ -7,7 +7,7 @@ export const faqs: FAQType[] = [
|
||||
answer: [
|
||||
"If you are a complete beginner who is just getting started, you can start by learning a backend programming language such as Python, Ruby, Java, Go etc. Once you have got the basic to intermediate understanding of the language, learn about the package manager for that language and learn how to install and use external packages into your applications. Learn the basics of some relational database e.g. PostgreSQL and learn how to run simple CRUD operations. Optionally, you can pick up and learn a web framework for the language of your choice as well. Learn how to build a simple RESTful API and implement simple Authentication/Authorization into it. While you are learning all the items mentioned above, don't forget to learn about Git and GitHub as well.",
|
||||
'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.',
|
||||
"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.",
|
||||
"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](https://roadmap.sh/backend/developer-skills) and be prepared for the fast-paced technology evolution in the industry.",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,4 +7,3 @@ Visit the following resources to learn more:
|
||||
- [Blockchain Fork](<https://en.wikipedia.org/wiki/Fork_(blockchain)>)
|
||||
- [What is a fork?](https://www.coinbase.com/learn/crypto-basics/what-is-a-fork)
|
||||
- [What Is a Hard Fork?](https://www.investopedia.com/terms/h/hard-fork.asp)
|
||||
- [Blockchain Forking](https://www.youtube.com/watch?v=bu1gcyyfz7w)
|
||||
@@ -0,0 +1,16 @@
|
||||
# JavaScript
|
||||
|
||||
JavaScript allows you to add interactivity to your pages. Common examples that you may have seen on the websites are sliders, click interactions, popups and so on. Apart from being used on the frontend in browsers, there is Node.js which is an open-source, cross-platform, back-end JavaScript runtime environment that runs on the V8 engine and executes JavaScript code outside a web browser.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Visit Dedicated JavaScript Roadmap](/javascript)
|
||||
- [W3Schools – JavaScript Tutorial](https://www.w3schools.com/js/)
|
||||
- [The Modern JavaScript Tutorial](https://javascript.info/)
|
||||
- [Codecademy - Learn JavaScript](https://www.codecademy.com/learn/introduction-to-javascript)
|
||||
- [JavaScript Crash Course for Beginners](https://youtu.be/hdI2bqOjy3c)
|
||||
- [Node.js Crash Course](https://www.youtube.com/watch?v=fBNz5xF-Kx4)
|
||||
- [Node.js Tutorial for Beginners](https://www.youtube.com/watch?v=TlB_eWDSMt4)
|
||||
- [W3Schools – Node.js Tutorial](https://www.w3schools.com/nodejs/)
|
||||
- [What is NPM?](https://www.w3schools.com/nodejs/nodejs_npm.asp)
|
||||
- [Official Documentation](https://nodejs.org/en/learn/getting-started/introduction-to-nodejs)
|
||||
@@ -0,0 +1,13 @@
|
||||
# Java
|
||||
|
||||
Java is general-purpose language, primarily used for Internet-based applications.
|
||||
It was created in 1995 by James Gosling at Sun Microsystems and is one of the most popular options for backend developers.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Visit Dedicated Java Roadmap](/java)
|
||||
- [Java Website](https://www.java.com/)
|
||||
- [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)
|
||||
@@ -0,0 +1,14 @@
|
||||
# Go
|
||||
|
||||
Go is an open source programming language supported by Google. Go can be used to write cloud services, CLI tools, used for API development, and much more.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Visit Dedicated Go Roadmap](/golang)
|
||||
- [A Tour of Go – Go Basics](https://go.dev/tour/welcome/1)
|
||||
- [Go Reference Documentation](https://go.dev/doc/)
|
||||
- [Go by Example - annotated example programs](https://gobyexample.com/)
|
||||
- [Learn Go | Codecademy](https://www.codecademy.com/learn/learn-go)
|
||||
- [W3Schools Go Tutorial ](https://www.w3schools.com/go/)
|
||||
- [Making a RESTful JSON API in Go](https://thenewstack.io/make-a-restful-json-api-go/)
|
||||
- [Go, the Programming Language of the Cloud](https://thenewstack.io/go-the-programming-language-of-the-cloud/)
|
||||
@@ -0,0 +1,10 @@
|
||||
# C#
|
||||
|
||||
C# (pronounced "C sharp") is a general purpose programming language made by Microsoft. It is used to perform different tasks and can be used to create web apps, games, mobile apps, etc.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [C# Learning Path](https://docs.microsoft.com/en-us/learn/paths/csharp-first-steps/?WT.mc_id=dotnet-35129-website)
|
||||
- [C# on W3 schools](https://www.w3schools.com/cs/index.php)
|
||||
- [Introduction to C#](https://docs.microsoft.com/en-us/shows/CSharp-101/?WT.mc_id=Educationalcsharp-c9-scottha)
|
||||
- [C# tutorials](https://www.youtube.com/watch?v=gfkTfcpWqAY&list=PLTjRvDozrdlz3_FPXwb6lX_HoGXa09Yef)
|
||||
@@ -0,0 +1,8 @@
|
||||
# C++
|
||||
|
||||
C++ is a powerful general-purpose programming language. It can be used to develop operating systems, browsers, games, and so on. C++ supports different ways of programming like procedural, object-oriented, functional, and so on. This makes C++ powerful as well as flexible.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Learn Cpp](https://learncpp.com/)
|
||||
- [C++ Reference](https://en.cppreference.com/)
|
||||
@@ -0,0 +1,18 @@
|
||||
# Python
|
||||
|
||||
Python is a well known programming language which is both a strongly typed and a dynamically typed language. Being an interpreted language, code is executed as soon as it is written and the Python syntax allows for writing code in functional, procedural or object-oriented programmatic ways.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Visit Dedicated Python Roadmap](/python)
|
||||
- [Python Website](https://www.python.org/)
|
||||
- [Python Getting Started](https://www.python.org/about/gettingstarted/)
|
||||
- [Automate the Boring Stuff](https://automatetheboringstuff.com/)
|
||||
- [FreeCodeCamp.org - How to Learn Python ? ](https://www.freecodecamp.org/news/how-to-learn-python/)
|
||||
- [Python principles - Python basics](https://pythonprinciples.com/)
|
||||
- [W3Schools - Python Tutorial ](https://www.w3schools.com/python/)
|
||||
- [Python Crash Course](https://ehmatthes.github.io/pcc/)
|
||||
- [Codecademy - Learn Python 2](https://www.codecademy.com/learn/learn-python)
|
||||
- [An Introduction to Python for Non-Programmers](https://thenewstack.io/an-introduction-to-python-for-non-programmers/)
|
||||
- [Getting Started with Python and InfluxDB](https://thenewstack.io/getting-started-with-python-and-influxdb/)
|
||||
- [Python for Beginners - Learn Python in 1 Hour](https://www.youtube.com/watch?v=kqtD5dpn9C8&ab_channel=ProgrammingwithMosh)
|
||||
@@ -0,0 +1,10 @@
|
||||
# Rust
|
||||
|
||||
Rust is a modern systems programming language focusing on safety, speed, and concurrency. It accomplishes these goals by being memory safe without using garbage collection.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [The Rust Programming Language - online book](https://doc.rust-lang.org/book/)
|
||||
- [Rust by Example - collection of runnable examples](https://doc.rust-lang.org/stable/rust-by-example/index.html)
|
||||
- [Rust vs. Go: Why They’re Better Together](https://thenewstack.io/rust-vs-go-why-theyre-better-together/)
|
||||
- [Rust by the Numbers: The Rust Programming Language in 2021](https://thenewstack.io/rust-by-the-numbers-the-rust-programming-language-in-2021/)
|
||||
@@ -0,0 +1,10 @@
|
||||
# Ruby
|
||||
|
||||
Ruby is a high-level, interpreted programming language that blends Perl, Smalltalk, Eiffel, Ada, and Lisp. Ruby focuses on simplicity and productivity along with a syntax that reads and writes naturally. Ruby supports procedural, object-oriented and functional programming and is dynamically typed.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [Ruby Website](https://www.ruby-lang.org/en/)
|
||||
- [Learn Ruby in 20 minutes](https://www.ruby-lang.org/en/documentation/quickstart/)
|
||||
- [Learn Ruby | Codecademy](https://www.codecademy.com/learn/learn-ruby)
|
||||
- [Ruby, An Introduction to a Programmer’s Best Friend](https://thenewstack.io/ruby-a-programmers-best-friend/)
|
||||
@@ -0,0 +1,3 @@
|
||||
# Pick a Language
|
||||
|
||||
Pick a programming language to practice data structures and algorithms with. You should pick a language that you are comfortable with or plan to adopt.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Language Syntax
|
||||
|
||||
Language syntax refers to the set of rules that dictate how programs written in a particular programming language must be structured. This can include rules for how to declare variables, how to call functions, how to structure control flow statements, and so on. Syntax varies significantly between different programming languages, so it is critical to grasp the specific syntax of the language you are using. It’s similar to grammar in human languages - putting words in the wrong order or including extraneous punctuation can make a sentence hard to understand, and the same applies to programming. Incorrect syntax leads to syntax errors which prevent your code from executing.
|
||||
|
||||
Learn the language syntax of the programming language you are using.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Control Structures
|
||||
|
||||
Control structures are fundamental elements in most programming languages that facilitate the flow of control through a program. There are three main types of control structures: Sequential, Selection and Iteration. Sequential control structures are the default mode where instructions happen one after another. Selection control structures (often called "conditional" or "decision" structures) allow one set of instructions to be executed if a condition is true and another if it's false. These typically include `if...else` statements. Iteration control structures (also known as "loops") allow a block of code to be repeated multiple times. Common loop structures include `for`, `while`, and `do...while` loops. All these control structures play a vital role in shaping the program logic.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Functions
|
||||
|
||||
Functions in programming are named sections of a program that perform a specific task. They allow us to write a piece of code once and reuse it in different places throughout the program, making our code more modular and easier to maintain. Functions often take in input, do something with it, and return output. Functions can be categorized into four main types: built-in functions (like `print()`, provided by the programming language), user-defined functions (written by the user for a specific use case), anonymous functions (also known as lambda functions, which are not declared using the standard `def` keyword), and higher-order functions (functions that take other functions as arguments or return a function).
|
||||
@@ -0,0 +1,3 @@
|
||||
# OOP Basics
|
||||
|
||||
Object-oriented programming (OOP) is a programming paradigm that uses "objects" to design applications and software. In OOP, each object is an instance of a class. A class defines the properties (often known as attributes or fields) and methods (actions) that are common to all objects of a certain kind. A key principle of OOP is the ability to hide certain parts of the objects’ data from the outside, a concept known as encapsulation. Other key principles are inheritance, a way to form new classes using classes that have already been defined, and polymorphism, the concept of designing objects to share behaviors and being able to override shared behaviors with specifics.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Pseudo Code
|
||||
|
||||
Pseudo code is a plain language description of the steps in an algorithm or another system. It is intended for human reading rather than machine reading. Pseudo code often uses control structures and terms common to popular high-level programming languages without strictly adhering to the syntax of any particular one. The foremost aim of pseudocode is to explain the inner "algorithmic thinking" behind coding, rather than focusing on the syntax of a particular language. A nice feature of pseudocode is that it is largely able to be understood by a wider range of people than the corresponding code in a specific programming language, which enhances its roles in drafting, documentation, learning, and collaboration aspects.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Programming Fundamentals
|
||||
|
||||
Programming Fundamentals are the basic concepts and principles that form the foundation of any computer programming language. These include understanding variables, which store data for processing, control structures such as loops and conditional statements that direct the flow of a program, data structures which organize and store data efficiently, and algorithms which step by step instructions to solve specific problems or perform specific tasks. Mastery of these fundamentals forms the basis for learning any specific programming language and for writing efficient, effective code.
|
||||
@@ -0,0 +1,3 @@
|
||||
# What are Data Structures?
|
||||
|
||||
Data structures are specialized formats for organizing and storing data in a computer so that it can be used efficiently. They provide a means to manage large amounts of data efficiently for uses such as large databases and internet indexing services. They are critical to programming and are used in almost all software systems including web development, operating systems, image editing, and much more. Some common types of data structures are arrays, linked lists, queues, stacks, trees, and graphs. The choice of the data structure often begins from the choice of an abstract data type, a broad type encapsulating various possible data structures."
|
||||
@@ -0,0 +1,3 @@
|
||||
# Importance of Data Structures
|
||||
|
||||
Data structures are crucial in the field of computer science and coding because they offer a method of organizing and storing data in an efficient and manageable format. They're critical because they form the foundation for modern algorithm design. Your ability to choose or design the most suited data structure for a particular task can be the difference between a solution that's functional and efficient and one that isn't. They allow data to be processed in a variety of ways - stored, sorted, ordered, or accessed - which is integral to software or database development. By implementing effective data structures, programmers can enhance performance, ease coding procedures, allow flexibility of data and most importantly, reduce complexity of code in a significant manner. Markdown offers the advantage of clear, straightforward formatting to express these concepts.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Data Structures
|
||||
|
||||
Data Structures are a fundamental element of computer science. They provide a specific way to organize and store data so that it can be accessed and used efficiently. Different types of data structures include arrays, linked lists, stacks, queues, hash tables, trees, and graphs. Each of these has its unique characteristics and use-cases, and is optimal for certain kinds of operations. For example, arrays are excellent for random access, while linked lists work well for frequent insertions and deletions. The correct choice of data structure can significantly enhance the performance of your programs.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Array
|
||||
|
||||
An **Array** is a linear data structure that holds elements of the same type, which means we can store only a specific type of elements in the array. It uses contiguous memory space to store elements. In an array, we can directly access any element based on its index which makes it an efficient data structure. Arrays have two types: one-dimensional and multi-dimensional. In a one-dimensional array, data is stored in a linear form while a multi-dimensional array can store data in the form of a matrix or in 3-D format.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user