mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-14 02:32:00 +08:00
Compare commits
122 Commits
refactor/r
...
feat/analy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1c83b1d13 | ||
|
|
e00bd629f5 | ||
|
|
7cb5587097 | ||
|
|
2a1c56fdf3 | ||
|
|
8083120227 | ||
|
|
6b87218cd5 | ||
|
|
57ed6e2760 | ||
|
|
a102624596 | ||
|
|
708454f0a8 | ||
|
|
3eaeb7ff9e | ||
|
|
19be07817d | ||
|
|
2e8360a29b | ||
|
|
53f86bb319 | ||
|
|
90851f38f3 | ||
|
|
bb9362412e | ||
|
|
9b54116548 | ||
|
|
7ee47f46b2 | ||
|
|
abae4d3361 | ||
|
|
62f31a4964 | ||
|
|
6edf2869f8 | ||
|
|
a367be60dc | ||
|
|
3429c17074 | ||
|
|
8e97088f21 | ||
|
|
78e62f9de5 | ||
|
|
0b463a78e8 | ||
|
|
bb64dabc75 | ||
|
|
d1f480d9b9 | ||
|
|
4b86af3a70 | ||
|
|
a1c564534a | ||
|
|
9bbf8ea04b | ||
|
|
ffe8d4c2f6 | ||
|
|
c0a52283d4 | ||
|
|
90cee9a223 | ||
|
|
96417de7ed | ||
|
|
28a8f8493b | ||
|
|
971dd8d2bd | ||
|
|
ae7352b8f3 | ||
|
|
5d9a5bd05c | ||
|
|
b8c60093a6 | ||
|
|
00467decca | ||
|
|
6e1e334406 | ||
|
|
2867e25f2c | ||
|
|
e89b847ddb | ||
|
|
edace11348 | ||
|
|
96fb1b87e2 | ||
|
|
bff8915c6e | ||
|
|
de06961d85 | ||
|
|
5a38999ec2 | ||
|
|
7c20f9987a | ||
|
|
cccc4a7cd9 | ||
|
|
ad233daea2 | ||
|
|
64711eb6f9 | ||
|
|
a74c7fe19a | ||
|
|
c2137397b8 | ||
|
|
a43bbe9768 | ||
|
|
c2d635bf89 | ||
|
|
e36d352d13 | ||
|
|
f45b337f36 | ||
|
|
025334738a | ||
|
|
af28049b28 | ||
|
|
e7259abd0e | ||
|
|
df09e4e29e | ||
|
|
d38b1d52bf | ||
|
|
eda161232b | ||
|
|
888342ea37 | ||
|
|
d3d18847ee | ||
|
|
5f7e8a99b6 | ||
|
|
9230f79cb2 | ||
|
|
bd0bfa562d | ||
|
|
2569c3a965 | ||
|
|
fe6829311e | ||
|
|
e2e569617d | ||
|
|
599e4528ab | ||
|
|
d7d3a2a089 | ||
|
|
f2136d2207 | ||
|
|
500bd49a62 | ||
|
|
338e28df1b | ||
|
|
1860f32375 | ||
|
|
0cf801acfd | ||
|
|
847e9b9edb | ||
|
|
2cfd645256 | ||
|
|
8f36beb3bd | ||
|
|
6de872d1ea | ||
|
|
a7eab6f81f | ||
|
|
b7b96b7d7b | ||
|
|
30d3a86784 | ||
|
|
852dce9d6a | ||
|
|
4401a4c9aa | ||
|
|
8973031252 | ||
|
|
a4c221505c | ||
|
|
f3c4468a7b | ||
|
|
4d0bc30fc6 | ||
|
|
740bdf2aec | ||
|
|
6525b1bdee | ||
|
|
afc6f2d5ae | ||
|
|
871d43c295 | ||
|
|
bc32dc780b | ||
|
|
5685b30c42 | ||
|
|
7e3508cdf4 | ||
|
|
2ba3e64c1c | ||
|
|
ca30955e9f | ||
|
|
c2e1d8fd28 | ||
|
|
ab5cc9dd6d | ||
|
|
74267a6061 | ||
|
|
9c2c06affd | ||
|
|
0e0eea635b | ||
|
|
82400cd7a6 | ||
|
|
55fdc9f957 | ||
|
|
f8b9b58968 | ||
|
|
b79411c49e | ||
|
|
5ba951e555 | ||
|
|
464bcb6c66 | ||
|
|
b9aee719b2 | ||
|
|
5d9eecc2b9 | ||
|
|
80a0caba2f | ||
|
|
2937923fb1 | ||
|
|
a33018de1d | ||
|
|
c236e863a0 | ||
|
|
b7f94a7679 | ||
|
|
107fa4842c | ||
|
|
3e059cc3e9 | ||
|
|
ebd34612a2 |
@@ -3,6 +3,6 @@
|
||||
"enabled": false
|
||||
},
|
||||
"_variables": {
|
||||
"lastUpdateCheck": 1745231680828
|
||||
"lastUpdateCheck": 1748277554631
|
||||
}
|
||||
}
|
||||
1
.astro/types.d.ts
vendored
1
.astro/types.d.ts
vendored
@@ -1 +1,2 @@
|
||||
/// <reference types="astro/client" />
|
||||
/// <reference path="content.d.ts" />
|
||||
@@ -79,6 +79,7 @@ Visit the following resources to learn more:
|
||||
- `@course@`
|
||||
- `@podcast@`
|
||||
- `@video@`
|
||||
- `@book@`
|
||||
|
||||
It's important to add a valid type, this will help us categorize the content and display it properly on the roadmap. The order of the links based on type is same as above.
|
||||
|
||||
|
||||
55
package.json
55
package.json
@@ -31,20 +31,28 @@
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^9.2.0",
|
||||
"@astrojs/react": "^4.2.4",
|
||||
"@astrojs/sitemap": "^3.3.0",
|
||||
"@astrojs/node": "^9.2.1",
|
||||
"@astrojs/react": "^4.2.7",
|
||||
"@astrojs/sitemap": "^3.4.0",
|
||||
"@fingerprintjs/fingerprintjs": "^4.6.2",
|
||||
"@microsoft/clarity": "^1.0.0",
|
||||
"@nanostores/react": "^1.0.0",
|
||||
"@napi-rs/image": "^1.9.2",
|
||||
"@resvg/resvg-js": "^2.6.2",
|
||||
"@roadmapsh/editor": "workspace:*",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"@tanstack/react-query": "^5.74.4",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"astro": "^5.7.4",
|
||||
"@tailwindcss/vite": "^4.1.7",
|
||||
"@tanstack/react-query": "^5.76.1",
|
||||
"@tiptap/core": "^2.12.0",
|
||||
"@tiptap/extension-document": "^2.12.0",
|
||||
"@tiptap/extension-paragraph": "^2.12.0",
|
||||
"@tiptap/extension-placeholder": "^2.12.0",
|
||||
"@tiptap/extension-text": "^2.12.0",
|
||||
"@tiptap/pm": "^2.12.0",
|
||||
"@tiptap/react": "^2.12.0",
|
||||
"@tiptap/suggestion": "^2.12.0",
|
||||
"@types/react": "^19.1.4",
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"astro": "^5.7.13",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"dom-to-image": "^2.6.0",
|
||||
@@ -52,42 +60,43 @@
|
||||
"gray-matter": "^4.0.3",
|
||||
"htm": "^3.1.1",
|
||||
"image-size": "^2.0.2",
|
||||
"jose": "^6.0.10",
|
||||
"jose": "^6.0.11",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.503.0",
|
||||
"lucide-react": "^0.511.0",
|
||||
"luxon": "^3.6.1",
|
||||
"markdown-it-async": "^2.2.0",
|
||||
"nanoid": "^5.1.5",
|
||||
"nanostores": "^1.0.1",
|
||||
"node-html-parser": "^7.0.1",
|
||||
"npm-check-updates": "^18.0.0",
|
||||
"npm-check-updates": "^18.0.1",
|
||||
"playwright": "^1.52.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"react": "^19.1.0",
|
||||
"react-calendar-heatmap": "^1.10.0",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-resizable-panels": "^2.1.8",
|
||||
"react-resizable-panels": "^3.0.2",
|
||||
"react-textarea-autosize": "^8.5.9",
|
||||
"react-tooltip": "^5.28.1",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"remark-parse": "^11.0.0",
|
||||
"roadmap-renderer": "^1.0.7",
|
||||
"sanitize-html": "^2.16.0",
|
||||
"satori": "^0.12.2",
|
||||
"sanitize-html": "^2.17.0",
|
||||
"satori": "^0.13.1",
|
||||
"satori-html": "^0.3.2",
|
||||
"sharp": "^0.34.1",
|
||||
"shiki": "^3.2.2",
|
||||
"shiki": "^3.4.2",
|
||||
"slugify": "^1.6.6",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"tiptap-markdown": "^0.8.10",
|
||||
"turndown": "^7.2.0",
|
||||
"unified": "^11.0.5",
|
||||
"zustand": "^5.0.3"
|
||||
"zustand": "^5.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ai-sdk/google": "^1.2.12",
|
||||
"@ai-sdk/google": "^1.2.18",
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@types/dom-to-image": "^2.6.7",
|
||||
@@ -97,17 +106,17 @@
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/react-calendar-heatmap": "^1.9.0",
|
||||
"@types/react-slick": "^0.23.13",
|
||||
"@types/sanitize-html": "^2.15.0",
|
||||
"@types/sanitize-html": "^2.16.0",
|
||||
"@types/turndown": "^5.0.5",
|
||||
"ai": "^4.3.9",
|
||||
"ai": "^4.3.16",
|
||||
"csv-parser": "^3.2.0",
|
||||
"gh-pages": "^6.3.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"markdown-it": "^14.1.0",
|
||||
"openai": "^4.95.1",
|
||||
"openai": "^4.100.0",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-astro": "^0.14.1",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"tsx": "^4.19.3"
|
||||
"tsx": "^4.19.4"
|
||||
}
|
||||
}
|
||||
|
||||
1923
pnpm-lock.yaml
generated
1923
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
BIN
public/images/gifs/bot.gif
Normal file
BIN
public/images/gifs/bot.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 821 KiB |
BIN
public/images/gifs/wave.gif
Normal file
BIN
public/images/gifs/wave.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 370 KiB |
BIN
public/images/party.gif
Normal file
BIN
public/images/party.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 936 KiB |
BIN
public/pdfs/roadmaps/ai-agents.pdf
Normal file
BIN
public/pdfs/roadmaps/ai-agents.pdf
Normal file
Binary file not shown.
Binary file not shown.
2064
public/roadmap-content/ai-agents.json
Normal file
2064
public/roadmap-content/ai-agents.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -111,7 +111,7 @@
|
||||
},
|
||||
{
|
||||
"title": "Step by Step Process for Planning an A/B Test",
|
||||
"url": "https://towardsdatascience.com/step-by-step-for-planning-an-a-b-test-ef3c93143c0b",
|
||||
"url": "https://medium.com/data-science/step-by-step-for-planning-an-a-b-test-ef3c93143c0b",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -906,6 +906,11 @@
|
||||
"title": "Ollama: Easily run LLMs locally",
|
||||
"url": "https://klu.ai/glossary/ollama",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is Ollama? Running Local LLMs Made Simple",
|
||||
"url": "https://www.youtube.com/watch?v=5RIOQuHOihY",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -658,11 +658,6 @@
|
||||
"title": "Angular Official Docs - Event Binding",
|
||||
"url": "https://angular.dev/guide/templates/event-binding",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Event Binding in Angular",
|
||||
"url": "https://www.knowledgehut.com/blog/web-development/event-binding-in-angular",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -2780,23 +2780,8 @@
|
||||
"description": "API authentication is the process of verifying the identity of clients attempting to access an API, ensuring that only authorized users or applications can interact with the API's resources. Common methods include API keys, OAuth 2.0, JSON Web Tokens (JWT), basic authentication, and OpenID Connect. These techniques vary in complexity and security level, from simple token-based approaches to more sophisticated protocols that handle both authentication and authorization. API authentication protects sensitive data, prevents unauthorized access, enables usage tracking, and can provide granular control over resource access. The choice of authentication method depends on factors such as security requirements, types of clients, ease of implementation, and scalability needs. Implementing robust API authentication is crucial for maintaining the integrity, security, and controlled usage of web services and applications in modern, interconnected software ecosystems.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "SSO - Single Sign On",
|
||||
"url": "https://roadmap.sh/guides/sso",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "OAuth - Open Authorization",
|
||||
"url": "https://roadmap.sh/guides/oauth",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "JWT Authentication",
|
||||
"url": "https://roadmap.sh/guides/jwt-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Token Based Authentication",
|
||||
"url": "https://roadmap.sh/guides/token-authentication",
|
||||
"title": "Basic Authentication",
|
||||
"url": "https://roadmap.sh/guides/basic-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -2805,8 +2790,23 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Basic Authentication",
|
||||
"url": "https://roadmap.sh/guides/basic-authentication",
|
||||
"title": "Token Based Authentication",
|
||||
"url": "https://roadmap.sh/guides/token-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "JWT Authentication",
|
||||
"url": "https://roadmap.sh/guides/jwt-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "OAuth - Open Authorization",
|
||||
"url": "https://roadmap.sh/guides/oauth",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "SSO - Single Sign On",
|
||||
"url": "https://roadmap.sh/guides/sso",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -328,6 +328,11 @@
|
||||
"title": "What is a Crypto Wallet? A Beginner’s Guide",
|
||||
"url": "https://crypto.com/university/crypto-wallets",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Choose your wallet - Ethereum",
|
||||
"url": "https://ethereum.org/en/wallets/find-wallet/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -982,6 +987,11 @@
|
||||
"url": "https://chain.link/education/smart-contracts",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Introduction to smart contracts - Ethereum",
|
||||
"url": "https://ethereum.org/en/smart-contracts/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Smart Contracts",
|
||||
"url": "https://app.daily.dev/tags/smart-contracts?ref=roadmapsh",
|
||||
@@ -1238,6 +1248,11 @@
|
||||
"url": "https://www.coinbase.com/learn/crypto-basics/what-is-a-crypto-wallet",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Choose your wallet - Ethereum",
|
||||
"url": "https://ethereum.org/en/wallets/find-wallet/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Crypto",
|
||||
"url": "https://app.daily.dev/tags/crypto?ref=roadmapsh",
|
||||
@@ -1799,6 +1814,16 @@
|
||||
"title": "What Is a Dapp? Decentralized Apps Explained",
|
||||
"url": "https://www.coindesk.com/learn/what-is-a-dapp-decentralized-apps-explained/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore Top dApps on Ethereum and its Layer 2s",
|
||||
"url": "https://www.ethereum-ecosystem.com/apps",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore Top Ethereum dApps",
|
||||
"url": "https://eth.blockscout.com/apps",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2355,7 +2380,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "dApp Security:All You Need to Know",
|
||||
"title": "dApp Security: All You Need to Know",
|
||||
"url": "https://www.immunebytes.com/blog/dapp-security/#Benefits_of_DApps_Security",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2572,6 +2597,11 @@
|
||||
"url": "https://ethereum.org/en/developers/docs/scaling/zk-rollups",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What are Zero-Knowledge proofs? - Ethereum",
|
||||
"url": "https://ethereum.org/en/zero-knowledge-proofs/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Why and How zk-SNARK Works",
|
||||
"url": "https://medium.com/@imolfar/why-and-how-zk-snark-works-1-introduction-the-medium-of-a-proof-d946e931160",
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
"title": "Pick a Language",
|
||||
"description": "You need to pick a programming language to learn the Computer Science concepts. My personal recommendation would be to pick C++ or C and the reason for that is:\n\n* They allow you to deal with pointers and memory allocation/deallocation, so you feel the data structures and algorithms in your bones. In higher level languages like Python or Java, these are hidden from you. In day to day work, that's terrific, but when you're learning how these low-level data structures are built, it's great to feel close to the metal.\n* You will be able to find a lot of resources for the topics listed in this roadmap using C or C++. You can find a lot of resources for Python and Java, but they are not as abundant as C++ and C.\n\nGiven below is the list of resources; pick ones relevant to the language of your choice.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated C++ Roadmap",
|
||||
"url": "https://roadmap.sh/cpp",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Learn C++ - W3Schools",
|
||||
"url": "https://www.w3schools.com/cpp/",
|
||||
@@ -126,7 +131,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "W3Schools Go Tutorial ",
|
||||
"title": "W3Schools Go Tutorial",
|
||||
"url": "https://www.w3schools.com/go/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -219,7 +224,7 @@
|
||||
"description": "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.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "C++ Roadmap",
|
||||
"title": "Visit Dedicated C++ Roadmap",
|
||||
"url": "https://roadmap.sh/cpp",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -301,7 +306,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Java Website",
|
||||
"title": "Java",
|
||||
"url": "https://www.java.com/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -455,6 +460,11 @@
|
||||
"title": "Hash Table",
|
||||
"description": "Hash Table, Map, HashMap, Dictionary or Associative are all the names of the same data structure. It is one of the most commonly used data structures.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Hash Tables - Princeton University",
|
||||
"url": "https://algs4.cs.princeton.edu/34hash/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Hash Table | Illustrated Data Structures",
|
||||
"url": "https://www.youtube.com/watch?v=jalSiaIi8j4",
|
||||
@@ -470,26 +480,6 @@
|
||||
"url": "https://www.youtube.com/watch?v=0M_kIqhwbFo&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=9",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Table Doubling, Karp-Rabin",
|
||||
"url": "https://www.youtube.com/watch?v=BRO7mVIFt08&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=10",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Open Addressing, Cryptographic Hashing",
|
||||
"url": "https://www.youtube.com/watch?v=rvdJDijO2Ro&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=11",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "PyCon 2010: The Mighty Dictionary",
|
||||
"url": "https://www.youtube.com/watch?v=C4Kc8xzcA68",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "PyCon 2017: The Dictionary Even Mightier",
|
||||
"url": "https://www.youtube.com/watch?v=66P5FMkWoVU",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "(Advanced) Randomization: Universal & Perfect Hashing",
|
||||
"url": "https://www.youtube.com/watch?v=z0lJ2k0sl1g&list=PLUl4u3cNGP6317WaSNfmCvGym2ucw3oGp&index=11",
|
||||
@@ -512,9 +502,9 @@
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Dynamic Arrays - Coursera",
|
||||
"url": "https://www.coursera.org/lecture/data-structures/dynamic-arrays-EwbnV",
|
||||
"type": "course"
|
||||
"title": "What is Array in Data Structure? Types & Syntax",
|
||||
"url": "https://www.simplilearn.com/tutorials/data-structure-tutorial/arrays-in-data-structure",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Array Data Structure | Illustrated Data Structures",
|
||||
@@ -522,8 +512,8 @@
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "UC Berkeley CS61B - Linear and Multi-Dim Arrays (Start watching from 15m 32s)",
|
||||
"url": "https://archive.org/details/ucberkeley_webcast_Wp8oiO_CZZE",
|
||||
"title": "Jagged Arrays",
|
||||
"url": "https://www.youtube.com/watch?v=1jtrQqYpt7g",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
@@ -537,8 +527,8 @@
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Jagged Arrays",
|
||||
"url": "https://www.youtube.com/watch?v=1jtrQqYpt7g",
|
||||
"title": "UC Berkeley CS61B - Linear and Multi-Dim Arrays (Start watching from 15m 32s)",
|
||||
"url": "https://archive.org/details/ucberkeley_webcast_Wp8oiO_CZZE",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
@@ -547,6 +537,11 @@
|
||||
"title": "Tree",
|
||||
"description": "A tree is non-linear and a hierarchical data structure consisting of a collection of nodes such that each node of the tree stores a value and a list of references to other nodes (the “children”).\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Tree Data Structure",
|
||||
"url": "https://www.programiz.com/dsa/trees",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Tree | Illustrated Data Structures",
|
||||
"url": "https://www.youtube.com/watch?v=S2W3SXGPVyU",
|
||||
@@ -674,8 +669,13 @@
|
||||
"description": "An unbalanced binary tree is one that is not balanced.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Balanced Binary Tree",
|
||||
"url": "https://www.programiz.com/dsa/balanced-binary-tree",
|
||||
"title": "Balanced vs Unbalanced Binary Tree",
|
||||
"url": "https://stackoverflow.com/questions/59206128/balanced-vs-unbalanced-binary-tree-clarification-needed",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Unbalanced Binary Tree",
|
||||
"url": "https://eng.libretexts.org/Bookshelves/Computer_Science/Databases_and_Data_Structures/Open_Data_Structures_-_An_Introduction_(Morin)/06%3A_Binary_Trees/6.02%3A_BinarySearchTree_-_An_Unbalanced_Binary_Search_Treee",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
@@ -815,6 +815,11 @@
|
||||
"url": "https://www.coursera.org/lecture/data-structures/introduction-2OpTs",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Heap Data Structure",
|
||||
"url": "https://www.programiz.com/dsa/heap-data-structure",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "CS 61B Lecture 24: Priority Queues",
|
||||
"url": "https://archive.org/details/ucberkeley_webcast_yIUFT6AKBGE",
|
||||
@@ -878,7 +883,12 @@
|
||||
"description": "The Big O notation can be used to describe how the running time of an algorithm scales with the growth of the input size, ignoring implementation details such as programming language and computer speed. Specifically, it denotes the upper bound of the growth rate of a function that relates the running time of an algorithm to its input size. It can be used to compare algorithms and determine which one is better.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "moviesCS 61B Lecture 19: Asymptotic Analysis",
|
||||
"title": "Big-O Notation: A Simple Explanation with Examples",
|
||||
"url": "https://medium.com/better-programming/big-o-notation-a-simple-explanation-with-examples-a56347d1daca",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "CS 61B Lecture 19: Asymptotic Analysis",
|
||||
"url": "https://archive.org/details/ucberkeley_webcast_VIS4YDpuP98",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -982,6 +992,11 @@
|
||||
"title": "Linear",
|
||||
"description": "Linear algorithms are algorithms that have a runtime that is directly proportional to the size of the input. This means that the runtime of the algorithm will increase linearly with the size of the input. For example, if the input size is 10, the runtime will be 10 times the runtime of the algorithm when the input size is 1. If the input size is 100, the runtime will be 100 times the runtime of the algorithm when the input size is 1.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Linear Search Algorithm",
|
||||
"url": "https://www.geeksforgeeks.org/linear-search/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Big O Notation — Calculating Time Complexity",
|
||||
"url": "https://www.youtube.com/watch?v=Z0bH0cMY0E8",
|
||||
@@ -1028,8 +1043,19 @@
|
||||
},
|
||||
"m0umGQNdvg95UiNpQZsQN": {
|
||||
"title": "Factorial",
|
||||
"description": "Factorial complexity algorithms have a runtime of `O(n!)`. This is the worst case scenario for an algorithm. Factorial complexity algorithms are very inefficient and should be avoided.\n\n def generate_permutations(s):\n # Base case: If the string length is 1, return a list containing the string\n if len(s) == 1:\n return [s]\n \n # Initialize the result list\n permutations = []\n \n # Recursively generate all permutations\n for i in range(len(s)):\n # Current character\n current_char = s[i]\n # Remaining characters\n remaining_chars = s[:i] + s[i + 1 :]\n # Generate all permutations of the remaining characters\n for perm in generate_permutations(remaining_chars):\n # Add the current character to the front of each generated permutation\n permutations.append(current_char + perm)\n \n return permutations",
|
||||
"links": []
|
||||
"description": "Factorial complexity algorithms have a runtime of `O(n!)`. This is the worst case scenario for an algorithm. Factorial complexity algorithms are very inefficient and should be avoided.\n\n def generate_permutations(s):\n # Base case: If the string length is 1, return a list containing the string\n if len(s) == 1:\n return [s]\n \n # Initialize the result list\n permutations = []\n \n # Recursively generate all permutations\n for i in range(len(s)):\n # Current character\n current_char = s[i]\n # Remaining characters\n remaining_chars = s[:i] + s[i + 1 :]\n # Generate all permutations of the remaining characters\n for perm in generate_permutations(remaining_chars):\n # Add the current character to the front of each generated permutation\n permutations.append(current_char + perm)\n \n return permutations\n \n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Big O Cheat Sheet - Time Complexity Chart",
|
||||
"url": "https://www.freecodecamp.org/news/big-o-cheat-sheet-time-complexity-chart/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Factorial Explained",
|
||||
"url": "https://www.youtube.com/watch?v=pxh__ugRKz8",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"7a6-AnBI-3tAU1dkOvPkx": {
|
||||
"title": "Common Algorithms",
|
||||
@@ -1203,6 +1229,11 @@
|
||||
"url": "https://www.coursera.org/lecture/algorithms-part1/selection-UQxFT",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Selection Sort",
|
||||
"url": "https://en.wikipedia.org/wiki/Selection_sort",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Selection Sort in 3 Minutes",
|
||||
"url": "https://www.youtube.com/watch?v=g-PGLbMth_g",
|
||||
@@ -1215,7 +1246,7 @@
|
||||
"description": "Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time by comparisons. It is much less efficient on large lists than more advanced algorithms such as quicksort, heapsort, or merge sort.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Insertion Sort Algorithm",
|
||||
"title": "Insertion Sort",
|
||||
"url": "https://www.programiz.com/dsa/insertion-sort",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1324,6 +1355,11 @@
|
||||
"title": "In-Order Traversal",
|
||||
"description": "In-order traversal is a tree traversal algorithm that visits the left subtree, the root, and then the right subtree. This is the most common way to traverse a binary search tree. It is also used to create a sorted list of nodes in a binary search tree.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Tree Traversal Techniques",
|
||||
"url": "https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Tree | Illustrated Data Structures",
|
||||
"url": "https://www.youtube.com/watch?v=S2W3SXGPVyU",
|
||||
@@ -1335,6 +1371,11 @@
|
||||
"title": "Post Order Traversal",
|
||||
"description": "Post-order traversal is a type of tree traversal that visits the left subtree, then the right subtree, and finally the root node. This is the opposite of pre-order traversal, which visits the root node first, then the left subtree, and finally the right subtree.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Postorder Traversal of Binary Tree",
|
||||
"url": "https://www.geeksforgeeks.org/postorder-traversal-of-binary-tree/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Tree | Illustrated Data Structures",
|
||||
"url": "https://www.youtube.com/watch?v=S2W3SXGPVyU",
|
||||
@@ -1552,7 +1593,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Knights Tour Proble",
|
||||
"title": "Knights Tour Problem",
|
||||
"url": "https://www.codesdope.com/course/algorithms-knights-tour-problem/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1708,6 +1749,11 @@
|
||||
"title": "LFU Cache",
|
||||
"description": "LFU Cache is a data structure that stores key-value pairs. It has a fixed size and when it is full, it removes the least frequently used key-value pair. It is a variation of the LRU Cache and is used in many applications such as caching web pages, caching database queries, and caching images.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Least Frequently Used (LFU) Cache Implementation",
|
||||
"url": "https://www.geeksforgeeks.org/least-frequently-used-lfu-cache-implementation/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "1117. Data Structure - LFU Cache",
|
||||
"url": "https://jojozhuang.github.io/algorithm/data-structure-lfu-cache/",
|
||||
@@ -1720,7 +1766,7 @@
|
||||
"description": "String search and manipulation is a very important topic in computer science. It is used in many different applications, such as searching or replacing a specific pattern, word or character in a string.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "String-searching algorithm",
|
||||
"title": "String-searching Algorithm",
|
||||
"url": "https://en.wikipedia.org/wiki/String-searching_algorithm",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -1734,6 +1780,11 @@
|
||||
"title": "Search Pattern in Text",
|
||||
"url": "https://www.coursera.org/learn/data-structures/lecture/tAfHI/search-pattern-in-text",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Pattern Searching",
|
||||
"url": "https://www.geeksforgeeks.org/pattern-searching/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1747,7 +1798,12 @@
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Suffix array introduction",
|
||||
"title": "Suffix Arrays - Princeton University",
|
||||
"url": "https://algs4.cs.princeton.edu/63suffix/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Suffix Array Introduction",
|
||||
"url": "https://www.youtube.com/watch?v=zqKlL3ZpTqs",
|
||||
"type": "video"
|
||||
},
|
||||
@@ -1757,7 +1813,7 @@
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Suffix arrays: building",
|
||||
"title": "Suffix Arrays: building",
|
||||
"url": "https://www.youtube.com/watch?v=ZWlbhBjjwyA",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -1773,7 +1829,7 @@
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "A beginner guide to Brute Force Algorithm for substring search",
|
||||
"title": "A Beginner Guide to Brute Force Algorithm for Substring Search",
|
||||
"url": "https://nulpointerexception.com/2019/02/10/a-beginner-guide-to-brute-force-algorithm-for-substring-search/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1808,6 +1864,11 @@
|
||||
"title": "Boyer Moore Algorithm",
|
||||
"url": "https://www.coursera.org/learn/algorithms-part2/lecture/CYxOT/boyer-moore",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Boyer-Moore String-search Algorithm",
|
||||
"url": "https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1921,7 +1982,7 @@
|
||||
"description": "Little Endian is a way of storing data in memory. It is the opposite of Big Endian. In Little Endian, the least significant byte is stored first. In Big Endian, the most significant byte is stored first.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Big Endian vs Little Endian.mp4",
|
||||
"title": "Big Endian vs Little Endian",
|
||||
"url": "https://www.youtube.com/watch?v=JrNF0KRAlyo",
|
||||
"type": "video"
|
||||
},
|
||||
@@ -1936,6 +1997,11 @@
|
||||
"title": "Common UML Diagrams",
|
||||
"description": "UML is a standard way of visualizing a software system. It is a general-purpose, developmental, modeling language in the field of software engineering that is intended to provide a standard way to visualize the design of a system.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Unified Modeling Language (UML) Description",
|
||||
"url": "https://www.uml-diagrams.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "UML Diagrams Full Course (Unified Modeling Language)",
|
||||
"url": "https://www.youtube.com/watch?v=WnMQ8HlmeXc",
|
||||
@@ -2091,7 +2157,7 @@
|
||||
"type": "opensource"
|
||||
},
|
||||
{
|
||||
"title": "Design Patterns - Wikipedia",
|
||||
"title": "Design Patterns",
|
||||
"url": "https://en.wikipedia.org/wiki/Software_design_pattern",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2314,7 +2380,7 @@
|
||||
"description": "Combinatorics is the study of counting. It is a branch of mathematics that is used to solve problems in a variety of fields, including computer science, statistics, and physics. In computer science, combinatorics is used to solve problems related to counting the number of possible outcomes of a given problem. For example, if you are given a set of 10 objects, how many different ways can you arrange them? Or, if you are given a set of 10 objects, how many different ways can you choose 3 objects from that set? These are examples of combinatorial problems.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Probability and Combinatorics Topic",
|
||||
"title": "Probability and Combinatorics",
|
||||
"url": "https://www.khanacademy.org/math/probability/probability-and-combinatorics-topic",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2368,21 +2434,6 @@
|
||||
"title": "Greedy Algs. II & Intro to NP Completeness",
|
||||
"url": "https://youtu.be/qcGnJ47Smlo?list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&t=2939",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "NP Completeness II & Reductions",
|
||||
"url": "https://www.youtube.com/watch?v=e0tGC6ZQdQE&index=16&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "NP Completeness III",
|
||||
"url": "https://www.youtube.com/watch?v=fCX1BGT3wjE&index=17&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "NP Completeness IV",
|
||||
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2419,21 +2470,6 @@
|
||||
"title": "Greedy Algs. II & Intro to NP Completeness",
|
||||
"url": "https://youtu.be/qcGnJ47Smlo?list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&t=2939",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "NP Completeness II & Reductions",
|
||||
"url": "https://www.youtube.com/watch?v=e0tGC6ZQdQE&index=16&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "NP Completeness III",
|
||||
"url": "https://www.youtube.com/watch?v=fCX1BGT3wjE&index=17&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "NP Completeness IV",
|
||||
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2558,21 +2594,6 @@
|
||||
"url": "https://www.youtube.com/watch?v=YX40hbAHx3s",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Complexity: Approximation Algorithms",
|
||||
"url": "https://www.youtube.com/watch?v=MEz1J9wY2iM&list=PLUl4u3cNGP6317WaSNfmCvGym2ucw3oGp&index=24",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Complexity: Fixed-Parameter Algorithms",
|
||||
"url": "https://www.youtube.com/watch?v=4q-jmGrmxKs&index=25&list=PLUl4u3cNGP6317WaSNfmCvGym2ucw3oGp",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Lecture 23: Computational Complexity",
|
||||
"url": "https://www.youtube.com/watch?v=moPtwq_cVH8&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=24",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Greedy Algs. II & Intro to NP Completeness",
|
||||
"url": "https://youtu.be/qcGnJ47Smlo?list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&t=2939",
|
||||
@@ -2610,6 +2631,11 @@
|
||||
"title": "Travelling Salesman Problem",
|
||||
"description": "The Travelling Salesman Problem (TSP) is a classic problem in computer science. It is a problem that is NP-complete, which means that it is a problem that is hard to solve. It is also a problem that is used to test the efficiency of algorithms.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Traveling Salesman Problem",
|
||||
"url": "https://en.wikipedia.org/wiki/Travelling_salesman_problem",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is the Traveling Salesman Problem?",
|
||||
"url": "https://www.youtube.com/watch?v=1pmBjIZ20pE",
|
||||
@@ -2715,7 +2741,7 @@
|
||||
"description": "Balanced search trees are a type of data structure that allow for fast insertion, deletion, and lookup of data. They are a type of self-balancing binary search tree, which means that they are a binary tree that maintains the binary search tree property while also keeping the tree balanced. This means that the tree is always approximately balanced, which allows for fast insertion, deletion, and lookup of data.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Self-balancing binary search tree - Wikipedia",
|
||||
"title": "Self-balancing Binary Search Tree - Wikipedia",
|
||||
"url": "https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2750,6 +2776,11 @@
|
||||
"url": "https://www.coursera.org/learn/data-structures/lecture/22BgE/split-and-merge",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "AVL Tree - Programiz",
|
||||
"url": "https://www.programiz.com/dsa/avl-tree",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "MIT AVL Trees / AVL Sort",
|
||||
"url": "https://www.youtube.com/watch?v=FNeL18KsWPc&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=6",
|
||||
@@ -2871,7 +2902,7 @@
|
||||
"type": "opensource"
|
||||
},
|
||||
{
|
||||
"title": "System Design: The complete course",
|
||||
"title": "System Design: The Complete Course",
|
||||
"url": "https://dev.to/karanpratapsingh/system-design-the-complete-course-10fo",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2891,7 +2922,7 @@
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "System design interview: Scale to 1 million users",
|
||||
"title": "System Design interview: Scale to 1 million users",
|
||||
"url": "https://www.youtube.com/watch?v=YkGHxOg9d3M",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -2922,6 +2953,11 @@
|
||||
"title": "Load Balancing",
|
||||
"description": "Load balancing is the process of distributing network or application traffic across a cluster of servers. Load balancing is used to improve responsiveness and reliability of applications, maximize throughput, minimize response time, and avoid overload of any single server.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is Load Balancing? | How load balancers work",
|
||||
"url": "https://www.cloudflare.com/learning/performance/what-is-load-balancing/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Load Balancers 101",
|
||||
"url": "https://www.youtube.com/watch?v=galcDRNd5Ow",
|
||||
@@ -2987,13 +3023,13 @@
|
||||
"description": "A proxy server is an intermediary piece of hardware/software sitting between the client and the backend server. It receives requests from clients and relays them to the origin servers. Typically, proxies are used to filter requests, log requests, or sometimes transform requests (by adding/removing headers, encrypting/decrypting, or compression).\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Proxy - System Design",
|
||||
"url": "https://dev.to/karanpratapsingh/system-design-the-complete-course-10fo#proxy",
|
||||
"title": "Proxy Servers",
|
||||
"url": "https://roadmap.sh/guides/proxy-servers",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Proxy Servers",
|
||||
"url": "https://roadmap.sh/guides/proxy-servers",
|
||||
"title": "Proxy - System Design",
|
||||
"url": "https://dev.to/karanpratapsingh/system-design-the-complete-course-10fo#proxy",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
@@ -3003,7 +3039,7 @@
|
||||
"description": "The CAP theorem states that it is impossible for a distributed data store to simultaneously provide more than two out of Consistency, Availability and Partition Tolerance.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "CAP Theorem - Wikipedia",
|
||||
"title": "CAP Theorem",
|
||||
"url": "https://en.wikipedia.org/wiki/CAP_theorem",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3040,7 +3076,7 @@
|
||||
"description": "Architectural patterns are the fundamental organization of a system, defining how the system is composed and how its components interact. Architectural patterns are identified by their name, like client-server, peer-to-peer, and layered.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "List of software architecture styles and patterns",
|
||||
"title": "List of Software Architecture Styles and Patterns",
|
||||
"url": "https://en.wikipedia.org/wiki/List_of_software_architecture_styles_and_patterns",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -3081,6 +3117,16 @@
|
||||
"url": "https://roadmap.sh/graphql",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "GraphQL",
|
||||
"url": "https://graphql.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "GraphQL Documentation",
|
||||
"url": "https://graphql.org/learn/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Apollo GraphQL Tutorials",
|
||||
"url": "https://www.apollographql.com/tutorials/",
|
||||
@@ -3381,8 +3427,19 @@
|
||||
},
|
||||
"q3nRhTYS5wg9tYnQe2sCF": {
|
||||
"title": "BASE",
|
||||
"description": "The rise in popularity of NoSQL databases provided a flexible and fluidity with ease to manipulate data and as a result, a new database model was designed, reflecting these properties. The acronym BASE is slightly more confusing than ACID but however, the words behind it suggest ways in which the BASE model is different and acronym BASE stands for:-\n\n* **B**asically **A**vailable\n* **S**oft state\n* **E**ventual consistency",
|
||||
"links": []
|
||||
"description": "The rise in popularity of NoSQL databases provided a flexible and fluidity with ease to manipulate data and as a result, a new database model was designed, reflecting these properties. The acronym BASE is slightly more confusing than ACID but however, the words behind it suggest ways in which the BASE model is different and acronym BASE stands for:-\n\n* **B**asically **A**vailable\n* **S**oft state\n* **E**ventual consistency\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ACID vs. BASE Database Model",
|
||||
"url": "https://phoenixnap.com/kb/acid-vs-base",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What Is BASE in Database Engineering?",
|
||||
"url": "https://www.lifewire.com/abandoning-acid-in-favor-of-base-1019674",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uqfeiQ9K--QkGNwks4kjk": {
|
||||
"title": "CAP Theorem",
|
||||
@@ -3430,6 +3487,11 @@
|
||||
"title": "Indexes",
|
||||
"description": "An index is a data structure that you build and assign on top of an existing table that basically looks through your table and tries to analyze and summarize so that it can create shortcuts.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Database Indexes Explained",
|
||||
"url": "https://www.essentialsql.com/what-is-a-database-index/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Database Indexing Explained",
|
||||
"url": "https://www.youtube.com/watch?v=-qNSXK7s7_w",
|
||||
@@ -3461,6 +3523,11 @@
|
||||
"title": "What are Transactions?",
|
||||
"url": "https://fauna.com/blog/database-transaction",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Database Transaction",
|
||||
"url": "https://en.wikipedia.org/wiki/Database_transaction",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -3566,7 +3633,7 @@
|
||||
},
|
||||
"pZ5x_zDYGzW9VxYycyXtN": {
|
||||
"title": "OSI Model",
|
||||
"description": "The OSI and TCP/IP model is used to help the developer to design their system for interoperability. The OSI model has 7 layers while the TCP/IP model has a more summarized form of the OSI model only consisting 4 layers. This is important if you're are trying to design a system to communicate with other systems.\n\nVisit the following resources to learn more:",
|
||||
"description": "The OSI and TCP/IP model is used to help the developer to design their system for interoperability. The OSI model has 7 layers while the TCP/IP model has a more summarized form of the OSI model only consisting 4 layers. This is important if you're trying to design a system to communicate with other systems.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Cloudflare - What is the OSI model",
|
||||
@@ -3582,7 +3649,7 @@
|
||||
},
|
||||
"Fed5y1D95WPpqoVg7kmob": {
|
||||
"title": "TCP/IP Model",
|
||||
"description": "The OSI and TCP/IP model is used to help the developer to design their system for interoperability. The OSI model has 7 layers while the TCP/IP model has a more summarized form of the OSI model only consisting 4 layers. This is important if you're are trying to design a system to communicate with other systems.\n\nVisit the following resources to learn more:",
|
||||
"description": "The OSI and TCP/IP model is used to help the developer to design their system for interoperability. The OSI model has 7 layers while the TCP/IP model has a more summarized form of the OSI model only consisting 4 layers. This is important if you're trying to design a system to communicate with other systems.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Cloudflare - What is the OSI model",
|
||||
@@ -3672,6 +3739,16 @@
|
||||
"title": "TLS & HTTPS",
|
||||
"description": "TLS (Transport Layer Security) is a cryptographic protocol that provides privacy and data integrity between two communicating applications. It is widely used to secure HTTP, although it can be used with any protocol. TLS is often used in combination with HTTPS, which is HTTP over TLS.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is TLS & How Does it Work? - Internet Society",
|
||||
"url": "https://www.internetsociety.org/deploy360/tls/basics/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is TLS (Transport Layer Security)? - Cloudflare",
|
||||
"url": "https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "SSL and HTTPS",
|
||||
"url": "https://www.youtube.com/watch?v=S2iBR2ZlZf0",
|
||||
@@ -3792,6 +3869,11 @@
|
||||
"title": "Hashing Algorithms",
|
||||
"description": "Hashing algorithms are used to generate a unique value for a given input. This value is called a hash. Hashing algorithms are used to verify the integrity of data, to store passwords, and to generate unique identifiers for data.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Hashing Algorithm Overview:",
|
||||
"url": "https://www.okta.com/identity-101/hashing-algorithms/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Algorithms",
|
||||
"url": "https://app.daily.dev/tags/algorithms?ref=roadmapsh",
|
||||
@@ -3850,12 +3932,12 @@
|
||||
"description": "Computers are everywhere. They are in our phones, our cars, our homes, and even in our pockets. But how do they actually work? How do they take in information, and how do they output information?\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How CPU executes a program",
|
||||
"title": "How CPU Executes A Program",
|
||||
"url": "https://www.youtube.com/watch?v=XM4lGflQFvA",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "How computers calculate - ALU",
|
||||
"title": "How Computers Calculate - ALU",
|
||||
"url": "https://youtu.be/1I5ZMmrOfnA",
|
||||
"type": "video"
|
||||
},
|
||||
@@ -3897,7 +3979,12 @@
|
||||
"description": "Computers calculate using the binary system, where all data is represented as 0s and 1s. These binary states correspond to the ON/OFF positions of transistors, which are the building blocks of logic gates (AND, OR, NOT). Numbers, characters, and instructions are broken into binary sequences (bits), and grouped into bytes (8 bits). Arithmetic operations like addition are performed through logic gates, which combine binary values. The CPU executes these calculations by following a fetch-decode-execute cycle. Complex calculations, such as handling decimals, use floating-point representation. Programs written in high-level languages are compiled into machine code for the CPU to execute.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How computers calculate - ALU",
|
||||
"title": "How Does A Computer Calculate Numbers?",
|
||||
"url": "https://www.sciencing.com/computer-calculate-numbers-4705975/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "How Computers Calculate - ALU",
|
||||
"url": "https://youtu.be/1I5ZMmrOfnA",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -3905,8 +3992,13 @@
|
||||
},
|
||||
"U3379F4AO1KSmGtVmPr27": {
|
||||
"title": "Registers and RAM",
|
||||
"description": "**_Registers_** are the smallest data-holding elements built into the processor itself. Registers are the memory locations that are directly accessible by the processor. The registers hold the instruction or operands currently accessed by the CPU.\n\nRegisters are the high-speed accessible storage elements. The processor accesses the registers within one CPU clock cycle. The processor can decode the instructions and perform operations on the register contents at more than one operation per CPU clock cycle.\n\n**_Memory_** is a hardware device that stores computer programs, instructions, and data. The memory that is internal to the processor is primary memory (RAM), and the memory that is external to the processor is secondary (**Hard Drive**). Primary memory or RAM is a volatile memory, meaning the primary memory data exist when the system's power is on, and the data vanishes as the system is switched off. The primary memory contains the data required by the currently executing program in the CPU. If the data required by the processor is not in primary memory, then the data is transferred from secondary storage to primary memory, and then it is fetched by the processor.\n\nVisit the following resources to learn more:",
|
||||
"description": "**_Registers_** are the smallest data-holding elements built into the processor itself. Registers are the memory locations that are directly accessible by the processor. The registers hold the instruction or operands currently accessed by the CPU.\n\nRegisters are the high-speed accessible storage elements. The processor accesses the registers within one CPU clock cycle. The processor can decode the instructions and perform operations on the register contents at more than one operation per CPU clock cycle.\n\n**_Memory_** is a hardware device that stores computer programs, instructions, and data. The memory that is internal to the processor is primary memory (RAM), and the memory that is external to the processor is secondary (**Hard Drive**).\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "RAM vs. Registers - What's the Difference?",
|
||||
"url": "https://thisvsthat.io/ram-vs-registers",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Registers and RAM",
|
||||
"url": "https://youtu.be/fpnE6UAfbtU",
|
||||
@@ -3981,6 +4073,11 @@
|
||||
"title": "Process Forking",
|
||||
"description": "Process forking is a way to create a new process from an existing process. The new process is a copy of the existing process. The new process is called a child process and the existing process is called a parent process.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Fork System Call in Operating System",
|
||||
"url": "https://www.geeksforgeeks.org/fork-system-call-in-operating-system/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Understanding fork() system call for new process creation",
|
||||
"url": "https://www.youtube.com/watch?v=PwxTbksJ2fo",
|
||||
@@ -4053,13 +4150,24 @@
|
||||
},
|
||||
"Ge2nagN86ofa2y-yYR1lv": {
|
||||
"title": "Scheduling Algorithms",
|
||||
"description": "CPU Scheduling is the process of selecting a process from the ready queue and allocating the CPU to it. The selection of a process is based on a particular scheduling algorithm. The scheduling algorithm is chosen depending on the type of system and the requirements of the processes.\n\nHere is the list of some of the most commonly used scheduling algorithms:\n\n* **First Come First Serve (FCFS):** The process that arrives first is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Job First (SJF):** The process with the smallest execution time is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Remaining Time First (SRTF):** The process with the smallest remaining execution time is allocated the CPU first. It is a preemptive algorithm.\n* **Round Robin (RR):** The process is allocated the CPU for a fixed time slice. The time slice is usually 10 milliseconds. It is a preemptive algorithm.\n* **Priority Scheduling:** The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Feedback Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. If a process is preempted, it is moved to the next queue. It is a preemptive algorithm.\n* **Highest Response Ratio Next(HRRN):** CPU is allotted to the next process which has the highest response ratio and not to the process having less burst time. It is a Non-Preemptive algorithm.\n* **Lottery Scheduling:** The process is allocated the CPU based on a lottery system. It is a preemptive algorithm.",
|
||||
"links": []
|
||||
"description": "CPU Scheduling is the process of selecting a process from the ready queue and allocating the CPU to it. The selection of a process is based on a particular scheduling algorithm. The scheduling algorithm is chosen depending on the type of system and the requirements of the processes.\n\nHere is the list of some of the most commonly used scheduling algorithms:\n\n* **First Come First Serve (FCFS):** The process that arrives first is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Job First (SJF):** The process with the smallest execution time is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Remaining Time First (SRTF):** The process with the smallest remaining execution time is allocated the CPU first. It is a preemptive algorithm.\n* **Round Robin (RR):** The process is allocated the CPU for a fixed time slice. The time slice is usually 10 milliseconds. It is a preemptive algorithm.\n* **Priority Scheduling:** The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Feedback Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. If a process is preempted, it is moved to the next queue. It is a preemptive algorithm.\n* **Highest Response Ratio Next(HRRN):** CPU is allotted to the next process which has the highest response ratio and not to the process having less burst time. It is a Non-Preemptive algorithm.\n* **Lottery Scheduling:** The process is allocated the CPU based on a lottery system. It is a preemptive algorithm.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "CPU Scheduling in Operating System",
|
||||
"url": "https://www.scaler.com/topics/operating-system/cpu-scheduling/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cpQvB0qMDL3-NWret7oeA": {
|
||||
"title": "CPU Interrupts",
|
||||
"description": "CPU Interrupts are a way for the CPU to communicate with the rest of the computer. They are a way for the CPU to tell the rest of the computer that it needs to do something. For example, if the CPU is running a program and it needs to read from the keyboard, it will send an interrupt to the keyboard to tell it to send the data to the CPU. The CPU will then wait for the keyboard to send the data and then continue running the program.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "System Interrupts 100% CPU",
|
||||
"url": "https://www.wikihow.com/System-Interrupts-100-Cpu",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Computing",
|
||||
"url": "https://app.daily.dev/tags/computing?ref=roadmapsh",
|
||||
@@ -4109,7 +4217,7 @@
|
||||
"description": "Skip lists are a data structure that allows you to perform operations on a sorted list in O(log n) time. Skip lists are a probabilistic data structure, which means that the probability of a certain operation taking a certain amount of time is a certain value. In the case of skip lists, the probability of an operation taking O(log n) time is 1.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Skip Lists - Wikipedia",
|
||||
"title": "Skip Lists",
|
||||
"url": "https://en.wikipedia.org/wiki/Skip_list",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -4153,21 +4261,6 @@
|
||||
"title": "Greedy Algs. II & Intro to NP Completeness",
|
||||
"url": "https://youtu.be/qcGnJ47Smlo?list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&t=2939",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "NP Completeness II & Reductions",
|
||||
"url": "https://www.youtube.com/watch?v=e0tGC6ZQdQE&index=16&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "NP Completeness III",
|
||||
"url": "https://www.youtube.com/watch?v=fCX1BGT3wjE&index=17&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "NP Completeness IV",
|
||||
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
},
|
||||
"f1djN0GxoeVPr_0cl6vMq": {
|
||||
"title": "Static Typing",
|
||||
"description": "In C++, static typing means that the data type of a variable is determined at compile time, before the program is executed. This means that a variable can only be used with data of a specific type, and the compiler ensures that the operations performed with the variable are compatible with its type. If there is a mismatch , the compiler will adjust the data type of variable to match another provided it's feasible . This process is known as `Type Conversion`. If compiler not able to achieve type conversion , `Invalid Type Conversion` error will be raised during compilation of the code .\n\nC++ is a statically typed language, which means that it uses static typing to determine data types and perform type checking during compile time. This helps with ensuring type safety and can prevent certain types of errors from occurring during the execution of the program.\n\nHere's a simple code example to demonstrate static typing in C++:\n\n #include <iostream>\n \n int main() {\n int num = 65; // 'num' is statically typed as an integer\n double pi = 3.14159; // 'pi' is statically typed as a double\n char c = 'c'; // 'c' is statically typed as a char\n \n c = num; // This asssigment would convert num's value to ASCII equivalent character\n num = pi; // This assignment would convert pi's value from double type to int type\n \n std::cout << \"The value of num is: \" << num << '\\n';\n std::cout << \"The value of pi is: \" << pi << '\\n';\n std::cout << \"The value of c is: \"<< c << '\\n';\n return 0;\n }\n \n\nIn the code above, the variable `num` is statically typed as an `int`, `pi` is statically typed as a `double`, and `c` is statically typed as a `char`. If you attempt to assign the value of `pi` to `num`, the value `3.14159` will be converted to the integer `3` and assigned to `num`. Similarly, when the value of `num` is assigned to `c`, the compiler will convert the value `65` to its corresponding [ASCII](https://www.ascii-code.com) code, which is `A`.\n\nLearn more from the following resources:",
|
||||
"description": "In C++, static typing means that the data type of a variable is determined at compile time, before the program is executed. This means that a variable can only be used with data of a specific type, and the compiler ensures that the operations performed with the variable are compatible with its type. If there is a mismatch, the compiler will adjust the data type of variable to match another provided it's feasible. This process is known as `Type Conversion`. If the compiler is not able to achieve type conversion, an `Invalid Type Conversion` error will be raised during compilation of the code.\n\nC++ is a statically typed language, which means that it uses static typing to determine data types and perform type checking during compile time. This helps with ensuring type safety and can prevent certain types of errors from occurring during the execution of the program.\n\nHere's a simple code example to demonstrate static typing in C++:\n\n #include <iostream>\n \n int main() {\n int num = 65; // 'num' is statically typed as an integer\n double pi = 3.14159; // 'pi' is statically typed as a double\n char c = 'c'; // 'c' is statically typed as a char\n \n c = num; // This asssigment would convert num's value to ASCII equivalent character\n num = pi; // This assignment would convert pi's value from double type to int type\n \n std::cout << \"The value of num is: \" << num << '\\n';\n std::cout << \"The value of pi is: \" << pi << '\\n';\n std::cout << \"The value of c is: \"<< c << '\\n';\n return 0;\n }\n \n\nIn the code above, the variable `num` is statically typed as an `int`, `pi` is statically typed as a `double`, and `c` is statically typed as a `char`. If you attempt to assign the value of `pi` to `num`, the value `3.14159` will be converted to the integer `3` and assigned to `num`. Similarly, when the value of `num` is assigned to `c`, the compiler will convert the value `65` to its corresponding [ASCII](https://www.ascii-code.com) code, which is `A`.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Type-Coversion",
|
||||
@@ -281,8 +281,24 @@
|
||||
},
|
||||
"k9c5seRkhgm_yHPpiz2X0": {
|
||||
"title": "unique_ptr",
|
||||
"description": "",
|
||||
"links": []
|
||||
"description": "One of C++'s main features includes variants of the normal _raw_ C pointers. One of these is the `unique_ptr`, which is a type of smart pointer that claims exclusive ownership over a value.\n\nThese types of pointers **can be moved** (`std::move`), but not **copied** and are automatically deleted when out of scope. The recommended way to create a `unique_ptr` is using `std::make_unique`.\n\n #include <memory>\n #include <iostream>\n \n int main() {\n std::unique_ptr<int> uptr = std::make_unique<int>(10);\n std::cout << *uptr << std::endl;\n \n std::unique_ptr<int> uptr2 = uptr; // compile error\n std::unique_ptr<int> uptr2 = std::move(uptr); // transferring ownership\n }",
|
||||
"links": [
|
||||
{
|
||||
"title": "std::unique_ptr - Detailed Reference",
|
||||
"url": "https://en.cppreference.com/w/cpp/memory/unique_ptr",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Smart Pointers – unique_ptr",
|
||||
"url": "https://www.learncpp.com/cpp-tutorial/unique-ptr/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "When should you use std::unique_ptr? - StackOverflow Discussion",
|
||||
"url": "https://stackoverflow.com/questions/13782051/when-should-you-use-stdunique-ptr",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uEGEmbxegATIrvGfobJb9": {
|
||||
"title": "Raw Pointers",
|
||||
|
||||
@@ -4,9 +4,19 @@
|
||||
"description": "Fundamental IT skills form the backbone of cybersecurity proficiency and encompass a broad range of technical knowledge. These skills include understanding computer hardware and software, networking concepts, and operating systems (particularly Windows and Linux). Proficiency in at least one programming language, such as Python or JavaScript, is increasingly important for automation and scripting tasks. Database management, including SQL, is crucial for handling and securing data. Knowledge of cloud computing platforms like AWS or Azure is becoming essential as organizations migrate to cloud environments. Familiarity with basic cybersecurity concepts such as encryption, access control, and common attack vectors provides a foundation for more advanced security work. Additionally, troubleshooting skills, the ability to interpret logs, and a basic understanding of web technologies are vital. These fundamental IT skills enable cybersecurity professionals to effectively protect systems, identify vulnerabilities, and respond to incidents in increasingly complex technological landscapes.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Top 10 in demand IT skills",
|
||||
"title": "7 In-Demand IT Skills to Boost Your Resume in 2025",
|
||||
"url": "https://www.coursera.org/articles/key-it-skills-for-your-career",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Top 10 in Demand IT Skills",
|
||||
"url": "https://www.comptia.org/blog/top-it-skills-in-demand",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "IT Skills: Definition and Examples",
|
||||
"url": "https://www.indeed.com/career-advice/finding-a-job/it-skills",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -189,7 +199,7 @@
|
||||
"description": "iCloud is a cloud storage and cloud computing service provided by Apple Inc. It allows users to store data, such as documents, photos, and music, on remote servers and synchronize them across their Apple devices, including iPhones, iPads, and MacBooks.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "iCloud Website",
|
||||
"title": "iCloud",
|
||||
"url": "https://www.icloud.com/",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -200,7 +210,7 @@
|
||||
"description": "Google Workspace, formerly known as G Suite, is a collection of cloud-based productivity and collaboration tools developed by Google. It includes popular applications such as Gmail for email, Google Drive for file storage and sharing, Google Docs for document creation and editing, Google Sheets for spreadsheets, and Google Meet for video conferencing. From a cybersecurity perspective, Google Workspace presents both advantages and challenges. It offers robust built-in security features like two-factor authentication, encryption of data in transit and at rest, and advanced threat protection. However, its cloud-based nature means organizations must carefully manage access controls, data sharing policies, and compliance with various regulations. Security professionals must be vigilant about potential phishing attacks targeting Google accounts, data leakage through improper sharing settings, and the risks associated with third-party app integrations. Understanding how to properly configure and monitor Google Workspace is crucial for maintaining the security of an organization's collaborative environment and protecting sensitive information stored within these widely-used tools.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Google Workspace Website",
|
||||
"title": "Google Workspace",
|
||||
"url": "https://workspace.google.com/intl/en_uk/",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -227,7 +237,7 @@
|
||||
"description": "Hack The Box (HTB) is a popular online platform designed for security enthusiasts, penetration testers, and ethical hackers to develop and enhance their skills by engaging in real-world cybersecurity challenges. The platform provides a wide array of virtual machines (VMs), known as \"boxes,\" each with a unique set of security vulnerabilities to exploit.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Hack The Box Website",
|
||||
"title": "Hack The Box",
|
||||
"url": "https://www.hackthebox.com/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -370,6 +380,11 @@
|
||||
"title": "CompTIA Security+",
|
||||
"description": "CompTIA Security+ is a highly recognized and respected certification for individuals seeking to start their careers in the field of cybersecurity. This certification is vendor-neutral, meaning it doesn't focus on any specific technology or platform, and provides a solid foundation in cybersecurity principles, concepts, and best practices.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "CompTIA SY0-701 Security+ Exam Course Playlist",
|
||||
"url": "https://www.youtube.com/playlist?list=PLG49S3nxzAnl4QDVqK-hOnoqcSKEIDDuv",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "CompTIA Security+ Course",
|
||||
"url": "https://www.youtube.com/watch?v=yLf2jRY39Rc&list=PLIhvC56v63IIyU0aBUed4qwP0nSCORAdB",
|
||||
@@ -435,7 +450,7 @@
|
||||
"description": "The GIAC Security Essentials Certification (GSEC) is an advanced cybersecurity certification that demonstrates an individual's knowledge and skills in addressing security threats and vulnerabilities in various systems. Developed by the Global Information Assurance Certification (GIAC), this certification is suitable for security professionals, IT managers, and network administrators who want to enhance their expertise in the core cybersecurity concepts and practices.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "GSEC Certification Website",
|
||||
"title": "GSEC Certification",
|
||||
"url": "https://www.giac.org/certifications/security-essentials-gsec/",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -446,7 +461,7 @@
|
||||
"description": "The GIAC Penetration Tester (GPEN) certification is an advanced-level credential designed for professionals who want to demonstrate their expertise in the field of penetration testing and ethical hacking. Created by the Global Information Assurance Certification (GIAC) organization, GPEN validates an individual's ability to conduct legal, systematic, and effective penetration tests to assess the security of computer networks, systems, and applications.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "GPEN Certification Website",
|
||||
"title": "GPEN Certification",
|
||||
"url": "https://www.giac.org/certifications/penetration-tester-gpen/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -462,7 +477,7 @@
|
||||
"description": "The GIAC Web Application Penetration Tester (GWAPT) certification validates an individual's ability to perform in-depth web application security assessments and exploit vulnerabilities. GWAPT focuses on using ethical hacking methodologies to conduct web application penetration testing with the goal of identifying, evaluating, and mitigating security risks.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "GWAPT Certification Website",
|
||||
"title": "GWAPT Certification",
|
||||
"url": "https://www.giac.org/certifications/web-application-penetration-tester-gwapt/",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -473,9 +488,14 @@
|
||||
"description": "GIAC is a globally recognized organization that provides certifications for information security professionals. Established in 1999, its primary aim is to validate the knowledge and skills of professionals in various cybersecurity domains. GIAC certifications focus on practical and hands-on abilities to ensure that certified individuals possess the necessary expertise to tackle real-world cybersecurity challenges.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "GIAC Website",
|
||||
"title": "GIAC",
|
||||
"url": "https://www.giac.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Get Certified - GIAC",
|
||||
"url": "https://www.giac.org/get-certified/?msc=main-nav",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -584,7 +604,7 @@
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Linux Roadmap",
|
||||
"title": "Visit Linux Roadmap",
|
||||
"url": "https://roadmap.sh/linux",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -615,8 +635,8 @@
|
||||
"description": "**macOS** is an operating system developed by Apple Inc. for its line of Mac computers. Known for its user-friendly interface and integration with other Apple products, macOS features a Unix-based architecture, offering stability, security, and performance. It includes a suite of built-in applications, such as Safari, Mail, and Finder, and supports a wide range of third-party software. macOS provides seamless integration with services like iCloud, Continuity, and Handoff, enhancing productivity and connectivity across Apple devices. Regular updates and a focus on design and usability make macOS a popular choice for both personal and professional use.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "MacOS Website",
|
||||
"url": "https://www.apple.com/uk/macos/macos-sequoia/",
|
||||
"title": "macOS",
|
||||
"url": "https://www.apple.com/macos/macos-sequoia/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -874,7 +894,7 @@
|
||||
"description": "**Loopback** refers to a special network interface used to send traffic back to the same device for testing and diagnostic purposes. The loopback address for IPv4 is `127.0.0.1`, while for IPv6 it is `::1`. When a device sends a request to the loopback address, the network data does not leave the local machine; instead, it is processed internally, allowing developers to test applications or network services without requiring external network access. Loopback is commonly used to simulate network traffic, check local services, or debug issues locally.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Understanding the loopback address and loopback interfaces",
|
||||
"title": "Understanding the Loopback Address and Loopback Interfaces",
|
||||
"url": "https://study-ccna.com/loopback-interface-loopback-address/",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -917,7 +937,7 @@
|
||||
"description": "A default gateway is a network node, typically a router or a firewall, that serves as the access point or intermediary between a local network and external networks, such as the internet. When a device on a local network needs to communicate with a device outside its own subnet—such as accessing a website or sending an email—it sends the data to the default gateway, which then routes it to the appropriate external destination. The default gateway acts as a traffic director, ensuring that data packets are correctly forwarded between the internal network and external networks, making it a crucial component for enabling communication beyond the local network's boundaries.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a default gateway?",
|
||||
"title": "What is a Default Gateway?",
|
||||
"url": "https://nordvpn.com/blog/what-is-a-default-gateway/?srsltid=AfmBOoosi5g4acnT9Gv_B86FMGr72hWDhk8J-4jr1HvxPCSu96FikCyw",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -949,7 +969,7 @@
|
||||
"description": "A **DMZ**, also known as a **Demilitarized Zone**, is a specific part of a network that functions as a buffer or separation between an organization's internal, trusted network and the external, untrusted networks like the internet. The primary purpose of a DMZ is to isolate critical systems and data from the potentially hostile external environment and provide an extra layer of security.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a DMZ network?",
|
||||
"title": "What is a DMZ Network?",
|
||||
"url": "https://www.fortinet.com/resources/cyberglossary/what-is-dmz",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -964,6 +984,11 @@
|
||||
"title": "ARP",
|
||||
"description": "Address Resolution Protocol (ARP) is a crucial mechanism used in networking that allows the Internet Protocol (IP) to map an IP address to a corresponding physical address, commonly known as a Media Access Control (MAC) address. This protocol is essential for enabling devices within a Local Area Network (LAN) to communicate by translating IP addresses into specific hardware addresses.\n\nWhen one device on a LAN wants to communicate with another, it needs to know the MAC address associated with the target device’s IP address. ARP facilitates this by sending out an ARP request, which broadcasts the target IP to all devices in the network. Each device checks the requested IP against its own. The device that recognizes the IP as its own responds with an ARP reply, which includes its MAC address.\n\nOnce the requesting device receives the MAC address, it updates its ARP cache—a table that stores IP-to-MAC address mappings—allowing it to send data directly to the correct hardware address.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ARP - Wikipedia",
|
||||
"url": "https://en.wikipedia.org/wiki/Address_Resolution_Protocol",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is Address Resolution Protocol?",
|
||||
"url": "https://www.fortinet.com/resources/cyberglossary/what-is-arp",
|
||||
@@ -1055,7 +1080,12 @@
|
||||
"description": "IP, or Internet Protocol, is a fundamental concept in cybersecurity that refers to the way data is transferred across networks, specifically the internet. It is a core component of the internet's architecture and serves as the primary building block for communication between devices connected to the network. An IP address is a unique identifier assigned to each device connected to a network, like a computer or smartphone. It comprises a series of numbers separated by dots (e.g., 192.168.1.1). IP addresses can be either IPv4 (32-bit) or the newer IPv6 (128-bit) format, which provides more available addresses. They allow devices to send and receive data packets to and from other devices on the internet.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is an IP address and what does it mean?",
|
||||
"title": "Check Your IP Address",
|
||||
"url": "https://ipleak.net",
|
||||
"type": "website"
|
||||
},
|
||||
{
|
||||
"title": "What is an IP Address and What does it mean?",
|
||||
"url": "https://www.kaspersky.com/resource-center/definitions/what-is-an-ip-address",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1304,12 +1334,12 @@
|
||||
"description": "Mesh topology is a network architecture where devices or nodes are interconnected with multiple direct, point-to-point links to every other node in the network. This structure allows data to travel from source to destination through multiple paths, enhancing reliability and fault tolerance. In a full mesh topology, every node is connected to every other node, while in a partial mesh, only some nodes have multiple connections. Mesh networks are highly resilient to failures, as traffic can be rerouted if a link goes down. They're commonly used in wireless networks, IoT applications, and critical infrastructure where redundancy and self-healing capabilities are crucial. However, mesh topologies can be complex and expensive to implement, especially in large networks due to the high number of connections required.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is mesh topology?",
|
||||
"title": "What is Mesh Topology?",
|
||||
"url": "https://www.lenovo.com/gb/en/glossary/mesh-topology",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Mesh topology explained",
|
||||
"title": "Mesh Topology explained",
|
||||
"url": "https://www.computerhope.com/jargon/m/mesh.htm",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -1372,6 +1402,11 @@
|
||||
"title": "FTP",
|
||||
"description": "FTP is a standard network protocol used to transfer files from one host to another host over a TCP-based network, such as the Internet. Originally developed in the 1970s, it's one of the earliest protocols for transferring files between computers and remains widely used today.\n\nFTP operates on a client-server model, where one computer acts as the client (the sender or requester) and the other acts as the server (the receiver or provider). The client initiates a connection to the server, usually by providing a username and password for authentication, and then requests a file transfer.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "File Transfer Protocol",
|
||||
"url": "https://en.wikipedia.org/wiki/File_Transfer_Protocol",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "FTP meaning and uses",
|
||||
"url": "https://www.investopedia.com/terms/f/ftp-file-transfer-protocol.asp",
|
||||
@@ -1405,7 +1440,7 @@
|
||||
"description": "HTTP (Hypertext Transfer Protocol) and HTTPS (HTTP Secure) are fundamental protocols for web communication. HTTP is the foundation for data exchange on the World Wide Web, allowing browsers to request resources from web servers. However, HTTP transmits data in plain text, making it vulnerable to eavesdropping and man-in-the-middle attacks. HTTPS addresses these security concerns by adding a layer of encryption using SSL/TLS (Secure Sockets Layer/Transport Layer Security). This encryption protects the confidentiality and integrity of data in transit, securing sensitive information such as login credentials and financial transactions. HTTPS also provides authentication, ensuring that users are communicating with the intended website. In recent years, there has been a significant push towards HTTPS adoption across the web, with major browsers marking HTTP sites as \"not secure.\" This shift has greatly enhanced overall web security, though it's important to note that HTTPS secures the connection, not necessarily the content of the website itself.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "An overview of HTTP",
|
||||
"title": "An Overview of HTTP",
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1513,7 +1548,7 @@
|
||||
},
|
||||
"CIoLaRv5I3sCr9tBnZHEi": {
|
||||
"title": "Hypervisor",
|
||||
"description": "A hypervisor, also known as a virtual machine monitor (VMM), is software or firmware that enables the creation and management of virtual machines (VMs) by abstracting the underlying hardware. It allows multiple VMs to run on a single physical machine, each operating independently with its own operating system and applications. Hypervisors facilitate better resource utilization by allowing a physical server to host several virtual environments, optimizing hardware efficiency.\n\nThere are two types of hypervisors:\n\n* **Type 1 hypervisor**, or bare-metal hypervisor, runs directly on the physical hardware without a host operating system. It provides better performance and is commonly used in enterprise environments. Examples include VMware ESXi and Microsoft Hyper-V.\n* **Type 2 hypervisor** runs on top of an existing operating system, relying on the host OS for resource management. These are typically used for personal or development purposes, with examples like VMware Workstation and Oracle VirtualBox.\n\nHypervisors are fundamental in cloud computing, virtualization, and server consolidation, allowing for flexible and efficient resource management and isolation between virtual environments.\n\nLearn more from the following resources:",
|
||||
"description": "A hypervisor, also known as a virtual machine monitor (VMM), is software or firmware that enables the creation and management of virtual machines (VMs) by abstracting the underlying hardware. It allows multiple VMs to run on a single physical machine, each operating independently with its own operating system and applications. Hypervisors facilitate better resource utilization by allowing a physical server to host several virtual environments, optimizing hardware efficiency.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a hypervisor?",
|
||||
@@ -1696,11 +1731,6 @@
|
||||
"title": "dig",
|
||||
"description": "`dig`, short for the Domain Information Groper, is a powerful and flexible command-line tool used to perform DNS queries and obtain valuable information about domains, IPs, and DNS records. This utility, available on UNIX-based systems like Linux and macOS, provides an essential function to help diagnose and resolve various issues related to domain name resolution and network connectivity. It is highly useful for network administrators and cybersecurity professionals when troubleshooting DNS-related problems.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How to use Linux dig command",
|
||||
"url": "https://www.google.com/search?client=firefox-b-d&q=linux+dig+command",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "How to look up DNS records with dig",
|
||||
"url": "https://www.youtube.com/watch?v=3AOKomsmeUY",
|
||||
@@ -1712,6 +1742,11 @@
|
||||
"title": "arp",
|
||||
"description": "ARP is a protocol used by the Internet Protocol (IP) to map an IP address to a physical address, also known as a Media Access Control (MAC) address. ARP is essential for routing data between devices in a Local Area Network (LAN) as it allows for the translation of IP addresses to specific hardware on the network. When a device wants to communicate with another device on the same LAN, it needs to determine the corresponding MAC address for the target IP address. ARP helps in this process by broadcasting an ARP request containing the target IP address. All devices within the broadcast domain receive this ARP request and compare the target IP address with their own IP address. If a match is found, the device with the matching IP address sends an ARP reply which contains its MAC address. The device that initiated the ARP request can now update its ARP cache (a table that stores IP-to-MAC mappings) with the new information, and then proceed to send data to the target's MAC address.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ARP - Wikipedia",
|
||||
"url": "https://en.wikipedia.org/wiki/Address_Resolution_Protocol",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is Address Resolution Protocol?",
|
||||
"url": "https://www.fortinet.com/resources/cyberglossary/what-is-arp",
|
||||
@@ -1896,8 +1931,14 @@
|
||||
},
|
||||
"vYvFuz7lAJXZ1vK_4999a": {
|
||||
"title": "Local Auth",
|
||||
"description": "Local authentication refers to the process of verifying a user's identity on a specific device or system without relying on external servers or networks. It typically involves storing and checking credentials directly on the device itself. Common methods include username/password combinations, biometrics (fingerprint, face recognition), or PIN codes. Local authentication is often used for device access, offline applications, or as a fallback when network-based authentication is unavailable. While it offers quick access and works without internet connectivity, it can be less secure than centralized authentication systems and more challenging to manage across multiple devices. Local authentication is commonly used in personal devices, standalone systems, and scenarios where network-based authentication is impractical or unnecessary.",
|
||||
"links": []
|
||||
"description": "Local authentication refers to the process of verifying a user's identity on a specific device or system without relying on external servers or networks. It typically involves storing and checking credentials directly on the device itself. Common methods include username/password combinations, biometrics (fingerprint, face recognition), or PIN codes. Local authentication is often used for device access, offline applications, or as a fallback when network-based authentication is unavailable. While it offers quick access and works without internet connectivity, it can be less secure than centralized authentication systems and more challenging to manage across multiple devices. Local authentication is commonly used in personal devices, standalone systems, and scenarios where network-based authentication is impractical or unnecessary.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Local authentication, registration, and other settings",
|
||||
"url": "https://learn.microsoft.com/en-us/power-pages/security/authentication/set-authentication-identity",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"_hYN0gEi9BL24nptEtXWU": {
|
||||
"title": "Security Skills and Knowledge",
|
||||
@@ -2256,7 +2297,7 @@
|
||||
},
|
||||
"WG7DdsxESm31VcLFfkVTz": {
|
||||
"title": "Authentication vs Authorization",
|
||||
"description": "Authentication vs Authorization\n-------------------------------\n\n**Authentication** is the process of validating the identity of a user, device, or system. It confirms that the entity attempting to access the resource is who or what they claim to be. The most common form of authentication is the use of usernames and passwords. Other methods include:\n\n**Authorization** comes into play after the authentication process is complete. It involves granting or denying access to a resource, based on the authenticated user's privileges. Authorization determines what actions the authenticated user or entity is allowed to perform within a system or application.",
|
||||
"description": "**Authentication** is the process of validating the identity of a user, device, or system. It confirms that the entity attempting to access the resource is who or what they claim to be. The most common form of authentication is the use of usernames and passwords. Other methods include:\n\n**Authorization** comes into play after the authentication process is complete. It involves granting or denying access to a resource, based on the authenticated user's privileges. Authorization determines what actions the authenticated user or entity is allowed to perform within a system or application.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Two-factor authentication (2FA)",
|
||||
@@ -2320,6 +2361,11 @@
|
||||
"title": "False Negative / False Positive",
|
||||
"description": "A false positive happens when the security tool mistakenly identifies a non-threat as a threat. For example, it might raise an alarm for a legitimate user's activity, indicating a potential attack when there isn't any. A high number of false positives can cause unnecessary diverting of resources and time, investigating false alarms. Additionally, it could lead to user frustration if legitimate activities are being blocked.\n\nA false negative occurs when the security tool fails to detect an actual threat or attack. This could result in a real attack going unnoticed, causing damage to the system, data breaches, or other negative consequences. A high number of false negatives indicate that the security system needs to be improved to capture real threats effectively.\n\nTo have an effective cybersecurity system, security professionals aim to maximize true positives and true negatives, while minimizing false positives and false negatives. Balancing these aspects ensures that the security tools maintain their effectiveness without causing undue disruptions to a user's experience.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Difference Between False Positive and False Negative",
|
||||
"url": "https://www.differencebetween.net/science/difference-between-false-positive-and-false-negative/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is a false positive virus?",
|
||||
"url": "https://www.youtube.com/watch?v=WrcAGBvIT14",
|
||||
@@ -2430,7 +2476,7 @@
|
||||
},
|
||||
"v7CD_sHqLWbm9ibXXESIK": {
|
||||
"title": "Learn how Malware works and Types",
|
||||
"description": "Malware, short for malicious software, refers to any software intentionally created to cause harm to a computer system, server, network, or user. It is a broad term that encompasses various types of harmful software created by cybercriminals for various purposes. In this guide, we will delve deeper into the major types of malware and their characteristics.\n\nVirus\n-----\n\nA computer virus is a type of malware that, much like a biological virus, attaches itself to a host (e.g., a file or software) and replicates when the host is executed. Viruses can corrupt, delete or modify data, and slow down system performance.\n\nWorm\n----\n\nWorms are self-replicating malware that spread through networks without human intervention. They exploit system vulnerabilities, consuming bandwidth and sometimes carrying a payload to infect target machines.\n\nTrojan Horse\n------------\n\nA trojan horse is a piece of software disguised as a legitimate program but contains harmful code. Users unknowingly download and install it, giving the attacker unauthorized access to the computer or network. Trojans can be used to steal data, create a backdoor, or launch additional malware attacks.\n\nRansomware\n----------\n\nRansomware is a type of malware that encrypts its victims' files and demands a ransom, typically in the form of cryptocurrency, for the decryption key. If the victim refuses or fails to pay within a specified time, the encrypted data may be lost forever.\n\nSpyware\n-------\n\nSpyware is a type of malware designed to collect and relay information about a user or organization without their consent. It can capture keystrokes, record browsing history, and access personal data such as usernames and passwords.\n\nAdware\n------\n\nAdware is advertising-supported software that automatically displays or downloads advertising materials, often in the form of pop-up ads, on a user's computer. While not always malicious, adware can be intrusive and open the door for other malware infections.\n\nRootkit\n-------\n\nA rootkit is a type of malware designed to hide or obscure the presence of other malicious programs on a computer system. This enables it to maintain persistent unauthorized access to the system and can make it difficult for users or security software to detect and remove infected files.\n\nKeylogger\n---------\n\nKeyloggers are a type of malware that monitor and record users' keystrokes, allowing attackers to capture sensitive information, such as login credentials or financial information entered on a keyboard.\n\nUnderstanding the different types of malware can help you better identify and protect against various cyber threats. As the cyber landscape continues to evolve, it's essential to stay informed about emerging malware and equip yourself with the necessary security skills and knowledge.",
|
||||
"description": "Malware, short for malicious software, refers to any software intentionally created to cause harm to a computer system, server, network, or user. It is a broad term that encompasses various types of harmful software created by cybercriminals for various purposes. In this guide, we will delve deeper into the major types of malware and their characteristics.\n\nVirus\n-----\n\nA computer virus is a type of malware that, much like a biological virus, attaches itself to a host (e.g., a file or software) and replicates when the host is executed. Viruses can corrupt, delete or modify data, and slow down system performance.\n\nWorm\n----\n\nWorms are self-replicating malware that spread through networks without human intervention. They exploit system vulnerabilities, consuming bandwidth and sometimes carrying a payload to infect target machines.\n\nTrojan Horse\n------------\n\nA trojan horse is a piece of software disguised as a legitimate program but contains harmful code. Users unknowingly download and install it, giving the attacker unauthorized access to the computer or network. Trojans can be used to steal data, create a backdoor, or launch additional malware attacks.\n\nRansomware\n----------\n\nRansomware is a type of malware that encrypts its victims' files and demands a ransom, typically in the form of cryptocurrency, for the decryption key. If the victim refuses or fails to pay within a specified time, the encrypted data may be lost forever.\n\nSpyware\n-------\n\nSpyware is a type of malware designed to collect and relay information about a user or organization without their consent. It can capture keystrokes, record browsing history, and access personal data such as usernames and passwords.\n\nAdware\n------\n\nAdware is advertising-supported software that automatically displays or downloads advertising materials, often in the form of pop-up ads, on a user's computer. While not always malicious, adware can be intrusive and open the door for other malware infections.\n\nRootkit\n-------\n\nA rootkit is a type of malware designed to hide or obscure the presence of other malicious programs on a computer system. This enables it to maintain persistent unauthorized access to the system and can make it difficult for users or security software to detect and remove infected files.\n\nKeylogger\n---------\n\nKeyloggers are a type of malware that monitor and record users' keystrokes, allowing attackers to capture sensitive information, such as login credentials or financial information entered on a keyboard.\n\nLearn more from the following resources:",
|
||||
"links": []
|
||||
},
|
||||
"Hoou7kWyfB2wx_yFHug_H": {
|
||||
@@ -2438,7 +2484,7 @@
|
||||
"description": "**Nmap** (Network Mapper) is an open-source network scanning tool used to discover hosts and services on a network, identify open ports, and detect vulnerabilities. It provides detailed information about networked devices, including their IP addresses, operating systems, and running services. Nmap supports various scanning techniques such as TCP SYN scan, UDP scan, and service version detection. It's widely used for network security assessments, vulnerability scanning, and network inventory management, helping administrators and security professionals understand and secure their network environments.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "NMAP Website",
|
||||
"title": "NMAP",
|
||||
"url": "https://nmap.org/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2446,6 +2492,11 @@
|
||||
"title": "NMAP Cheat Sheet",
|
||||
"url": "https://www.tutorialspoint.com/nmap-cheat-sheet",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nmap Tutorial to find Network Vulnerabilities",
|
||||
"url": "https://www.youtube.com/watch?v=4t4kBkMsDbQ",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2507,7 +2558,7 @@
|
||||
"description": "hping is a versatile and powerful command-line based packet crafting tool that allows network administrators, security professionals, and system auditors to manipulate and analyze network packets at a granular level. hping can be used to perform stress testing, firewall testing, scanning, and packet generation, among other functionalities.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "hping source code",
|
||||
"title": "hping",
|
||||
"url": "https://salsa.debian.org/debian/hping3",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2538,6 +2589,11 @@
|
||||
"title": "arp",
|
||||
"description": "ARP is a protocol used by the Internet Protocol (IP) to map an IP address to a physical address, also known as a Media Access Control (MAC) address. ARP is essential for routing data between devices in a Local Area Network (LAN) as it allows for the translation of IP addresses to specific hardware on the network. When a device wants to communicate with another device on the same LAN, it needs to determine the corresponding MAC address for the target IP address. ARP helps in this process by broadcasting an ARP request containing the target IP address. All devices within the broadcast domain receive this ARP request and compare the target IP address with their own IP address. If a match is found, the device with the matching IP address sends an ARP reply which contains its MAC address. The device that initiated the ARP request can now update its ARP cache (a table that stores IP-to-MAC mappings) with the new information, and then proceed to send data to the target's MAC address.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ARP - Wikipedia",
|
||||
"url": "https://en.wikipedia.org/wiki/Address_Resolution_Protocol",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is Address Resolution Protocol?",
|
||||
"url": "https://www.fortinet.com/resources/cyberglossary/what-is-arp",
|
||||
@@ -2660,6 +2716,11 @@
|
||||
"title": "memdump",
|
||||
"description": "**memdump** is a tool or process used to capture the contents of a computer's physical memory (RAM) for analysis. This \"memory dump\" can be useful in digital forensics, debugging, or incident response to identify active processes, open files, network connections, or potentially malicious code running in memory. By analyzing a memory dump, security professionals can investigate malware, recover encryption keys, or gather evidence in case of a breach. Tools like `memdump` (Linux utility) or `DumpIt` (Windows) are commonly used to perform this process.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "memdump - Github",
|
||||
"url": "https://github.com/tchebb/memdump",
|
||||
"type": "opensource"
|
||||
},
|
||||
{
|
||||
"title": "memdump",
|
||||
"url": "https://www.kali.org/tools/memdump/",
|
||||
@@ -2688,7 +2749,7 @@
|
||||
"description": "Autopsy is a versatile and powerful open-source digital forensics platform that is primarily used for incident response, cyber security investigations, and data recovery. As an investigator, you can utilize Autopsy to quickly and efficiently analyze a compromised system, extract crucial artifacts, and generate comprehensive reports. Integrated with The Sleuth Kit and other plug-ins, Autopsy allows examiners to automate tasks and dig deep into a system's structure to discover the root cause of an incident.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Autopsy Website",
|
||||
"title": "Autopsy",
|
||||
"url": "https://www.autopsy.com/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2772,6 +2833,16 @@
|
||||
"url": "https://www.techtarget.com/searchdatamanagement/definition/hashing",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Hashing Algorithm Overview: Types, Methodologies & Usage",
|
||||
"url": "https://www.okta.com/identity-101/hashing-algorithms/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Understanding Cryptography Types:",
|
||||
"url": "https://geekflare.com/cybersecurity/cryptography-types/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Hashing Explained",
|
||||
"url": "https://www.youtube.com/watch?v=EOe1XUykdP4",
|
||||
@@ -2901,7 +2972,7 @@
|
||||
"description": "The International Organization for Standardization (ISO) is an international standard-setting body composed of representatives from various national standards organizations. It promotes worldwide proprietary, industrial, and commercial standards. In the domain of cyber security, there are several important ISO standards that help organizations to protect their sensitive data and to be resilient against cyber threats.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ISO Website",
|
||||
"title": "International Organization for Standardization",
|
||||
"url": "https://www.iso.org/home.html",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2917,7 +2988,7 @@
|
||||
"description": "**NIST (National Institute of Standards and Technology)** is a U.S. federal agency that develops and promotes measurement standards, technology, and best practices. In the context of cybersecurity, NIST provides widely recognized guidelines and frameworks, such as the **NIST Cybersecurity Framework (CSF)**, which offers a structured approach to managing and mitigating cybersecurity risks. NIST also publishes the **NIST Special Publication (SP) 800 series**, which includes standards and guidelines for securing information systems, protecting data, and ensuring system integrity. These resources are essential for organizations seeking to enhance their security posture and comply with industry regulations.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "NIST Website",
|
||||
"title": "NIST",
|
||||
"url": "https://www.nist.gov/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3052,6 +3123,16 @@
|
||||
"title": "Kali Linux",
|
||||
"url": "https://www.kali.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Kali Tools",
|
||||
"url": "https://www.kali.org/tools",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Kali Docs",
|
||||
"url": "https://www.kali.org/docs/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -3060,7 +3141,7 @@
|
||||
"description": "**LOLBAS** (Living Off the Land Binaries and Scripts) refers to a collection of legitimate system binaries and scripts that can be abused by attackers to perform malicious actions while evading detection. These tools, which are often part of the operating system or installed software, can be leveraged for various purposes, such as executing commands, accessing data, or modifying system configurations, thereby allowing attackers to carry out their activities without deploying custom malware. The use of LOLBAS techniques makes it harder for traditional security solutions to detect and prevent malicious activities since the binaries and scripts used are typically trusted and deemed legitimate.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "LOLBAS project",
|
||||
"title": "LOLBAS Project",
|
||||
"url": "https://lolbas-project.github.io/#",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3081,7 +3162,7 @@
|
||||
"description": "Event logs are digital records that document activities and occurrences within computer systems and networks. They serve as a crucial resource for cybersecurity professionals, providing a chronological trail of system operations, user actions, and security-related events. These logs capture a wide range of information, including login attempts, file access, system changes, and application errors. In the context of security, event logs play a vital role in threat detection, incident response, and forensic analysis. They help identify unusual patterns, track potential security breaches, and reconstruct the sequence of events during an attack. Effective log management involves collecting logs from various sources, securely storing them, and implementing tools for log analysis and correlation. However, the sheer volume of log data can be challenging to manage, requiring advanced analytics and automation to extract meaningful insights and detect security incidents in real-time.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is an event log?",
|
||||
"title": "What is an Event Log?",
|
||||
"url": "https://www.crowdstrike.com/cybersecurity-101/observability/event-log/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3113,7 +3194,7 @@
|
||||
"description": "**NetFlow** is a network protocol developed by Cisco for collecting and analyzing network traffic data. It provides detailed information about network flows, including the source and destination IP addresses, ports, and the amount of data transferred. NetFlow data helps network administrators monitor traffic patterns, assess network performance, and identify potential security threats. By analyzing flow data, organizations can gain insights into bandwidth usage, detect anomalies, and optimize network resources. NetFlow is widely supported across various network devices and often integrated with network management and security tools for enhanced visibility and control.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Cisco NetFlow Website",
|
||||
"title": "Cisco NetFlow",
|
||||
"url": "https://www.cisco.com/c/en/us/products/ios-nx-os-software/ios-netflow/index.html",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3145,12 +3226,12 @@
|
||||
"description": "Firewall logs are detailed records of network traffic and security events captured by firewall devices. These logs provide crucial information about connection attempts, allowed and blocked traffic, and potential security incidents. They typically include data such as source and destination IP addresses, ports, protocols, timestamps, and the action taken by the firewall. Security professionals analyze these logs to monitor network activity, detect unusual patterns, investigate security breaches, and ensure policy compliance. Firewall logs are essential for troubleshooting network issues, optimizing security rules, and conducting forensic analysis after an incident. However, the volume of log data generated can be overwhelming, necessitating the use of log management tools and security information and event management (SIEM) systems to effectively process, correlate, and derive actionable insights from the logs. Regular review and analysis of firewall logs are critical practices in maintaining a robust security posture and responding promptly to potential threats.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is firewall logging and why is it important?",
|
||||
"title": "What is Firewall Logging and Why is it Important?",
|
||||
"url": "https://cybriant.com/what-is-firewall-logging-and-why-is-it-important/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Reviewing firewall logs",
|
||||
"title": "Reviewing Firewall Logs",
|
||||
"url": "https://www.youtube.com/watch?v=XiJ30f8V_T4",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -3174,7 +3255,7 @@
|
||||
},
|
||||
"6oAzYfwsHQYNVbi7c2Tly": {
|
||||
"title": "NAC-based",
|
||||
"description": "Network Access Control (NAC) based hardening is a crucial component in enhancing the security of your network infrastructure. NAC provides organizations with the ability to control and manage access to the network resources, ensuring that only authorized users and devices can connect to the network. It plays a vital role in reducing the attack surface and preventing unauthorized access to sensitive data and resources. By implementing NAC-based hardening in your cybersecurity strategy, you protect your organization from threats and maintain secure access to critical resources.\n\nLearn more from the following resouces:",
|
||||
"description": "Network Access Control (NAC) based hardening is a crucial component in enhancing the security of your network infrastructure. NAC provides organizations with the ability to control and manage access to the network resources, ensuring that only authorized users and devices can connect to the network. It plays a vital role in reducing the attack surface and preventing unauthorized access to sensitive data and resources. By implementing NAC-based hardening in your cybersecurity strategy, you protect your organization from threats and maintain secure access to critical resources.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is Network Access Control",
|
||||
@@ -3268,12 +3349,12 @@
|
||||
"description": "A **jump server**, also known as a **bastion host** or **jump host**, is a critical security component in many network architectures. It is a dedicated, locked-down, and secure server that sits within a protected network, and provides a controlled access point for users and administrators to access specific components within the system. This intermediate server acts as a bridge between untrusted networks and the internal privileged systems, thereby reducing the attack surface and securing the environment.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a jump server?",
|
||||
"title": "What is a Jump Server?",
|
||||
"url": "https://www.ssh.com/academy/iam/jump-server",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is a bastion host and why is it so important?",
|
||||
"title": "What is a Bastion Host and Why is it so important?",
|
||||
"url": "https://www.youtube.com/watch?v=pI6glWVEkcY",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -3289,7 +3370,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Endpoints are the IT frontdoor - Gaurd them!",
|
||||
"title": "Endpoints are the IT Frontdoor - Guard them!",
|
||||
"url": "https://www.youtube.com/watch?v=Njqid_JpqTs",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -3407,12 +3488,12 @@
|
||||
"description": "Antivirus software is a specialized program designed to detect, prevent, and remove malicious software, such as viruses, worms, and trojans, from computer systems. It works by scanning files and programs for known malware signatures, monitoring system behavior for suspicious activity, and providing real-time protection against potential threats. Regular updates are essential for antivirus software to recognize and defend against the latest threats. While it is a critical component of cybersecurity, antivirus solutions are often part of a broader security strategy that includes firewalls, anti-malware tools, and user education to protect against a wide range of cyber threats.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is antivirus software?",
|
||||
"title": "What is Antivirus Software?",
|
||||
"url": "https://www.webroot.com/gb/en/resources/tips-articles/what-is-anti-virus-software",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is an antivirus and how does it keep us safe?",
|
||||
"title": "What is an Antivirus and how does it keep us safe?",
|
||||
"url": "https://www.youtube.com/watch?v=jW626WMWNAE",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -3423,7 +3504,12 @@
|
||||
"description": "Anti-malware is a type of software designed to detect, prevent, and remove malicious software, such as viruses, worms, trojans, ransomware, and spyware, from computer systems. By continuously scanning files, applications, and incoming data, anti-malware solutions protect devices from a wide range of threats that can compromise system integrity, steal sensitive information, or disrupt operations. Advanced anti-malware programs utilize real-time monitoring, heuristic analysis, and behavioral detection techniques to identify and neutralize both known and emerging threats, ensuring that systems remain secure against evolving cyber attacks.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is antimalware?",
|
||||
"title": "Anti-malware Definition",
|
||||
"url": "https://www.computertechreviews.com/definition/anti-malware/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is Antimalware?",
|
||||
"url": "https://riskxchange.co/1006974/cybersecurity-what-is-anti-malware/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3455,13 +3541,13 @@
|
||||
"description": "Data Loss Prevention (DLP) refers to a set of strategies, tools, and processes used by organizations to ensure that sensitive data is not lost, accessed, or misused by unauthorized users. DLP solutions monitor, detect, and block the movement of critical information outside an organization’s network, helping to prevent data breaches, leaks, and other security incidents.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is data loss prevention (DLP)?",
|
||||
"url": "https://www.techtarget.com/whatis/definition/data-loss-prevention-DLP",
|
||||
"title": "What is DLP (data loss prevention)?",
|
||||
"url": "https://www.cloudflare.com/es-es/learning/access-management/what-is-dlp/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is DLP (data loss prevention)?",
|
||||
"url": "https://www.cloudflare.com/es-es/learning/access-management/what-is-dlp/",
|
||||
"title": "What is Data Loss Prevention (DLP)?",
|
||||
"url": "https://www.techtarget.com/whatis/definition/data-loss-prevention-DLP",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
@@ -3487,7 +3573,7 @@
|
||||
"description": "Firewalls are network security devices that monitor and control incoming and outgoing traffic based on predetermined security rules. Traditional firewalls operate at the network layer, filtering traffic based on IP addresses, ports, and protocols. They provide basic protection by creating a barrier between trusted internal networks and untrusted external networks.\n\nNext-generation firewalls (NGFWs) build upon this foundation, offering more advanced features to address modern cyber threats. NGFWs incorporate deep packet inspection, application-level filtering, and integrated intrusion prevention systems. They can identify and control applications regardless of port or protocol, enabling more granular security policies. NGFWs often include additional security functions such as SSL/TLS inspection, antivirus scanning, and threat intelligence integration. This evolution allows for more comprehensive network protection, better visibility into network traffic, and improved defense against sophisticated attacks in today's complex and dynamic threat landscape.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a firewall?",
|
||||
"title": "What is a Firewall?",
|
||||
"url": "https://www.kaspersky.com/resource-center/definitions/firewall",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3736,6 +3822,16 @@
|
||||
"title": "Known vs Unknown",
|
||||
"description": "\"known\" and \"unknown\" refer to the classification of threats based on the visibility and familiarity of the attack or vulnerability.\n\n* **Known Threats** are those that have been previously identified and documented, such as malware signatures, vulnerabilities, or attack patterns. Security solutions like antivirus software and intrusion detection systems typically rely on databases of known threats to recognize and block them. These threats are easier to defend against because security teams have the tools and knowledge to detect and mitigate them.\n \n* **Unknown Threats**, on the other hand, refer to new, emerging, or sophisticated threats that have not been previously encountered or documented. These can include zero-day vulnerabilities, which are software flaws not yet known to the vendor or the public, or advanced malware designed to evade traditional defenses. Unknown threats require more advanced detection techniques, such as behavioral analysis, machine learning, or heuristic-based detection, to identify anomalies and suspicious activities that don't match known patterns.\n \n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "From Known to Unknown",
|
||||
"url": "https://securitysandman.com/2025/01/06/from-known-to-unknown-shifting-cybersecurity-to-proactive-ai-detection/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Catching all Threats - Known, Unknown, and Unknown Unknown",
|
||||
"url": "https://www.scworld.com/perspective/catching-all-threats-known-unknown-and-unknown-unknown-before-they-can-harm-you",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Detecting known threats",
|
||||
"url": "https://www.youtube.com/watch?v=hOaHDVMQ9_s",
|
||||
@@ -3795,7 +3891,7 @@
|
||||
"description": "Joe Sandbox is an advanced malware analysis platform that allows security professionals to analyze suspicious files, URLs, and documents in a controlled and isolated environment known as a sandbox. This platform provides in-depth behavioral analysis by executing the potentially malicious code in a virtualized environment to observe its actions, such as file modifications, network communications, and registry changes, without risking the integrity of the actual network or systems. Joe Sandbox supports a wide range of file types and can detect and analyze complex, evasive malware that may attempt to avoid detection in less sophisticated environments. The insights generated from Joe Sandbox are crucial for understanding the nature of the threat, aiding in the development of countermeasures, and enhancing overall cybersecurity defenses.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Joe Sandbox Website",
|
||||
"title": "Joe Sandbox",
|
||||
"url": "https://www.joesandbox.com/#windows",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3811,7 +3907,7 @@
|
||||
"description": "ANY.RUN is an interactive online malware analysis platform that allows users to safely execute and analyze suspicious files and URLs in a controlled, virtualized environment. This sandbox service provides real-time insights into the behavior of potentially malicious software, such as how it interacts with the system, what files it modifies, and what network connections it attempts to make. Users can observe and control the analysis process, making it a valuable tool for cybersecurity professionals to identify and understand new threats, assess their impact, and develop appropriate countermeasures. ANY.RUN is particularly useful for dynamic analysis, enabling a deeper understanding of malware behavior in real-time.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ANY.RUN Website",
|
||||
"title": "Any.run",
|
||||
"url": "https://any.run/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -3985,13 +4081,18 @@
|
||||
"title": "Dumpster Diving",
|
||||
"description": "Dumpster Diving in the context of cybersecurity refers to the practice of searching through discarded materials in trash or recycling bins to find confidential information. This technique may seem unsophisticated, but it can be extremely effective in obtaining valuable data such as passwords, account information, network diagrams, or any other sensitive information that has not been properly destroyed.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Dumpster Diving",
|
||||
"url": "https://www.techtarget.com/searchsecurity/definition/dumpster-diving",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is Dumpster Diving",
|
||||
"url": "https://powerdmarc.com/dumpster-diving-in-cybersecurity/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Dumpster diving for sensitive information",
|
||||
"title": "Dumpster Diving for Sensitive Information",
|
||||
"url": "https://www.youtube.com/watch?v=Pom86gq4mk4",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -4162,7 +4263,7 @@
|
||||
"description": "A Man-in-the-Middle (MITM) attack occurs when a malicious actor intercepts communication between two parties, such as a user and a website, without their knowledge. The attacker can eavesdrop, alter, or inject false information into the communication, often to steal sensitive data like login credentials or manipulate transactions. MITM attacks are commonly executed through compromised Wi-Fi networks or by exploiting security vulnerabilities in protocols.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Wikipedia - Man-in-the-middle attack",
|
||||
"title": "Man-in-the-middle attack",
|
||||
"url": "https://en.wikipedia.org/wiki/Man-in-the-middle_attack",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -4203,7 +4304,23 @@
|
||||
"urtsyYWViEzbqYLoNfQAh": {
|
||||
"title": "DNS Poisoning",
|
||||
"description": "DNS spoofing or DNS cache poisoning, occurs when fake information is inserted into a DNS server’s cache.This causes DNS queries to return incorrect IP addresses, directing users to the wrong websites. Hackers exploit this to reroute traffic to malicious sites. The issue persists until the cached information is corrected.When the cache is poisoned, it misdirects traffic until the incorrect information is fixed. This technique exploits vulnerabilities in the DNS system and can spread to other servers, causing widespread issues.\n\nVisit the following resources to learn more:",
|
||||
"links": []
|
||||
"links": [
|
||||
{
|
||||
"title": "What is DNS Cache Poisoning? | DNS spoofing",
|
||||
"url": "https://www.cloudflare.com/learning/dns/dns-cache-poisoning/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What Is DNS Poisoning?",
|
||||
"url": "https://www.fortinet.com/resources/cyberglossary/dns-poisoning",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "DNS Poisoning (DNS Spoofing): Definition, Technique & Defense",
|
||||
"url": "https://www.okta.com/identity-101/dns-poisoning/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"LfWJJaT3fv0p6fUeS8b84": {
|
||||
"title": "Deauth Attack",
|
||||
@@ -4276,7 +4393,7 @@
|
||||
},
|
||||
"nOND14t7ISgSH3zNpV3F8": {
|
||||
"title": "Memory Leak",
|
||||
"description": "A Memory Leak occurs when a computer program consumes memory but fails to release it back to the operating system after it is no longer needed. Over time, this can lead to reduced system performance, increased memory usage, and, in severe cases, the program or system may crash due to the exhaustion of available memory.",
|
||||
"description": "A Memory Leak occurs when a computer program consumes memory but fails to release it back to the operating system after it is no longer needed. Over time, this can lead to reduced system performance, increased memory usage, and, in severe cases, the program or system may crash due to the exhaustion of available memory.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What are memory leaks?",
|
||||
@@ -4443,7 +4560,7 @@
|
||||
"description": "A legal department within an organization is responsible for handling all legal matters that affect the business, ensuring compliance with laws and regulations, and providing advice on various legal issues. Its primary functions include managing contracts, intellectual property, employment law, and regulatory compliance, as well as addressing disputes, litigation, and risk management. The legal department also plays a crucial role in corporate governance, ensuring that the company operates within the boundaries of the law while minimizing legal risks. In some cases, they work with external legal counsel for specialized legal matters, such as mergers and acquisitions or complex litigation.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Key functions of a legal team",
|
||||
"title": "Key Functions of a Legal Team",
|
||||
"url": "https://uk.practicallaw.thomsonreuters.com/w-009-3932?transitionType=Default&contextData=(sc.Default)&firstPage=true",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -4703,12 +4820,12 @@
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "AWS Roadmap",
|
||||
"title": "Visit Dedicated AWS Roadmap",
|
||||
"url": "https://roadmap.sh/aws",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "AWS Website",
|
||||
"title": "AWS",
|
||||
"url": "https://aws.amazon.com",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -4728,15 +4845,20 @@
|
||||
"title": "GCP",
|
||||
"description": "Google Cloud Platform (GCP) is a collection of cloud computing services offered by Google, which provides infrastructure and platform services to businesses or individuals. It enables users to either build their own applications or services on the provided resources, or utilize ready-to-use services provided by Google. GCP covers a wide range of services, including (but not limited to) compute, storage, databases, networking, and many more.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Google Cloud Platform Video Course",
|
||||
"url": "https://www.youtube.com/watch?v=fZOz13joN0o",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Google Cloud Platform",
|
||||
"url": "https://cloud.google.com",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Cloud Computing, Hosting Services, and APIs",
|
||||
"url": "https://cloud.google.com/gcp",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Google Cloud Platform Video Course",
|
||||
"url": "https://www.youtube.com/watch?v=fZOz13joN0o",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -4745,7 +4867,7 @@
|
||||
"description": "Azure is Microsoft's comprehensive cloud computing platform that offers a wide range of services for building, deploying, and managing applications. It provides infrastructure as a service (IaaS), platform as a service (PaaS), and software as a service (SaaS) solutions, supporting various programming languages, tools, and frameworks. Azure's services include virtual machines, storage, databases, AI and machine learning, IoT, and more. It offers global data center coverage, integrated DevOps tools, and robust security features, making it a versatile platform for businesses of all sizes to innovate, scale, and transform their operations in the cloud.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Azure Website",
|
||||
"title": "Azure",
|
||||
"url": "https://azure.microsoft.com",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -4787,9 +4909,19 @@
|
||||
"description": "Dropbox is a widely used cloud storage service that allows you to store, access, and share files, documents, and media with ease across various devices. Launched in 2007, Dropbox has become one of the most popular cloud storage solutions, catering to both individual users and businesses. The service is available on multiple platforms, including Windows, macOS, Linux, iOS, and Android.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Dropbox Website",
|
||||
"title": "Dropbox",
|
||||
"url": "https://dropbox.com",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "How to Use Dropbox - a guide to your account",
|
||||
"url": "https://learn.dropbox.com/self-guided-learning/dropbox-fundamentals-course/how-to-use-dropbox",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Dropbox Tutorial for Beginners",
|
||||
"url": "https://www.youtube.com/watch?v=b0Nk9muiUcQ",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -4830,7 +4962,7 @@
|
||||
"description": "Google Drive is a cloud-based storage solution provided by Google, which offers users the ability to store, share, and collaborate on files and documents across different platforms and devices. It is integrated with Google's productivity suite, including Google Docs, Sheets, Slides, and Forms, allowing seamless collaboration with team members in real-time.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Google Drive Website",
|
||||
"title": "Google Drive",
|
||||
"url": "https://drive.google.com",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -4841,7 +4973,7 @@
|
||||
"description": "iCloud is a cloud storage and cloud computing service provided by Apple Inc. It allows users to store data, such as documents, photos, and music, on remote servers and synchronize them across their Apple devices, including iPhones, iPads, and MacBooks.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "iCloud Website",
|
||||
"title": "iCloud",
|
||||
"url": "https://www.icloud.com/",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -4909,12 +5041,7 @@
|
||||
"description": "Go, also known as Golang, is an open-source programming language created by Google. Launched in 2009, it was designed to overcome issues present in other languages and offer a more secure, robust, and efficient development experience.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Go tutorial for beginners",
|
||||
"url": "https://www.youtube.com/watch?v=yyUHQIec83I",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Go Roadmap",
|
||||
"title": "Visit Dedicated Go Roadmap",
|
||||
"url": "https://roadmap.sh/golang",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -4922,6 +5049,11 @@
|
||||
"title": "Go in 100 seconds",
|
||||
"url": "https://www.youtube.com/watch?v=446E-r0rXHI",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Go Tutorial for beginners",
|
||||
"url": "https://www.youtube.com/watch?v=yyUHQIec83I",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -4930,7 +5062,7 @@
|
||||
"description": "JavaScript (often abbreviated as JS) is a widely-used, high-level programming language. It is predominantly used for creating and enhancing the interactive elements of web pages, making it an integral part of the web development space. JavaScript was initially known as LiveScript and was created by Brendan Eich in 1995, but it later got renamed to JavaScript.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "JavaScript Roadmap",
|
||||
"title": "Visit Dedicated JavaScript Roadmap",
|
||||
"url": "https://roadmap.sh/javascript",
|
||||
"type": "article"
|
||||
},
|
||||
|
||||
973
public/roadmap-content/docker.json
Normal file
973
public/roadmap-content/docker.json
Normal file
@@ -0,0 +1,973 @@
|
||||
{
|
||||
"Py9nst2FDJ1_hoXeX_qSF": {
|
||||
"title": "Introduction",
|
||||
"description": "Docker is an open-source platform that automates application deployment, scaling, and management using lightweight, portable containers. Containers are standalone executable units containing all necessary dependencies, libraries, and configuration files for consistent application execution across various environments.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker",
|
||||
"url": "https://www.docker.com/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Docs",
|
||||
"url": "https://docs.docker.com/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"74JxgfJ_1qmVNZ_QRp9Ne": {
|
||||
"title": "What are Containers?",
|
||||
"description": "Containers are lightweight, portable, and isolated software environments that package applications with their dependencies for consistent execution across different platforms. They streamline development, deployment, and management while ensuring applications run reliably regardless of underlying infrastructure.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Introduction to Containers - AWS Skill Builder",
|
||||
"url": "https://explore.skillbuilder.aws/learn/course/106/introduction-to-containers",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "What is a Container?",
|
||||
"url": "https://www.docker.com/resources/what-container/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Containers",
|
||||
"url": "https://app.daily.dev/tags/containers?ref=roadmapsh",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"i4ijY3T5gLgNz0XqRipXe": {
|
||||
"title": "Why do we need Containers?",
|
||||
"description": "Containers solve environment inconsistency issues when working in teams by standardizing runtime environments. Before containers, significant time was lost configuring local environments to run projects shared by teammates, leading to \"works on my machine\" problems.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Need for Containers",
|
||||
"url": "https://www.redhat.com/en/topics/containers",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"3hatcMVLDbMuz73uTx-9P": {
|
||||
"title": "Bare Metal vs VMs vs Containers",
|
||||
"description": "Bare metal runs applications directly on hardware with maximum performance but limited flexibility. VMs use hypervisors to run multiple OS instances with strong isolation but higher overhead. Containers share the host OS kernel, providing lightweight isolation with better resource efficiency than VMs while maintaining portability.\n\nYou can learn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "History of Virtualization",
|
||||
"url": "https://courses.devopsdirective.com/docker-beginner-to-pro/lessons/01-history-and-motivation/03-history-of-virtualization",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Bare Metal Machine",
|
||||
"url": "https://glossary.cncf.io/bare-metal-machine/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is a Virtual Machine?",
|
||||
"url": "https://azure.microsoft.com/en-au/resources/cloud-computing-dictionary/what-is-a-virtual-machine",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"43drPbTwPqJQPyzwYUdBT": {
|
||||
"title": "Docker and OCI",
|
||||
"description": "The Open Container Initiative (OCI) is a Linux Foundation project which aims at creating industry standards for container formats and runtimes. Its primary goal is to ensure the compatibility and interoperability of container environments through defined technical specifications.\n\nYou can learn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Open Container Initiative",
|
||||
"url": "https://opencontainers.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "OCI - Wikipedia",
|
||||
"url": "https://en.wikipedia.org/wiki/Open_Container_Initiative",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mw-weCutd2ECKlx2DE_ZJ": {
|
||||
"title": "Package Managers",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"uKjB2qntFTpPuYUT9sdxd": {
|
||||
"title": "Users / Groups Permissions",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"W5kX5jn49hghRgkEw6_S3": {
|
||||
"title": "Shell Commands",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"InlMtuaUJ9EXO-OD9x1jj": {
|
||||
"title": "Shell Scripting",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"XxT9UUjbKW1ARyERSLH_W": {
|
||||
"title": "Programming Languages",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"EqYWfBL5l5OOquok_OvOW": {
|
||||
"title": "Application Architecture",
|
||||
"description": "Application architecture in containerized environments focuses on designing applications to leverage containerization benefits. This includes microservices patterns, service decomposition, inter-service communication, data persistence strategies, and designing for scalability and fault tolerance in distributed systems.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Microservices Architecture",
|
||||
"url": "https://microservices.io/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Application Design Patterns",
|
||||
"url": "https://docs.docker.com/get-started/docker-concepts/building-images/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Container Design Patterns",
|
||||
"url": "https://kubernetes.io/blog/2016/06/container-design-patterns/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Twelve-Factor App Methodology",
|
||||
"url": "https://12factor.net/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Microservices vs Monolith Architecture",
|
||||
"url": "https://www.youtube.com/watch?v=GBTdnfD6s5Q",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"jrH1qE6EnFXL4fTyYU8gR": {
|
||||
"title": "Underlying Technologies",
|
||||
"description": "Docker containers use Linux kernel technologies for isolation and resource management: namespaces for process isolation, cgroups for resource limits, and union filesystems for efficient layered storage. These enable lightweight, portable, and secure containers that share the host kernel.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Underlying Technologies",
|
||||
"url": "https://www.docker.com/resources/what-container/#underlying-technologies",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Underlying Technologies - Medium",
|
||||
"url": "https://medium.com/@furkan.turkal/how-does-docker-actually-work-the-hard-way-a-technical-deep-diving-c5b8ea2f0422",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"BvV8VCX39wRB-g8WvGF1g": {
|
||||
"title": "Namespaces",
|
||||
"description": "Docker namespaces are a Linux kernel feature that creates isolated environments for containers by providing separate instances of global system resources. Docker uses PID, NET, MNT, UTS, IPC, and USER namespaces to ensure each container believes it has its own unique resources, enabling lightweight, portable, and secure containerization.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Namespaces",
|
||||
"url": "https://docs.docker.com/engine/security/userns-remap/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Linux Namespaces",
|
||||
"url": "https://man7.org/linux/man-pages/man7/namespaces.7.html",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fRl4EfNwlBiidzn3IV34-": {
|
||||
"title": "cgroups",
|
||||
"description": "cgroups (control groups) are Linux kernel features that limit and manage system resources like CPU, memory, and I/O for process groups. Docker uses cgroups to enforce resource constraints on containers, ensuring predictable performance and preventing containers from consuming excessive system resources.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Control Groups",
|
||||
"url": "https://www.docker.com/resources/what-container/#control-groups",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Control Groups - Medium",
|
||||
"url": "https://medium.com/@furkan.turkal/how-does-docker-actually-work-the-hard-way-a-technical-deep-diving-c5b8ea2f0422",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "An introduction to cgroups, runc & containerD",
|
||||
"url": "https://www.youtube.com/watch?v=u1LeMndEk70",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"vEUfw_vobshuZI0-q8RZo": {
|
||||
"title": "Union Filesystems",
|
||||
"description": "Union filesystems (UnionFS) create virtual, layered file structures by overlaying multiple directories without modifying originals. Docker uses this to manage storage efficiently by minimizing duplication and reducing image sizes through layered filesystem approach that keeps directory contents separate while mounted together.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "AUFS (Advanced Multi-Layered Unification Filesystem)",
|
||||
"url": "http://aufs.sourceforge.net/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "OverlayFS (Overlay Filesystem)",
|
||||
"url": "https://www.kernel.org/doc/html/latest/filesystems/overlayfs.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Btrfs (B-Tree Filesystem)",
|
||||
"url": "https://btrfs.readthedocs.io/en/stable/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "ZFS (Z File System)",
|
||||
"url": "https://zfsonlinux.org/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"01nDXqxVdMv4SeXc0nYHH": {
|
||||
"title": "Installation / Setup",
|
||||
"description": "Docker provides Docker Desktop, a desktop application that simplifies installation and setup with GUI capabilities. Alternatively, you can install Docker Engine for command-line only functionality without graphical interface components.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Desktop website",
|
||||
"url": "https://www.docker.com/products/docker-desktop",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Engine",
|
||||
"url": "https://docs.docker.com/engine/install/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"NCdsPRhJy7UtQFNLo1J1f": {
|
||||
"title": "Docker Desktop (Win/Mac/Linux)",
|
||||
"description": "Docker Desktop is a comprehensive development environment for Windows, macOS, and Linux with a GUI. It includes Docker Engine, CLI, Buildx, Extensions, Compose, Kubernetes, and credentials helper, providing everything needed for container development on desktop platforms.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Desktop Documentation",
|
||||
"url": "https://docs.docker.com/desktop/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Get Started Guide",
|
||||
"url": "https://docs.docker.com/get-started/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Hub",
|
||||
"url": "https://hub.docker.com/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Docker",
|
||||
"url": "https://app.daily.dev/tags/docker?ref=roadmapsh",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"0NKqLUWtJMlXn-m6wpA6f": {
|
||||
"title": "Docker Engine ( Linux )",
|
||||
"description": "Docker Engine is the core open-source containerization runtime that creates and manages containers, builds images, and provides the Docker API. It runs on Linux, Windows, and macOS, serving as the foundation for Docker Desktop and standalone Docker installations on servers.\n\nFor more information about docker engine see:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Engine Installation Guide",
|
||||
"url": "https://docs.docker.com/engine/install/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Engine - Docker Documentation",
|
||||
"url": "https://docs.docker.com/engine/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Docker",
|
||||
"url": "https://app.daily.dev/tags/docker?ref=roadmapsh",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Engine for Linux Servers Setup and Tips",
|
||||
"url": "https://www.youtube.com/watch?v=YeF7ObTnDwc",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"kIqx7Inf50mE9W0juwNBz": {
|
||||
"title": "Basics of Docker",
|
||||
"description": "Docker is a platform that simplifies building, packaging, and deploying applications in lightweight, portable containers. Key components include Dockerfiles (build instructions), Images (snapshots), and Containers (running instances). Essential commands cover pulling images, building from Dockerfiles, running containers with port mapping, and managing both containers and images.\n\nWhat is a Container?\n--------------------\n\nA container is a lightweight, standalone, and executable software package that includes all the dependencies (libraries, binaries, and configuration files) required to run an application. Containers isolate applications from their environment, ensuring they work consistently across different systems.\n\nDocker Components\n-----------------\n\nThere are three key components in the Docker ecosystem:\n\n* **Dockerfile**: A text file containing instructions (commands) to build a Docker image.\n* **Docker Image**: A snapshot of a container, created from a Dockerfile. Images are stored in a registry, like Docker Hub, and can be pulled or pushed to the registry.\n* **Docker Container**: A running instance of a Docker image.\n\nDocker Commands\n---------------\n\nBelow are some essential Docker commands you'll use frequently:\n\n* `docker pull <image>`: Download an image from a registry, like Docker Hub.\n* `docker build -t <image_name> <path>`: Build an image from a Dockerfile, where `<path>` is the directory containing the Dockerfile.\n* `docker image ls`: List all images available on your local machine.\n* `docker run -d -p <host_port>:<container_port> --name <container_name> <image>`: Run a container from an image, mapping host ports to container ports.\n* `docker container ls`: List all running containers.\n* `docker container stop <container>`: Stop a running container.\n* `docker container rm <container>`: Remove a stopped container.\n* `docker image rm <image>`: Remove an image from your local machine.",
|
||||
"links": []
|
||||
},
|
||||
"uUPYXmwu27SBPqKZx6U_q": {
|
||||
"title": "Data Persistence",
|
||||
"description": "Docker enables you to run containers that are isolated pieces of code, including applications and their dependencies, separated from the host operating system. Containers are ephemeral by default, which means any data stored in the container will be lost once it is terminated. To overcome this problem and retain data across container lifecycle, Docker provides various data persistence methods.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Data Persistence - Docker Documentation",
|
||||
"url": "https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"086zZYjtzdCaDHm-MkSqg": {
|
||||
"title": "Ephemeral Container Filesystem",
|
||||
"description": "By default, the storage within a Docker container is ephemeral, meaning that any data changes or modifications made inside a container will only persist until the container is stopped and removed. Once the container is stopped and removed, all the associated data will be lost. This is because Docker containers are designed to be stateless by nature. This temporary or short-lived storage is called the \"ephemeral container file system\". It is an essential feature of Docker, as it enables fast and consistent deployment of applications across different environments without worrying about the state of a container.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Data Persistence - Docker Documentation",
|
||||
"url": "https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Concepts - Persisting container data",
|
||||
"url": "https://www.youtube.com/watch?v=10_2BjqB_Ls",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"woemCQmWTR-hIoWAci3d5": {
|
||||
"title": "Volume Mounts",
|
||||
"description": "Volume mounts are a way to map a folder or file on the host system to a folder or file inside a container. This allows the data to persist outside the container even when the container is removed. Additionally, multiple containers can share the same volume, making data sharing between containers easy.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Volumes",
|
||||
"url": "https://docs.docker.com/storage/volumes/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Volume Flags",
|
||||
"url": "https://docs.docker.com/storage/bind-mounts/#choose-the--v-or---mount-flag",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Volumes explained in 6 minutes",
|
||||
"url": "https://www.youtube.com/watch?v=p2PH_YPCsis",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"wZcCW1ojGzUakHCv2AaI1": {
|
||||
"title": "Bind Mounts",
|
||||
"description": "Bind mounts have limited functionality compared to volumes. When you use a bind mount, a file or directory on the host machine is mounted into a container. The file or directory is referenced by its absolute path on the host machine. By contrast, when you use a volume, a new directory is created within Docker's storage directory on the host machine, and Docker manages that directory's contents.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Bind Mounts",
|
||||
"url": "https://docs.docker.com/storage/bind-mounts/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"LShK3-1EGGuXnEvdScFR7": {
|
||||
"title": "Using 3rd Party Container Images",
|
||||
"description": "Third-party images are pre-built Docker container images that are available on Docker Hub or other container registries. These images are created and maintained by individuals or organizations and can be used as a starting point for your containerized applications.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Hub Registry",
|
||||
"url": "https://hub.docker.com/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"jKSE_wKYf4P9wnSh_LkMi": {
|
||||
"title": "Databases",
|
||||
"description": "Running your database in a Docker container can help streamline your development process and ease deployment. Docker Hub provides numerous pre-made images for popular databases such as MySQL, PostgreSQL, and MongoDB.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Containerized Databases",
|
||||
"url": "https://docs.docker.com/guides/use-case/databases/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "How to Setup MySQL Database with Docker",
|
||||
"url": "https://www.youtube.com/watch?v=igc2zsOKPJs",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"HlTxLqKNFMhghtKF6AcWu": {
|
||||
"title": "Interactive Test Environments",
|
||||
"description": "Docker allows you to create isolated, disposable environments that can be deleted once you're done with testing. This makes it much easier to work with third party software, test different dependencies or versions, and quickly experiment without the risk of damaging your local setup.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Launch a Dev Environment",
|
||||
"url": "https://docs.docker.com/desktop/dev-environments/create-dev-env/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Test Environments - Medium",
|
||||
"url": "https://manishsaini74.medium.com/containerized-testing-orchestrating-test-environments-with-docker-5201bfadfdf2",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"YzpB7rgSR4ueQRLa0bRWa": {
|
||||
"title": "Command Line Utilities",
|
||||
"description": "Docker images can include command line utilities or standalone applications that we can run inside containers.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Images",
|
||||
"url": "https://docs.docker.com/engine/reference/commandline/images/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Run",
|
||||
"url": "https://docs.docker.com/reference/cli/docker/container/run/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Pull",
|
||||
"url": "https://docs.docker.com/engine/reference/commandline/pull/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"5OEfBQaYNOCi999x6QUqW": {
|
||||
"title": "Building Container Images",
|
||||
"description": "Container images are executable packages that include everything required to run an application: code, runtime, system tools, libraries, and settings. By building custom images, you can deploy applications seamlessly with all their dependencies on any Docker-supported platform. The key component in building a container image is the `Dockerfile`. It is essentially a script containing instructions on how to assemble a Docker image. Each instruction in the Dockerfile creates a new layer in the image, making it easier to track changes and minimize the image size. Here's a simple example of a Dockerfile:\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Dockerfile Examples",
|
||||
"url": "https://github.com/dockersamples",
|
||||
"type": "opensource"
|
||||
},
|
||||
{
|
||||
"title": "Docker Image Builder",
|
||||
"url": "https://docs.docker.com/reference/cli/docker/buildx/build/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Dockerfile Reference",
|
||||
"url": "https://docs.docker.com/engine/reference/builder/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"yGRQcx64S-yBGEoOeMc55": {
|
||||
"title": "Dockerfiles",
|
||||
"description": "A Dockerfile is a text document that contains a list of instructions used by the Docker engine to build an image. Each instruction in the Dockerfile adds a new layer to the image. Docker will build the image based on these instructions, and then you can run containers from the image.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Dockerfile Examples",
|
||||
"url": "https://github.com/dockersamples",
|
||||
"type": "opensource"
|
||||
},
|
||||
{
|
||||
"title": "Dockerfile Reference",
|
||||
"url": "https://docs.docker.com/engine/reference/builder/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Dockerfile Best Practices",
|
||||
"url": "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"frshJqVMP8D7o_7tMZMPI": {
|
||||
"title": "Efficient Layer Caching",
|
||||
"description": "When building container images, Docker caches the newly created layers. These layers can then be used later on when building other images, reducing the build time and minimizing bandwidth usage. However, to make the most of this caching mechanism, you should be aware of how to efficiently use layer caching. Docker creates a new layer for each instruction (e.g., `RUN`, `COPY`, `ADD`, etc.) in the Dockerfile. If the instruction hasn't changed since the last build, Docker will reuse the existing layer.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Layer Caching",
|
||||
"url": "https://docs.docker.com/build/cache/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Layer Caching",
|
||||
"url": "https://www.youtube.com/watch?v=_nMpndIyaBU",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"-8wAzF6_3gruiM3VYMvB0": {
|
||||
"title": "Image Size and Security",
|
||||
"description": "Reducing Docker image size is crucial for optimizing storage, transfer speeds, and deployment times. Key strategies include using minimal base images like Alpine Linux, leveraging multi-stage builds to exclude unnecessary build tools, removing unnecessary files and packages, and minimizing the number of layers by combining commands.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Multi-stage builds",
|
||||
"url": "https://docs.docker.com/build/building/multi-stage/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Best Practices",
|
||||
"url": "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Security",
|
||||
"url": "https://app.daily.dev/tags/security?ref=roadmapsh",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"3VKPiMfbGBxv9m_SljIQV": {
|
||||
"title": "Container Registries",
|
||||
"description": "A Container Registry is a centralized storage and distribution system for Docker container images. It allows developers to easily share and deploy applications in the form of these images. Container registries play a crucial role in the deployment of containerized applications, as they provide a fast, reliable, and secure way to distribute container images across various production environments.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Registry",
|
||||
"url": "https://docs.docker.com/registry/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Hub",
|
||||
"url": "https://hub.docker.com/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Artifact Registry",
|
||||
"url": "https://cloud.google.com/artifact-registry",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Amazon ECR",
|
||||
"url": "https://aws.amazon.com/ecr/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Azure Container Registry",
|
||||
"url": "https://azure.microsoft.com/en-in/products/container-registry",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rxVR62_yXIjc-L4GFSV6u": {
|
||||
"title": "Dockerhub",
|
||||
"description": "Docker Hub is a cloud-based registry service that serves as the primary public repository for Docker container images. It allows users to store, share, and distribute Docker images, offering both free public repositories and paid private ones and integrates seamlessly with Docker CLI, enabling easy pushing and pulling of images. It features official images maintained by software vendors, automated builds linked to source code repositories, and webhooks for triggering actions based on repository events.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "DockerHub",
|
||||
"url": "https://hub.docker.com/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "DockerHub Repositories",
|
||||
"url": "https://docs.docker.com/docker-hub/repos/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "DockerHub Webhooks",
|
||||
"url": "https://docs.docker.com/docker-hub/webhooks/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Vs4WQwgJFhA63U9Gf2ym0": {
|
||||
"title": "Image Tagging Best Practices",
|
||||
"description": "Docker image tagging best practices center on creating clear, consistent, and informative labels. Adopt semantic versioning for releases, avoid the ambiguous \"latest\" tag in production, and include relevant metadata like build dates or Git commit hashes. Implement a strategy distinguishing between environments, use descriptive tags for variants, and automate tagging in CI/CD pipelines. Regularly clean up old tags and document your conventions to maintain clarity and facilitate team-wide adoption. These practices ensure efficient image management and improve collaboration across your organization.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Tags",
|
||||
"url": "https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Image Tagging Best Practices",
|
||||
"url": "https://medium.com/@nirmalkushwah08/docker-image-tagging-strategy-4aa886fb4fcc",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Semantic Versioning",
|
||||
"url": "https://semver.org/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fh5aERX7c-lY9FPsmftoF": {
|
||||
"title": "Others (ghcr, ecr, gcr, acr, etc)",
|
||||
"description": "Container images can be stored in many different registries, not just Dockerhub. Most major cloud platforms now provide container registries such as \"Artifact Registry\" on Google Cloud Platform, Elastic Container Registry on AWS and Azure Container Registry on Microsoft Azure. GitHub also provides it's own registry which is useful when container builds are included in your GitHub Actions workflow.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "DockerHub",
|
||||
"url": "https://hub.docker.com/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Artifact Registry",
|
||||
"url": "https://cloud.google.com/artifact-registry",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Amazon ECR",
|
||||
"url": "https://aws.amazon.com/ecr/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Azure Container Registry",
|
||||
"url": "https://azure.microsoft.com/en-in/products/container-registry",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "GitHub Container Registry",
|
||||
"url": "https://docs.github.com/en/packages/guides/about-github-container-registry",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"z2eeBXPzo-diQ67Fcfyhc": {
|
||||
"title": "Running Containers",
|
||||
"description": "The `docker run` command creates and starts containers from images in one step. It combines `docker create` and `docker start` operations, allowing you to execute applications in isolated environments with various configuration options like port mapping, volumes, and environment variables.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Run",
|
||||
"url": "https://docs.docker.com/engine/reference/commandline/run/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Containers",
|
||||
"url": "https://docs.docker.com/engine/reference/commandline/container/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Exec",
|
||||
"url": "https://docs.docker.com/engine/reference/commandline/exec/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Stop",
|
||||
"url": "https://docs.docker.com/engine/reference/commandline/stop/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"6eu5NRA1sJuaHTlHtNurc": {
|
||||
"title": "docker run",
|
||||
"description": "The `docker run` command creates and starts a new container from a specified image. It combines `docker create` and `docker start` operations, offering a range of options to customize the container's runtime environment. Users can set environment variables, map ports and volumes, define network connections, and specify resource limits. The command supports detached mode for background execution, interactive mode for shell access, and the ability to override the default command defined in the image. Common flags include `-d` for detached mode, `-p` for port mapping, `-v` for volume mounting, and `--name` for assigning a custom container name. Understanding `docker run` is fundamental to effectively deploying and managing Docker containers.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Run",
|
||||
"url": "https://docs.docker.com/engine/reference/commandline/run/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"jjA9E0J8N2frfeJCNtA1m": {
|
||||
"title": "docker compose",
|
||||
"description": "Docker Compose is a tool for defining and running multi-container applications using a YAML file (`docker-compose.yml`). It describes application services, networks, and volumes, enabling you to create, manage, and run entire containerized applications with single commands for simplified orchestration.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Curated Docker Compose Samples",
|
||||
"url": "https://github.com/docker/awesome-compose?tab=readme-ov-file",
|
||||
"type": "opensource"
|
||||
},
|
||||
{
|
||||
"title": "Docker Compose documentation",
|
||||
"url": "https://docs.docker.com/compose/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Compose Tutorial",
|
||||
"url": "https://www.youtube.com/watch?v=DM65_JyGxCo",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mAaEz-bwB5DLaBbOSYGMn": {
|
||||
"title": "Runtime Configuration Options",
|
||||
"description": "Docker runtime configuration options give you powerful control over your containers' environments. By tweaking resource limits, network settings, security profiles, and logging drivers, you can optimize performance and enhance security. You'll also find options for setting environment variables, mounting volumes, and overriding default behaviors – all crucial for tailoring containers to your specific needs. For more advanced users, there are tools to adjust kernel capabilities and set restart policies. Whether you're using command-line flags or Docker Compose files, these options help ensure your containers run smoothly and consistently, no matter where they're deployed.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Documentation",
|
||||
"url": "https://docs.docker.com/engine/reference/run/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Runtime Arguments",
|
||||
"url": "https://galea.medium.com/docker-runtime-arguments-604593479f45",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"78YFahP3Fg-c27reLkuK4": {
|
||||
"title": "Container Security",
|
||||
"description": "Container security encompasses a broad set of practices and tools aimed at protecting containerized applications from development through deployment and runtime. It involves securing the container image, ensuring that only trusted and non-vulnerable code is used, implementing strong access controls for container environments, and configuring containers to follow the principle of least privilege. Additionally, it includes monitoring for unexpected behavior, protecting communication between containers, and maintaining the host environment’s security. Effective container security integrates seamlessly into DevSecOps workflows to provide continuous visibility and protection across the container lifecycle without disrupting development speed or agility.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Security",
|
||||
"url": "https://docs.docker.com/engine/security/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Kubernetes Security Best Practices",
|
||||
"url": "https://www.aquasec.com/cloud-native-academy/kubernetes-in-production/kubernetes-security-best-practices-10-steps-to-securing-k8s/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"vYug8kcwrMoWf8ft4UDNI": {
|
||||
"title": "Runtime Security",
|
||||
"description": "Runtime security in Docker focuses on ensuring the safety and integrity of containers during their execution, safeguarding against vulnerabilities and malicious activities that could arise while the containerized application is running. This involves monitoring container behavior for anomalies, implementing access controls to limit permissions, and employing tools to detect and respond to suspicious activity in real time. Effective runtime security also ensures that only verified images are deployed and continuously audits the system to maintain compliance, thereby providing a robust defense layer to prevent exploits and maintain the desired security posture throughout the container lifecycle.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Security",
|
||||
"url": "https://docs.docker.com/engine/security/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Security Best Practices",
|
||||
"url": "https://docs.docker.com/build/building/best-practices/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"M5UG-ZcyhBPbksZd0ZdNt": {
|
||||
"title": "Image Security",
|
||||
"description": "Image security is a crucial aspect of deploying Docker containers in your environment. Ensuring the images you use are secure, up to date, and free of vulnerabilities is essential. In this section, we will review best practices and tools for securing and managing your Docker images. When pulling images from public repositories, always use trusted, official images as a starting point for your containerized applications. Official images are vetted by Docker and are regularly updated with security fixes. You can find these images on the Docker Hub or other trusted registries.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Content Trust",
|
||||
"url": "https://docs.docker.com/engine/security/trust/content_trust/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Hub",
|
||||
"url": "https://hub.docker.com/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"b-LwyYiegbF0jIrn7HYRv": {
|
||||
"title": "Docker CLI",
|
||||
"description": "The Docker Command Line Interface (CLI) is a powerful tool used to interact with the Docker engine, enabling developers and operators to build, manage, and troubleshoot containers and related resources. With a wide range of commands, the Docker CLI provides control over all aspects of Docker, including creating and managing containers (`docker run`, `docker stop`), building images (`docker build`), managing networks (`docker network`), handling storage (`docker volume`), and inspecting system status (`docker ps`, `docker info`). Its intuitive syntax and flexibility allow users to automate complex workflows, streamline development processes, and maintain containerized applications with ease, making it a foundational utility for Docker management and orchestration.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker CLI",
|
||||
"url": "https://docs.docker.com/reference/cli/docker/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Compose",
|
||||
"url": "https://docs.docker.com/compose/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"3Nsg-F3wMKEzEsXw1MBZv": {
|
||||
"title": "Images",
|
||||
"description": "Docker images are lightweight, standalone packages containing everything needed to run software: application code, runtime, libraries, and system tools. Built in layers for efficient storage, they serve as blueprints for containers and can be shared through registries like Docker Hub for consistent deployment across environments.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What's the Difference Between Docker Images and Containers?",
|
||||
"url": "https://aws.amazon.com/compare/the-difference-between-docker-images-and-containers/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is an image?",
|
||||
"url": "https://www.youtube.com/watch?v=NyvT9REqLe4",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"jhwe-xfVc-C7qy8YuS5dZ": {
|
||||
"title": "Containers",
|
||||
"description": "Containers are isolated, lightweight environments that run applications using a shared operating system kernel, ensuring consistency and portability across different computing environments. They encapsulate everything needed to run an application, such as code, dependencies, and configurations, making it easy to move and run the containerized application anywhere. Using the Docker CLI, you can create, start, stop, and manage containers with commands like `docker run`, `docker ps` to list running containers, `docker stop` to halt them, and `docker exec` to interact with them in real time. The CLI provides a powerful interface for developers to build, control, and debug containers effortlessly, allowing for streamlined development and operational workflows.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker CLI Commands",
|
||||
"url": "https://docs.docker.com/engine/reference/commandline/cli/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker CLI Commands Cheat Sheet",
|
||||
"url": "https://docs.docker.com/get-started/docker_cheatsheet.pdf",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"eHtVLB6v3h7hatJb-9cZK": {
|
||||
"title": "Volumes",
|
||||
"description": "Docker volumes are persistent storage solutions used to manage and store data outside the container's filesystem, ensuring data remains intact even if the container is deleted or recreated. They are ideal for storing application data, logs, and configuration files that need to persist across container restarts and updates. With the Docker CLI, you can create and manage volumes using commands like `docker volume create` to define a new volume, `docker volume ls` to list all volumes, and `docker run -v` to mount a volume to a specific container. This approach helps maintain data integrity, simplifies backup processes, and supports data sharing between containers, making volumes a core part of stateful containerized applications.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Volumes",
|
||||
"url": "https://docs.docker.com/storage/volumes/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Volume Commands",
|
||||
"url": "https://docs.docker.com/engine/reference/commandline/volume/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"w5QjzvOaciK2rotOkjvjQ": {
|
||||
"title": "Networks",
|
||||
"description": "Docker networks enable containers to communicate with each other and with external systems, providing the necessary connectivity for microservices architectures. By default, Docker offers several network types such as bridge, host, and overlay, each suited for different use cases like isolated environments, high-performance scenarios, or multi-host communication. Using the Docker CLI, you can create, inspect, and manage networks with commands like `docker network create` to define custom networks, `docker network ls` to list existing networks, and `docker network connect` to attach a container to a network. This flexibility allows developers to control how containers interact, ensuring secure and efficient communication across distributed applications.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Networks",
|
||||
"url": "https://docs.docker.com/network/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Network Commands",
|
||||
"url": "https://docs.docker.com/engine/reference/commandline/network/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Networking",
|
||||
"url": "https://www.youtube.com/watch?v=bKFMS5C4CG0",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hHXTth0ZP8O-iMGR9xfu9": {
|
||||
"title": "Developer Experience",
|
||||
"description": "Docker significantly enhances the developer experience by providing a consistent, isolated environment for building, testing, and running applications, eliminating the “it works on my machine” problem. With Docker, developers can package their applications and dependencies into portable containers, ensuring consistency across different environments, from local development to staging and production. The simplified setup and reproducibility of environments accelerate onboarding, minimize conflicts, and allow developers to focus on coding rather than troubleshooting configurations. Moreover, tools like Docker Compose enable quick orchestration of complex multi-container applications, making it easier to prototype, iterate, and collaborate, ultimately streamlining the entire development lifecycle.\n\nFor more details and practical examples:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Developer Experience Wishlist - Docker",
|
||||
"url": "https://courses.devopsdirective.com/docker-beginner-to-pro/lessons/11-development-workflow/00-devx-wishlist#key-devx-features",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Developer Experience",
|
||||
"url": "https://www.docker.com/blog/cto-chat-overcoming-the-developer-experience-gap-feat-redmonk-flow-io/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"4p5d3rzCHy4vjg2PRX-2k": {
|
||||
"title": "Hot Reloading",
|
||||
"description": "Even though we can speed up the image building with layer caching enable, we don't want to have to rebuild our container image with every code change. Instead, we want the state of our application in the container to reflect changes immediately. We can achieve this through a combination of bind mounts and hot reloading utilities!\n\nHave a look at the following resources for sample implementations:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Hot Reloading - Docker",
|
||||
"url": "https://courses.devopsdirective.com/docker-beginner-to-pro/lessons/11-development-workflow/01-hot-reloading",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"LiAV9crrTHhLqeZhD25a2": {
|
||||
"title": "Debuggers",
|
||||
"description": "In order to make developing with containers competitive with developing locally, we need the ability to run and attach to debuggers inside the container.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Buildx Debug",
|
||||
"url": "https://docs.docker.com/reference/cli/docker/buildx/debug/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Debuggers in Docker",
|
||||
"url": "https://courses.devopsdirective.com/docker-beginner-to-pro/lessons/11-development-workflow/02-debug-and-test",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Kmyo1_Mor9WHLkRhNShRZ": {
|
||||
"title": "Tests",
|
||||
"description": "We want to run tests in an environment as similar as possible to production, so it only makes sense to do so inside of our containers! This can include unit tests, integration tests, and end-to-end tests, all run within Docker containers to simulate real-world scenarios while avoiding interference from external dependencies. Using Docker CLI and tools like Docker Compose, you can create isolated testing environments, run tests in parallel, and spin up and tear down the necessary infrastructure automatically.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Running Tests - Docker",
|
||||
"url": "https://courses.devopsdirective.com/docker-beginner-to-pro/lessons/11-development-workflow/03-tests",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Testing",
|
||||
"url": "https://app.daily.dev/tags/testing?ref=roadmapsh",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"oyqw4tr-taZcxt5kREh1g": {
|
||||
"title": "Continuous Integration",
|
||||
"description": "Continuous integration is the idea of executing some actions (for example build, test, etc...) automatically as you push code to your version control system.\n\nFor containers, there are a number of things we may want to do:\n\n* Build the container images\n* Execute tests\n* Scan container images for vulnerabilities\n* Tag images with useful metadata\n* Push to a container registry\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Continuous Integration - Docker",
|
||||
"url": "https://courses.devopsdirective.com/docker-beginner-to-pro/lessons/11-development-workflow/04-continuous-integration-github-actions",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about CI/CD",
|
||||
"url": "https://app.daily.dev/tags/cicd?ref=roadmapsh",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"qXOGqORi3EdqwsP9Uhi9m": {
|
||||
"title": "Deploying Containers",
|
||||
"description": "Deploying containers is a crucial step in using Docker and containerization to manage applications more efficiently, easily scale, and ensure consistent performance across environments. This topic will give you an overview of how to deploy Docker containers to create and run your applications.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Deployment",
|
||||
"url": "https://docs.docker.com/get-started/deployment/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Compose",
|
||||
"url": "https://docs.docker.com/compose/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Swarm",
|
||||
"url": "https://docs.docker.com/engine/swarm/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"PP_RRBo_pThe2mgf6xzMP": {
|
||||
"title": "PaaS Options",
|
||||
"description": "Platform-as-a-Service (PaaS) options for deploying containers provide a simplified and managed environment where developers can build, deploy, and scale containerized applications without worrying about the underlying infrastructure. Popular PaaS offerings include Google Cloud Run, Azure App Service, AWS Elastic Beanstalk, and Heroku, which abstract away container orchestration complexities while offering automated scaling, easy integration with CI/CD pipelines, and monitoring capabilities. These platforms support rapid development and deployment by allowing teams to focus on application logic rather than server management, providing a seamless way to run containers in production with minimal operational overhead.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PaaS Options for Deploying Containers",
|
||||
"url": "https://www.docker.com/resources/what-container/#paas-options",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Azure Container Instances",
|
||||
"url": "https://azure.microsoft.com/en-us/services/container-instances/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Google Cloud Run",
|
||||
"url": "https://cloud.google.com/run",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "IBM Cloud Code Engine",
|
||||
"url": "https://www.ibm.com/cloud/code-engine",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Amazon Elastic Container Service",
|
||||
"url": "https://aws.amazon.com/ecs/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"RqXpX2XabtHYVjgg1EZR_": {
|
||||
"title": "Kubernetes",
|
||||
"description": "Kubernetes is an open-source container orchestration platform designed to automate the deployment, scaling, and management of containerized applications. It provides a robust framework for handling complex container workloads by organizing containers into logical units called pods, managing service discovery, load balancing, and scaling through declarative configurations. Kubernetes enables teams to deploy containers across clusters of machines, ensuring high availability and fault tolerance through self-healing capabilities like automatic restarts, replacements, and rollback mechanisms. With its extensive ecosystem and flexibility, Kubernetes has become the de facto standard for running large-scale, distributed applications, simplifying operations and improving the reliability of containerized workloads.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Kubernetes",
|
||||
"url": "https://kubernetes.io/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docker Swarm",
|
||||
"url": "https://docs.docker.com/engine/swarm/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"r1eJZDZYouUjnGwAtRbyU": {
|
||||
"title": "Nomad",
|
||||
"description": "Nomad is a cluster manager and scheduler that enables you to deploy, manage and scale your containerized applications. It automatically handles node failures, resource allocation, and container orchestration. Nomad supports running Docker containers as well as other container runtime(s) and non-containerized applications.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Nomad Documentation",
|
||||
"url": "https://www.nomadproject.io/docs",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ks6PFN-0Z9zH7gtWaWgxz": {
|
||||
"title": "Docker Swarm",
|
||||
"description": "Docker Swarm is Docker’s native container orchestration tool that allows users to deploy, manage, and scale containers across a cluster of Docker hosts. By transforming a group of Docker nodes into a single, unified cluster, Swarm provides high availability, load balancing, and automated container scheduling using simple declarative commands. With features like service discovery, rolling updates, and integrated security through TLS encryption, Docker Swarm offers an approachable alternative to more complex orchestrators like Kubernetes. Its tight integration with the Docker CLI and ease of setup make it a suitable choice for small to medium-sized deployments where simplicity and straightforward management are priorities.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Docker Swarm",
|
||||
"url": "https://docs.docker.com/engine/swarm/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -354,8 +354,14 @@
|
||||
},
|
||||
"h7gEQNbGiabDA1q1Bk_IB": {
|
||||
"title": "Emotional Intelligence",
|
||||
"description": "Emotional intelligence is crucial for an Engineering Manager. It helps them understand team dynamics, enhances communication, and strengthens relationships. Their main responsibilities include recognizing team members' emotions, gauging their reactions appropriately, and managing their responses effectively.\n\nEngineering Managers often face challenges in dealing with various personalities within a team. By applying emotional intelligence, they can navigate these difficulties, resolve conflicts, and maintain a positive working environment. Their challenge is to balance their own emotions while addressing those of their team.\n\nSuccess in this aspect requires strong listening skills, empathy, and patience. Engineering Managers also need to continuously improve their emotional intelligence through self-reflection and seeking feedback. This helps them foster a team environment where everyone is understood and valued.",
|
||||
"links": []
|
||||
"description": "Emotional intelligence is crucial for an Engineering Manager. It helps them understand team dynamics, enhances communication, and strengthens relationships. Their main responsibilities include recognizing team members' emotions, gauging their reactions appropriately, and managing their responses effectively.\n\nEngineering Managers often face challenges in dealing with various personalities within a team. By applying emotional intelligence, they can navigate these difficulties, resolve conflicts, and maintain a positive working environment. Their challenge is to balance their own emotions while addressing those of their team.\n\nSuccess in this aspect requires strong listening skills, empathy, and patience. Engineering Managers also need to continuously improve their emotional intelligence through self-reflection and seeking feedback. This helps them foster a team environment where everyone is understood and valued.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Daniel Goleman on the different kinds of empathy",
|
||||
"url": "https://www.youtube.com/watch?v=WdDVvLEKoc8",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ZuZuzwy-Frsn_PFJZVuAQ": {
|
||||
"title": "Defining and Enforcing Values",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"VlNNwIEDWqQXtqkHWJYzC": {
|
||||
"title": "Internet",
|
||||
"description": "The Internet is a global network of interconnected computer networks that use the Internet Protocol Suite (TCP/IP) to communicate. It enables the exchange of data, information, and services across the world, connecting billions of devices and users. The Internet has revolutionized communication, commerce, education, and entertainment, becoming an integral part of modern society. It supports various applications and services, from web browsing and instant messaging to streaming media and online gaming. While offering unprecedented access to information and connectivity, the Internet also raises concerns about privacy, security, and digital divide issues.\n\nVisit the following resources to learn more:",
|
||||
"description": "The Internet, a global network of interconnected computer networks using TCP/IP, facilitates worldwide data exchange, connecting billions of devices. It has revolutionized communication, commerce, education, and entertainment, supporting diverse applications from web browsing to streaming. While offering vast information access, it also presents privacy, security, and digital divide challenges.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Introduction to Internet",
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"yCnn-NfSxIybUQ2iTuUGq": {
|
||||
"title": "How does the internet work?",
|
||||
"description": "The Internet works through a global network of interconnected computers and servers, communicating via standardized protocols. Data is broken into packets and routed through various network nodes using the Internet Protocol (IP). These packets travel across different physical infrastructures, including fiber optic cables, satellites, and wireless networks. The Transmission Control Protocol (TCP) ensures reliable delivery and reassembly of packets at their destination. Domain Name System (DNS) servers translate human-readable website names into IP addresses. When you access a website, your device sends a request to the appropriate server, which responds with the requested data. This process, facilitated by routers, switches, and other networking equipment, enables the seamless exchange of information across vast distances, forming the backbone of our digital communications.\n\nVisit the following resources to learn more:",
|
||||
"description": "The internet is a global network connecting computers and devices for information sharing, enabling activities like browsing websites, sending emails, and streaming videos. It acts as a vast web linking everything, facilitating communication and access to online resources and services worldwide.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Introduction to Internet",
|
||||
@@ -43,7 +43,7 @@
|
||||
},
|
||||
"R12sArWVpbIs_PHxBqVaR": {
|
||||
"title": "What is HTTP?",
|
||||
"description": "HTTP (Hypertext Transfer Protocol) is the foundation of data communication on the World Wide Web. It's an application-layer protocol that defines how messages are formatted and transmitted between web browsers and servers. HTTP operates on a request-response model: clients (usually web browsers) send requests to servers, which then respond with the requested data. The protocol is stateless, meaning each request is independent of any previous requests. HTTP supports various methods (GET, POST, PUT, DELETE, etc.) for different types of interactions with resources. HTTPS is the secure version of HTTP, using encryption to protect data in transit. HTTP/2 and HTTP/3 are more recent versions, offering improved performance through features like multiplexing and header compression. Understanding HTTP is crucial for web development, as it underpins how web applications communicate and function.\n\nVisit the following resources to learn more:",
|
||||
"description": "HTTP (Hypertext Transfer Protocol) is the main way information is sent and received on the internet. It's like the language web browsers and servers use to talk to each other. When you visit a website, your browser sends an HTTP request to the server, and the server sends back the website data. Each request is separate, so the server doesn't remember past requests. HTTP has different commands (like GET for getting data, POST for sending it). HTTPS is a secure version of HTTP that encrypts information. Newer versions like HTTP/2 and HTTP/3 are faster. Knowing HTTP is important for anyone building websites.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Everything you need to know about HTTP",
|
||||
@@ -69,7 +69,7 @@
|
||||
},
|
||||
"ZhSuu2VArnzPDp6dPQQSC": {
|
||||
"title": "What is Domain Name?",
|
||||
"description": "A domain name is a human-readable address used to identify and access websites on the internet. It serves as a more memorable alternative to the numerical IP addresses that computers use to locate web servers. Domain names consist of two main parts: the second-level domain (usually the website or organization name) and the top-level domain (such as .com, .org, or .net). They are registered through domain registrars and must be unique within their top-level domain. Domain names are crucial for branding, marketing, and establishing an online presence. The Domain Name System (DNS) translates domain names into IP addresses, allowing users to access websites by typing in familiar names rather than complex number sequences. Domain names can be customized, transferred, and renewed, playing a vital role in the structure and accessibility of the World Wide Web.\n\nVisit the following resources to learn more:",
|
||||
"description": "A domain name is a human-friendly web address (e.g., [google.com](http://google.com)) that acts as a memorable substitute for numerical IP addresses. It comprises a second-level domain (like \"google\") and a top-level domain (like \".com\"). Registered via domain registrars, these unique names are key for branding and online presence. The Domain Name System (DNS) translates these names into IP addresses, enabling easy website access. Domain names are vital for web structure and accessibility.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a Domain Name?",
|
||||
@@ -90,7 +90,7 @@
|
||||
},
|
||||
"aqMaEY8gkKMikiqleV5EP": {
|
||||
"title": "What is hosting?",
|
||||
"description": "Web hosting is a service that allows individuals and organizations to make their websites accessible on the internet. It involves storing website files on powerful computers called servers, which are connected to a high-speed network. When users enter a domain name in their browser, the web host serves the website's content. Hosting services range from shared hosting (where multiple websites share server resources) to dedicated hosting (where a server is exclusively used by one client). Other types include VPS (Virtual Private Server) hosting, cloud hosting, and managed WordPress hosting. Web hosts typically provide additional services such as email hosting, domain registration, SSL certificates, and technical support. The choice of web hosting impacts a website's performance, security, and scalability, making it a crucial decision for any online presence.\n\nVisit the following resources to learn more:",
|
||||
"description": "Web hosting is like renting space on the internet for your website. It means storing your website's files on special computers called servers, so people can visit your site. When someone types your website address, the hosting service sends them your site's content. There are different kinds of hosting, like shared hosting (sharing a server with others) or dedicated hosting (having a server all to yourself). Hosting companies often offer other services too, like email, domain names, and security certificates. Choosing the right hosting is important for how well your website works.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is Web Hosting?",
|
||||
@@ -111,7 +111,7 @@
|
||||
},
|
||||
"hkxw9jPGYphmjhTjw8766": {
|
||||
"title": "DNS and how it works?",
|
||||
"description": "DNS (Domain Name System) is a hierarchical, decentralized naming system for computers, services, or other resources connected to the Internet or a private network. It translates human-readable domain names (like [www.example.com](http://www.example.com)) into IP addresses (like 192.0.2.1) that computers use to identify each other. DNS servers distributed worldwide work together to resolve these queries, forming a global directory service. The system uses a tree-like structure with root servers at the top, followed by top-level domain servers (.com, .org, etc.), authoritative name servers for specific domains, and local DNS servers. DNS is crucial for the functioning of the Internet, enabling users to access websites and services using memorable names instead of numerical IP addresses.\n\nVisit the following resources to learn more:",
|
||||
"description": "DNS (Domain Name System) translates human-readable domain names (e.g., [www.example.com](http://www.example.com)) into IP addresses computers use. It's a global, decentralized system of servers. When you enter a domain name, DNS servers find the corresponding IP address, letting your browser connect to the website. This makes navigating the internet easy, as you don't need to remember numeric IP addresses.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is DNS?",
|
||||
@@ -142,7 +142,7 @@
|
||||
},
|
||||
"P82WFaTPgQEPNp5IIuZ1Y": {
|
||||
"title": "Browsers and how they work?",
|
||||
"description": "A web browser is a software application that enables a user to access and display web pages or other online content through its graphical user interface.\n\nVisit the following resources to learn more:",
|
||||
"description": "Web browsers are apps that let you view websites. When you type a web address, the browser asks a server for the page. It then reads the HTML (structure), CSS (style), and JavaScript (interactivity) to show you the webpage. Browsers have rendering engines (like Blink in Chrome or Gecko in Firefox) to display content and JavaScript engines (like V8 in Chrome) to run code. They also handle things like security, bookmarks, and history.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How Browsers Work",
|
||||
@@ -168,7 +168,7 @@
|
||||
},
|
||||
"yWG2VUkaF5IJVVut6AiSy": {
|
||||
"title": "HTML",
|
||||
"description": "HTML (Hypertext Markup Language) is the standard markup language used to create web pages and web applications. It provides a structure for content on the World Wide Web, using a system of elements and attributes to define the layout and content of a document. HTML elements are represented by tags, which browsers interpret to render the visual and auditory elements of a web page. The language has evolved through several versions, with HTML5 being the current standard, introducing semantic elements, improved multimedia support, and enhanced form controls. HTML works in conjunction with CSS for styling and JavaScript for interactivity, forming the foundation of modern web development.\n\nVisit the following resources to learn more:",
|
||||
"description": "HTML (Hypertext Markup Language) is the standard for creating web pages, structuring content with elements and attributes. Browsers interpret HTML tags to render pages. HTML5, the current standard, adds semantic elements, multimedia support, and form controls. It works with CSS for styling and JavaScript for interactivity, forming web development's foundation.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Responsive Web Design Certification - Co-Learn HTML & CSS with guided projects",
|
||||
@@ -199,7 +199,7 @@
|
||||
},
|
||||
"PCirR2QiFYO89Fm-Ev3o1": {
|
||||
"title": "Learn the basics",
|
||||
"description": "HTML stands for HyperText Markup Language. It is used on the frontend and gives the structure to the webpage which you can style using CSS and make interactive using JavaScript.\n\nVisit the following resources to learn more:",
|
||||
"description": "HTML (HyperText Markup Language) is the backbone of webpages. It structures the content you see online. You use CSS to style this HTML structure and JavaScript to make it interactive. Think of HTML as the skeleton of a website.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "W3Schools: Learn HTML",
|
||||
@@ -220,7 +220,7 @@
|
||||
},
|
||||
"z8-556o-PaHXjlytrawaF": {
|
||||
"title": "Writing Semantic HTML",
|
||||
"description": "Semantic HTML refers to the use of HTML markup to reinforce the meaning of web content, rather than merely defining its appearance. It involves using HTML elements that clearly describe their purpose and content. Semantic HTML improves accessibility, SEO, and code readability. Key elements include `<header>`, `<nav>`, `<main>`, `<article>`, `<section>`, `<aside>`, and `<footer>`. It also encompasses using appropriate heading levels (`<h1>` to `<h6>`), lists (`<ul>`, `<ol>`,`<li>`), and data tables (`<table>`, `<th>`, `<td>`). Semantic HTML helps screen readers interpret page content, enables better browser rendering, and provides clearer structure for developers. By using semantically correct elements, developers create more meaningful, accessible, and maintainable web documents that are easier for both humans and machines to understand and process.\n\nVisit the following resources to learn more:",
|
||||
"description": "Semantic HTML uses markup to convey the meaning of web content, not just its appearance, by employing elements like `<header>`, `<nav>`, `<main>`, `<article>`, `<section>`, `<aside>`, and `<footer>`. This practice enhances accessibility, SEO, and code readability. It includes proper use of headings, lists, and tables. Semantic HTML aids screen readers, improves browser rendering, and offers clearer structure for developers, leading to more meaningful, accessible, and maintainable web documents for both humans and machines.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Guide to Writing Semantic HTML",
|
||||
@@ -246,7 +246,7 @@
|
||||
},
|
||||
"V5zucKEHnIPPjwHqsMPHF": {
|
||||
"title": "Forms and Validations",
|
||||
"description": "Before submitting data to the server, it is important to ensure all required form controls are filled out, in the correct format. This is called client-side form validation, and helps ensure data submitted matches the requirements set forth in the various form controls.\n\nVisit the following resources to learn more:",
|
||||
"description": "Before sending data from a form to a server, it's important to check if all required fields are filled in correctly. This is called client-side form validation. It helps make sure the data sent matches what the form expects, improving data quality and user experience.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "MDN Web Docs: Client-side form validation",
|
||||
@@ -267,7 +267,7 @@
|
||||
},
|
||||
"iJIqi7ngpGHWAqtgdjgxB": {
|
||||
"title": "Accessibility",
|
||||
"description": "Website accessibility is the practice of designing and developing websites that can be used by everyone, including people with disabilities. It involves implementing features and standards that make web content perceivable, operable, understandable, and robust for all users, regardless of their physical or cognitive abilities. This includes providing text alternatives for images, ensuring keyboard navigation, using sufficient color contrast, offering captions for audio content, and creating a consistent and predictable layout. Adhering to accessibility guidelines not only improves usability for people with disabilities but also enhances the overall user experience for all visitors while potentially increasing a site's reach and legal compliance.\n\nVisit the following resources to learn more:",
|
||||
"description": "Website accessibility means making sites usable by everyone, including those with disabilities. This involves clear design, like text for images, keyboard navigation, good color contrast, and captions. Following accessibility rules helps all users, not just those with disabilities, and can also help with legal compliance and reaching more people.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Accessibility for Developers by Google",
|
||||
@@ -288,7 +288,7 @@
|
||||
},
|
||||
"mH_qff8R7R6eLQ1tPHLgG": {
|
||||
"title": "SEO Basics",
|
||||
"description": "SEO (Search Engine Optimization) basics involve strategies to improve a website's visibility and ranking in search engine results. Key elements include creating relevant, high-quality content, proper use of keywords, optimizing meta tags and URLs, ensuring mobile-friendliness, improving site speed, and building quality backlinks. SEO also focuses on user experience, including easy navigation and responsive design. Technical aspects like XML sitemaps, HTTPS security, and structured data markup play crucial roles. Understanding user intent, regularly updating content, and adhering to search engine guidelines are essential practices. Effective SEO combines on-page optimization, off-page tactics, and technical enhancements to increase organic traffic, improve user engagement, and enhance online presence in an increasingly competitive digital landscape.\n\nVisit the following resources to learn more:",
|
||||
"description": "SEO (Search Engine Optimization) helps websites show up higher in search results. This means using good keywords, creating quality content, making sure your site is fast and mobile-friendly, and getting links from other trusted sites. Good navigation and design also help. Technical things like sitemaps and secure HTTPS are important too. SEO aims to get more visitors from search engines by improving your site both on the page and technically.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Complete SEO Course for Beginners",
|
||||
@@ -314,7 +314,7 @@
|
||||
},
|
||||
"ZhJhf1M2OphYbEmduFq-9": {
|
||||
"title": "CSS",
|
||||
"description": "CSS (Cascading Style Sheets) is a styling language used to describe the presentation of a document written in HTML or XML. It defines how elements should be displayed on screen, on paper, or in other media. CSS separates the design from the content, allowing for greater flexibility and control over the layout, colors, and fonts of web pages. It uses a system of selectors to target HTML elements and apply styles to them. CSS supports responsive design through media queries, enabling the creation of layouts that adapt to different screen sizes and devices. The cascade, inheritance, and specificity are key concepts in CSS that determine how styles are applied when multiple rules target the same element. Modern CSS includes features like Flexbox and Grid for advanced layout control, animations, and transitions for creating dynamic user interfaces.\n\nVisit the following resources to learn more:",
|
||||
"description": "CSS (Cascading Style Sheets) is what makes websites look good. It's a language used to style HTML documents, controlling things like layout, colors, and fonts. CSS keeps the design separate from the HTML content, which makes websites easier to manage. It uses \"selectors\" to pick HTML elements and apply styles. CSS also helps make websites responsive, meaning they look good on any device, thanks to media queries. Important ideas in CSS are the cascade (how styles override each other), inheritance (how styles pass from parent to child elements), and specificity (which style rule wins). Modern CSS has cool tools like Flexbox and Grid for layout, plus animations and transitions for interactive designs.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "CSS Complete Course",
|
||||
@@ -345,7 +345,7 @@
|
||||
},
|
||||
"YFjzPKWDwzrgk2HUX952L": {
|
||||
"title": "Learn the basics",
|
||||
"description": "CSS or Cascading Style Sheets is the language used to style the frontend of any website. CSS is a cornerstone technology of the World Wide Web, alongside HTML and JavaScript.\n\nVisit the following resources to learn more:",
|
||||
"description": "CSS (Cascading Style Sheets) is the language for styling websites. It makes the frontend look good. Along with HTML and JavaScript, CSS is a key part of the World Wide Web. It controls colors, fonts, layout, and more to visually design webpages.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "W3Schools — Learn CSS",
|
||||
@@ -371,7 +371,7 @@
|
||||
},
|
||||
"dXeYVMXv-3MRQ1ovOUuJW": {
|
||||
"title": "Making Layouts",
|
||||
"description": "Making layouts in web design involves organizing content and visual elements on a page to create an effective and aesthetically pleasing user interface. Modern layout techniques primarily use CSS, with key approaches including:\n\n1. Flexbox for one-dimensional layouts (rows or columns)\n2. CSS Grid for two-dimensional layouts\n3. Responsive design principles for adaptability across devices\n4. CSS frameworks like Bootstrap or Tailwind for rapid development\n5. Custom CSS properties (variables) for consistent styling\n6. Media queries for device-specific adjustments\n7. CSS positioning and float for specific element placement\n\nThese tools allow designers to create complex, responsive layouts that maintain consistency and usability across various screen sizes and devices. Effective layouts consider visual hierarchy, user flow, accessibility, and content prioritization to enhance the overall user experience and achieve design goals.\n\nVisit the following resources to learn more:",
|
||||
"description": "Making web layouts means arranging content on a page well. Modern CSS uses Flexbox (for rows/columns) and CSS Grid (for 2D layouts). Responsive design makes sites fit all devices. Frameworks like Bootstrap or Tailwind help build faster. Good layouts think about what's important, how users move through the page, and if everyone can use it. This makes the site look good and work well.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Learn and Practice Flexbox",
|
||||
@@ -397,7 +397,7 @@
|
||||
},
|
||||
"TKtWmArHn7elXRJdG6lDQ": {
|
||||
"title": "Responsive Design",
|
||||
"description": "Responsive web design is an approach to web development that creates dynamic changes to the appearance of a website, depending on the screen size and orientation of the device being used to view it. It uses fluid grids, flexible images, and CSS media queries to adapt the layout to the viewing environment. The goal is to build web pages that detect the visitor's screen size and orientation and change the layout accordingly, providing an optimal viewing experience across a wide range of devices, from desktop computers to mobile phones. This approach eliminates the need for a different design and development phase for each new gadget on the market, while ensuring a consistent and intuitive user experience across all devices.\n\nVisit the following resources to learn more:",
|
||||
"description": "Responsive web design makes websites look good on all devices, from big desktops to small phones. It uses flexible layouts, images that resize, and CSS media queries to change how the site appears based on screen size. This way, users get a great experience no matter what device they use, and developers don't need to make separate sites for each gadget.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Conquering Responsive Layouts",
|
||||
@@ -410,7 +410,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "The Beginner’s Guide to Responsive Web Design",
|
||||
"title": "The Beginner's Guide to Responsive Web Design",
|
||||
"url": "https://kinsta.com/blog/responsive-web-design/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -423,7 +423,7 @@
|
||||
},
|
||||
"ODcfFEorkfJNupoQygM53": {
|
||||
"title": "JavaScript",
|
||||
"description": "JavaScript is a high-level, interpreted programming language that is a core technology of the World Wide Web. It allows for dynamic, client-side scripting in web browsers, enabling interactive web pages and user interfaces. JavaScript supports object-oriented, imperative, and functional programming styles. It's also used server-side through Node.js, for desktop application development with frameworks like Electron, and in various other contexts. The language features dynamic typing, first-class functions, and prototype-based object-orientation. JavaScript's ubiquity in web development, coupled with its versatility and continuous evolution through ECMAScript standards, has made it one of the most popular programming languages in use today.\n\nVisit the following resources to learn more:",
|
||||
"description": "JavaScript is a key programming language for the web. It makes websites interactive, like when things move or change when you click them. It works in web browsers but also on servers (with Node.js) and for desktop apps. JavaScript is flexible and always updating, making it very popular for all kinds of web development.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated JavaScript Roadmap",
|
||||
@@ -449,7 +449,7 @@
|
||||
},
|
||||
"A4brX0efjZ0FFPTB4r6U0": {
|
||||
"title": "Fetch API / Ajax (XHR)",
|
||||
"description": "The Fetch API is a modern JavaScript interface for making HTTP requests in web browsers. It provides a more powerful and flexible way to send and receive data compared to older methods like XMLHttpRequest. Fetch uses Promises, allowing for cleaner asynchronous code. It supports various data formats, custom headers, and different types of requests (GET, POST, etc.). The API is designed to be extensible and integrates well with other web technologies. While simpler for basic use cases, Fetch also handles complex scenarios like request cancellation and reading streamed responses. It's widely supported in modern browsers and has become the standard for network requests in client-side JavaScript applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "The Fetch API is a new way for JavaScript in browsers to make HTTP requests (getting or sending data online). It's better and more flexible than older ways like XMLHttpRequest. Fetch uses Promises, making code that waits for data cleaner. It handles different data types and request methods (GET, POST). It's now the standard way for websites to talk to servers and is supported by most browsers.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Fetch API MDN Docs",
|
||||
@@ -475,7 +475,7 @@
|
||||
},
|
||||
"0MAogsAID9R04R5TTO2Qa": {
|
||||
"title": "Learn DOM Manipulation",
|
||||
"description": "The Document Object Model (DOM) is a programming interface built for HTML and XML documents. It represents the page that allows programs and scripts to dynamically update the document structure, content, and style. With DOM, we can easily access and manipulate tags, IDs, classes, attributes, etc.\n\nVisit the following resources to learn more:",
|
||||
"description": "The Document Object Model (DOM) is how programs see HTML and XML documents. It lets scripts change a page's structure, content, and style dynamically. With the DOM, you can easily work with HTML tags, IDs, classes, and attributes to make webpages interactive.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "DOM Tree",
|
||||
@@ -506,7 +506,7 @@
|
||||
},
|
||||
"wQSjQqwKHfn5RGPk34BWI": {
|
||||
"title": "Learn the Basics",
|
||||
"description": "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.\n\nVisit the following resources to learn more:",
|
||||
"description": "JavaScript makes webpages interactive. Think of sliders, what happens when you click something, or pop-up messages – that's often JavaScript at work. It adds dynamic behavior to the static structure created by HTML and styled by CSS.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "W3Schools – JavaScript Tutorial",
|
||||
@@ -527,7 +527,7 @@
|
||||
},
|
||||
"MXnFhZlNB1zTsBFDyni9H": {
|
||||
"title": "VCS Hosting",
|
||||
"description": "Repo hosting services provide platforms for storing, managing, and collaborating on software projects using version control systems, primarily Git. These services offer features like issue tracking, pull requests, code review tools, wikis, and continuous integration/continuous deployment (CI/CD) pipelines. Popular platforms include GitHub, GitLab, Bitbucket, and SourceForge, each with unique offerings. GitHub, owned by Microsoft, is the largest and most widely used, known for its open-source community. GitLab offers a complete DevOps platform with built-in CI/CD. Bitbucket, part of Atlassian's suite, integrates well with other Atlassian tools. These services facilitate team collaboration, code sharing, and project management, making them integral to modern software development workflows. They also often provide features like access control, branch protection, and integration with various development tools, enhancing the overall development process.\n\nVisit the following resources to learn more:",
|
||||
"description": "Repo hosting services, like GitHub, GitLab, and Bitbucket, are platforms for storing, managing, and collaborating on software projects using version control systems, mainly Git. They facilitate teamwork, code sharing, and project tracking in software development.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Git & Github Roadmap",
|
||||
@@ -558,7 +558,7 @@
|
||||
},
|
||||
"NIY7c4TQEEHx0hATu-k5C": {
|
||||
"title": "Version Control Systems",
|
||||
"description": "Version Control Systems (VCS) are tools that help developers track and manage changes to code over time. They allow multiple people to work on a project simultaneously, maintaining a history of modifications. Git is the most popular VCS, known for its distributed nature and branching model. Other systems include Subversion (SVN) and Mercurial. VCS enables features like branching for parallel development, merging to combine changes, and reverting to previous states. They facilitate collaboration through remote repositories, pull requests, and code reviews. VCS also provides backup and recovery capabilities, conflict resolution, and the ability to tag specific points in history. By maintaining a detailed record of changes and supporting non-linear development, VCS has become an essential tool in modern software development, enhancing productivity, code quality, and team collaboration.\n\nVisit the following resources to learn more:",
|
||||
"description": "Version Control Systems (VCS) like Git, Subversion, and Mercurial track code changes, enabling multiple developers to collaborate by maintaining a history of modifications. Git is popular for its distributed model and branching. VCS features include branching for parallel work, merging changes, reverting to past states, remote repositories, pull requests, and code reviews. They also offer backup, recovery, conflict resolution, and tagging. VCS is crucial in modern software development for productivity, code quality, and collaboration.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Git & Github Roadmap",
|
||||
@@ -584,7 +584,7 @@
|
||||
},
|
||||
"R_I4SGYqLk5zze5I1zS_E": {
|
||||
"title": "Git",
|
||||
"description": "Git is a distributed version control system designed to handle projects of any size with speed and efficiency. Created by Linus Torvalds in 2005, Git tracks changes in source code during software development, allowing multiple developers to work together on non-linear development. It provides strong support for branching, merging, and distributed development workflows. Git maintains a complete history of all changes, enabling easy rollbacks and comparisons between versions. Its distributed nature means each developer has a full copy of the repository, allowing for offline work and backup. Git's speed, flexibility, and robust branching and merging capabilities have made it the most widely used version control system in software development, particularly for open-source projects.\n\nVisit the following resources to learn more:",
|
||||
"description": "Git is a tool for tracking code changes in software projects. It lets many developers work together by keeping a history of all changes. Git is great for branching (working on different features at once) and merging (combining changes). Everyone has a full copy of the project, so they can work offline. Git is fast, flexible, and the most popular version control system, especially for open-source projects.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Git & Github Roadmap",
|
||||
@@ -615,7 +615,7 @@
|
||||
},
|
||||
"IqvS1V-98cxko3e9sBQgP": {
|
||||
"title": "Package Managers",
|
||||
"description": "Package managers are tools that automate the process of installing, updating, configuring, and removing software packages in a consistent manner. They handle dependency resolution, version management, and package distribution for programming languages and operating systems. Popular package managers include npm for JavaScript, pip for Python, and apt for Debian-based Linux distributions. These tools maintain a centralized repository of packages, allowing developers to easily share and reuse code. Package managers simplify project setup, ensure consistency across development environments, and help manage complex dependency trees. They play a crucial role in modern software development by streamlining workflow, enhancing collaboration, and improving code reusability.\n\nVisit the following resources to learn more:",
|
||||
"description": "Package managers are tools that help install, update, and remove software pieces (packages). They manage versions and what other packages are needed. Examples are npm for JavaScript and pip for Python. They make it easy to share and reuse code by keeping packages in one place. Package managers simplify project setup and help keep things consistent. They are very important for modern software development by making work smoother and improving teamwork.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PNPM: The Faster, More Performant NPM",
|
||||
@@ -646,7 +646,7 @@
|
||||
},
|
||||
"qmTVMJDsEhNIkiwE_UTYu": {
|
||||
"title": "GitHub",
|
||||
"description": "GitHub has become a central hub for open-source projects and is widely used by developers, companies, and organizations for both private and public repositories. It was acquired by Microsoft in 2018 but continues to operate as a relatively independent entity. GitHub's popularity has made it an essential tool in modern software development workflows and a key platform for showcasing coding projects and contributing to open-source software.\n\nVisit the following resources to learn more:",
|
||||
"description": "GitHub is a popular website for hosting Git projects. It's a key place for open-source software and is used by developers and companies for both public and private code. Microsoft bought GitHub in 2018. It's a vital tool for modern software development, showing off projects, and contributing to open-source.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Git & Github Roadmap",
|
||||
@@ -677,7 +677,7 @@
|
||||
},
|
||||
"zIoSJMX3cuzCgDYHjgbEh": {
|
||||
"title": "GitLab",
|
||||
"description": "GitLab is a web-based DevOps platform that provides a complete solution for the software development lifecycle. GitLab emphasizes an all-in-one approach, integrating various development tools into a single platform. It's available as both a cloud-hosted service and a self-hosted solution, giving organizations flexibility in deployment. GitLab's focus on DevOps practices and its comprehensive feature set make it popular among enterprises and teams seeking a unified platform for their entire development workflow. While similar to GitHub in many respects, GitLab's integrated CI/CD capabilities and self-hosting options are often cited as key differentiators.\n\nVisit the following resources to learn more:",
|
||||
"description": "GitLab is a web platform for the entire software development process (DevOps). It offers many tools in one place. You can use it online or host it yourself. GitLab is popular with businesses that want one platform for all their development work. It's like GitHub but often highlighted for its built-in CI/CD (automating build and release) and self-hosting options.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "GitLab Website",
|
||||
@@ -703,7 +703,7 @@
|
||||
},
|
||||
"DILBiQp7WWgSZ5hhtDW6A": {
|
||||
"title": "Bitbucket",
|
||||
"description": "Bitbucket is a web-based version control repository hosting service owned by Atlassian. It provides Git and Mercurial version control systems for both open source and private projects. Bitbucket offers features such as pull requests, branch permissions, and in-line commenting for code review. It integrates seamlessly with other Atlassian products like Jira and Trello, facilitating project management and issue tracking. Bitbucket provides both cloud-hosted and self-hosted options, catering to different organizational needs. It supports continuous integration and deployment (CI/CD) through Bitbucket Pipelines.\n\nVisit the following resources to learn more:",
|
||||
"description": "Bitbucket, by Atlassian, hosts Git and Mercurial code projects. It’s for both open-source and private work. Bitbucket has features like pull requests and code review comments. It works well with other Atlassian tools like Jira for project management. You can use it online or host it yourself. Bitbucket also supports CI/CD (automating build and release) with Bitbucket Pipelines.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How to use BitBucket?",
|
||||
@@ -724,7 +724,7 @@
|
||||
},
|
||||
"yrq3nOwFREzl-9EKnpU-e": {
|
||||
"title": "yarn",
|
||||
"description": "Yarn is a fast, reliable, and secure package manager for JavaScript, developed by Facebook as an alternative to npm (Node Package Manager). It addresses issues of consistency, security, and performance in dependency management. Yarn uses a lockfile to ensure consistent installations across different environments and offers parallel installation of packages, significantly speeding up the process. It features offline mode, allowing installation from cached packages, and provides improved network performance through request queuing and retries. Yarn's focus on security includes checksum verification of every installed package. While it shares many features with npm, Yarn's emphasis on speed, reliability, and security has made it a popular choice among developers, especially for larger projects. Recent versions of Yarn (Berry) introduce new features like Plug'n'Play for even faster and more efficient package resolution.\n\nVisit the following resources to learn more:",
|
||||
"description": "Yarn is a JavaScript package manager developed by Facebook, offering a fast, reliable, and secure alternative to npm. It ensures consistent installations with a lockfile, speeds up processes with parallel installations and offline mode, and enhances security via checksum verification. Yarn, especially later versions like Berry with Plug'n'Play, is favored for large projects due to its performance and efficiency in dependency management.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Yarn - Getting Started",
|
||||
@@ -745,7 +745,7 @@
|
||||
},
|
||||
"SLxA5qJFp_28TRzr1BjxZ": {
|
||||
"title": "pnpm",
|
||||
"description": "pnpm (performant npm) is a fast, disk-space efficient package manager for JavaScript and Node.js projects. It addresses inefficiencies in npm and Yarn by using a unique approach to storing and linking dependencies. pnpm creates a single, global store for all packages and uses hard links to reference them in project node\\_modules, significantly reducing disk space usage and installation time. It strictly adheres to package.json specifications, ensuring consistent installs across environments. pnpm offers features like workspace support for monorepos, side-by-side versioning, and improved security through better isolation of dependencies. While less widely adopted than npm or Yarn, pnpm's performance benefits and efficient disk usage are attracting increasing attention in the JavaScript community.\n\nVisit the following resources to learn more:",
|
||||
"description": "pnpm (performant npm) is a fast and disk-space-saving package manager for JavaScript. It's different from npm and Yarn because it stores packages in one global place and links to them. This means less disk space used and faster installs. pnpm follows `package.json` strictly, making sure installs are consistent. It supports monorepos and is more secure by keeping dependencies separate. It's gaining popularity for its speed and efficiency.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PNPM Website",
|
||||
@@ -766,7 +766,7 @@
|
||||
},
|
||||
"ib_FHinhrw8VuSet-xMF7": {
|
||||
"title": "npm",
|
||||
"description": "npm (Node Package Manager) is the default package manager for Node.js, providing a vast ecosystem of reusable JavaScript code. It allows developers to easily share, discover, and install packages (libraries and tools) for their projects. npm consists of a command-line interface for package installation and management, and an online repository of open-source packages. It handles dependency management, version control, and script running for Node.js projects. The npm registry is the largest software registry in the world, containing over a million packages. npm's package.json file defines project metadata and dependencies, enabling reproducible builds across different environments. Despite competition from alternatives like Yarn, npm remains the most widely used package manager in the JavaScript ecosystem.\n\nVisit the following resources to learn more:",
|
||||
"description": "npm (Node Package Manager) is the main tool for managing code packages in Node.js. It helps developers find, share, and use JavaScript code easily. Think of it as a big library where you can get tools and code bits for your projects. npm uses a file called `package.json` to keep track of what your project needs, making it easy to build your project anywhere. Even with other tools like Yarn, npm is still the most popular choice for JavaScript developers.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How to NPM",
|
||||
@@ -802,10 +802,10 @@
|
||||
},
|
||||
"eXezX7CVNyC1RuyU_I4yP": {
|
||||
"title": "Pick a Framework",
|
||||
"description": "Web frameworks are designed to write web applications. Frameworks are collections of libraries that aid in the development of a software product or website. Frameworks for web application development are collections of various tools. Frameworks vary in their capabilities and functions, depending on the tasks set. They define the structure, establish the rules, and provide the development tools required.\n\nVisit the following resources to learn more:",
|
||||
"description": "Web frameworks are toolkits for building web applications. They are sets of libraries that help create software or websites. Different frameworks have different features, depending on what you need to do. They give structure, rules, and tools for development. Examples are React, Angular, and Vue.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "15 Crazy New JS Framework Features You Don’t Know Yet",
|
||||
"title": "15 Crazy New JS Framework Features You Don't Know Yet",
|
||||
"url": "https://www.youtube.com/watch?v=466U-2D86bc",
|
||||
"type": "video"
|
||||
},
|
||||
@@ -818,7 +818,7 @@
|
||||
},
|
||||
"-bHFIiXnoUQSov64WI9yo": {
|
||||
"title": "Angular",
|
||||
"description": "Angular is a popular open-source web application framework developed and maintained by Google. It uses TypeScript, a statically typed superset of JavaScript, to build scalable and efficient single-page applications (SPAs). Angular follows a component-based architecture, where the user interface is composed of reusable, self-contained components. The framework provides features like two-way data binding, dependency injection, and a powerful template syntax, which simplify the development of complex web applications. Angular also includes a comprehensive set of tools for testing, routing, and state management, making it a full-fledged solution for front-end development. Its modular structure and emphasis on best practices make it particularly suitable for large-scale enterprise applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "Angular is a popular tool from Google for building websites and web apps. It uses TypeScript (a type of JavaScript) to create large, efficient single-page applications (SPAs), where content loads in one go without needing to reload the whole page. Angular builds UIs with reusable components, like building blocks. It has features like two-way data binding (data updates automatically in different places), dependency injection (helps manage code parts), and a strong template system. Angular also offers tools for testing, page navigation, and managing app data, making it great for big, complex projects.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Angular Roadmap",
|
||||
@@ -844,7 +844,7 @@
|
||||
},
|
||||
"ERAdwL1G9M1bnx-fOm5ZA": {
|
||||
"title": "Vue.js",
|
||||
"description": "Vue.js is a progressive JavaScript framework for building user interfaces. It's designed to be incrementally adoptable, allowing developers to integrate it into projects gradually. Vue uses a template-based approach with a virtual DOM for efficient rendering. It features a reactive and composable component system, making it easy to organize and reuse code. Vue's core library focuses on the view layer, but it can be easily extended with official and community-built tools for state management, routing, and build tooling. Known for its gentle learning curve and flexibility, Vue has gained popularity for both small projects and large-scale applications. Its performance, lightweight nature, and comprehensive documentation have contributed to its widespread adoption in the web development community.\n\nVisit the following resources to learn more:",
|
||||
"description": "Vue.js is a JavaScript framework for building website interfaces. It's easy to start with and can be added to projects bit by bit. Vue uses templates and a virtual DOM (a lightweight copy of the real page) to show things on screen efficiently. It has a system of reusable components, making code organized. While Vue mainly handles what users see, it works well with other tools for things like managing data or page navigation. It's known for being easy to learn, flexible, and fast, making it popular for all kinds of projects.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Vue Roadmap",
|
||||
@@ -870,7 +870,7 @@
|
||||
},
|
||||
"tG5v3O4lNIFc2uCnacPak": {
|
||||
"title": "React",
|
||||
"description": "React is an open-source JavaScript library for building user interfaces, primarily for single-page applications. Developed and maintained by Facebook, it allows developers to create reusable UI components that efficiently update and render as data changes. React uses a virtual DOM for performance optimization and supports a unidirectional data flow. Its component-based architecture promotes modularity and reusability. React's ecosystem includes tools like Redux for state management and React Native for mobile app development. The library's declarative nature, efficient rendering, and strong community support have made it one of the most popular choices for front-end development in modern web applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "React is a JavaScript tool from Facebook for building UIs, especially for single-page apps. It lets you create reusable UI parts that update when data changes. React uses a virtual DOM for speed and has a one-way data flow. This component style makes code neat and reusable. React also works with tools like Redux for data management and React Native for mobile apps. It's popular because it's clear, fast, and has a big community.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Full Stack React Developer Course",
|
||||
@@ -896,7 +896,7 @@
|
||||
},
|
||||
"ZR-qZ2Lcbu3FtqaMd3wM4": {
|
||||
"title": "Svelte",
|
||||
"description": "Svelte is a modern JavaScript framework for building user interfaces that takes a unique approach to web development. Unlike traditional frameworks that do most of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app. It compiles your code to efficient vanilla JavaScript, resulting in smaller bundle sizes and better runtime performance. Svelte uses a component-based architecture and features a simple, intuitive syntax that allows developers to write less code. It includes built-in state management, CSS scoping, and animations. Svelte's approach eliminates the need for a virtual DOM, leading to faster initial loads and updates. Its simplicity and performance benefits have been gaining it increasing popularity in the front-end development community.\n\nVisit the following resources to learn more:",
|
||||
"description": "Svelte is a JavaScript tool for building UIs. Unlike others, Svelte does its main work during build time, not in the browser. It turns your code into small, fast, plain JavaScript. Svelte uses components and has a simple syntax, so you write less code. It includes features for managing data, styling, and animations. Because Svelte doesn't use a virtual DOM, pages load and update quickly. It's liked for its simplicity and speed.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Svelte Course Playlist for beginners",
|
||||
@@ -927,7 +927,7 @@
|
||||
},
|
||||
"DxOSKnqAjZOPP-dq_U7oP": {
|
||||
"title": "Solid JS",
|
||||
"description": "SolidJS is a declarative, efficient, and flexible JavaScript library for building user interfaces. It uses a fine-grained reactivity system that updates only what changes, resulting in high performance. SolidJS compiles templates to real DOM nodes and updates them in-place, avoiding the overhead of a virtual DOM. It offers a syntax similar to React, making it familiar to many developers, but with a different underlying mechanism. SolidJS supports JSX, provides built-in state management, and emphasizes composition over inheritance. Its small size and lack of runtime overhead make it particularly suitable for applications requiring high performance. While newer compared to some frameworks, SolidJS is gaining popularity for its simplicity, performance, and developer-friendly approach to reactive programming.\n\nVisit the following resources to learn more:",
|
||||
"description": "SolidJS is a JavaScript tool for building website interfaces that is known for being very fast and efficient. It updates only the parts of the page that change, which makes it perform well. SolidJS doesn't use a virtual DOM; instead, it directly changes the real DOM. Its syntax is like React, so many developers find it familiar. It supports JSX, has built-in ways to manage data, and is small in size. SolidJS is getting popular for its speed, simplicity, and smart way of handling updates.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "SolidJS Website",
|
||||
@@ -953,7 +953,7 @@
|
||||
},
|
||||
"N5DCb6bDfgUnSdHPLYY4g": {
|
||||
"title": "Qwik",
|
||||
"description": "Qwik is an open-source front-end framework designed for optimal performance and near-instant loading of web applications. It focuses on delivering a \"resumable\" application model, where the app can start running with minimal JavaScript downloaded. Qwik achieves this through fine-grained lazy loading, serialization of the application state, and prefetching. It uses a unique approach to hydration, only loading JavaScript for interactive elements when needed. Qwik is built for modern web standards and aims to solve performance issues common in large-scale web applications. While still relatively new compared to established frameworks, Qwik's innovative approach to performance optimization is garnering attention in the web development community.\n\nLearn more from the following resources:",
|
||||
"description": "Qwik is a new front-end tool for making websites load super fast. It uses a \"resumable\" idea, meaning apps start with very little JavaScript. Qwik only loads code for interactive parts when you need them. This makes even big websites feel quick. It's getting noticed for its smart way of making sites fast.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Qwik Website",
|
||||
@@ -969,7 +969,7 @@
|
||||
},
|
||||
"XDTD8el6OwuQ55wC-X4iV": {
|
||||
"title": "Writing CSS",
|
||||
"description": "Modern CSS emphasizes responsive design with techniques like media queries and fluid typography. It also includes methodologies like CSS-in-JS and utility-first frameworks (e.g., Tailwind CSS). Features such as CSS Logical Properties improve internationalization, while CSS Houdini allows for more powerful custom styling. Modern CSS focuses on performance optimization, maintainability, and creating adaptive, accessible designs across various devices and screen sizes, significantly improving the capabilities and efficiency of web styling.\n\nVisit the following resources to learn more:",
|
||||
"description": "Modern CSS helps make websites that look good on any device using things like media queries and flexible text sizes. It also includes new ways to write CSS, like CSS-in-JS or using frameworks like Tailwind CSS. Features like CSS Logical Properties help with different languages, and CSS Houdini gives more power for custom styles. The main goals are to make CSS fast, easy to keep up, and to create designs that work well for everyone on any screen.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Modern CSS: A Comprehensive Guide",
|
||||
@@ -990,7 +990,7 @@
|
||||
},
|
||||
"eghnfG4p7i-EDWfp3CQXC": {
|
||||
"title": "Tailwind",
|
||||
"description": "Tailwind CSS is a utility-first CSS framework that provides low-level utility classes to build custom designs without leaving your HTML. It offers a highly customizable set of pre-defined classes for layout, typography, color, and more, allowing rapid UI development. Tailwind emphasizes flexibility and composability, enabling developers to create unique designs without writing custom CSS. It uses a mobile-first approach and includes a built-in purge feature to remove unused styles in production, resulting in smaller file sizes. Tailwind's philosophy promotes consistency in design while maintaining the freedom to create custom looks. Its popularity has grown due to its efficiency in prototyping and building responsive designs quickly.\n\nVisit the following resources to learn more:",
|
||||
"description": "Tailwind CSS is a special kind of CSS framework that gives you lots of small, ready-to-use style classes. You use these classes directly in your HTML to build custom designs quickly, without writing much custom CSS. It's very flexible and lets you create unique looks. Tailwind is designed for mobile first and automatically removes unused styles to keep your website files small. It's popular because it helps build and try out responsive designs fast.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Tailwind CSS Full Course for Beginners",
|
||||
@@ -1026,7 +1026,7 @@
|
||||
},
|
||||
"nPg_YWpMJtlhU2t2UD_6B": {
|
||||
"title": "CSS Architecture",
|
||||
"description": "CSS architecture refers to the methodologies and organizational strategies used to structure and maintain CSS code in large-scale web projects. It focuses on creating scalable, maintainable, and modular stylesheets to manage the growing complexity of web applications. Key concepts include naming conventions (like BEM or SMACSS), component-based design, separation of concerns, and the use of preprocessors (such as Sass or Less). CSS architecture often employs techniques like CSS modules, utility classes, or CSS-in-JS solutions to improve code reusability and reduce specificity conflicts. The goal is to create a systematic approach to styling that enhances collaboration among developers, reduces code duplication, and facilitates easier updates and maintenance of the visual design across an entire application or website.\n\nVisit the following resources to learn more:",
|
||||
"description": "CSS architecture organizes CSS in large web projects for scalability and maintenance. It involves naming conventions (like BEM), component-based design, and tools like preprocessors. Techniques like CSS modules or utility classes enhance reusability and reduce conflicts. The aim is a systematic styling approach for better collaboration, less code duplication, and easier updates.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How to Organize Your CSS with a Modular Architecture",
|
||||
@@ -1047,7 +1047,7 @@
|
||||
},
|
||||
"UTW1pP59dUehuf0zeHXqL": {
|
||||
"title": "CSS Preprocessors",
|
||||
"description": "CSS preprocessors are scripting languages that extend the capabilities of standard CSS, allowing developers to write more maintainable and efficient stylesheets. They introduce features like variables, nesting, mixins, functions, and mathematical operations, which are then compiled into standard CSS. Popular preprocessors include Sass, Less, and Stylus. These tools enable developers to organize styles more logically, reuse code more effectively, and create complex CSS structures with less repetition. Preprocessors often support features like partials for modular stylesheets and built-in color manipulation functions. By using a preprocessor, developers can write more DRY (Don't Repeat Yourself) code, manage large-scale projects more easily, and potentially improve the performance of their stylesheets through optimization during the compilation process.\n\nVisit the following resources to learn more:",
|
||||
"description": "CSS preprocessors (like Sass, Less, Stylus) add extra features to CSS, making it easier to write and manage. They offer things like variables (for colors, sizes), nesting (to organize styles), mixins (reusable style blocks), and functions. This helps keep CSS tidy and avoid repetition, especially in big projects. The preprocessor code is then turned into regular CSS that browsers understand. They make CSS more powerful and efficient.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Sass Website",
|
||||
@@ -1078,7 +1078,7 @@
|
||||
},
|
||||
"dRDmS072xeNLX7p_X565w": {
|
||||
"title": "BEM",
|
||||
"description": "The Block, Element, Modifier methodology (commonly referred to as BEM) is a popular naming convention for classes in HTML and CSS. Developed by the team at Yandex, its goal is to help developers better understand the relationship between the HTML and CSS in a given project.\n\nVisit the following resources to learn more:",
|
||||
"description": "BEM (Block, Element, Modifier) is a way to name CSS classes in HTML. It helps developers see how HTML and CSS relate in a project. The goal is to make CSS more modular, reusable, and easier to understand, especially in large projects, by creating clear, consistent naming rules.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "BEM Official Website",
|
||||
@@ -1099,7 +1099,7 @@
|
||||
},
|
||||
"kukEE5rMSPa4NeNjx21kt": {
|
||||
"title": "Sass",
|
||||
"description": "Sass (Syntactically Awesome Style Sheets) is a mature, stable, and powerful professional-grade CSS extension language. It extends CSS with features like variables, nested rules, mixins, inline imports, and more, all with fully CSS-compatible syntax. Sass allows for more organized, maintainable, and reusable styles in complex projects. It compiles to clean, standard CSS, supporting two syntaxes: the original indented syntax and the more popular SCSS (Sassy CSS) syntax. Sass provides functionality like control directives for libraries, making it easier to write well-structured, scalable CSS. Its features help reduce repetition in CSS and save time, making it a popular choice among frontend developers for managing large, complex stylesheets.\n\nVisit the following resources to learn more:",
|
||||
"description": "Sass (Syntactically Awesome Style Sheets) is a tool that makes writing CSS easier and more powerful. It adds features like variables (to store colors or sizes), nested rules (to organize your styles better), and mixins (to reuse styles). This helps keep your CSS neat and easy to manage, especially in big projects. Sass code gets turned into regular CSS that browsers can understand. It has two ways of writing: the original indented style and the more common SCSS style. Sass is popular because it saves time and makes CSS less repetitive.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Sass Website",
|
||||
@@ -1125,7 +1125,7 @@
|
||||
},
|
||||
"9WlPENh9g1xOv-zA64Tfg": {
|
||||
"title": "PostCSS",
|
||||
"description": "PostCSS is a tool for transforming CSS with JavaScript plugins. It allows developers to enhance their CSS workflow by automating repetitive tasks, adding vendor prefixes, and implementing future CSS features. PostCSS works as a preprocessor, but unlike Sass or Less, it's highly modular and customizable. Users can choose from a wide range of plugins or create their own to suit specific needs. Popular plugins include Autoprefixer for adding vendor prefixes, cssnext for using future CSS features, and cssnano for minification. PostCSS integrates well with various build tools and can be used alongside traditional CSS preprocessors. Its flexibility and performance make it a popular choice for optimizing CSS in modern web development workflows.\n\nVisit the following resources to learn more:",
|
||||
"description": "PostCSS is a tool that uses JavaScript plugins to change CSS. It can automate tasks, add browser prefixes (like `-webkit-`), and let you use future CSS features now. It's like a CSS preprocessor but more flexible, as you pick the plugins you need. Popular plugins include Autoprefixer, cssnext, and cssnano (for making CSS smaller). PostCSS works with many build tools and is liked for its flexibility and speed in modern web development.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PostCSS Website",
|
||||
@@ -1146,7 +1146,7 @@
|
||||
},
|
||||
"i9z0stM4uKu27Cz6NIgNX": {
|
||||
"title": "Build Tools",
|
||||
"description": "Build tools are software utilities designed to automate the process of creating executable applications from source code. They handle tasks such as compiling, linking, minifying, and bundling code, as well as running tests and managing dependencies. Common build tools include Make, Gradle, Maven, Webpack, and Gulp. These tools streamline development workflows by reducing manual steps, ensuring consistency across different environments, and optimizing output for production. They often support features like incremental builds, parallel processing, and custom task definitions. Build tools are crucial in modern software development, especially for large-scale projects, as they improve efficiency, reduce errors, and facilitate continuous integration and deployment processes.\n\nVisit the following resources to learn more:",
|
||||
"description": "Build tools automate making apps from source code. They compile, link, minify (shrink), and bundle code, run tests, and manage dependencies. Examples are Webpack, Vite, and Parcel for web development. Build tools speed up development, ensure consistency, and optimize code for users. They're key for modern software, especially big projects, making work more efficient and enabling continuous integration (CI).\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Webpack Website",
|
||||
@@ -1172,7 +1172,7 @@
|
||||
},
|
||||
"9VcGfDBBD8YcKatj4VcH1": {
|
||||
"title": "Linters and Formatters",
|
||||
"description": "Linters and formatters are tools used in software development to improve code quality and consistency. Linters analyze source code to detect programming errors, bugs, stylistic issues, and suspicious constructs, often enforcing a set of predefined or custom rules. Formatters automatically restructure code to adhere to a consistent style, adjusting elements like indentation, line breaks, and spacing. Together, these tools help maintain code standards across projects and teams, enhance readability, catch potential errors early, and reduce the cognitive load on developers during code reviews. Popular examples include ESLint for JavaScript linting and Prettier for code formatting, both of which can be integrated into development workflows and IDEs for real-time feedback and automatic corrections.\n\nVisit the following resources to learn more:",
|
||||
"description": "Linters and formatters boost code quality. Linters find errors, bugs, and style issues by checking code against rules. Formatters automatically fix style, like indents and spacing. They help keep code consistent, readable, and catch errors early. ESLint and Prettier are popular examples, often used in code editors for live feedback.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What Is a Linter?",
|
||||
@@ -1183,7 +1183,7 @@
|
||||
},
|
||||
"hkSc_1x09m7-7BO7WzlDT": {
|
||||
"title": "Module Bundlers",
|
||||
"description": "Module bundlers are development tools that combine multiple JavaScript files and their dependencies into a single file, optimized for web browsers. They resolve and manage dependencies, transform and optimize code, and often support features like code splitting and lazy loading. Popular module bundlers include Webpack, Rollup, and Parcel. These tools address challenges in managing complex JavaScript applications by organizing code into modules, eliminating global scope pollution, and improving load times. Bundlers typically support various file formats, enable the use of modern JavaScript features through transpilation, and integrate with task runners and other build tools. Their primary goal is to streamline the development process and enhance application performance in production environments.\n\nVisit the following resources to learn more:",
|
||||
"description": "Module bundlers are tools that take many JavaScript files and combine them into one, which is better for web browsers. They handle dependencies, improve code, and can split code for faster loading. Webpack, Rollup, and Parcel are examples. They help manage big JavaScript projects by organizing code into modules and making load times better. Bundlers also let you use new JavaScript features by changing them to older versions if needed. They make development smoother and apps run better.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "The Complete JavaScript Module Bundlers Guide",
|
||||
@@ -1209,7 +1209,7 @@
|
||||
},
|
||||
"NS-hwaWa5ebSmNNRoxFDp": {
|
||||
"title": "Parcel",
|
||||
"description": "Parcel is a zero-configuration web application bundler that simplifies the process of building and deploying web projects. It supports multiple programming languages and file types out of the box, including JavaScript, CSS, HTML, and various image formats. Parcel automatically analyzes dependencies, transforms code, and optimizes assets without requiring a complex configuration file. It offers features like hot module replacement, code splitting, and tree shaking by default. Parcel's main selling point is its ease of use and fast build times, achieved through parallel processing and caching. While it may lack some advanced features of more established bundlers like Webpack, Parcel's simplicity and performance make it an attractive option for rapid prototyping and smaller projects.\n\nVisit the following resources to learn more:",
|
||||
"description": "Parcel is a web app bundler that needs no setup. It makes building websites easy by handling many file types like JavaScript, CSS, and HTML automatically. Parcel figures out dependencies, changes code, and optimizes files without needing a config file. It has features like live updates, code splitting, and removing unused code by default. It's known for being easy and fast, great for quick projects or smaller sites.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Parcel Website",
|
||||
@@ -1230,7 +1230,7 @@
|
||||
},
|
||||
"sCjErk7rfWAUvhl8Kfm3n": {
|
||||
"title": "Rollup",
|
||||
"description": "Rollup is a module bundler for JavaScript that compiles small pieces of code into larger, more complex structures. It specializes in producing smaller, more efficient bundles for ES modules. Rollup excels at tree-shaking, eliminating unused code for leaner outputs. It's particularly well-suited for libraries and applications using the ES module format. Rollup supports various output formats, including UMD and CommonJS, making it versatile for different deployment scenarios. While it may require more configuration than some alternatives, Rollup's focus on ES modules and its efficient bundling make it popular for projects prioritizing small bundle sizes and modern JavaScript practices.\n\nVisit the following resources to learn more:",
|
||||
"description": "Rollup is a tool that bundles JavaScript code, making small pieces into bigger ones. It's good at making small, efficient bundles, especially for ES modules (a modern way to organize JavaScript). Rollup is great at \"tree-shaking,\" which means it removes unused code to make files smaller. It can output code in different formats, so it's flexible. While it might need more setup, Rollup is liked for its small bundles and focus on modern JavaScript.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Rollup Website and Docs",
|
||||
@@ -1251,7 +1251,7 @@
|
||||
},
|
||||
"twufEtHgxcRUWAUQ9bXus": {
|
||||
"title": "Webpack",
|
||||
"description": "Webpack is a popular open-source JavaScript module bundler that transforms, bundles, or packages resources for the web. It takes modules with dependencies and generates static assets representing those modules. Webpack can handle not just JavaScript, but also other assets like CSS, images, and fonts. It uses loaders to preprocess files and plugins to perform a wider range of tasks like bundle optimization. Webpack's key features include code splitting, lazy loading, and a rich ecosystem of extensions. It supports hot module replacement for faster development and tree shaking to eliminate unused code. While it has a steeper learning curve compared to some alternatives, Webpack's flexibility and powerful features make it a standard tool in many modern JavaScript development workflows, especially for complex applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "Webpack is an open-source JavaScript module bundler that processes and packages web resources, including JavaScript, CSS, images, and fonts. It uses loaders for preprocessing and plugins for tasks like optimization. Key features are code splitting, lazy loading, hot module replacement for faster development, and tree shaking to remove unused code. Despite a steeper learning curve, Webpack's flexibility and power make it a standard for complex JavaScript applications.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Webpack",
|
||||
@@ -1277,7 +1277,7 @@
|
||||
},
|
||||
"4W7UXfdKIUsm1bUrjdTVT": {
|
||||
"title": "esbuild",
|
||||
"description": "esbuild is a high-performance JavaScript bundler and minifier designed for speed and efficiency. Created by Evan Wallace, it's written in Go and compiles to native code, making it significantly faster than traditional JavaScript-based build tools. esbuild supports modern JavaScript features, TypeScript, and JSX out of the box, with near-instant bundling times even for large projects. It offers a simple API and command-line interface, making it easy to integrate into existing build pipelines. While primarily focused on speed, esbuild also provides basic code splitting, tree shaking, and source map generation. Its extreme performance makes it particularly suitable for development environments and as a foundation for other build tools, though it may lack some advanced features found in more mature bundlers.\n\nVisit the following resources to learn more:",
|
||||
"description": "esbuild is a very fast JavaScript bundler and minifier. It's written in Go, so it's much quicker than older tools. esbuild handles modern JavaScript, TypeScript, and JSX. It bundles code almost instantly, even for big projects. It's easy to use with a simple API. While super fast, it might not have all the advanced features of older bundlers, but it's great for development and as a base for other tools.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Let's Learn esbuild! (with Sunil Pai) — Learn With Jason",
|
||||
@@ -1303,7 +1303,7 @@
|
||||
},
|
||||
"0Awx3zEI5_gYEIrD7IVX6": {
|
||||
"title": "Vite",
|
||||
"description": "Vite is a modern build tool and development server designed for fast and lean development of web applications. Created by Evan You, the author of Vue.js, Vite leverages native ES modules in the browser to enable near-instantaneous server start and lightning-fast hot module replacement (HMR). It supports various frameworks including Vue, React, and Svelte out of the box. Vite uses Rollup for production builds, resulting in highly optimized bundles. It offers features like CSS pre-processor support, TypeScript integration, and plugin extensibility. Vite's architecture, which separates dev and build concerns, allows for faster development cycles and improved developer experience, particularly for large-scale projects where traditional bundlers might struggle with performance.\n\nVisit the following resources to learn more:",
|
||||
"description": "Vite is a modern build tool by Evan You (creator of Vue.js) that offers rapid web development through native ES modules, enabling instant server starts and fast Hot Module Replacement (HMR). It supports frameworks like Vue, React, and Svelte, uses Rollup for optimized production builds, and includes features like CSS pre-processor support and TypeScript integration. Vite enhances developer experience, especially for large projects, by separating development and build processes for faster cycles.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Vite - The Build Tool for the Web",
|
||||
@@ -1334,7 +1334,7 @@
|
||||
},
|
||||
"zbkpu_gvQ4mgCiZKzS1xv": {
|
||||
"title": "Prettier",
|
||||
"description": "Prettier is an opinionated code formatter that supports multiple programming languages, including JavaScript, TypeScript, CSS, and more. It automatically formats code to adhere to a consistent style, eliminating debates about code formatting in development teams. Prettier parses code and reprints it with its own rules, taking maximum line length into account and wrapping code when necessary. It integrates with most editors and can be run as part of the development workflow or in pre-commit hooks. Prettier's main benefits include saving time on code reviews, reducing cognitive load for developers, and maintaining a consistent code style across projects. Its \"zero-config\" philosophy and wide language support have made it a popular tool in modern development environments.\n\nVisit the following resources to learn more:",
|
||||
"description": "Prettier is a tool that automatically formats your code to look consistent. It works with many languages like JavaScript, TypeScript, and CSS. Prettier takes your code and rewrites it following its own style rules, like line length. This stops arguments about code style and saves time. It can be used in code editors or run automatically before you commit code. Prettier is popular because it's easy to use and keeps code looking the same for everyone.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Prettier Website",
|
||||
@@ -1355,7 +1355,7 @@
|
||||
},
|
||||
"NFjsI712_qP0IOmjuqXar": {
|
||||
"title": "ESLint",
|
||||
"description": "ESLint is a popular open-source static code analysis tool for identifying and fixing problems in JavaScript code. It enforces coding standards, detects potential errors, and promotes consistent coding practices across projects. ESLint is highly configurable, allowing developers to define custom rules or use preset configurations. It supports modern JavaScript features, JSX, and TypeScript through plugins. ESLint can be integrated into development workflows through IDE extensions, build processes, or git hooks, providing real-time feedback to developers. Its ability to automatically fix many issues it detects makes it a valuable tool for maintaining code quality and consistency, especially in large teams or projects. ESLint's extensibility and wide adoption in the JavaScript ecosystem have made it a standard tool in modern JavaScript development.\n\nVisit the following resources to learn more:",
|
||||
"description": "ESLint is a tool that checks JavaScript code for problems. It helps keep code style consistent and finds errors. ESLint is very flexible; you can set your own rules or use ready-made ones. It works with modern JavaScript, JSX, and TypeScript. You can use ESLint in your code editor or when you build your project to get live feedback. It can even fix many issues automatically, which is great for team projects.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ESLint Website",
|
||||
@@ -1381,7 +1381,7 @@
|
||||
},
|
||||
"igg4_hb3XE3vuvY8ufV-4": {
|
||||
"title": "Testing",
|
||||
"description": "Testing apps involves systematically evaluating software to ensure it meets requirements, functions correctly, and maintains quality. Key testing types include:\n\n1. Unit testing: Verifying individual components or functions\n2. Integration testing: Checking interactions between different parts of the app\n3. Functional testing: Ensuring the app meets specified requirements\n4. UI/UX testing: Evaluating the user interface and experience\n5. Performance testing: Assessing app speed, responsiveness, and stability\n6. Security testing: Identifying vulnerabilities and ensuring data protection\n7. Accessibility testing: Verifying usability for people with disabilities\n8. Compatibility testing: Checking functionality across different devices and platforms\n\nModern testing often incorporates automated testing tools and continuous integration/continuous deployment (CI/CD) pipelines. Test-driven development (TDD) and behavior-driven development (BDD) are popular methodologies that emphasize writing tests before or alongside code. Effective testing strategies help identify bugs early, improve code quality, and ensure a reliable user experience.\n\nVisit the following resources to learn more:",
|
||||
"description": "Testing apps means checking if software works right and meets goals. Types include unit (small parts), integration (how parts work together), functional (if it does what it should), UI/UX (look and feel), performance (speed), security, accessibility (for disabilities), and compatibility (on different devices). Modern testing uses automated tools and CI/CD (automating build and release). TDD and BDD are ways to write tests with code. Good testing finds bugs early and makes apps better.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "The Different Types of Software Tests",
|
||||
@@ -1402,7 +1402,7 @@
|
||||
},
|
||||
"hVQ89f6G0LXEgHIOKHDYq": {
|
||||
"title": "Vitest",
|
||||
"description": "Vitest is a fast and lightweight testing framework for JavaScript and TypeScript projects, designed as a Vite-native alternative to Jest. It leverages Vite's transformation pipeline and config resolution, offering near-instant test execution and hot module replacement (HMR) for tests. Vitest provides a Jest-compatible API, making migration easier for projects already using Jest. It supports features like snapshot testing, mocking, and code coverage out of the box. Vitest's architecture allows for parallel test execution and watch mode, significantly speeding up the testing process. Its integration with Vite's ecosystem makes it particularly well-suited for projects already using Vite, but it can be used in any JavaScript project. Vitest's focus on speed and developer experience has made it an increasingly popular choice for modern web development workflows.\n\nVisit the following resources to learn more:",
|
||||
"description": "Vitest is a speedy testing tool for JavaScript and TypeScript, made to work well with Vite (a build tool). It runs tests very quickly and updates them live as you code. Vitest is similar to Jest (another testing tool), so it's easy to switch if you're used to Jest. It includes features like snapshot testing, mocking, and checking code coverage. Vitest can run multiple tests at once and watch for changes, making testing faster. It's great for Vite projects but works with any JavaScript project too.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Vitest - Next Generation Testing Framework",
|
||||
@@ -1423,7 +1423,7 @@
|
||||
},
|
||||
"g5itUjgRXd9vs9ujHezFl": {
|
||||
"title": "Jest",
|
||||
"description": "Jest is a popular JavaScript testing framework developed by Facebook. It provides a comprehensive solution for unit testing JavaScript code, with a focus on simplicity and minimal configuration. Jest offers features such as automatic mocking, code coverage reporting, and snapshot testing. It supports testing of both synchronous and asynchronous code, and can be used with various JavaScript frameworks and libraries, including React, Angular, and Vue. Jest's built-in assertion library and test runner make it easy to write and execute tests quickly. Its ability to run tests in parallel and its intelligent test-watching mode contribute to fast test execution, making it a preferred choice for many developers and organizations in the JavaScript ecosystem.\n\nVisit the following resources to learn more:",
|
||||
"description": "Jest is a popular JavaScript testing tool from Facebook. It's made for easy unit testing (testing small code parts). Jest has features like auto mocking, code coverage reports, and snapshot testing. It works with React, Angular, and Vue. Jest has its own tools for writing and running tests fast. It can run tests at the same time and watches for changes, making it a top choice for JavaScript developers.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Jest Website",
|
||||
@@ -1444,7 +1444,7 @@
|
||||
},
|
||||
"jramLk8FGuaEH4YpHIyZT": {
|
||||
"title": "Playwright",
|
||||
"description": "Playwright is an open-source automation framework developed by Microsoft for end-to-end testing of web applications. It provides a single API to automate Chromium, Firefox, and WebKit browsers across Windows, macOS, and Linux. Playwright supports multiple programming languages including JavaScript, TypeScript, Python, and .NET. It offers features like auto-waiting, network interception, and mobile emulation. The framework excels in handling modern web apps with dynamic content, providing reliable automation through its ability to wait for elements to be ready before acting on them. Playwright's cross-browser and cross-platform capabilities, combined with its powerful tooling for debugging and test generation, make it a robust choice for automated testing of web applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "Playwright by Microsoft is a tool for testing websites automatically. It can control Chromium, Firefox, and WebKit browsers on different systems (Windows, macOS, Linux) with one API. It works with JavaScript, TypeScript, Python, and .NET. Features include auto-waiting for elements, network control, and mobile simulation. Playwright is good for testing modern, dynamic web apps. Its cross-browser support and debugging tools make it strong for automated testing.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Playwright Website",
|
||||
@@ -1465,7 +1465,7 @@
|
||||
},
|
||||
"DaynCz5RR26gjT6N6gTDL": {
|
||||
"title": "Cypress",
|
||||
"description": "Cypress framework is a JavaScript-based end-to-end testing framework built on top of Mocha – a feature-rich JavaScript test framework running on and in the browser, making asynchronous testing simple and convenient. It also uses a BDD/TDD assertion library and a browser to pair with any JavaScript testing framework.\n\nVisit the following resources to learn more:",
|
||||
"description": "Cypress is a tool for testing websites from start to finish, just like a user would. It's written in JavaScript and based on Mocha (another JavaScript testing tool). Cypress runs tests directly in the browser, which makes testing things that happen over time (asynchronous testing) easier. It also uses common testing approaches like BDD/TDD and can work with other JavaScript testing tools.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Cypress Website",
|
||||
@@ -1491,7 +1491,7 @@
|
||||
},
|
||||
"U5mD5FmVx7VWeKxDpQxB5": {
|
||||
"title": "Authentication Strategies",
|
||||
"description": "Authentication strategies are methods or techniques used to verify the identity of a user or system in order to grant access to a protected resource. There are several different authentication strategies that can be used, including:\n\n* Basic Authentication\n* Session Based Authentication\n* Token Based Authentication\n* JWT Authentication\n* OAuth\n* SSO\n\nYou don't necessarily need to learn all of these, how to implement and the ins and outs from the get go. But it's important to know what they are and how they work. This will help you make better decisions when choosing an authentication strategy for your application.\n\nVisit the following resources to learn more:",
|
||||
"description": "Authentication strategies verify a user's identity to grant access. Common methods include Basic Auth (username/password), Session-based (server remembers login), Token-based (like JWT, a secure digital key), OAuth (for third-party access like \"Login with Google\"), and SSO (Single Sign-On, one login for many apps). Knowing these helps choose the right security for your app.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Basic Authentication",
|
||||
@@ -1527,7 +1527,7 @@
|
||||
},
|
||||
"RDWbG3Iui6IPgp0shvXtg": {
|
||||
"title": "Web Security Basics",
|
||||
"description": "Web security knowledge encompasses understanding and implementing practices to protect websites, web applications, and web services from various cyber threats. Key areas include:\n\n1. HTTPS and TLS for secure data transmission\n2. Cross-Site Scripting (XSS) prevention\n3. SQL injection protection\n4. Cross-Site Request Forgery (CSRF) mitigation\n5. Content Security Policy (CSP) implementation\n6. Secure authentication and session management\n7. Input validation and sanitization\n8. Protection against clickjacking\n9. Secure cookie handling\n10. Regular security updates and patch management\n\nWeb security also involves understanding common vulnerabilities listed in the OWASP Top Ten, implementing proper access controls, and using secure coding practices. It requires ongoing education and vigilance as new threats emerge. Effective web security strategies often include a combination of preventive measures, regular security audits, and incident response planning to ensure the confidentiality, integrity, and availability of web resources and user data.\n\nVisit the following resources to learn more:",
|
||||
"description": "Web security involves protecting websites and applications from cyber threats through practices like HTTPS/TLS for secure data, XSS/SQL injection/CSRF prevention, CSP implementation, secure authentication/session management, input validation, clickjacking protection, secure cookie handling, and regular updates. It also means understanding OWASP Top Ten vulnerabilities, access controls, secure coding, and staying informed on new threats. A good strategy combines prevention, audits, and incident response to protect web resources and data.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "OWASP Website",
|
||||
@@ -1558,7 +1558,7 @@
|
||||
},
|
||||
"AfH2zCbqzw0Nisg1yyISS": {
|
||||
"title": "CORS",
|
||||
"description": "Cross-Origin Resource Sharing (CORS) is a security mechanism implemented by web browsers to control access to resources (like APIs or fonts) on a web page from a different domain than the one serving the web page. It extends and adds flexibility to the Same-Origin Policy, allowing servers to specify who can access their resources. CORS works through a system of HTTP headers, where browsers send a preflight request to the server hosting the cross-origin resource, and the server responds with headers indicating whether the actual request is allowed. This mechanism helps prevent unauthorized access to sensitive data while enabling legitimate cross-origin requests. CORS is crucial for modern web applications that often integrate services and resources from multiple domains, balancing security needs with the functionality requirements of complex, distributed web systems.\n\nVisit the following resources to learn more:",
|
||||
"description": "Cross-Origin Resource Sharing (CORS) is a browser security feature that controls how web pages access resources from different domains. It allows servers to specify who can access their assets (like APIs or fonts). CORS uses HTTP headers; browsers may send a preflight request to check if the actual request is allowed. This prevents unauthorized access while enabling legitimate cross-origin requests, vital for modern apps using multiple domain services.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Cross-Origin Resource Sharing (CORS)",
|
||||
@@ -1579,7 +1579,7 @@
|
||||
},
|
||||
"uum7vOhOUR38vLuGZy8Oa": {
|
||||
"title": "HTTPS",
|
||||
"description": "Hypertext transfer protocol secure (HTTPS) is the secure version of HTTP, which is the primary protocol used to send data between a web browser and a website. HTTPS is encrypted in order to increase security of data transfer. This is particularly important when users transmit sensitive data, such as by logging into a bank account, email service, or health insurance provider.\n\nVisit the following resources to learn more:",
|
||||
"description": "HTTPS (Hypertext Transfer Protocol Secure) is the secure version of HTTP, the main way data is sent between your browser and websites. HTTPS encrypts this data, making it safer. This is very important for sensitive information like bank logins or emails. It keeps your data private and secure online.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is HTTPS?",
|
||||
@@ -1610,7 +1610,7 @@
|
||||
},
|
||||
"rmcm0CZbtNVC9LZ14-H6h": {
|
||||
"title": "Content Security Policy",
|
||||
"description": "Content Security Policy (CSP) is a security standard implemented by web browsers to prevent cross-site scripting (XSS), clickjacking, and other code injection attacks. It works by allowing web developers to specify which sources of content are trusted and can be loaded on a web page. CSP is typically implemented through HTTP headers or meta tags, defining rules for various types of resources like scripts, stylesheets, images, and fonts. By restricting the origins from which content can be loaded, CSP significantly reduces the risk of malicious code execution. It also provides features like reporting violations to help developers identify and fix potential security issues. While powerful, implementing CSP requires careful configuration to balance security with functionality, especially for sites using third-party resources or inline scripts.\n\nVisit the following resources to learn more:",
|
||||
"description": "Content Security Policy (CSP) is a security feature in web browsers that helps stop attacks like cross-site scripting (XSS) and clickjacking. It lets website creators tell the browser which sources of content (like scripts, styles, and images) are safe to load. This is done using special instructions sent with the webpage. By limiting where content can come from, CSP makes it much harder for bad code to run on a site. It can also report problems, helping developers find and fix security holes. Setting up CSP needs care to make sure everything works right, especially if the site uses content from other places.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "MDN Content Security Policy (CSP)",
|
||||
@@ -1631,7 +1631,7 @@
|
||||
},
|
||||
"JanR7I_lNnUCXhCMGLdn-": {
|
||||
"title": "OWASP Security Risks",
|
||||
"description": "OWASP (Open Web Application Security Project) identifies and ranks the most critical security risks to web applications. The OWASP Top 10 list includes vulnerabilities such as injection flaws, broken authentication, sensitive data exposure, XML external entities (XXE), broken access control, security misconfigurations, cross-site scripting (XSS), insecure deserialization, using components with known vulnerabilities, and insufficient logging and monitoring. These risks represent common attack vectors exploited by malicious actors to compromise web applications and their underlying systems. OWASP provides guidelines and best practices for mitigating these risks, emphasizing the importance of secure coding practices, regular security assessments, and implementing robust security controls throughout the software development lifecycle. Understanding and addressing these risks is crucial for developers and organizations to enhance the security posture of their web applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "OWASP (Open Web Application Security Project) lists the biggest security dangers for web apps. The OWASP Top 10 includes things like injection attacks, bad authentication, data exposure, and using old, unsafe code. These are common ways hackers break into websites. OWASP gives advice on how to fix these problems by coding securely, checking for issues often, and using strong security from start to finish. Knowing these risks helps protect web apps.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "OWASP Web Application Security Testing Checklist",
|
||||
@@ -1662,7 +1662,7 @@
|
||||
},
|
||||
"ruoFa3M4bUE3Dg6GXSiUI": {
|
||||
"title": "Web Components",
|
||||
"description": "Web Components are a set of standardized browser technologies that allow developers to create reusable, encapsulated HTML elements for web pages and applications. They consist of three main technologies: Custom Elements for defining new HTML tags, Shadow DOM for encapsulating styles and markup, and HTML Templates for declaring fragments of reusable HTML. Web Components enable the creation of modular, shareable components that work across different frameworks and browsers. They provide strong encapsulation, reducing style conflicts and promoting code reuse. While adoption has been slower compared to popular JavaScript frameworks, Web Components offer a standards-based approach to component development, ensuring long-term compatibility and interoperability in web ecosystems.\n\nVisit the following resources to learn more:",
|
||||
"description": "Web Components are a way to create your own reusable HTML elements that work in any browser and with any JavaScript framework. They use three main ideas: Custom Elements (making new HTML tags), Shadow DOM (keeping styles and structure separate and private), and HTML Templates (bits of HTML you can reuse). This helps make code modular and shareable, and reduces conflicts between styles. Web Components are a standard way to build parts of websites, aiming for long-term use and working well together.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Web Components - MDN",
|
||||
@@ -1688,7 +1688,7 @@
|
||||
},
|
||||
"hwPOGT0-duy3KfI8QaEwF": {
|
||||
"title": "Type Checkers",
|
||||
"description": "Type checkers are tools that analyze code to detect and prevent type-related errors without executing the program. They enforce type consistency, helping developers catch mistakes early in the development process. Popular type checkers include TypeScript for JavaScript, Flow for JavaScript, and mypy for Python. These tools add static typing to dynamically typed languages, offering benefits like improved code reliability, better documentation, and enhanced developer tooling support. Type checkers can infer types in many cases and allow for gradual adoption in existing projects. They help prevent common runtime errors, facilitate refactoring, and improve code maintainability. While adding some overhead to the development process, type checkers are widely adopted in large-scale applications for their ability to catch errors before runtime and improve overall code quality.\n\nVisit the following resources to learn more:",
|
||||
"description": "Type checkers are tools that look at your code to find and stop errors related to data types (like numbers, strings, etc.) before you run the program. They help make sure your code uses types correctly, catching mistakes early. Examples include TypeScript and Flow for JavaScript, and mypy for Python. These tools add static typing (checking types before running) to languages that usually check types while running. This makes code more reliable, easier to understand, and helps with refactoring. While they add a bit of work, type checkers are great for big projects to improve code quality.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Flow - Static Type Checker for JavaScript",
|
||||
@@ -1709,7 +1709,7 @@
|
||||
},
|
||||
"VxiQPgcYDFAT6WgSRWpIA": {
|
||||
"title": "Custom Elements",
|
||||
"description": "One of the key features of the Web Components standard is the ability to create custom elements that encapsulate your functionality on an HTML page, rather than having to make do with a long, nested batch of elements that together provide a custom page feature.\n\nVisit the following resources to learn more:",
|
||||
"description": "Custom Elements are a part of Web Components that let you create your own HTML tags. This means you can make reusable parts for your webpage that have their own special behavior, instead of using lots of nested standard HTML tags. It helps keep your HTML cleaner and your components easier to manage.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Using Custom Elements - MDN",
|
||||
@@ -1730,7 +1730,7 @@
|
||||
},
|
||||
"Hk8AVonOd693_y1sykPqd": {
|
||||
"title": "HTML Templates",
|
||||
"description": "The `<template>` HTML element is a mechanism for holding HTML that is not to be rendered immediately when a page is loaded but may be instantiated subsequently during runtime using JavaScript. Think of a template as a content fragment that is being stored for subsequent use in the document.\n\nVisit the following resources to learn more:",
|
||||
"description": "The `<template>` HTML tag holds HTML content that isn't shown right away when a page loads. Instead, JavaScript can use it later to create new elements on the page. It's like a blueprint for HTML parts you want to reuse.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Using Templates and Slots - MDN",
|
||||
@@ -1751,7 +1751,7 @@
|
||||
},
|
||||
"-SpsNeOZBkQfDA-rwzgPg": {
|
||||
"title": "Shadow DOM",
|
||||
"description": "The Shadow DOM is a web standard that provides encapsulation for JavaScript, CSS, and templating in web components. It allows developers to create isolated DOM trees within elements, separate from the main document DOM. This encapsulation prevents styles and scripts from leaking in or out, ensuring that component internals remain separate from the rest of the page. Shadow DOM enables more modular and maintainable code by reducing naming conflicts and CSS specificity issues. It's particularly useful for creating reusable custom elements with self-contained styling and behavior. While primarily used in web components, Shadow DOM can also be leveraged in various scenarios to improve code organization and reduce unintended side effects in complex web applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "The Shadow DOM is a way to keep the HTML, CSS, and JavaScript of a web component separate from the rest of the webpage. Think of it as creating a private little DOM (Document Object Model) inside an element. This stops styles and scripts from accidentally affecting other parts of the page, and vice-versa. It helps make code cleaner and easier to manage, especially for custom elements that you want to reuse. It's mainly for web components but can be used anytime you want to keep code self-contained.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Using shadow DOM | MDN web docs",
|
||||
@@ -1772,7 +1772,7 @@
|
||||
},
|
||||
"0asdhvwBH3gn-ercktV7A": {
|
||||
"title": "TypeScript",
|
||||
"description": "TypeScript is a strongly-typed, object-oriented programming language that builds upon JavaScript by adding optional static typing and other features. Developed and maintained by Microsoft, it compiles to plain JavaScript, allowing it to run in any environment that supports JavaScript. TypeScript offers enhanced IDE support with better code completion, refactoring, and error detection during development. It introduces concepts like interfaces, generics, and decorators, enabling more robust software architecture. TypeScript is particularly valuable for large-scale applications, as it improves code maintainability and readability. Its type system helps catch errors early in the development process, reducing runtime errors. With its growing ecosystem and adoption in popular frameworks like Angular, TypeScript has become a standard tool in modern web development.\n\nVisit the following resources to learn more:",
|
||||
"description": "TypeScript, by Microsoft, is a strongly-typed language that extends JavaScript with optional static typing. It compiles to plain JavaScript, running anywhere JavaScript does. TypeScript enhances IDE support with better code completion, refactoring, and error detection. It introduces interfaces, generics, and decorators for robust software architecture, proving valuable for large apps by improving maintainability and readability. Its type system catches errors early, reducing runtime issues. TypeScript is a standard in modern web development.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated TypeScript Roadmap",
|
||||
@@ -1808,7 +1808,7 @@
|
||||
},
|
||||
"Cxspmb14_0i1tfw-ZLxEu": {
|
||||
"title": "SSR",
|
||||
"description": "Server-side rendering (SSR) is a technique used in web development where web pages are generated on the server and sent to the client as fully rendered HTML. This approach contrasts with client-side rendering, where the browser builds the page using JavaScript. SSR improves initial page load time and search engine optimization (SEO) by providing complete content to crawlers. It's particularly beneficial for content-heavy sites and applications requiring fast first-page loads. SSR can be implemented with various frameworks like Next.js for React or Nuxt.js for Vue.js. While it can increase server load and complexity, SSR offers advantages in performance perception, especially on slower devices or networks, and can be combined with client-side hydration for dynamic interactivity after initial load.\n\nVisit the following resources to learn more:",
|
||||
"description": "Server-side rendering (SSR) means websites are built on the server before being sent to your browser as complete HTML. This is different from client-side rendering, where the browser builds the page. SSR helps pages load faster at first and is better for search engines. It's good for sites with lots of content. Frameworks like Next.js (for React) and Nuxt.js (for Vue) use SSR. It can make servers busier but improves performance, especially on slow devices.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Server-Side Rendering (SSR)",
|
||||
@@ -1834,7 +1834,7 @@
|
||||
},
|
||||
"OL8I6nOZ8hGGWmtxg_Mv8": {
|
||||
"title": "Svelte",
|
||||
"description": "Svelte is a modern JavaScript framework for building user interfaces that takes a unique approach to web development. Unlike traditional frameworks that do most of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app. It compiles your code to efficient vanilla JavaScript, resulting in smaller bundle sizes and better runtime performance. Svelte uses a component-based architecture and features a simple, intuitive syntax that allows developers to write less code. It includes built-in state management, CSS scoping, and animations. Svelte's approach eliminates the need for a virtual DOM, leading to faster initial loads and updates. Its simplicity and performance benefits have been gaining it increasing popularity in the front-end development community.\n\nVisit the following resources to learn more:",
|
||||
"description": "Svelte is a JavaScript tool for building UIs. Unlike others, Svelte does its main work during build time, not in the browser. It turns your code into small, fast, plain JavaScript. Svelte uses components and has a simple syntax, so you write less code. It includes features for managing data, styling, and animations. Because Svelte doesn't use a virtual DOM, pages load and update quickly. It's liked for its simplicity and speed.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Svelte Course Playlist for beginners",
|
||||
@@ -1865,7 +1865,7 @@
|
||||
},
|
||||
"3TE_iYvbklXK0be-5f2M7": {
|
||||
"title": "Vue.js",
|
||||
"description": "Vue.js is a progressive JavaScript framework for building user interfaces. It's designed to be incrementally adoptable, allowing developers to integrate it into projects gradually. Vue uses a template-based approach with a virtual DOM for efficient rendering. It features a reactive and composable component system, making it easy to organize and reuse code. Vue's core library focuses on the view layer, but it can be easily extended with official and community-built tools for state management, routing, and build tooling. Known for its gentle learning curve and flexibility, Vue has gained popularity for both small projects and large-scale applications. Its performance, lightweight nature, and comprehensive documentation have contributed to its widespread adoption in the web development community.\n\nVisit the following resources to learn more:",
|
||||
"description": "Vue.js is a JavaScript framework for building website interfaces. It's easy to start with and can be added to projects bit by bit. Vue uses templates and a virtual DOM (a lightweight copy of the real page) to show things on screen efficiently. It has a system of reusable components, making code organized. While Vue mainly handles what users see, it works well with other tools for things like managing data or page navigation. It's known for being easy to learn, flexible, and fast, making it popular for all kinds of projects.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Vue.js Website",
|
||||
@@ -1891,7 +1891,7 @@
|
||||
},
|
||||
"k6rp6Ua9qUEW_DA_fOg5u": {
|
||||
"title": "Angular",
|
||||
"description": "Angular is a popular open-source web application framework developed and maintained by Google. It uses TypeScript, a statically typed superset of JavaScript, to build scalable and efficient single-page applications (SPAs). Angular follows a component-based architecture, where the user interface is composed of reusable, self-contained components. The framework provides features like two-way data binding, dependency injection, and a powerful template syntax, which simplify the development of complex web applications. Angular also includes a comprehensive set of tools for testing, routing, and state management, making it a full-fledged solution for front-end development. Its modular structure and emphasis on best practices make it particularly suitable for large-scale enterprise applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "Angular is a popular tool from Google for building websites and web apps. It uses TypeScript (a type of JavaScript) to create large, efficient single-page applications (SPAs), where content loads in one go without needing to reload the whole page. Angular builds UIs with reusable components, like building blocks. It has features like two-way data binding (data updates automatically in different places), dependency injection (helps manage code parts), and a strong template system. Angular also offers tools for testing, page navigation, and managing app data, making it great for big, complex projects.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Angular Roadmap",
|
||||
@@ -1917,7 +1917,7 @@
|
||||
},
|
||||
"SGDf_rbfmFSHlxI-Czzlz": {
|
||||
"title": "React",
|
||||
"description": "React is an open-source JavaScript library for building user interfaces, primarily for single-page applications. Developed and maintained by Facebook, it allows developers to create reusable UI components that efficiently update and render as data changes. React uses a virtual DOM for performance optimization and supports a unidirectional data flow. Its component-based architecture promotes modularity and reusability. React's ecosystem includes tools like Redux for state management and React Native for mobile app development. The library's declarative nature, efficient rendering, and strong community support have made it one of the most popular choices for front-end development in modern web applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "React is a JavaScript tool from Facebook for building UIs, especially for single-page apps. It lets you create reusable UI parts that update when data changes. React uses a virtual DOM for speed and has a one-way data flow. This component style makes code neat and reusable. React also works with tools like Redux for data management and React Native for mobile apps. It's popular because it's clear, fast, and has a big community.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Full Stack React Developer Course",
|
||||
@@ -1943,7 +1943,7 @@
|
||||
},
|
||||
"KJRkrFZIihCUBrOf579EU": {
|
||||
"title": "react-router",
|
||||
"description": "React Router is a popular routing library for React applications that enables dynamic, client-side routing. It allows developers to create single-page applications with multiple views, managing the URL and history of the browser while keeping the UI in sync with the URL. React Router provides a declarative way to define routes, supporting nested routes, route parameters, and programmatic navigation. It offers components like BrowserRouter, Route, and Link to handle routing logic and navigation. The library also supports features such as lazy loading of components, route guards, and custom history management. React Router's integration with React's component model makes it a go-to solution for managing navigation and creating complex, multi-view applications in React ecosystems.\n\nVisit the following resources to learn more:",
|
||||
"description": "React Router is a key tool for React apps that need different pages or views. It handles the website's URL, letting you create single-page apps that feel like they have multiple pages. It provides components to define routes (paths to different views) and link between them. Features include lazy loading (loading parts only when needed) and route guards (controlling access to pages). It's the standard for navigation in React apps.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "React Router Website",
|
||||
@@ -1964,7 +1964,7 @@
|
||||
},
|
||||
"zNFYAJaSq0YZXL5Rpx1NX": {
|
||||
"title": "Next.js",
|
||||
"description": "Next.js is a React-based open-source framework for building server-side rendered and statically generated web applications. It provides features like automatic code splitting, optimized performance, and simplified routing out of the box. Next.js supports both static site generation (SSG) and server-side rendering (SSR), allowing developers to choose the most appropriate rendering method for each page. The framework offers built-in CSS support, API routes for backend functionality, and easy deployment options. Next.js is known for its developer-friendly experience, with features like hot module replacement and automatic prefetching. Its ability to create hybrid apps that combine static and server-rendered pages makes it popular for building scalable, SEO-friendly web applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "Next.js is a React framework for building websites that can be server-rendered (built on the server) or static (built beforehand). It offers features like auto code splitting, fast performance, and easy page routing. You can choose how each page is made. Next.js has CSS support, API routes (for backend tasks), and simple deployment. It's known for being developer-friendly, with live updates and smart preloading. It's great for big, SEO-friendly sites.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Official Website",
|
||||
@@ -1985,7 +1985,7 @@
|
||||
},
|
||||
"BBsXxkbbEG-gnbM1xXKrj": {
|
||||
"title": "Nuxt.js",
|
||||
"description": "Nuxt.js is a higher-level framework built on top of Vue.js, designed to create universal or single-page Vue applications. It simplifies the development process by providing a structured directory layout, automatic routing, and server-side rendering capabilities out of the box. Nuxt.js offers features like static site generation, code splitting, and asynchronous data fetching. It supports both client-side and server-side rendering, allowing developers to choose the most appropriate approach for each page. Nuxt.js emphasizes developer experience and performance optimization, making it popular for building scalable, SEO-friendly Vue applications. Its modular architecture and extensive plugin ecosystem enable easy integration of additional functionalities.\n\nVisit the following resources to learn more:",
|
||||
"description": "Nuxt.js is a framework based on Vue.js for building web apps. It makes development easier with a ready-made structure, auto page routing, and server-side rendering. Nuxt.js can also create static sites, split code, and fetch data. You can choose how each page is rendered. It focuses on good developer experience and performance, making it popular for large, SEO-friendly Vue apps. It's also easy to add more features with its plugins.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Nuxt.js Fundamentals",
|
||||
@@ -2011,7 +2011,7 @@
|
||||
},
|
||||
"P4st_telfCwKLSAU2WsQP": {
|
||||
"title": "Svelte Kit",
|
||||
"description": "SvelteKit is a framework for building web applications using Svelte, a component-based JavaScript framework. It provides a full-stack development experience, handling both server-side and client-side rendering. SvelteKit offers features like file-based routing, code-splitting, and server-side rendering out of the box. It supports both static site generation and server-side rendering, allowing developers to choose the most appropriate approach for each page. SvelteKit emphasizes simplicity and performance, leveraging Svelte's compile-time approach to generate highly optimized JavaScript. It includes built-in development tools, easy deployment options, and integrates well with various backend services. SvelteKit's efficient development experience and flexibility make it an attractive option for building modern, performant web applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "SvelteKit is a framework for building websites and apps with Svelte (a JavaScript framework). It helps you build both the parts that run on a server and the parts that run in the user's browser. SvelteKit comes with useful things like easy page routing (how you go from one page to another), making sure only needed code is loaded, and server-side rendering (building pages on the server for faster loading). You can make static sites (pages that don't change much) or dynamic ones. It's designed to be simple and fast, thanks to Svelte's smart way of compiling code. SvelteKit also has good tools for developers and makes deploying your site easy.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Svelte Kit",
|
||||
@@ -2037,7 +2037,7 @@
|
||||
},
|
||||
"L7AllJfKvClaam3y-u6DP": {
|
||||
"title": "GraphQL",
|
||||
"description": "GraphQL is a query language and runtime for APIs, developed by Facebook. GraphQL's flexibility and efficiency make it popular for building complex applications, especially those with diverse client requirements. It's particularly useful for mobile applications where bandwidth efficiency is crucial. While it requires a paradigm shift from REST, many developers and organizations find GraphQL's benefits outweigh the learning curve, especially for large-scale or rapidly evolving APIs.\n\nVisit the following resources to learn more:",
|
||||
"description": "GraphQL, by Facebook, is a way to get data for apps. Unlike older methods, it lets apps ask for exactly the data they need, no more, no less. This is great for mobile apps where saving data is important. It's different from REST but good for big or changing APIs.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "visit Dedicated GraphQL Roadmap",
|
||||
@@ -2073,7 +2073,7 @@
|
||||
},
|
||||
"5eUbDdOTOfaOhUlZAmmXW": {
|
||||
"title": "Apollo",
|
||||
"description": "Apollo GraphQL is a comprehensive platform for building and managing GraphQL-based data layers in modern applications. It provides a set of open-source tools and libraries that simplify the implementation of GraphQL on both the client and server sides. On the client side, Apollo Client offers powerful caching, state management, and data fetching capabilities, integrating seamlessly with various front-end frameworks. On the server side, Apollo Server facilitates the creation of GraphQL APIs, handling queries, mutations, and subscriptions efficiently. The Apollo platform also includes developer tools for schema management, performance monitoring, and API governance. By abstracting away much of the complexity of GraphQL implementation, Apollo enables developers to build faster, more scalable, and more maintainable applications with a unified data graph.\n\nVisit the following resources to learn more:",
|
||||
"description": "Apollo GraphQL is a platform for using GraphQL in apps. It has tools for both client (front-end) and server (back-end). Apollo Client helps with caching, data, and state in the front-end. Apollo Server helps build GraphQL APIs. Apollo also offers tools for managing your data graph, checking performance, and more. It simplifies GraphQL, helping build faster and more scalable apps.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Apollo Website",
|
||||
@@ -2099,7 +2099,7 @@
|
||||
},
|
||||
"0moPO23ol33WsjVXSpTGf": {
|
||||
"title": "Relay Modern",
|
||||
"description": "Relay is a JavaScript framework developed by Facebook for building data-driven React applications. It's specifically designed to work with GraphQL, providing a declarative approach to fetching and managing data in complex web applications. Relay optimizes data fetching by colocating data requirements with components, enabling efficient updates and minimizing over-fetching. It handles caching, real-time updates, and optimistic UI updates out of the box. Relay's architecture promotes scalability and performance in large applications by managing data dependencies and reducing network requests. While it has a steeper learning curve compared to simpler data-fetching solutions, Relay offers significant benefits for applications with complex data requirements, especially when used in conjunction with React and GraphQL.\n\nVisit the following resources to learn more:",
|
||||
"description": "Relay is a Facebook JavaScript library for React apps that use a lot of data. It works with GraphQL to get and manage data efficiently. Relay puts data needs next to components, so it only fetches what's needed and updates well. It handles caching, live updates, and quick UI changes. Relay helps big apps perform better by managing data smartly. It's a bit harder to learn but great for complex data needs with React and GraphQL.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "facebook/relay",
|
||||
@@ -2120,7 +2120,7 @@
|
||||
},
|
||||
"n0q32YhWEIAUwbGXexoqV": {
|
||||
"title": "Static Site Generators",
|
||||
"description": "Static site generators (SSGs) are tools that create HTML websites from raw data and templates, producing pre-rendered pages at build time rather than at runtime. They combine the benefits of static websites (speed, security, simplicity) with the flexibility of dynamic sites. SSGs typically use markup languages like Markdown for content, templating engines for layouts, and generate a fully static website that can be hosted on simple web servers or content delivery networks. Popular SSGs include Jekyll, Hugo, Gatsby, and Eleventy. They're well-suited for blogs, documentation sites, and content-focused websites. SSGs offer advantages in performance, version control integration, and reduced server-side complexity, making them increasingly popular for a wide range of web projects.\n\nVisit the following resources to learn more:",
|
||||
"description": "Static Site Generators (SSGs) are tools that build websites as simple HTML files. They take your content (like Markdown files) and templates, and create all the pages beforehand. This makes websites fast, secure, and easy to host. Examples are Jekyll, Hugo, and Eleventy. They are great for blogs and documentation. SSGs offer good performance and are simple to manage.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a Static Site Generator?",
|
||||
@@ -2146,7 +2146,7 @@
|
||||
},
|
||||
"CMrss8E2W0eA6DVEqtPjT": {
|
||||
"title": "Vuepress",
|
||||
"description": "VuePress is a static site generator powered by Vue.js, primarily designed for creating documentation websites. It generates pre-rendered static HTML for each page, resulting in fast loading times and SEO-friendly content. VuePress features a Vue-powered theming system, automatic code syntax highlighting, and a default theme optimized for technical documentation. It supports markdown content with Vue components, allowing for dynamic and interactive documentation. VuePress offers built-in search functionality, responsive layouts, and easy customization through plugins and themes. While primarily used for documentation, it's versatile enough for blogs and simple websites. VuePress's combination of simplicity, performance, and Vue.js integration makes it popular for creating modern, fast-loading documentation sites and technical blogs.\n\nVisit the following resources to learn more:",
|
||||
"description": "VuePress is a Vue.js-powered static site generator ideal for documentation. It creates pre-rendered static HTML for fast, SEO-friendly sites, featuring a Vue-based theming system, code syntax highlighting, and a default theme for technical docs. It supports Markdown with Vue components for dynamic content, built-in search, responsive layouts, and customization via plugins/themes. While great for docs, it also suits blogs and simple websites, valued for its simplicity, performance, and Vue.js integration.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Vuepress",
|
||||
@@ -2167,7 +2167,7 @@
|
||||
},
|
||||
"XWJxV42Dpu2D3xDK10Pn3": {
|
||||
"title": "Nuxt.js",
|
||||
"description": "Nuxt.js is a higher-level framework built on top of Vue.js, designed to create universal or single-page Vue applications. It simplifies the development process by providing a structured directory layout, automatic routing, and server-side rendering capabilities out of the box. Nuxt.js offers features like static site generation, code splitting, and asynchronous data fetching. It supports both client-side and server-side rendering, allowing developers to choose the most appropriate approach for each page. Nuxt.js emphasizes developer experience and performance optimization, making it popular for building scalable, SEO-friendly Vue applications. Its modular architecture and extensive plugin ecosystem enable easy integration of additional functionalities.\n\nVisit the following resources to learn more:",
|
||||
"description": "Nuxt.js is a framework based on Vue.js for building web apps. It makes development easier with a ready-made structure, auto page routing, and server-side rendering. Nuxt.js can also create static sites, split code, and fetch data. You can choose how each page is rendered. It focuses on good developer experience and performance, making it popular for large, SEO-friendly Vue apps. It's also easy to add more features with its plugins.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Nuxt.js Fundamentals",
|
||||
@@ -2193,7 +2193,7 @@
|
||||
},
|
||||
"iUxXq7beg55y76dkwhM13": {
|
||||
"title": "Astro",
|
||||
"description": "Astro is a modern static site generator (SSG) and web framework designed for building fast, content-focused websites. It allows developers to use multiple frontend frameworks (like React, Vue, or Svelte) within the same project, automatically rendering components to static HTML at build time. Astro's unique \"partial hydration\" approach only sends JavaScript to the browser when necessary, resulting in significantly smaller bundle sizes and faster load times. The framework supports file-based routing, markdown content, and built-in optimizations for images and assets. Astro's component islands architecture enables developers to create interactive components while maintaining the performance benefits of static HTML, making it particularly well-suited for content-rich sites like blogs, documentation, and marketing pages.\n\nVisit the following resources to learn more:",
|
||||
"description": "Astro is a static site generator for fast, content-focused websites. It lets you use various frontend frameworks (like React, Vue, Svelte) and renders components to static HTML. Astro's \"partial hydration\" only sends JavaScript when needed, leading to smaller bundles and quicker loads. It supports file-based routing and markdown, making it great for blogs, docs, and marketing sites.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Astro Web Framework Crash Course",
|
||||
@@ -2219,7 +2219,7 @@
|
||||
},
|
||||
"io0RHJWIcVxDhcYkV9d38": {
|
||||
"title": "Eleventy",
|
||||
"description": "Eleventy (11ty) is a simple to use, easy to customize, highly performant and powerful static site generator with a helpful set of plugins (e.g. navigation, build-time image transformations, cache assets). Pages can be built and written with a variety of template languages (HTML, Markdown, JavaScript, Liquid, Nunjucks, Handlebars, Mustache, EJS, Haml, Pug or JS template literals). But it also offers the possibility to dynamically create pages from local data or external sources that are compiled at build time. It has zero client-side JavaScript dependencies.\n\nVisit the following resources to learn more:",
|
||||
"description": "Eleventy (or 11ty) is a tool for building fast websites that don't change often (static sites). It's easy to use and change to fit your needs. You can write pages using many different template languages like HTML, Markdown, or JavaScript. Eleventy can also create pages from data you have or from other websites when you build your site. A big plus is that it doesn't add any extra JavaScript to the user's browser, making sites load quickly. It also has helpful plugins for things like navigation or changing images.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Eleventy",
|
||||
@@ -2240,7 +2240,7 @@
|
||||
},
|
||||
"V70884VcuXkfrfHyLGtUg": {
|
||||
"title": "Next.js",
|
||||
"description": "Next.js is a React-based open-source framework for building server-side rendered and statically generated web applications. It provides features like automatic code splitting, optimized performance, and simplified routing out of the box. Next.js supports both static site generation (SSG) and server-side rendering (SSR), allowing developers to choose the most appropriate rendering method for each page. The framework offers built-in CSS support, API routes for backend functionality, and easy deployment options. Next.js is known for its developer-friendly experience, with features like hot module replacement and automatic prefetching. Its ability to create hybrid apps that combine static and server-rendered pages makes it popular for building scalable, SEO-friendly web applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "Next.js is a React framework for building websites that can be server-rendered (built on the server) or static (built beforehand). It offers features like auto code splitting, fast performance, and easy page routing. You can choose how each page is made. Next.js has CSS support, API routes (for backend tasks), and simple deployment. It's known for being developer-friendly, with live updates and smart preloading. It's great for big, SEO-friendly sites.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Next.js Website",
|
||||
@@ -2261,7 +2261,7 @@
|
||||
},
|
||||
"PoM77O2OtxPELxfrW1wtl": {
|
||||
"title": "PWAs",
|
||||
"description": "Progressive Web Apps (PWAs) are web applications that use modern web capabilities to deliver an app-like experience to users. They combine the best features of web and native apps, offering reliability, speed, and engagement. PWAs are built using web technologies (HTML, CSS, JavaScript) but provide features typically associated with native apps, such as offline functionality, push notifications, and home screen installation. They are responsive, work across different devices and browsers, and are discoverable through search engines. PWAs use service workers for background processing and caching, enabling faster load times and offline access. This approach allows developers to create cross-platform applications that are both cost-effective to develop and easy to maintain, while providing users with a seamless, app-like experience directly through their web browser.\n\nVisit the following resources to learn more:",
|
||||
"description": "Progressive Web Apps (PWAs) are websites that act like native apps. They use web tech (HTML, CSS, JavaScript) but offer features like offline use, push alerts, and home screen icons. PWAs work on many devices and browsers, load fast, and can be found on search engines. They use service workers for background tasks and caching. PWAs are a cost-effective way to build cross-platform apps with a smooth user experience.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Learn PWA",
|
||||
@@ -2287,7 +2287,7 @@
|
||||
},
|
||||
"VOGKiG2EZVfCBAaa7Df0W": {
|
||||
"title": "Mobile Apps",
|
||||
"description": "Mobile applications are software programs designed to run on mobile devices such as smartphones and tablets. They are typically distributed through app stores like Google Play or Apple's App Store. Mobile apps can be native (built specifically for one platform using languages like Swift for iOS or Kotlin for Android), hybrid (web technologies wrapped in a native container), or cross-platform (using frameworks like React Native or Flutter). These apps leverage device-specific features such as GPS, cameras, and sensors to provide rich, interactive experiences. They cover a wide range of functions from productivity and entertainment to social networking and e-commerce. Mobile app development considers factors like user interface design, performance optimization, offline functionality, and security to ensure a smooth user experience across various devices and operating systems.",
|
||||
"description": "Mobile apps are programs for phones and tablets, usually from app stores. They can be native (for one OS like iOS or Android), hybrid (web tech in a native shell), or cross-platform (like React Native). Apps use phone features like GPS and cameras. They do many things from games to shopping. Good mobile apps focus on easy use, speed, offline working, and security.",
|
||||
"links": [
|
||||
{
|
||||
"title": "React Native",
|
||||
@@ -2318,7 +2318,7 @@
|
||||
},
|
||||
"dsTegXTyupjS8iU6I7Xiv": {
|
||||
"title": "React Native",
|
||||
"description": "React Native is an open-source mobile application development framework created by Facebook. It allows developers to build native mobile apps for iOS and Android using JavaScript and React. React Native translates JavaScript code into native components, providing near-native performance and a genuine native user interface. It enables code reuse across platforms, speeding up development and reducing costs. The framework offers hot reloading for quick iterations, access to native APIs, and a large ecosystem of third-party plugins. React Native's \"learn once, write anywhere\" philosophy and its ability to bridge web and mobile development make it popular for creating cross-platform mobile applications, especially among teams already familiar with React for web development.\n\nVisit the following resources to learn more:",
|
||||
"description": "React Native, by Facebook, lets you build real mobile apps for iOS and Android using JavaScript and React. It turns your code into native app parts, so apps look and feel native and run fast. You can share code between iOS and Android, saving time and money. It has features like hot reloading (see changes instantly) and access to phone features. It's great for web developers who know React and want to make mobile apps.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated React Roadmap",
|
||||
@@ -2339,7 +2339,7 @@
|
||||
},
|
||||
"dIQXjFEUAJAGxxfAYceHU": {
|
||||
"title": "Flutter",
|
||||
"description": "Flutter is an open-source UI software development kit created by Google for building natively compiled, multi-platform applications from a single codebase. It uses the Dart programming language and provides a rich set of pre-designed widgets for creating responsive and visually appealing user interfaces. Flutter's architecture allows for fast development with hot reload, enabling developers to see changes instantly. It supports iOS, Android, web, and desktop platforms, offering true cross-platform development. Flutter uses a custom rendering engine, Skia, to draw UI components, ensuring consistent appearance across devices. While known for mobile app development, Flutter's expanding ecosystem and performance improvements have increased its adoption for web and desktop applications as well.\n\nVisit the following resources to learn more:",
|
||||
"description": "Flutter, by Google, is a tool for building apps for many platforms (iOS, Android, web, desktop) from one set of code. It uses the Dart language and has many ready-made UI parts (widgets) for making good-looking apps. Flutter is fast to develop with because of its hot reload (see changes instantly). It draws its own UI, so apps look the same everywhere. It's very popular for mobile apps and growing for web and desktop too.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Flutter course for beginners",
|
||||
@@ -2375,7 +2375,7 @@
|
||||
},
|
||||
"xmRv6-L45m5MDpHmdHFCL": {
|
||||
"title": "Ionic",
|
||||
"description": "Ionic is an open-source UI toolkit for building high-quality, cross-platform mobile and desktop applications using web technologies (HTML, CSS, and JavaScript). It leverages popular web frameworks like Angular, React, or Vue.js for application logic, while providing a rich set of pre-built UI components and native device functionalities. Ionic uses Cordova or Capacitor to wrap web apps for native deployment, allowing developers to create hybrid apps that can access device features through plugins. The framework emphasizes performance and native look-and-feel, offering adaptive styling for different platforms. With its focus on web standards and cross-platform compatibility, Ionic enables developers to maintain a single codebase for multiple platforms, making it a popular choice for rapid mobile app development.\n\nVisit the following resources to learn more:",
|
||||
"description": "Ionic is a tool for building mobile and desktop apps using web tech (HTML, CSS, JavaScript) and frameworks like Angular, React, or Vue. It gives you ready-made UI parts and access to phone features. Ionic wraps your web app so it can be a native app. It tries to make apps look and feel native on different platforms. Ionic is good for quickly making apps for many platforms with one codebase.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ionic-team/ionic-framework",
|
||||
@@ -2401,7 +2401,7 @@
|
||||
},
|
||||
"KMA7NkxFbPoUDtFnGBFnj": {
|
||||
"title": "Desktop Apps",
|
||||
"description": "Desktop applications applications typically use frameworks like Electron, NW.js (Node-WebKit), or Tauri, which combine a JavaScript runtime with a native GUI toolkit. This approach allows developers to use their web development skills to create cross-platform desktop apps. Electron, developed by GitHub, is particularly popular, powering applications like Visual Studio Code, Atom, and Discord. These frameworks provide APIs to access native system features, enabling JavaScript to interact with the file system, system tray, and other OS-specific functionalities. While offering rapid development and cross-platform compatibility, JavaScript desktop apps can face challenges in terms of performance and resource usage compared to traditional native applications. However, they benefit from the vast ecosystem of JavaScript libraries and tools, making them an attractive option for many developers and businesses.\n\nVisit the following resources to learn more:",
|
||||
"description": "JavaScript can build desktop apps using tools like Electron, NW.js, or Tauri. These mix JavaScript with a native look and feel, letting web developers make desktop apps for different systems (like Windows, Mac, Linux). Electron is very popular (used by VS Code, Discord). These tools let JavaScript access computer features like files. While they make development fast and work on many systems, they can sometimes be slower or use more resources than fully native apps. But, they benefit from all the JavaScript tools available.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Electron Website",
|
||||
@@ -2422,7 +2422,7 @@
|
||||
},
|
||||
"mQHpSyMR4Rra4mqAslgiS": {
|
||||
"title": "Electron",
|
||||
"description": "Electron is an open-source framework developed by GitHub that enables developers to build cross-platform desktop applications using web technologies. It combines the Chromium rendering engine with the Node.js runtime, allowing applications to be written in HTML, CSS, and JavaScript. Electron provides APIs to access native operating system functions, bridging the gap between web and desktop development. It's widely used for creating popular applications like Visual Studio Code, Atom, and Discord. Electron apps benefit from rapid development cycles, cross-platform compatibility, and access to a vast ecosystem of web technologies and Node.js modules. However, they can face challenges with resource usage and performance compared to native applications. Despite these trade-offs, Electron remains a popular choice for developers seeking to leverage web skills for desktop app development.\n\nVisit the following resources to learn more:",
|
||||
"description": "Electron is a framework for building cross-platform desktop apps with web tech (HTML, CSS, JavaScript). It uses Chromium and Node.js, allowing access to native OS functions. Popular apps like VS Code use Electron. It enables fast development and cross-platform use, but can be resource-intensive. Still, it's a go-to for web developers creating desktop apps.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Electron Website",
|
||||
@@ -2448,7 +2448,7 @@
|
||||
},
|
||||
"GJctl0tVXe4B70s35RkLT": {
|
||||
"title": "Tauri",
|
||||
"description": "Tauri is an open-source framework for building lightweight, secure desktop applications using web technologies. It allows developers to create cross-platform apps with HTML, CSS, and JavaScript, while using a Rust backend for core functionality. Tauri offers smaller bundle sizes compared to Electron, as it leverages the operating system's native webview instead of bundling Chromium. It provides robust security features, including a custom protocol for IPC (Inter-Process Communication) and fine-grained permissions. Tauri supports multiple JavaScript frameworks and offers API bindings for various system-level operations. Its emphasis on performance, security, and small binary sizes makes it an attractive option for developers seeking to create efficient desktop applications with web technologies.\n\nVisit the following resources to learn more:",
|
||||
"description": "Tauri is a tool for building small, secure desktop apps using web technologies like HTML, CSS, and JavaScript. It uses a Rust backend for the main logic. Unlike Electron, Tauri uses the computer's built-in web viewer, making apps smaller. It has strong security features and works with many JavaScript frameworks. Tauri is good for making fast, secure desktop apps with web skills, focusing on small app size and good performance.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Tauri Website",
|
||||
@@ -2469,7 +2469,7 @@
|
||||
},
|
||||
"2MRvAK9G9RGM_auWytcKh": {
|
||||
"title": "Flutter",
|
||||
"description": "Flutter is an open-source UI software development kit created by Google for building natively compiled, multi-platform applications from a single codebase. It uses the Dart programming language and provides a rich set of pre-designed widgets for creating responsive and visually appealing user interfaces. Flutter's architecture allows for fast development with hot reload, enabling developers to see changes instantly. It supports iOS, Android, web, and desktop platforms, offering true cross-platform development. Flutter uses a custom rendering engine, Skia, to draw UI components, ensuring consistent appearance across devices. While known for mobile app development, Flutter's expanding ecosystem and performance improvements have increased its adoption for web and desktop applications as well.\n\nVisit the following resources to learn more:",
|
||||
"description": "Flutter, by Google, is a tool for building apps for many platforms (iOS, Android, web, desktop) from one set of code. It uses the Dart language and has many ready-made UI parts (widgets) for making good-looking apps. Flutter is fast to develop with because of its hot reload (see changes instantly). It draws its own UI, so apps look the same everywhere. It's very popular for mobile apps and growing for web and desktop too.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Flutter course for beginners",
|
||||
@@ -2505,7 +2505,7 @@
|
||||
},
|
||||
"-DsETM9xLgHyGZthptj1Y": {
|
||||
"title": "PRPL Pattern",
|
||||
"description": "The PRPL pattern is a web application architecture strategy designed to improve performance, especially on mobile devices. PRPL stands for Push, Render, Pre-cache, and Lazy-load. It focuses on optimizing the initial load time and subsequent navigation speed. The pattern involves pushing critical resources for the initial route, rendering the initial route as quickly as possible, pre-caching remaining routes, and lazy-loading other routes and non-critical assets. This approach aims to deliver a near-instant loading experience for users, particularly on slower networks and less powerful devices. PRPL is often implemented using modern web technologies like service workers and is commonly associated with Progressive Web Apps (PWAs), though it can be applied to various web application architectures.\n\nVisit the following resources to learn more:",
|
||||
"description": "The PRPL pattern helps websites load fast, especially on mobile. PRPL means: Push critical resources, Render the first page quickly, Pre-cache other pages, and Lazy-load non-essential stuff. This makes the site feel instant, even on slow connections. It often uses service workers and is common in Progressive Web Apps (PWAs).\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PRPL Pattern - Google Developers",
|
||||
@@ -2521,7 +2521,7 @@
|
||||
},
|
||||
"xD5WfEP7Ez0oi3890UgmH": {
|
||||
"title": "RAIL Model",
|
||||
"description": "The RAIL Model is a user-centric performance model developed by Google that focuses on improving web application responsiveness and user experience. RAIL stands for Response, Animation, Idle, and Load. It provides specific performance goals: Responses to user input should occur within 100ms; Animations should run at 60 frames per second (16ms per frame); Idle time should be used to complete deferred work; and Load time for interactive content should be under 5 seconds. The model emphasizes the importance of perceived performance, encouraging developers to prioritize user interactions and break up long tasks. By adhering to RAIL guidelines, developers can create web applications that feel fast and responsive, enhancing user satisfaction and engagement.\n\nVisit the following resources to learn more:",
|
||||
"description": "The RAIL Model from Google helps make websites feel fast. RAIL stands for Response (quick reaction to clicks), Animation (smooth visuals), Idle (using downtime well), and Load (fast page start). It sets goals like responding in 100ms and animations at 60fps. Following RAIL makes users happier because the site feels responsive.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "RAIL Model - Google Developers",
|
||||
@@ -2537,7 +2537,7 @@
|
||||
},
|
||||
"X0Y3-IpPiFUCsNDK4RFxw": {
|
||||
"title": "Performance Metrics",
|
||||
"description": "Web performance metrics are quantitative measures of the performance of a web page or application. They are used to assess the speed and efficiency of a web page, and they can help identify areas for improvement. Some common web performance metrics include:\n\n* Load time: The time it takes for a web page to fully load and become interactive.\n* First contentful paint (FCP): The time it takes for the first content to appear on the page.\n* Time to interactive (TTI): The time it takes for the page to become fully interactive.\n* First input delay (FID): The time it takes for the page to respond to the first user input.\n* Total blocking time (TBT): The time it takes for the page to become fully interactive, taking into account the time spent blocking the main thread.\n\nVisit the following resources to learn more:",
|
||||
"description": "Web performance metrics measure how fast and efficient a webpage is. They help find ways to improve. Key metrics include: Load time (how long to fully load), First Contentful Paint (FCP - when first content shows), Time to Interactive (TTI - when page is usable), First Input Delay (FID - time to respond to first click/tap), and Total Blocking Time (TBT - time main thread is blocked).\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Web Performance Metrics - Google Developers",
|
||||
@@ -2563,7 +2563,7 @@
|
||||
},
|
||||
"RIhHMHLsLLPhNl05Q9aBf": {
|
||||
"title": "Using Lighthouse",
|
||||
"description": "Lighthouse is an open-source tool developed by Google that is used to audit the performance, accessibility, and SEO of web pages. It is available as a browser extension and as a command-line tool, and it can be run on any web page to generate a report with recommendations for improvement. Lighthouse works by simulating the load and interaction of a web page and measuring various performance metrics, such as load time, time to first paint, and time to interactive. It also checks for common issues such as incorrect image sizes, missing alt text, and broken links. Lighthouse provides a comprehensive and easy-to-use tool for identifying and fixing performance and accessibility issues on web pages. It is widely used by web developers and is integrated into many popular development tools.\n\nVisit the following resources to learn more:",
|
||||
"description": "Lighthouse, a Google open-source tool, audits web page performance, accessibility, and SEO. Available as a browser extension and CLI tool, it simulates page load/interaction to measure metrics like load time and TTI, and checks for issues like incorrect image sizes or broken links. It offers comprehensive reports with improvement recommendations, widely used by developers and integrated into many dev tools for identifying and fixing web page issues.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Lighthouse - Google Developers",
|
||||
@@ -2584,7 +2584,7 @@
|
||||
},
|
||||
"3_sJHKTogkDoCjR518-OL": {
|
||||
"title": "Using DevTools",
|
||||
"description": "Browser Developer Tools, commonly known as DevTools, are built-in features in modern web browsers that provide a suite of debugging and development capabilities. These tools allow developers to inspect, edit, and debug HTML, CSS, and JavaScript in real-time on web pages. Key features include:\n\n1. DOM inspector for viewing and modifying page structure\n2. Console for JavaScript debugging and logging\n3. Network panel for analyzing HTTP requests and responses\n4. Performance profiler for optimizing page load and runtime performance\n5. Application panel for managing storage, caches, and service workers\n6. Source panel for setting breakpoints and debugging JavaScript\n7. Elements panel for live CSS editing\n8. Device emulation for testing responsive designs\n\nDevTools are essential for front-end development, performance optimization, and cross-browser compatibility testing, providing developers with crucial insights into web application behavior and structure.\n\nVisit the following resources to learn more:",
|
||||
"description": "Browser Developer Tools (DevTools) are built-in browser features for web development, allowing real-time inspection, editing, and debugging of HTML, CSS, and JavaScript. Key features include a DOM inspector, console for JavaScript, network panel, performance profiler, application panel for storage/caches/service workers, source panel for JavaScript debugging, elements panel for CSS editing, and device emulation. DevTools are vital for front-end development, optimization, and compatibility testing, offering insights into web app behavior.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Chrome DevTools - Google Developers",
|
||||
@@ -2605,7 +2605,7 @@
|
||||
},
|
||||
"raoa-75p_DyBAycvy3yVv": {
|
||||
"title": "Storage",
|
||||
"description": "The Web Storage API provides mechanisms for storing key-value pairs in a web browser. It includes two storage objects: localStorage and sessionStorage, which allow you to save data on the client side and persist it across multiple browser sessions, respectively. The Web Storage API is designed to be simple and easy to use, and it is widely supported across modern web browsers. It is often used as an alternative to cookies, as it allows for larger amounts of data to be stored and is more efficient in terms of performance.\n\nVisit the following resources to learn more:",
|
||||
"description": "The Web Storage API lets websites store information directly in your web browser. It has two main types: `localStorage` (keeps data even after you close the browser) and `sessionStorage` (keeps data only while the browser tab is open). This is useful for saving things like user preferences or items in a shopping cart. It's simpler and can hold more data than traditional cookies, and most modern browsers support it.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Web Storage API - MDN",
|
||||
@@ -2626,7 +2626,7 @@
|
||||
},
|
||||
"NDJR8UCoa31v45TBFP7we": {
|
||||
"title": "Web Sockets",
|
||||
"description": "Web Sockets is a technology that allows for full-duplex communication over a single TCP connection. It enables real-time, bi-directional communication between a client and a server, and is typically used in applications that require high-speed, low-latency communication, such as online gaming and real-time data streaming. Web Sockets utilizes a persistent connection between a client and a server, allowing for continuous data exchange without the need for the client to send additional requests to the server. This makes it more efficient and faster than other technologies, such as HTTP, which require a new request to be sent for each piece of data. Web Sockets is supported by most modern web browsers and can be used with a variety of programming languages and frameworks.\n\nVisit the following resources to learn more:",
|
||||
"description": "WebSockets enable real-time, two-way communication between a client and server over a single, persistent TCP connection. This is ideal for applications needing fast, low-latency data exchange, like online games or live data streams, as it avoids repeated HTTP requests. Most modern browsers support WebSockets, and it integrates with various programming languages and frameworks.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Web Sockets - MDN",
|
||||
@@ -2647,7 +2647,7 @@
|
||||
},
|
||||
"doPe92aUpo-8KWhi45lWK": {
|
||||
"title": "Server Sent Events",
|
||||
"description": "Server-Sent Events (SSE) is a technology that allows a web server to push data to a client in real-time. It uses an HTTP connection to send a stream of data from the server to the client, and the client can listen for these events and take action when they are received. SSE is useful for applications that require real-time updates, such as chat systems, stock tickers, and social media feeds. It is a simple and efficient way to establish a long-lived connection between a client and a server, and it is supported by most modern web browsers.\n\nTo use SSE, the client must create an EventSource object and specify the URL of the server-side script that will send the events. The server can then send events by writing them to the response stream with the proper formatting.\n\nVisit the following resources to learn more:",
|
||||
"description": "Server-Sent Events (SSE) let a web server send live updates to a webpage. It uses a regular HTTP connection for a one-way stream of data from server to client. This is good for things like live chats or news feeds. It's a simple way to keep a connection open for updates and works in most browsers. The webpage listens for these events and acts on them.\n\nTo use SSE, the client must create an EventSource object and specify the URL of the server-side script that will send the events. The server can then send events by writing them to the response stream with the proper formatting.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Server-Sent Events - MDN",
|
||||
@@ -2663,7 +2663,7 @@
|
||||
},
|
||||
"TldWoXiqKxM4X3JONKAR7": {
|
||||
"title": "Service Workers",
|
||||
"description": "Service Workers are a type of web worker that acts as a proxy between a web page and the network, allowing web developers to build offline-first and reliable applications. Service Workers can intercept network requests, access the cache, and make decisions on how to respond to a request based on the available resources. Service Workers are written in JavaScript and are registered by a web page. Once registered, they can control the page and all its requests, even when the page is not open in a browser. This allows Service Workers to enable features such as push notifications, background synchronization, and offline support. Service Workers are supported by most modern web browsers, and they are an essential component of progressive web applications (PWAs).\n\nVisit the following resources to learn more:",
|
||||
"description": "Service Workers are special JavaScript files that act like a middleman between a webpage and the internet. They help build apps that work offline. Service Workers can catch network requests, use stored (cached) data, and decide how to respond, even if the webpage isn't open. This allows for features like push notifications, background updates, and offline use. Most modern browsers support them, and they are key for Progressive Web Apps (PWAs).\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Service Workers - MDN",
|
||||
@@ -2684,7 +2684,7 @@
|
||||
},
|
||||
"YbGGYoKJEx29PlvopUBiM": {
|
||||
"title": "Location",
|
||||
"description": "The Geolocation API is a web API that provides access to the device's location data, such as latitude and longitude. It allows web developers to build location-based applications, such as mapping and location sharing, by using the device's GPS, Wi-Fi, and other sensors to determine the user's location. To use the Geolocation API, a web page must first request permission from the user to access their location. If permission is granted, the page can then use the `navigator.geolocation` object to access the device's location data. The API provides several methods for getting the user's current location, watching for location changes, and calculating distances between two locations.\n\nVisit the following resources to learn more:",
|
||||
"description": "The Geolocation API lets websites know a device's location (like latitude and longitude). This is for apps that use location, like maps. It uses GPS, Wi-Fi, and other sensors. The website must ask for permission first. If given, it can get the current location, watch for changes, or find distances. This helps make location-aware web apps.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Geolocation API - MDN",
|
||||
@@ -2700,7 +2700,7 @@
|
||||
},
|
||||
"6AlcArOiJMhHXguAosDzn": {
|
||||
"title": "Notifications",
|
||||
"description": "The Notifications API is a web API that allows web pages to display system-level notifications to the user. These notifications can be used to alert the user of important events, such as new messages or updates, even when the web page is not open in the browser. To use the Notifications API, a web page must first request permission from the user to display notifications. If permission is granted, the page can then use the `Notification` constructor to create a new notification and display it to the user. The notification can include a title, body text, and an icon, and it can be customized with options such as a timeout and a click action.\n\nVisit the following resources to learn more:",
|
||||
"description": "The Notifications API lets websites show system alerts to users, like for new messages or updates, even if the site isn't open. The site must ask for permission first. If allowed, it can create notifications with a title, text, and icon. These can also have a timeout or an action when clicked. It helps keep users informed about important events from the website.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Get Started With The Notifications API",
|
||||
@@ -2721,7 +2721,7 @@
|
||||
},
|
||||
"Fd0hQh1DleM0gMzCpGou4": {
|
||||
"title": "Device Orientation",
|
||||
"description": "The Device Orientation API is a web API that provides access to the device's orientation and motion data, such as its pitch, roll, and yaw. It allows web developers to build applications that can respond to the device's orientation and motion, such as augmented reality and motion-controlled games. To use the Device Orientation API, a web page must first request permission from the user to access the device's orientation data. If permission is granted, the page can then use the DeviceOrientationEvent object to access the device's orientation data and respond to changes in orientation. The API provides several properties for accessing the device's orientation and motion data, as well as events for detecting changes in orientation.\n\nVisit the following resources to learn more:",
|
||||
"description": "The Device Orientation API lets websites know how a device is tilted or moving (like its pitch, roll, and yaw). This is for apps that react to movement, like augmented reality or motion games. The site must ask for permission first. If allowed, it can get the device's orientation and react to changes. It helps make interactive, motion-aware web apps.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Device Orientation API - MDN",
|
||||
@@ -2737,7 +2737,7 @@
|
||||
},
|
||||
"MAM1nuVk-h4AvTUk4nvmj": {
|
||||
"title": "Payments",
|
||||
"description": "The Payment Request API is a web API that allows web developers to build checkout flows within their web applications. It provides a standardized, browser-based interface for collecting payment and shipping information from the user, and it supports a wide range of payment methods, including credit cards, debit cards, and digital wallets. To use the Payment Request API, a web page must first create a `PaymentRequest` object and specify the payment and shipping options that are available to the user. The page can then invoke the Payment Request UI by calling the `show()` method on the `PaymentRequest` object. The user can then select their preferred payment and shipping options and confirm the payment, at which point the Payment Request API will return a payment response object that can be used to complete the transaction.\n\nVisit the following resources to learn more:",
|
||||
"description": "The Payment Request API helps websites build checkout forms. It gives a standard way for browsers to get payment and shipping info from users. It works with credit cards, debit cards, and digital wallets. A webpage creates a `PaymentRequest`, shows options, and then the user confirms. The API then gives a response to finish the payment.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Payment Request API - MDN",
|
||||
@@ -2753,7 +2753,7 @@
|
||||
},
|
||||
"opu2bAsmdWHqWqtsCscLC": {
|
||||
"title": "Credentials",
|
||||
"description": "The Credential Management API is a web standard that allows websites to interact with the browser's credential manager to store, retrieve, and manage user credentials. It provides a programmatic interface for seamless and secure user authentication, enabling features like automatic sign-in and one-tap sign-up. The API supports various credential types, including passwords, federated identities, and public key credentials. By leveraging this API, developers can improve user experience by reducing login friction, implementing smoother account switching, and enhancing overall security. It works in conjunction with password managers and platform authenticators, helping to streamline authentication processes across devices and browsers while adhering to modern security practices.\n\nVisit the following resources to learn more:",
|
||||
"description": "The Credential Management API helps websites work with the browser's password manager. It lets sites store and get user logins securely, making sign-in and sign-up easier (like auto sign-in). It supports passwords and other login types. This API improves login experience and security by working with password managers and platform authenticators, making logins smoother across devices.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Credential Management API - MDN",
|
||||
@@ -2769,7 +2769,7 @@
|
||||
},
|
||||
"h26uS3muFCabe6ekElZcI": {
|
||||
"title": "SWC",
|
||||
"description": "The Speedy Web Compiler (SWC) is a fast, extensible JavaScript/TypeScript compiler written in Rust. It's designed as a faster alternative to Babel for transpiling modern JavaScript code into backwards-compatible versions. SWC can be used for both compilation and bundling, offering significant performance improvements over traditional JavaScript-based tools. It supports latest ECMAScript features, JSX, and TypeScript, and can be configured for custom transformations. SWC is commonly used in development environments to speed up build times and in production builds to optimize code. Its speed and compatibility make it increasingly popular in large-scale JavaScript projects and as a core component in other build tools and frameworks aiming for improved performance.\n\nVisit the following resources to learn more:",
|
||||
"description": "SWC (Speedy Web Compiler) is a super-fast tool for changing modern JavaScript and TypeScript code into older versions that more browsers can understand. It's written in Rust, making it much faster than tools like Babel. SWC can compile and bundle code, speeding up website building. It supports new JavaScript features, JSX (used with React), and TypeScript. It's popular for big JavaScript projects and is used by other tools to make them faster too.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "SWC Website",
|
||||
@@ -2800,7 +2800,7 @@
|
||||
},
|
||||
"wA2fSYsbBYU02VJXAvUz8": {
|
||||
"title": "Astro",
|
||||
"description": "Astro is a modern static site generator (SSG) and web framework designed for building fast, content-focused websites. It allows developers to use multiple frontend frameworks (like React, Vue, or Svelte) within the same project, automatically rendering components to static HTML at build time. Astro's unique \"partial hydration\" approach only sends JavaScript to the browser when necessary, resulting in significantly smaller bundle sizes and faster load times. The framework supports file-based routing, markdown content, and built-in optimizations for images and assets. Astro's component islands architecture enables developers to create interactive components while maintaining the performance benefits of static HTML, making it particularly well-suited for content-rich sites like blogs, documentation, and marketing pages.\n\nVisit the following resources to learn more:",
|
||||
"description": "Astro is a static site generator for fast, content-focused websites. It lets you use various frontend frameworks (like React, Vue, Svelte) and renders components to static HTML. Astro's \"partial hydration\" only sends JavaScript when needed, leading to smaller bundles and quicker loads. It supports file-based routing and markdown, making it great for blogs, docs, and marketing sites.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Astro Web Framework Crash Course",
|
||||
|
||||
@@ -222,7 +222,7 @@
|
||||
{
|
||||
"title": "Java Classes and Objects",
|
||||
"url": "https://www.youtube.com/watch?v=IUqKuGNasdM",
|
||||
"type": "article"
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -717,8 +717,14 @@
|
||||
},
|
||||
"J9yIXZTtwbFzH2u4dI1ep": {
|
||||
"title": "CSRF Protection",
|
||||
"description": "Cross-Site Request Forgery (CSRF) Protection in PHP is a method where a website can defend itself against unwanted actions performed on behalf of the users without their consent. It's a critical aspect of security as it safeguards users against potential harmful activities. Here's an example: if users are logged into a website and get tricked into clicking a deceitful link, CSRF attacks could be triggered. To protect your PHP applications from such attacks, you can generate a unique token for every session and include it as a hidden field for all form submissions. Afterwards, you need to verify this token on the server side before performing any action.\n\n <?php\n // Generate CSRF token\n if(empty($_SESSION['csrf'])) {\n $_SESSION['csrf'] = bin2hex(random_bytes(32));\n }\n \n // Verify CSRF token\n if(isset($_POST['csrf']) && $_POST['csrf'] === $_SESSION['csrf']) {\n // valid CSRF token, perform action\n }\n ?>\n \n\nVisit the following resources to learn more:\n\n* \\[@article@PHP Tutorial CSRF\\] ([https://www.phptutorial.net/php-tutorial/php-csrf/](https://www.phptutorial.net/php-tutorial/php-csrf/))",
|
||||
"links": []
|
||||
"description": "Cross-Site Request Forgery (CSRF) Protection in PHP is a method where a website can defend itself against unwanted actions performed on behalf of the users without their consent. It's a critical aspect of security as it safeguards users against potential harmful activities. Here's an example: if users are logged into a website and get tricked into clicking a deceitful link, CSRF attacks could be triggered. To protect your PHP applications from such attacks, you can generate a unique token for every session and include it as a hidden field for all form submissions. Afterwards, you need to verify this token on the server side before performing any action.\n\n <?php\n // Generate CSRF token\n if(empty($_SESSION['csrf'])) {\n $_SESSION['csrf'] = bin2hex(random_bytes(32));\n }\n \n // Verify CSRF token\n if(isset($_POST['csrf']) && $_POST['csrf'] === $_SESSION['csrf']) {\n // valid CSRF token, perform action\n }\n ?>\n \n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PHP Tutorial CSRF",
|
||||
"url": "https://www.phptutorial.net/php-tutorial/php-csrf/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"JbWFfJiCRrXDhnuIx_lqx": {
|
||||
"title": "Password Hashing",
|
||||
@@ -890,7 +896,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Inheritance",
|
||||
"url": "https://www.php.net/manual/en/keyword.extends.php",
|
||||
"url": "https://www.php.net/manual/en/language.oop5.inheritance.php",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
@@ -912,8 +918,8 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Polymorphism",
|
||||
"url": "https://www.php.net/manual/en/language.oop5.polymorphism.php",
|
||||
"type": "article"
|
||||
"url": "https://www.phptutorial.net/php-oop/php-polymorphism/",
|
||||
"type": "opensource"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
232
public/roadmap-content/prompt-engineering.json
Normal file
232
public/roadmap-content/prompt-engineering.json
Normal file
@@ -0,0 +1,232 @@
|
||||
{
|
||||
"jrH1qE6EnFXL4fTyYU8gR": {
|
||||
"title": "Introduction",
|
||||
"description": "Prompt engineering is the practice of designing effective inputs for Large Language Models to achieve desired outputs. This roadmap covers fundamental concepts, core techniques, model parameters, and advanced methods. It's a universal skill accessible to anyone, requiring no programming background, yet crucial for unlocking AI potential across diverse applications and domains.",
|
||||
"links": []
|
||||
},
|
||||
"74JxgfJ_1qmVNZ_QRp9Ne": {
|
||||
"title": "LLMs and how they work?",
|
||||
"description": "LLMs function as sophisticated prediction engines that process text sequentially, predicting the next token based on relationships between previous tokens and patterns from training data. They don't predict single tokens directly but generate probability distributions over possible next tokens, which are then sampled using parameters like temperature and top-K. The model repeatedly adds predicted tokens to the sequence, building responses iteratively. This token-by-token prediction process, combined with massive training datasets, enables LLMs to generate coherent, contextually relevant text across diverse applications and domains.",
|
||||
"links": []
|
||||
},
|
||||
"i4ijY3T5gLgNz0XqRipXe": {
|
||||
"title": "What is a Prompt?",
|
||||
"description": "A prompt is an input provided to a Large Language Model (LLM) to generate a response or prediction. It serves as the instruction or context that guides the AI model's output generation process. Effective prompts are clear, specific, well-structured, and goal-oriented, directly affecting the accuracy and relevance of AI responses.",
|
||||
"links": []
|
||||
},
|
||||
"43drPbTwPqJQPyzwYUdBT": {
|
||||
"title": "What is Prompt Engineering?",
|
||||
"description": "Prompt engineering is the practice of crafting effective input text to guide AI language models toward desired outputs. It involves designing prompts that communicate intent clearly to get accurate, relevant responses. This iterative process requires understanding how LLMs work as prediction engines and using techniques to optimize their performance for specific tasks.",
|
||||
"links": []
|
||||
},
|
||||
"Yb5cQiV2ETxPbBYCLOpt2": {
|
||||
"title": "OpenAI",
|
||||
"description": "OpenAI developed influential language models including GPT-3, GPT-4, and ChatGPT, setting industry standards for prompt engineering practices. Their API provides access to powerful LLMs with configurable parameters like temperature and max tokens. Many prompt engineering techniques and best practices originated from working with OpenAI systems.",
|
||||
"links": []
|
||||
},
|
||||
"o-6UKLZ6oCRbAKgRjH2uI": {
|
||||
"title": "Google",
|
||||
"description": "Google develops influential LLMs including Gemini, PaLM, and Bard. Through Vertex AI and Google Cloud Platform, they provide enterprise-grade model access with extensive prompt testing via Vertex AI Studio. Google's research has advanced many prompt engineering techniques, including Chain of Thought reasoning methods.",
|
||||
"links": []
|
||||
},
|
||||
"V8pDOwrRKKcHBTd4qlSsH": {
|
||||
"title": "Anthropic",
|
||||
"description": "Anthropic created Claude, a family of large language models known for safety features and constitutional AI training. Claude models excel at following instructions, maintaining context, and avoiding harmful outputs. Their strong instruction-following capabilities and built-in safety measures make them valuable for reliable, ethical AI applications.",
|
||||
"links": []
|
||||
},
|
||||
"Td2YzDFT4LPGDw8JMmQSQ": {
|
||||
"title": "Meta",
|
||||
"description": "Meta (formerly Facebook) develops the Llama family of open-source large language models. Llama models are available for research and commercial use, offering strong performance across various tasks. For prompt engineering, Meta's models provide transparency in training data and architecture, allowing developers to fine-tune and customize prompts for specific applications without vendor lock-in.",
|
||||
"links": []
|
||||
},
|
||||
"3wshuH7_DXgbhxsLzzI4D": {
|
||||
"title": "xAI",
|
||||
"description": "xAI is Elon Musk's AI company that created Grok, a conversational AI model trained on web data with a focus on real-time information and humor. Grok aims to be more truthful and less politically correct than other models. For prompt engineering, xAI offers unique capabilities in accessing current events and generating responses with a distinctive conversational style.",
|
||||
"links": []
|
||||
},
|
||||
"pamV5Z8DRKk2ioZbg6QVK": {
|
||||
"title": "LLM",
|
||||
"description": "Large Language Models (LLMs) are AI systems trained on vast text data to understand and generate human-like language. They work as prediction engines, analyzing input and predicting the next most likely token. LLMs perform tasks like text generation, translation, summarization, and Q&A. Understanding token processing is key to effective prompt engineering.",
|
||||
"links": []
|
||||
},
|
||||
"NPcaSEteeEA5g22wQ7nL_": {
|
||||
"title": "Tokens",
|
||||
"description": "Tokens are fundamental units of text that LLMs process, created by breaking down text into smaller components like words, subwords, or characters. Understanding tokens is crucial because models predict the next token in sequences, API costs are based on token count, and models have maximum token limits for input and output.",
|
||||
"links": []
|
||||
},
|
||||
"b-Xtkv6rt8QgzJXSShOX-": {
|
||||
"title": "Context Window",
|
||||
"description": "Context window refers to the maximum number of tokens an LLM can process in a single interaction, including both input prompt and generated output. When exceeded, older parts are truncated. Understanding this constraint is crucial for prompt engineering—you must balance providing sufficient context with staying within token limits.",
|
||||
"links": []
|
||||
},
|
||||
"SWDa3Su3VS815WQbvvNsa": {
|
||||
"title": "Hallucination",
|
||||
"description": "Hallucination in LLMs refers to generating plausible-sounding but factually incorrect or fabricated information. This occurs when models fill knowledge gaps or present uncertain information with apparent certainty. Mitigation techniques include requesting sources, asking for confidence levels, providing context, and always verifying critical information independently.",
|
||||
"links": []
|
||||
},
|
||||
"yfsjW1eze8mWT0iHxv078": {
|
||||
"title": "Model Weights / Parameters",
|
||||
"description": "Model weights and parameters are the learned values that define an LLM's behavior and knowledge. Parameters are the trainable variables adjusted during training, while weights represent their final values. Understanding parameter count helps gauge model capabilities - larger models typically have more parameters and better performance but require more computational resources.",
|
||||
"links": []
|
||||
},
|
||||
"Ke5GT163k_ek9SzbcbBGE": {
|
||||
"title": "Fine-Tuning vs Prompt Engg.",
|
||||
"description": "Fine-tuning trains models on specific data to specialize behavior, while prompt engineering achieves customization through input design without model modification. Prompt engineering is faster, cheaper, and more accessible. Fine-tuning offers deeper customization but requires significant resources and expertise.",
|
||||
"links": []
|
||||
},
|
||||
"gxydtFKmnXNY9I5kpTwjP": {
|
||||
"title": "RAG",
|
||||
"description": "Retrieval-Augmented Generation (RAG) combines LLMs with external knowledge retrieval to ground responses in verified, current information. RAG retrieves relevant documents before generating responses, reducing hallucinations and enabling access to information beyond the model's training cutoff. This approach improves accuracy and provides source attribution.",
|
||||
"links": []
|
||||
},
|
||||
"Pw5LWA9vNRY0N2M0FW16f": {
|
||||
"title": "Agents",
|
||||
"description": "AI agents are autonomous systems that use LLMs to reason, plan, and take actions to achieve specific goals. They combine language understanding with tool usage, memory, and decision-making to perform complex, multi-step tasks. Agents can interact with external APIs and services while maintaining context across interactions.",
|
||||
"links": []
|
||||
},
|
||||
"6W_ONYREbXHwPigoDx1cW": {
|
||||
"title": "Prompt Injection",
|
||||
"description": "Prompt injection is a security vulnerability where malicious users manipulate LLM inputs to override intended behavior, bypass safety measures, or extract sensitive information. Attackers embed instructions within data to make models ignore original prompts and follow malicious commands. Mitigation requires input sanitization, injection-resistant prompt design, and proper security boundaries.",
|
||||
"links": []
|
||||
},
|
||||
"Sj1CMZzZp8kF-LuHcd_UU": {
|
||||
"title": "AI vs AGI",
|
||||
"description": "AI (Artificial Intelligence) refers to systems that perform specific tasks intelligently, while AGI (Artificial General Intelligence) represents hypothetical AI with human-level reasoning across all domains. Current LLMs are narrow AI - powerful at language tasks but lacking true understanding or general intelligence like AGI would possess.",
|
||||
"links": []
|
||||
},
|
||||
"JgigM7HvmNOuKnp60v1Ce": {
|
||||
"title": "Sampling Parameters",
|
||||
"description": "Sampling parameters (temperature, top-K, top-P) control how LLMs select tokens from probability distributions, determining output randomness and creativity. These parameters interact: at extreme settings, one can override others (temperature 0 makes top-K/top-P irrelevant). A balanced starting point is temperature 0.2, top-P 0.95, top-K 30 for coherent but creative results. Understanding their interactions is crucial for optimal prompting—use temperature 0 for factual tasks, higher values for creativity, and combine settings strategically based on your specific use case.",
|
||||
"links": []
|
||||
},
|
||||
"iMwg-I76-Tg5dhu8DGO6U": {
|
||||
"title": "Temperature",
|
||||
"description": "Temperature controls the randomness in token selection during text generation. Lower values (0-0.3) produce deterministic, factual outputs. Medium values (0.5-0.7) balance creativity and coherence. Higher values (0.8-1.0) generate creative, diverse outputs but may be less coherent. Use low temperature for math/facts, high for creative writing.",
|
||||
"links": []
|
||||
},
|
||||
"FF8ai1v5GDzxXLQhpwuPj": {
|
||||
"title": "Top-K",
|
||||
"description": "Top-K restricts token selection to the K most likely tokens from the probability distribution. Low values (1-10) produce conservative, factual outputs. Medium values (20-50) balance creativity and quality. High values (50+) enable diverse, creative outputs. Use low K for technical tasks, high K for creative writing.",
|
||||
"links": []
|
||||
},
|
||||
"-G1U1jDN5st1fTUtQmMl1": {
|
||||
"title": "Top-P",
|
||||
"description": "Top-P (nucleus sampling) selects tokens from the smallest set whose cumulative probability exceeds threshold P. Unlike Top-K's fixed number, Top-P dynamically adjusts based on probability distribution. Low values (0.1-0.5) produce focused outputs, medium (0.6-0.9) balance creativity and coherence, high (0.9-0.99) enable creative diversity.",
|
||||
"links": []
|
||||
},
|
||||
"wSf7Zr8ZYBuKWX0GQX6J3": {
|
||||
"title": "Output Control",
|
||||
"description": "Output control encompasses techniques and parameters for managing LLM response characteristics including length, format, style, and content boundaries. Key methods include max tokens for length limits, stop sequences for precise boundaries, temperature for creativity control, and structured output requirements for format consistency. Effective output control combines prompt engineering techniques with model parameters to ensure responses meet specific requirements. This is crucial for production applications where consistent, appropriately formatted outputs are essential for user experience and system integration.",
|
||||
"links": []
|
||||
},
|
||||
"vK9Gf8dGu2UvvJJhhuHG9": {
|
||||
"title": "Max Tokens",
|
||||
"description": "Max tokens setting controls the maximum number of tokens an LLM can generate in response, directly impacting computation cost, response time, and energy consumption. Setting lower limits doesn't make models more concise—it simply stops generation when the limit is reached. This parameter is crucial for techniques like ReAct where models might generate unnecessary tokens after the desired response. Balancing max tokens involves considering cost efficiency, response completeness, and application requirements while ensuring critical information isn't truncated.",
|
||||
"links": []
|
||||
},
|
||||
"v3CylRlojeltcwnE76j8Q": {
|
||||
"title": "Stop Sequences",
|
||||
"description": "Stop sequences are specific strings that signal the LLM to stop generating text when encountered, providing precise control over output length and format. Common examples include newlines, periods, or custom markers like \"###\" or \"END\". This parameter is particularly useful for structured outputs, preventing models from generating beyond intended boundaries. Stop sequences are essential for ReAct prompting and other scenarios where you need clean, precisely bounded responses. They offer more control than max tokens by stopping at logical breakpoints rather than arbitrary token limits.",
|
||||
"links": []
|
||||
},
|
||||
"g8ylIg4Zh567u-E3yVVY4": {
|
||||
"title": "Repetition Penalties",
|
||||
"description": "Repetition penalties discourage LLMs from repeating words or phrases by reducing the probability of selecting previously used tokens. This includes frequency penalty (scales with usage count) and presence penalty (applies equally to any used token). These parameters improve output quality by promoting vocabulary diversity and preventing redundant phrasing.",
|
||||
"links": []
|
||||
},
|
||||
"YIVNjkmTOY61VmL0md9Pj": {
|
||||
"title": "Frequency Penalty",
|
||||
"description": "Frequency penalty reduces token probability based on how frequently they've appeared in the text, with higher penalties for more frequent tokens. This prevents excessive repetition and encourages varied language use. The penalty scales with usage frequency, making overused words less likely to be selected again, improving content diversity.",
|
||||
"links": []
|
||||
},
|
||||
"WpO8V5caudySVehOcuDvK": {
|
||||
"title": "Presence Penalty",
|
||||
"description": "Presence penalty reduces the likelihood of repeating tokens that have already appeared in the text, encouraging diverse vocabulary usage. Unlike frequency penalty which considers how often tokens appear, presence penalty applies the same penalty to any previously used token, promoting varied content and creativity.",
|
||||
"links": []
|
||||
},
|
||||
"j-PWO-ZmF9Oi9A5bwMRto": {
|
||||
"title": "Structured Outputs",
|
||||
"description": "Structured outputs involve prompting LLMs to return responses in specific formats like JSON, XML, or other organized structures rather than free-form text. This approach forces models to organize information systematically, reduces hallucinations by imposing format constraints, enables easy programmatic processing, and facilitates integration with applications. For example, requesting movie classification results as JSON with specified schema ensures consistent, parseable responses. Structured outputs are particularly valuable for data extraction, API integration, and applications requiring reliable data formatting.",
|
||||
"links": []
|
||||
},
|
||||
"GRerL9UXN73TwpCW2eTIE": {
|
||||
"title": "Zero-Shot Prompting",
|
||||
"description": "Zero-shot prompting provides only a task description without examples, relying on the model's training patterns. Simply describe the task clearly, provide input data, and optionally specify output format. Works well for simple classification, text generation, and Q&A, but may produce inconsistent results for complex tasks.",
|
||||
"links": []
|
||||
},
|
||||
"Iufv_LsgUNls-Alx_Btlh": {
|
||||
"title": "One-Shot / Few-Shot Prompting",
|
||||
"description": "One-shot provides a single example to guide model behavior, while few-shot includes multiple examples (3-5) to demonstrate desired patterns. Examples show output structure, style, and tone, increasing accuracy and consistency. Use few-shot for complex formatting, specialized tasks, and when zero-shot results are inconsistent.",
|
||||
"links": []
|
||||
},
|
||||
"fWo39-hehRgwmx7CF36mM": {
|
||||
"title": "System Prompting",
|
||||
"description": "System prompting sets the overall context, purpose, and operational guidelines for LLMs. It defines the model's role, behavioral constraints, output format requirements, and safety guardrails. System prompts provide foundational parameters that influence all subsequent interactions, ensuring consistent, controlled, and structured AI responses throughout the session.",
|
||||
"links": []
|
||||
},
|
||||
"XHWKGaSRBYT4MsCHwV-iR": {
|
||||
"title": "Role Prompting",
|
||||
"description": "Role prompting assigns a specific character, identity, or professional role to the LLM to generate responses consistent with that role's expertise, personality, and communication style. By establishing roles like \"teacher,\" \"travel guide,\" or \"software engineer,\" you provide the model with appropriate domain knowledge, perspective, and tone for more targeted, natural interactions.",
|
||||
"links": []
|
||||
},
|
||||
"5TNK1KcSzh9GTKiEJnM-y": {
|
||||
"title": "Contextual Prompting",
|
||||
"description": "Contextual prompting provides specific background information or situational details relevant to the current task, helping LLMs understand nuances and tailor responses accordingly. Unlike system or role prompts, contextual prompts supply immediate, task-specific information that's dynamic and changes based on the situation. For example: \"Context: You are writing for a blog about retro 80's arcade video games. Suggest 3 topics to write articles about.\" This technique ensures responses are relevant, accurate, and appropriately framed for the specific context provided.",
|
||||
"links": []
|
||||
},
|
||||
"2MboHh8ugkoH8dSd9d4Mk": {
|
||||
"title": "Step-back Prompting",
|
||||
"description": "Step-back prompting improves LLM performance by first asking a general question related to the specific task, then using that answer to inform the final response. This technique activates relevant background knowledge before attempting the specific problem. For example, before writing a video game level storyline, first ask \"What are key settings for engaging first-person shooter levels?\" then use those insights to create the specific storyline. This approach reduces biases and improves accuracy by grounding responses in broader principles.",
|
||||
"links": []
|
||||
},
|
||||
"weRaJxEplhKDyFWSMeoyI": {
|
||||
"title": "Chain of Thought (CoT) Prompting",
|
||||
"description": "Chain of Thought prompting improves LLM reasoning by generating intermediate reasoning steps before providing the final answer. Instead of jumping to conclusions, the model \"thinks through\" problems step by step. Simply adding \"Let's think step by step\" to prompts often dramatically improves accuracy on complex reasoning tasks and mathematical problems.",
|
||||
"links": []
|
||||
},
|
||||
"1EzqCoplXPiHjp9Z-vqn-": {
|
||||
"title": "Self-Consistency Prompting",
|
||||
"description": "Self-consistency prompting generates multiple reasoning paths for the same problem using higher temperature settings, then selects the most commonly occurring answer through majority voting. This technique combines sampling and voting to improve accuracy and provides pseudo-probability of answer correctness. While more expensive due to multiple API calls, it significantly enhances reliability for complex reasoning tasks by reducing the impact of single incorrect reasoning chains and leveraging diverse problem-solving approaches.",
|
||||
"links": []
|
||||
},
|
||||
"ob9D0W9B9145Da64nbi1M": {
|
||||
"title": "Tree of Thoughts (ToT) Prompting",
|
||||
"description": "Tree of Thoughts (ToT) generalizes Chain of Thought by allowing LLMs to explore multiple reasoning paths simultaneously rather than following a single linear chain. This approach maintains a tree structure where each thought represents a coherent step toward solving a problem, enabling the model to branch out and explore different reasoning directions. ToT is particularly effective for complex tasks requiring exploration and is well-suited for problems that benefit from considering multiple solution approaches before converging on the best answer.",
|
||||
"links": []
|
||||
},
|
||||
"8Ks6txRSUfMK7VotSQ4sC": {
|
||||
"title": "ReAct Prompting",
|
||||
"description": "ReAct (Reason and Act) prompting enables LLMs to solve complex tasks by combining reasoning with external tool interactions. It follows a thought-action-observation loop: analyze the problem, perform actions using external APIs, review results, and iterate until solved. Useful for research, multi-step problems, and tasks requiring current data.",
|
||||
"links": []
|
||||
},
|
||||
"diHNCiuKHeMVgvJ4OMwVh": {
|
||||
"title": "Automatic Prompt Engineering",
|
||||
"description": "Automatic Prompt Engineering (APE) uses LLMs to generate and optimize prompts automatically, reducing human effort while enhancing model performance. The process involves prompting a model to create multiple prompt variants, evaluating them using metrics like BLEU or ROUGE, then selecting the highest-scoring candidate. For example, generating 10 variants of customer order phrases for chatbot training, then testing and refining the best performers. This iterative approach helps discover effective prompts that humans might not consider, automating the optimization process.",
|
||||
"links": []
|
||||
},
|
||||
"Wvu9Q_kNhH1_JlOgxAjP6": {
|
||||
"title": "AI Red Teaming",
|
||||
"description": "AI red teaming involves deliberately testing AI systems to find vulnerabilities, biases, or harmful behaviors through adversarial prompting. Teams attempt to make models produce undesired outputs, bypass safety measures, or exhibit problematic behaviors. This process helps identify weaknesses and improve AI safety and robustness before deployment.",
|
||||
"links": []
|
||||
},
|
||||
"0H2keZYD8iTNyBgmNVhto": {
|
||||
"title": "Prompt Debiasing",
|
||||
"description": "Prompt debiasing involves techniques to reduce unwanted biases in LLM outputs by carefully crafting prompts. This includes using neutral language, diverse examples, and explicit instructions to avoid stereotypes or unfair representations. Effective debiasing helps ensure AI outputs are more fair, inclusive, and representative across different groups and perspectives.",
|
||||
"links": []
|
||||
},
|
||||
"HOqWHqAkxLX8f2ImSmZE7": {
|
||||
"title": "Prompt Ensembling",
|
||||
"description": "Prompt ensembling combines multiple different prompts or prompt variations to improve output quality and consistency. This technique involves running the same query with different prompt formulations and aggregating results through voting, averaging, or selection. Ensembling reduces variance and increases reliability by leveraging diverse prompt perspectives.",
|
||||
"links": []
|
||||
},
|
||||
"CvV3GIvQhsTvE-TQjTpIQ": {
|
||||
"title": "LLM Self Evaluation",
|
||||
"description": "LLM self-evaluation involves prompting models to assess their own outputs for quality, accuracy, or adherence to criteria. This technique can identify errors, rate confidence levels, or check if responses meet specific requirements. Self-evaluation helps improve output quality through iterative refinement and provides valuable feedback for prompt optimization.",
|
||||
"links": []
|
||||
},
|
||||
"P5nDyQbME53DOEfSkcY6I": {
|
||||
"title": "Calibrating LLMs",
|
||||
"description": "Calibrating LLMs involves adjusting models so their confidence scores accurately reflect their actual accuracy. Well-calibrated models express appropriate uncertainty - being confident when correct and uncertain when likely wrong. This helps users better trust and interpret model outputs, especially in critical applications where uncertainty awareness is crucial.",
|
||||
"links": []
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,6 @@
|
||||
"title": "CLI Tools",
|
||||
"description": "Here is the list of most common CLI tools for React development:",
|
||||
"links": [
|
||||
{
|
||||
"title": "create-react-app",
|
||||
"url": "https://create-react-app.dev",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "vite",
|
||||
"url": "https://vitejs.dev",
|
||||
|
||||
@@ -1820,7 +1820,7 @@
|
||||
},
|
||||
"Mt5W1IvuHevNXVRlh7z26": {
|
||||
"title": "OSI",
|
||||
"description": "The OSI and TCP/IP model is used to help the developer to design their system for interoperability. The OSI model has 7 layers while the TCP/IP model has a more summarized form of the OSI model only consisting 4 layers. This is important if you're are trying to design a system to communicate with other systems.\n\nVisit the following resources to learn more:",
|
||||
"description": "The OSI and TCP/IP model is used to help the developer to design their system for interoperability. The OSI model has 7 layers while the TCP/IP model has a more summarized form of the OSI model only consisting 4 layers. This is important if you're trying to design a system to communicate with other systems.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Cloudflare - What is the OSI model",
|
||||
|
||||
@@ -382,7 +382,7 @@
|
||||
"description": "`any` is a special type in TypeScript that represents a value of any type. When a value is declared with the any type, the compiler will not perform any type checks or type inference on that value.\n\nFor example:\n\n let anyValue: any = 42;\n \n // we can assign any value to anyValue, regardless of its type\n anyValue = 'Hello, world!';\n anyValue = true;\n \n\nLearn more from the following links:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Arrays",
|
||||
"title": "any",
|
||||
"url": "https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any",
|
||||
"type": "article"
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
},
|
||||
"OpJ2NMKCGXQezpzURE45R": {
|
||||
"title": "API Styles",
|
||||
"description": "Up until Vue 2, there was one way to create components in Vue. With Vue 3, a new methodology was introduced called the Composition API. Now, if we want to make a component in Vue, we have two ways to do it. You might be wondering what the difference is, exactly, so let’s take a look at how the newer Composition API differs from the Vue 2 methodology, which is now known as the Options API\n\nVisit the following resources to learn more:",
|
||||
"description": "Though Vue 2 supported many approaches to writing components, only one approach, the \"Options API\", was built in and accessible without plugins. Vue 3, retains the Options API (OAPI), and adds in the Composition API (CAPI). Composition API itself is actually a part of the Options API, but moves most of the component logic into a setup function. This led to a 3rd approach to writing components being built in called \"script/setup\", which removes much of the boilerplate from Composition API. These approaches are not mutually exclusive. Each component in your project can use any of these approaches, however for consistency it is recommended to stick primarily to one, and only deviate to one of the others when you have a good reason.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "The Difference Between the Composition API and Options API in Vue",
|
||||
@@ -152,7 +152,7 @@
|
||||
},
|
||||
"PPUU3Rb73aCpT4zcyvlJE": {
|
||||
"title": "Options API",
|
||||
"description": "We use Options API in a Vue application to write and define different components. With this API, we can use options such as data, methods, and mounted. To state it simply, Options API is an old way to structure a Vue.JS application. Due to some limitations in this API, Composition API was introduced in Vue 3.\n\nVisit the following resources to learn more:",
|
||||
"description": "Vue offers many approaches for how to write components, including the Options API. It is the only API that is available in all versions of Vue. Its primary focus is on providing a consistent, clean, and organized aproach to writing component logic. Each part of a component's logic is given a dedicated section (data, methods, computed, props, life-cycle hooks, etc). By putting the logic in the correct section it has access to the features of the framework automatically. With the official Vue ESLint plugin the order of these sections can be enforced across all components allowing developers to predicatably locate any part of the component, even if they've never looked at the file before.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "TypeScript with Options API",
|
||||
@@ -168,7 +168,7 @@
|
||||
},
|
||||
"a0qwdQTunxEjQ9A5wpF-q": {
|
||||
"title": "Composition API",
|
||||
"description": "With the release of Vue 3, developers now have access to the Composition API, a new way to write Vue components. This API allows features to be grouped together logically, rather than having to organize your single-file components by function. Using the Composition API can lead to more readable code, and gives the developer more flexibility when developing their applications.\n\nVisit the following resources to learn more:",
|
||||
"description": "Vue offers many approaches for how to write components, including the \"Composition API\", which is most commonly used via \"Script Setup\". This approach is based around pulling in low level atomic functions used by Vue's reactivity engine. By composing these low level functions together, you can craft your own system for writing components. One feature this allows for is extending reactivity outside of components. This means you can extract your custom functions for reuse in multiple components. When reusing reactive logic, your functions are called \"composables\".\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "TypeScript with Composition API",
|
||||
|
||||
BIN
public/roadmaps/ai-agents.png
Normal file
BIN
public/roadmaps/ai-agents.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 807 KiB |
BIN
public/roadmaps/prompt-engineering.png
Normal file
BIN
public/roadmaps/prompt-engineering.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 414 KiB |
@@ -87,6 +87,8 @@ Here is the list of available roadmaps with more being actively worked upon.
|
||||
- [Prompt Engineering Roadmap](https://roadmap.sh/prompt-engineering)
|
||||
- [Technical Writer Roadmap](https://roadmap.sh/technical-writer)
|
||||
- [DevRel Engineer Roadmap](https://roadmap.sh/devrel)
|
||||
- [AI Red Teaming Roadmap](https://roadmap.sh/ai-red-teaming)
|
||||
- [AI Agents Roadmap](https://roadmap.sh/ai-agents)
|
||||
|
||||
There are also interactive best practices:
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { Node } from '@roadmapsh/editor';
|
||||
import matter from 'gray-matter';
|
||||
import { HTMLElement, parse } from 'node-html-parser';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import type { Node } from '@roadmapsh/editor';
|
||||
import matter from 'gray-matter';
|
||||
import { htmlToMarkdown } from '../src/lib/html';
|
||||
import { markdownToHtml } from '../src/lib/markdown';
|
||||
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
|
||||
import { slugify } from '../src/lib/slugger';
|
||||
import { markdownToHtml } from '../src/lib/markdown';
|
||||
import { HTMLElement, parse } from 'node-html-parser';
|
||||
import { htmlToMarkdown } from '../src/lib/html';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -21,6 +21,23 @@ export const allowedLinkTypes = [
|
||||
'podcast',
|
||||
] as const;
|
||||
|
||||
export async function fetchRoadmapJson(roadmapId: string) {
|
||||
const response = await fetch(
|
||||
`https://roadmap.sh/api/v1-official-roadmap/${roadmapId}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch roadmap json: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (data.error) {
|
||||
throw new Error(`Failed to fetch roadmap json: ${data.error}`);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Directory containing the roadmaps
|
||||
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/data/roadmaps');
|
||||
const allRoadmaps = await fs.readdir(ROADMAP_CONTENT_DIR);
|
||||
@@ -53,13 +70,17 @@ if (!stats || !stats.isDirectory()) {
|
||||
for (const roadmapId of editorRoadmapIds) {
|
||||
console.log(`🚀 Starting ${roadmapId}`);
|
||||
|
||||
const roadmapDir = path.join(
|
||||
ROADMAP_CONTENT_DIR,
|
||||
roadmapId,
|
||||
`${roadmapId}.json`,
|
||||
);
|
||||
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
|
||||
let { nodes } = JSON.parse(roadmapContent) as {
|
||||
const data = await fetchRoadmapJson(roadmapId).catch((error) => {
|
||||
console.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
console.error(`Failed to fetch roadmap json: ${roadmapId}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
let { nodes } = data as {
|
||||
nodes: Node[];
|
||||
};
|
||||
nodes = nodes.filter(
|
||||
@@ -97,11 +118,11 @@ for (const roadmapId of editorRoadmapIds) {
|
||||
> = {};
|
||||
|
||||
for (const node of nodes) {
|
||||
const ndoeDirPatterWithoutExt = `${slugify(node.data.label)}@${node.id}`;
|
||||
const nodeDirPattern = `${ndoeDirPatterWithoutExt}.md`;
|
||||
const nodeDirPatternWithoutExt = `${slugify(node?.data?.label as string)}@${node.id}`;
|
||||
const nodeDirPattern = `${nodeDirPatternWithoutExt}.md`;
|
||||
if (!roadmapContentFiles.includes(nodeDirPattern)) {
|
||||
contentMap[nodeDirPattern] = {
|
||||
title: node.data.label,
|
||||
title: node?.data?.label as string,
|
||||
description: '',
|
||||
links: [],
|
||||
};
|
||||
@@ -169,7 +190,7 @@ for (const roadmapId of editorRoadmapIds) {
|
||||
const description = htmlToMarkdown(htmlStringWithoutLinks);
|
||||
|
||||
contentMap[node.id] = {
|
||||
title: node.data.label,
|
||||
title: node.data.label as string,
|
||||
description,
|
||||
links: listLinks,
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { RoadmapFrontmatter } from '../src/lib/roadmap';
|
||||
import { slugify } from '../src/lib/slugger';
|
||||
import OpenAI from 'openai';
|
||||
import { runPromisesInBatchSequentially } from '../src/lib/promise';
|
||||
import { httpGet } from '../src/lib/http';
|
||||
|
||||
// ERROR: `__dirname` is not defined in ES module scope
|
||||
// https://iamwebwiz.medium.com/how-to-fix-dirname-is-not-defined-in-es-module-scope-34d94a86694d
|
||||
@@ -50,13 +51,16 @@ if (roadmapFrontmatter.renderer !== 'editor') {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const roadmapDir = path.join(
|
||||
ROADMAP_CONTENT_DIR,
|
||||
roadmapId,
|
||||
`${roadmapId}.json`,
|
||||
const { response: roadmapContent, error } = await httpGet(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${roadmapId}`,
|
||||
);
|
||||
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
|
||||
let { nodes, edges } = JSON.parse(roadmapContent) as {
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let { nodes, edges } = roadmapContent as {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
};
|
||||
@@ -138,7 +142,7 @@ function writeTopicContent(
|
||||
}
|
||||
|
||||
async function writeNodeContent(node: Node & { parentTitle?: string }) {
|
||||
const nodeDirPattern = `${slugify(node.data.label)}@${node.id}.md`;
|
||||
const nodeDirPattern = `${slugify(node?.data?.label as string)}@${node.id}.md`;
|
||||
if (!roadmapContentFiles.includes(nodeDirPattern)) {
|
||||
console.log(`Missing file for: ${nodeDirPattern}`);
|
||||
return;
|
||||
@@ -152,7 +156,7 @@ async function writeNodeContent(node: Node & { parentTitle?: string }) {
|
||||
return;
|
||||
}
|
||||
|
||||
const topic = node.data.label;
|
||||
const topic = node.data.label as string;
|
||||
const parentTopic = node.parentTitle;
|
||||
|
||||
console.log(`⏳ Generating content for ${topic}...`);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Node } from '@roadmapsh/editor';
|
||||
import matter from 'gray-matter';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import type { Node } from '@roadmapsh/editor';
|
||||
import matter from 'gray-matter';
|
||||
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
|
||||
import { slugify } from '../src/lib/slugger';
|
||||
|
||||
@@ -48,13 +48,31 @@ if (roadmapFrontmatter.renderer !== 'editor') {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const roadmapDir = path.join(
|
||||
ROADMAP_CONTENT_DIR,
|
||||
roadmapId,
|
||||
`${roadmapId}.json`,
|
||||
);
|
||||
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
|
||||
let { nodes } = JSON.parse(roadmapContent) as {
|
||||
export async function fetchRoadmapJson(roadmapId: string) {
|
||||
const response = await fetch(
|
||||
`https://roadmap.sh/api/v1-official-roadmap/${roadmapId}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch roadmap json: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (data.error) {
|
||||
throw new Error(`Failed to fetch roadmap json: ${data.error}`);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
const roadmapContent = await fetchRoadmapJson(roadmapId);
|
||||
|
||||
if (!roadmapContent) {
|
||||
console.error(`Failed to fetch roadmap json: ${roadmapId}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let { nodes } = roadmapContent as {
|
||||
nodes: Node[];
|
||||
};
|
||||
nodes = nodes.filter(
|
||||
@@ -73,7 +91,7 @@ const roadmapContentFiles = await fs.readdir(roadmapContentDir, {
|
||||
});
|
||||
|
||||
nodes.forEach(async (node, index) => {
|
||||
const nodeDirPattern = `${slugify(node.data.label)}@${node.id}.md`;
|
||||
const nodeDirPattern = `${slugify(node.data.label as string)}@${node.id}.md`;
|
||||
if (roadmapContentFiles.includes(nodeDirPattern)) {
|
||||
console.log(`Skipping ${nodeDirPattern}`);
|
||||
return;
|
||||
|
||||
@@ -13,494 +13,394 @@ const __dirname = dirname(__filename);
|
||||
* @property {string} text - The text content of the node
|
||||
*/
|
||||
|
||||
const roadmapId = 'php';
|
||||
const roadmapId = 'ai-agents';
|
||||
|
||||
/** @type {Node[]} */
|
||||
const nodes = [
|
||||
{
|
||||
"id": "_hYN0gEi9BL24nptEtXWU",
|
||||
"text": "PHP > Introduction to PHP"
|
||||
id: 'ZF5_5Y5zqa75Ov22JACX6',
|
||||
text: 'AI Agents > Transformer Models and LLMs',
|
||||
},
|
||||
{
|
||||
"id": "_LhLDVZjLt1DoAP1NuUES",
|
||||
"text": "PHP > Introduction to PHP > What is PHP?"
|
||||
id: 'GAjuWyJl9CI1nqXBp6XCf',
|
||||
text: 'AI Agents > LLM Fundamentals > Model Mechanis > Tokenization',
|
||||
},
|
||||
{
|
||||
"id": "b2CuLrhsUNnb4OxI6RRAS",
|
||||
"text": "PHP > Introduction to PHP > Evolution and History"
|
||||
id: 'dyn1LSioema-Bf9lLTgUZ',
|
||||
text: 'AI Agents > LLM Fundamentals > Model Mechanis > Context Windows',
|
||||
},
|
||||
{
|
||||
"id": "6sHRQTcoKL3TlgNJlwyx8",
|
||||
"text": "PHP > Introduction to PHP > PHP Versions and Features"
|
||||
id: '1fiWPBV99E2YncqdCgUw2',
|
||||
text: 'AI Agents > LLM Fundamentals > Model Mechanis > Token Based Pricing',
|
||||
},
|
||||
{
|
||||
"id": "3_TuxOSzBuktBlBF05r_z",
|
||||
"text": "PHP > Installing PHP"
|
||||
id: 'L1zL1GzqjSAjF06pIIXhy',
|
||||
text: 'AI Agents > LLM Fundamentals > Generation Controls > Temperature',
|
||||
},
|
||||
{
|
||||
"id": "36Y1HkHxhuxh2qVQB8NVE",
|
||||
"text": "PHP > WAMP"
|
||||
id: 'icbp1NjurQfdM0dHnz6v2',
|
||||
text: 'AI Agents > LLM Fundamentals > Generation Controls > Top-p',
|
||||
},
|
||||
{
|
||||
"id": "-wniKEBwbF0Fi1fHpF-Gc",
|
||||
"text": "PHP > XAMPP"
|
||||
id: 'z_N-Y0zGkv8_qHPuVtimL',
|
||||
text: 'AI Agents > LLM Fundamentals > Generation Controls > Frequency Penalty',
|
||||
},
|
||||
{
|
||||
"id": "t7p7TU2khaxsZPYAdwFAA",
|
||||
"text": "PHP > MAMP"
|
||||
id: 'Vd8ycw8pW-ZKvg5WYFtoh',
|
||||
text: 'AI Agents > LLM Fundamentals > Generation Controls > Presence Penalty',
|
||||
},
|
||||
{
|
||||
"id": "7LjxtrmgJtTJc0_kP83Tr",
|
||||
"text": "PHP > LAMP"
|
||||
id: 'K0G-Lw069jXUJwZqHtybd',
|
||||
text: 'AI Agents > LLM Fundamentals > Generation Controls > Stopping Criteria',
|
||||
},
|
||||
{
|
||||
"id": "hzBUHSuFwLYNooF_vEmrs",
|
||||
"text": "PHP > Basic PHP Syntax"
|
||||
id: 'Bn_BkthrVX_vOuwQzvPZa',
|
||||
text: 'AI Agents > LLM Fundamentals > Generation Controls > Max Length',
|
||||
},
|
||||
{
|
||||
"id": "D0BtyxyjIBcpfn5wP23WC",
|
||||
"text": "PHP > Variables and Scope"
|
||||
id: 'DSJAhQhc1dQmBHQ8ZkTau',
|
||||
text: 'AI Agents > Model Families and Licences > Open Weight Models',
|
||||
},
|
||||
{
|
||||
"id": "srIHPZabaCGdB5VvUXaMa",
|
||||
"text": "PHP > Data Types"
|
||||
id: 'tJYmEDDwK0LtEux-kwp9B',
|
||||
text: 'AI Agents > Model Families and Licences > Closed Weight Models',
|
||||
},
|
||||
{
|
||||
"id": "pzReF4C0mcCWAnpfIJbwl",
|
||||
"text": "PHP > Casting Data Types"
|
||||
id: 'i2NE6haX9-7mdoV5LQ3Ah',
|
||||
text: 'AI Agents > Understand the Basics > Streamed vs Unstreamed Responses',
|
||||
},
|
||||
{
|
||||
"id": "2ykzBBdYhWuM-neGf0AWP",
|
||||
"text": "PHP > echo"
|
||||
id: 'N3yZfUxphxjiupqGpyaS9',
|
||||
text: 'AI Agents > Understand the Basics > Reasoning vs Standard Models',
|
||||
},
|
||||
{
|
||||
"id": "NQUmO90sqe7fnzod3Ia8H",
|
||||
"text": "PHP > print"
|
||||
id: '5OW_6o286mj470ElFyJ_5',
|
||||
text: 'AI Agents > Understand the Basics > Fine-tuning vs Prompt Engineering',
|
||||
},
|
||||
{
|
||||
"id": "wsC7OGXOyfCY4pLLNrR2v",
|
||||
"text": "PHP > print_r"
|
||||
id: 'UIm54UmICKgep6s8Itcyv',
|
||||
text: 'AI Agents > Understand the Basics > Embeddings and Vector Search',
|
||||
},
|
||||
{
|
||||
"id": "JCCeVC0hOrvIeyfg1ScKA",
|
||||
"text": "PHP > var_dump"
|
||||
id: 'qwVQOwBTLA2yUgRISzC8k',
|
||||
text: 'AI Agents > Understand the Basics > Understand the Basics of RAG',
|
||||
},
|
||||
{
|
||||
"id": "VLRLymQmLfscrBfzXKvHi",
|
||||
"text": "PHP > Constants"
|
||||
id: 'B8dzg61TGaknuruBgkEJd',
|
||||
text: 'AI Agents > Understand the Basics > Pricing of Common Models',
|
||||
},
|
||||
{
|
||||
"id": "IhKjvT6CjRz4dsSU7SNQo",
|
||||
"text": "PHP > Arrays"
|
||||
id: 'aFZAm44nP5NefX_9TpT0A',
|
||||
text: 'AI Agents > AI Agents 101 > What are AI Agents?',
|
||||
},
|
||||
{
|
||||
"id": "j2S8dP3HlAOOoZdpj-7Dx",
|
||||
"text": "PHP > Arrays > Indexed Arrays"
|
||||
id: '2zsOUWJQ8e7wnoHmq1icG',
|
||||
text: 'AI Agents > AI Agents 101 > What are Tools?',
|
||||
},
|
||||
{
|
||||
"id": "i_NRsOJNNp7AOqMgu5Jg8",
|
||||
"text": "PHP > Arrays > Associative Arrays"
|
||||
id: 'Eih4eybuYB3C2So8K0AT3',
|
||||
text: 'AI Agents > AI Agents 101 > Agent Loop',
|
||||
},
|
||||
{
|
||||
"id": "uARTOZ-ZwugSmbCJoRS5Y",
|
||||
"text": "PHP > Arrays > Multi-dimensional Arrays"
|
||||
id: 'LU76AhCYDjxdBhpMQ4eMU',
|
||||
text: 'AI Agents > AI Agents 101 > Agent Loop > Perception / User Input',
|
||||
},
|
||||
{
|
||||
"id": "38YksjvhXCbgnHqkl57Cz",
|
||||
"text": "PHP > Conditionals"
|
||||
id: 'ycPRgRYR4lEBQr_xxHKnM',
|
||||
text: 'AI Agents > AI Agents 101 > Agent Loop > Reason and Plan',
|
||||
},
|
||||
{
|
||||
"id": "-McOv-ZPTGayX7Mx2Thw1",
|
||||
"text": "PHP > Conditionals > if/else"
|
||||
id: 'sHYd4KsKlmw5Im3nQ19W8',
|
||||
text: 'AI Agents > AI Agents 101 > Agent Loop > Acting / Tool Invocation',
|
||||
},
|
||||
{
|
||||
"id": "bgJ9-m6Fiu3VCc-NZlbpn",
|
||||
"text": "PHP > Conditionals > switch"
|
||||
id: 'ZJTrun3jK3zBGOTm1jdMI',
|
||||
text: 'AI Agents > AI Agents 101 > Agent Loop > Observation & Reflection',
|
||||
},
|
||||
{
|
||||
"id": "3gNzX-bw2iqur7U7-_W38",
|
||||
"text": "PHP > Conditionals > match"
|
||||
id: 'PPdAutqJF5G60Eg9lYBND',
|
||||
text: 'AI Agents > AI Agents 101 > Example Usecases > Personal assistant',
|
||||
},
|
||||
{
|
||||
"id": "w0ntgFBhgGd5RUFd-qlPK",
|
||||
"text": "PHP > Conditionals > Null Coalescing Operator"
|
||||
id: 'PK8w31GlvtmAuU92sHaqr',
|
||||
text: 'AI Agents > AI Agents 101 > Example Usecases > Code generation',
|
||||
},
|
||||
{
|
||||
"id": "1NXSk8VZDr89jQTTkOL7x",
|
||||
"text": "PHP > Conditionals > Null Safe Operator"
|
||||
id: 'wKYEaPWNsR30TIpHaxSsq',
|
||||
text: 'AI Agents > AI Agents 101 > Example Usecases > Data analysis',
|
||||
},
|
||||
{
|
||||
"id": "qwt8xN4vuTrY-D0czYITI",
|
||||
"text": "PHP > Loops"
|
||||
id: '5oLc-235bvKhApxzYFkEc',
|
||||
text: 'AI Agents > AI Agents 101 > Example Usecases > Web Scraping / Crawling',
|
||||
},
|
||||
{
|
||||
"id": "WiGv7vi7Mtw-YqPMcnnyw",
|
||||
"text": "PHP > Functions"
|
||||
id: 'ok8vN7VtCgyef5x6aoQaL',
|
||||
text: 'AI Agents > AI Agents 101 > Example Usecases > NPC / Game AI',
|
||||
},
|
||||
{
|
||||
"id": "1nODJchgSuWbcvSlxnWeE",
|
||||
"text": "PHP > Functions > Function Declaration"
|
||||
id: 'Y8EqzFx3qxtrSh7bWbbV8',
|
||||
text: 'AI Agents > Prompt Engineering > What is Prompt Engineering',
|
||||
},
|
||||
{
|
||||
"id": "mpQKoBzsOa-5iWo08sOhQ",
|
||||
"text": "PHP > Functions > Parameters / Return Values"
|
||||
id: 'qFKFM2qNPEN7EoD0V-1SM',
|
||||
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Be specific in what you want',
|
||||
},
|
||||
{
|
||||
"id": "RgVP99rJJ8FVecIA45w20",
|
||||
"text": "PHP > Functions > Default / Optional Params"
|
||||
id: '6I42CoeWX-kkFXTKAY7rw',
|
||||
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Provide additional context',
|
||||
},
|
||||
{
|
||||
"id": "RkNjYva8o_jXp9suz5YdG",
|
||||
"text": "PHP > Functions > Named Arguments"
|
||||
id: 'sUwdtOX550tSdceaeFPmF',
|
||||
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Use relevant technical terms',
|
||||
},
|
||||
{
|
||||
"id": "Nr5m6wQLp7VyG3AucrSc8",
|
||||
"text": "PHP > Functions > Anonymous Functions"
|
||||
id: 'yulzE4ZNLhXOgHhG7BtZQ',
|
||||
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Use Examples in your Prompt',
|
||||
},
|
||||
{
|
||||
"id": "x7hA2KAzJIjc-prgCEw6V",
|
||||
"text": "PHP > Functions > Callback Functions"
|
||||
id: 'noTuUFnHSBzn7GKG9UZEi',
|
||||
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Iterate and Test your Prompts',
|
||||
},
|
||||
{
|
||||
"id": "mP1BIkqbWVVTU-zZv1ZL6",
|
||||
"text": "PHP > Functions > Arrow Functions"
|
||||
id: 'wwHHlEoPAx0TLxbtY6nMA',
|
||||
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Specify Length, format etc',
|
||||
},
|
||||
{
|
||||
"id": "D9ybK5INH5zSOcYMb5ZPi",
|
||||
"text": "PHP > Functions > Recursion"
|
||||
id: 'qakbxB8xe7Y8gejC5cZnK',
|
||||
text: 'AI Agents > AI Agents 101 > Tools / Actions > Tool Definition',
|
||||
},
|
||||
{
|
||||
"id": "rtmytETfyyLdcXUC0QyzL",
|
||||
"text": "PHP > Functions > Variadic Functions"
|
||||
id: 'kBtqT8AduLoYDWopj-V9_',
|
||||
text: 'AI Agents > Tools / Actions > Examples of Tools > Web Search',
|
||||
},
|
||||
{
|
||||
"id": "Kaaqu-mN7xvHN6CbIn616",
|
||||
"text": "PHP > File Handling > require"
|
||||
id: 'mS0EVCkWuPN_GkVPng4A2',
|
||||
text: 'AI Agents > Tools / Actions > Examples of Tools > Code Execution / REPL',
|
||||
},
|
||||
{
|
||||
"id": "-CyJbsg2ho3RvfzKnJj5C",
|
||||
"text": "PHP > File Handling > require_once"
|
||||
id: 'sV1BnA2-qBnXoKpUn-8Ub',
|
||||
text: 'AI Agents > Tools / Actions > Examples of Tools > Database Queries',
|
||||
},
|
||||
{
|
||||
"id": "hKfv7V6bl2LXssq9Ffi7C",
|
||||
"text": "PHP > File Handling > include"
|
||||
id: '52qxjZILV-X1isup6dazC',
|
||||
text: 'AI Agents > Tools / Actions > Examples of Tools > API Requests > Tools / Actions > Examples of Tools > API Requests',
|
||||
},
|
||||
{
|
||||
"id": "SwtLDgyPmDry20qS4FBfH",
|
||||
"text": "PHP > File Handling > include_once"
|
||||
id: 'qaNr5I-NQPnfrRH7ynGTl',
|
||||
text: 'AI Agents > Tools / Actions > Examples of Tools > Email / Slack / SMS',
|
||||
},
|
||||
{
|
||||
"id": "S9wTlkbv9-R6dohhZ47hs",
|
||||
"text": "PHP > File Operations > Reading Files"
|
||||
id: 'BoJqZvdGam4cd6G6yK2IV',
|
||||
text: 'AI Agents > Tools / Actions > Examples of Tools > File System Access',
|
||||
},
|
||||
{
|
||||
"id": "two4UycJaCfSp6jQqtTAb",
|
||||
"text": "PHP > File Operations > Writing Files"
|
||||
id: '1B0IqRNYdtbHDi1jHSXuI',
|
||||
text: 'AI Agents > Model Context Protocol (MCP)',
|
||||
},
|
||||
{
|
||||
"id": "tgIyG6vHWpe9sz6lHmj5a",
|
||||
"text": "PHP > File Operations > File Permissions"
|
||||
id: '9FryAIrWRHh8YlzKX3et5',
|
||||
text: 'AI Agents > Model Context Protocol (MCP) > Core Components > MCP Hosts',
|
||||
},
|
||||
{
|
||||
"id": "MRDjEjbkMpk7shcWAoPOF",
|
||||
"text": "PHP > File Operations > CSV Processing"
|
||||
id: 'CGVstUxVXLJcYZrwk3iNQ',
|
||||
text: 'AI Agents > Model Context Protocol (MCP) > Core Components > MCP Client',
|
||||
},
|
||||
{
|
||||
"id": "DB2cxZE58WCCavW2PNwmf",
|
||||
"text": "PHP > File Operations > JSON Processing"
|
||||
id: 'yv_-87FVM7WKn5iv6LW9q',
|
||||
text: 'AI Agents > Model Context Protocol (MCP) > Core Components > MCP Servers',
|
||||
},
|
||||
{
|
||||
"id": "ggkWo0DRSSDDkHpbiyUyf",
|
||||
"text": "PHP > File Operations > XML Processing"
|
||||
id: '1NXIN-Hbjl5rPy_mqxQYW',
|
||||
text: 'AI Agents > Model Context Protocol (MCP) > Creating MCP Servers',
|
||||
},
|
||||
{
|
||||
"id": "tn_iIfaJZVtPK6vFds7FH",
|
||||
"text": "PHP > HTTP / Request Handling > HTTP Methods"
|
||||
id: 'iBtJp24F_kJE3YlBsW60s',
|
||||
text: 'AI Agents > Model Context Protocol (MCP) > Creating MCP Servers > Deployment Modes > Local Desktop',
|
||||
},
|
||||
{
|
||||
"id": "GFYGFVfxkOoPI5mI4zSt1",
|
||||
"text": "PHP > HTTP / Request Handling > $_GET"
|
||||
id: 'dHNMX3_t1KSDdAWqgdJXv',
|
||||
text: 'AI Agents > Model Context Protocol (MCP) > Creating MCP Servers > Deployment Modes > Remote / Cloud',
|
||||
},
|
||||
{
|
||||
"id": "qNG-a4iIO-puZsMwAMzYC",
|
||||
"text": "PHP > HTTP / Request Handling > $_POST"
|
||||
id: 'TBH_DZTAfR8Daoh-njNFC',
|
||||
text: 'AI Agents > Agent Memory > What is Agent Memory?',
|
||||
},
|
||||
{
|
||||
"id": "A6rfW4uJhyfAX2b18_EEC",
|
||||
"text": "PHP > HTTP / Request Handling > $_REQUEST"
|
||||
id: 'M3U6RfIqaiut2nuOibY8W',
|
||||
text: 'AI Agents > Agent Memory > Short Term Memory',
|
||||
},
|
||||
{
|
||||
"id": "7Ja2at_N9tRTlvSGahrqn",
|
||||
"text": "PHP > HTTP / Request Handling > $_SERVER"
|
||||
id: 'Ue633fz6Xu2wa2-KOAtdP',
|
||||
text: 'AI Agents > Agent Memory > What is Agent Memory? > Long Term Memory',
|
||||
},
|
||||
{
|
||||
"id": "sYI7f1PYP7G30_Uj2mZRv",
|
||||
"text": "PHP > Form Processing"
|
||||
id: 'EfCCNqLMJpWKKtamUa5gK',
|
||||
text: 'AI Agents > Agent Memory > What is Agent Memory? > Episodic vs Semantic Memory',
|
||||
},
|
||||
{
|
||||
"id": "HNo8QO4aPbvgePiA4l6tq",
|
||||
"text": "PHP > File Uploads"
|
||||
id: 'wkS4yOJ3JdZQE_yBID8K7',
|
||||
text: 'AI Agents > Agent Memory > What is Agent Memory? > Maintaining Memory > RAG and Vector Databases',
|
||||
},
|
||||
{
|
||||
"id": "CGehmZjcgTWC7fQAvxmNW",
|
||||
"text": "PHP > State Management"
|
||||
id: 'QJqXHV8VHPTnfYfmKPzW7',
|
||||
text: 'AI Agents > Agent Memory > What is Agent Memory? > Maintaining Memory > User Profile Storage',
|
||||
},
|
||||
{
|
||||
"id": "so03-fK7E2WvTm6XsPq4i",
|
||||
"text": "PHP > State Management > Cookies"
|
||||
id: 'jTDC19BTWCqxqMizrIJHr',
|
||||
text: 'AI Agents > Agent Memory > Maintaining Memory > Summarization / Compression',
|
||||
},
|
||||
{
|
||||
"id": "qobzzgzArNHLLn9Oiqc6G",
|
||||
"text": "PHP > State Management > Sessions"
|
||||
id: 'm-97m7SI0XpBnhEE8-_1S',
|
||||
text: 'AI Agents > Agent Memory > Maintaining Memory > Forgetting / Aging Strategies',
|
||||
},
|
||||
{
|
||||
"id": "93oEIZttb85S23C1fLraP",
|
||||
"text": "PHP > Basics of Security > Input Validation"
|
||||
id: 'cW8O4vLLKEG-Q0dE8E5Zp',
|
||||
text: 'AI Agents > Agent Architectures > Common Architectures > RAG Agent',
|
||||
},
|
||||
{
|
||||
"id": "801vB_JMas4ucriUmfrLg",
|
||||
"text": "PHP > Basics of Security > SQL Injection"
|
||||
id: '53xDks6JQ33fHMa3XcuCd',
|
||||
text: 'AI Agents > Agent Architectures > Common Architectures > ReAct (Reason + Act)',
|
||||
},
|
||||
{
|
||||
"id": "DxqQrToZSayWplKdCkTgT",
|
||||
"text": "PHP > Basics of Security > XSS Prevention"
|
||||
id: 'qwdh5pkBbrF8LKPxbZp4F',
|
||||
text: 'AI Agents > Agent Architectures > Common Architectures > Chain of Thought (CoT)',
|
||||
},
|
||||
{
|
||||
"id": "J9yIXZTtwbFzH2u4dI1ep",
|
||||
"text": "PHP > Basics of Security > CSRF Protection"
|
||||
id: '6YLCMWzystao6byCYCTPO',
|
||||
text: 'AI Agents > Agent Architectures > Common Architectures > Planner Executor',
|
||||
},
|
||||
{
|
||||
"id": "JbWFfJiCRrXDhnuIx_lqx",
|
||||
"text": "PHP > Basics of Security > Password Hashing"
|
||||
id: 'Ep8RoZSy_Iq_zWXlGQLZo',
|
||||
text: 'AI Agents > Agent Architectures > Common Architectures > DAG Agents',
|
||||
},
|
||||
{
|
||||
"id": "HJJzKYXdK4BWITLP4APLZ",
|
||||
"text": "PHP > Basics of Security > Auth Mechanisms"
|
||||
id: 'Nmy1PoB32DcWZnPM8l8jT',
|
||||
text: 'AI Agents > Agent Architectures > Common Architectures > Tree-of-Thought',
|
||||
},
|
||||
{
|
||||
"id": "tfC1tCrbvH5J43WUpG9Yb",
|
||||
"text": "PHP > Basics of Security > Sanitization Techniques"
|
||||
id: 'hj1adjkG9nalXKZ-Youn0',
|
||||
text: 'AI Agents > Agent Architectures > Common Architectures > Tree-of-Thought',
|
||||
},
|
||||
{
|
||||
"id": "cJtPz1RMN1qDE4eRdv4N_",
|
||||
"text": "PHP > Database Connectivity > PDO"
|
||||
id: 'US6T5dXM8IY9V2qZnTOFW',
|
||||
text: 'AI Agents > Building Agents > Manual (from scratch)',
|
||||
},
|
||||
{
|
||||
"id": "YLuo0oZJzTCoiZoOSG57z",
|
||||
"text": "PHP > Database Connectivity > MySQLi"
|
||||
id: 'aafZxtjxiwzJH1lwHBODi',
|
||||
text: 'AI Agents > Building Agents > LLM Native "Function Calling"',
|
||||
},
|
||||
{
|
||||
"id": "SeqGIfcLuveZ2z5ZSXcOd",
|
||||
"text": "PHP > Advanced Database Techniques > Object-Relational Mapping (ORM)"
|
||||
id: 'AQtxTTxmBpfl8BMgJbGzc',
|
||||
text: 'AI Agents > Building Agents > LLM Native "Function Calling" > OpenAI Functions Calling',
|
||||
},
|
||||
{
|
||||
"id": "FY-F6n9j29hQrnFry3VGb",
|
||||
"text": "PHP > Advanced Database Techniques > Database Transactions"
|
||||
id: '37GBFVZ2J2d5r8bd1ViHq',
|
||||
text: 'AI Agents > Building Agents > LLM Native "Function Calling" > OpenAI Assistant API',
|
||||
},
|
||||
{
|
||||
"id": "txUyPR_tdC8iTJV3RtvBz",
|
||||
"text": "PHP > Advanced Database Techniques > Connection Pooling"
|
||||
id: '_iIsBJTVS6OBf_dsdmbVO',
|
||||
text: 'AI Agents > Building Agents > LLM Native "Function Calling" > Gemini Function Calling',
|
||||
},
|
||||
{
|
||||
"id": "M1nVsh_sCSFJRf6-7Ttsj",
|
||||
"text": "PHP > Advanced Database Techniques > Performance Optimization"
|
||||
id: '1EZFbDHA5J5_5BPMLMxXb',
|
||||
text: 'AI Agents > Building Agents > LLM Native "Function Calling" > Anthropic Tool Use',
|
||||
},
|
||||
{
|
||||
"id": "meplwvmHMtI3Sb_fyodzZ",
|
||||
"text": "PHP > Advanced Database Techniques > Database Migrations"
|
||||
id: 'Ka6VpCEnqABvwiF9vba7t',
|
||||
text: 'AI Agents > Building Agents > Building Using Frameworks > Langchain',
|
||||
},
|
||||
{
|
||||
"id": "yTviiPFR5b_dr3WyxdxxQ",
|
||||
"text": "PHP > OOP Fundamentals"
|
||||
id: 'iEHF-Jm3ck-Iu85EbCoDi',
|
||||
text: 'AI Agents > Building Agents > Building Using Frameworks > LlamaIndex',
|
||||
},
|
||||
{
|
||||
"id": "PIuplWreo7PFG3Mdn2t6W",
|
||||
"text": "PHP > OOP Fundamentals > Classes and Objects"
|
||||
id: 'XS-FsvtrXGZ8DPrwOsnlI',
|
||||
text: 'AI Agents > Building Agents > Building Using Frameworks > Haystack',
|
||||
},
|
||||
{
|
||||
"id": "oNUt1oT8pYBVvH0S2P6cb",
|
||||
"text": "PHP > OOP Fundamentals > Constructor / Destructor"
|
||||
id: '7YtnQ9-KIvGPSpDzEDexl',
|
||||
text: 'AI Agents > Building Agents > Building Using Frameworks > AutoGen',
|
||||
},
|
||||
{
|
||||
"id": "MRAPXshy9RoYdReY6grf_",
|
||||
"text": "PHP > OOP Fundamentals > Properties and Methods"
|
||||
id: 'uFPJqgU4qGvZyxTv-osZA',
|
||||
text: 'AI Agents > Building Agents > Building Using Frameworks > CrewAI',
|
||||
},
|
||||
{
|
||||
"id": "RD2RaBmA2XWkEa13PTCTX",
|
||||
"text": "PHP > OOP Fundamentals > Access Specifiers"
|
||||
id: 'eWxQiBrxIUG2JNcrdfIHS',
|
||||
text: 'AI Agents > Building Agents > Building Using Frameworks > Smol Depot',
|
||||
},
|
||||
{
|
||||
"id": "qlkpwXfOc1p7j37hrzffI",
|
||||
"text": "PHP > OOP Fundamentals > Static Methods and Properties"
|
||||
id: 'v8qLnyFRnEumodBYxQSXQ',
|
||||
text: 'AI Agents > Building Agents > Evaluation and Testing > Metrics to Track',
|
||||
},
|
||||
{
|
||||
"id": "c5q2e_jyMt8Pir5Od3lRi",
|
||||
"text": "PHP > OOP Fundamentals > Inheritance"
|
||||
id: 'qo_O4YAe4-MTP_ZJoXJHR',
|
||||
text: 'AI Agents > Evaluation and Testing > Unit Testing for Individual Tools',
|
||||
},
|
||||
{
|
||||
"id": "gtq5KrghF28f5G8nuDcYQ",
|
||||
"text": "PHP > Polymorphism"
|
||||
id: 'P9-SiIda3TSjHsfkI5OUV',
|
||||
text: 'AI Agents > Evaluation and Testing > Integration Testing for Flows',
|
||||
},
|
||||
{
|
||||
"id": "ub79EkMiOmPBwXLRuYFL8",
|
||||
"text": "PHP > Abstract classes"
|
||||
id: 'rHxdxN97ZcU7MPl8L1jzN',
|
||||
text: 'AI Agents > Evaluation and Testing > Human in the Loop Evaluation',
|
||||
},
|
||||
{
|
||||
"id": "vu0H-TsD7hkJgOQbSRj92",
|
||||
"text": "PHP > Interfaces"
|
||||
id: 'xp7TCTRE9HP60_rGzTUF6',
|
||||
text: 'AI Agents > Evaluation and Testing > Frameworks > LangSmith',
|
||||
},
|
||||
{
|
||||
"id": "GR09ns9B-0cONQaQ_uj-7",
|
||||
"text": "PHP > Traits"
|
||||
id: '0924QUH1wV7Mp-Xu0FAhF',
|
||||
text: 'AI Agents > Evaluation and Testing > Frameworks > DeepEval',
|
||||
},
|
||||
{
|
||||
"id": "9raJ06lKRZITbjWeLil-F",
|
||||
"text": "PHP > Namespaces"
|
||||
id: 'YzEDtGEaMaMWVt0W03HRt',
|
||||
text: 'AI Agents > Evaluation and Testing > Frameworks > Ragas',
|
||||
},
|
||||
{
|
||||
"id": "rSXsPWto7Jeyw3Szl9pvf",
|
||||
"text": "PHP > Magic methods"
|
||||
id: 'zs6LM8WEnb0ERWpiaQCgc',
|
||||
text: 'AI Agents > Debugging and Monitoring > Structured logging & tracing',
|
||||
},
|
||||
{
|
||||
"id": "sPW-Ti2VyNYzxq6EYkbn7",
|
||||
"text": "PHP > Type Declarations"
|
||||
id: 'zs6LM8WEnb0ERWpiaQCgc',
|
||||
text: 'AI Agents > Debugging and Monitoring > Structured logging & tracing',
|
||||
},
|
||||
{
|
||||
"id": "KEE50C6lOS4eX8sAbfhYe",
|
||||
"text": "PHP > Dependency injection"
|
||||
id: 'SS8mGqf9wfrNqenIWvN8Z',
|
||||
text: 'AI Agents > Observability Tools > LangSmith',
|
||||
},
|
||||
{
|
||||
"id": "zsscRQZIq5o0JZir9hlz-",
|
||||
"text": "PHP > Laravel"
|
||||
id: 'MLxP5N0Vrmwh-kyvNeGXn',
|
||||
text: 'AI Agents > Observability Tools > Helicone',
|
||||
},
|
||||
{
|
||||
"id": "57VSMVePOr9qUD5x_LNdf",
|
||||
"text": "PHP > Symfony"
|
||||
id: 'UoIheaJlShiceafrWALEH',
|
||||
text: 'AI Agents > Observability Tools > LangFuse',
|
||||
},
|
||||
{
|
||||
"id": "yVFDu2aTiEZ4PWMdKdW2P",
|
||||
"text": "PHP > Composer"
|
||||
id: '7UqPXUzqKYXklnB3x-tsv',
|
||||
text: 'AI Agents > Observability Tools > openllmetry',
|
||||
},
|
||||
{
|
||||
"id": "xZf2jjnCVHwYfDH2hs9kR",
|
||||
"text": "PHP > Packagist"
|
||||
id: 'SU2RuicMUo8tiAsQtDI1k',
|
||||
text: 'AI Agents > Security & Ethics > Prompt Injection / Jailbreaks',
|
||||
},
|
||||
{
|
||||
"id": "qFiTsf6Es-gwqe6J6bdL1",
|
||||
"text": "PHP > Autoloading"
|
||||
id: 'UVzLGXG6K7HQVHmw8ZAv2',
|
||||
text: 'AI Agents > Security & Ethics > Tool sandboxing / Permissioning',
|
||||
},
|
||||
{
|
||||
"id": "NfBKKwG2GGBPppOjoLLBg",
|
||||
"text": "PHP > PHPUnit"
|
||||
id: 'rdlYBJNNyZUshzsJawME4',
|
||||
text: 'AI Agents > Security & Ethics > Data Privacy + PII Redaction',
|
||||
},
|
||||
{
|
||||
"id": "d6MydchA52HIxfAUjmZui",
|
||||
"text": "PHP > Pest"
|
||||
id: 'EyLo2j8IQsIK91SKaXkmK',
|
||||
text: 'AI Agents > Security & Ethics > Bias & Toxicity Guardrails',
|
||||
},
|
||||
{
|
||||
"id": "6eWgZVLV479oQzl0fu-Od",
|
||||
"text": "PHP > Style Tools"
|
||||
id: '63nsfJFO1BwjLX_ZVaPFC',
|
||||
text: 'AI Agents > Security & Ethics > Safety + Red Team Testing',
|
||||
},
|
||||
{
|
||||
"id": "fSpvZ_4kGFMbFVCWhA8vn",
|
||||
"text": "PHP > Style Tools > PHPCodeSniffer"
|
||||
},
|
||||
{
|
||||
"id": "r07k_hT2z2EiIBH4q3F7-",
|
||||
"text": "PHP > Style Tools > PHP CS Fixer"
|
||||
},
|
||||
{
|
||||
"id": "PrG_5dyBblXsWYYRcOJMa",
|
||||
"text": "PHP > Static Analysis"
|
||||
},
|
||||
{
|
||||
"id": "12k71gNfwAcT9K5aLWgbZ",
|
||||
"text": "PHP > Static Analysis > PHPStan"
|
||||
},
|
||||
{
|
||||
"id": "T1XD93j6Lkpl88JSmys9b",
|
||||
"text": "PHP > Static Analysis > Psalm"
|
||||
},
|
||||
{
|
||||
"id": "B45YVzov8X_iOtneiFEqa",
|
||||
"text": "PHP > Static Analysis > Phan"
|
||||
},
|
||||
{
|
||||
"id": "KC6D81-T-FwQc7Osw1rlY",
|
||||
"text": "PHP > External Integrations > cURL"
|
||||
},
|
||||
{
|
||||
"id": "_Al4NXKVQAnk8OikwvXCL",
|
||||
"text": "PHP > External Integrations > Guzzle"
|
||||
},
|
||||
{
|
||||
"id": "SD9k16UlVve9WtNMDA5Za",
|
||||
"text": "PHP > PHP-FIG"
|
||||
},
|
||||
{
|
||||
"id": "3tONibbRgK7HCwGTE2Gqw",
|
||||
"text": "PHP > PHP-FIG > PSR Standards"
|
||||
},
|
||||
{
|
||||
"id": "_Dh78x_tPLqZweg--qZFQ",
|
||||
"text": "PHP > Performance Optimization > Profiling Techniques"
|
||||
},
|
||||
{
|
||||
"id": "Av-BMa57RvrLlAXLffOH0",
|
||||
"text": "PHP > Performance Optimization > Caching Strategies"
|
||||
},
|
||||
{
|
||||
"id": "bt7dK2PcOZ72B9HXPyMwL",
|
||||
"text": "PHP > Performance Optimization > Memory Management"
|
||||
},
|
||||
{
|
||||
"id": "VpwwF8j5ZtXVSbzNfE7Sx",
|
||||
"text": "PHP > Performance Optimization > Configuration Tuning"
|
||||
},
|
||||
{
|
||||
"id": "NieqZd1juaNYoZOrB7e31",
|
||||
"text": "PHP > Performance Optimization > Opcode Caching"
|
||||
},
|
||||
{
|
||||
"id": "AoGS-5MSkp8gtJFQVPSBE",
|
||||
"text": "PHP > Performance Optimization > PHP-FPM"
|
||||
},
|
||||
{
|
||||
"id": "qp5Xi12c0qcSzTanzJq0Z",
|
||||
"text": "PHP > System Interactions"
|
||||
},
|
||||
{
|
||||
"id": "VhyYNGhOdKKrz_-uTkrjD",
|
||||
"text": "PHP > System Interactions > Executing System Commands"
|
||||
},
|
||||
{
|
||||
"id": "NTKUMgsKGYISIyhgOJPQn",
|
||||
"text": "PHP > System Interactions > Process Control"
|
||||
},
|
||||
{
|
||||
"id": "fitjnLYKLHJ2P5G7JAvzm",
|
||||
"text": "PHP > System Interactions > Environment Variables"
|
||||
},
|
||||
{
|
||||
"id": "DTaAZaU1CwzW7esoDhj85",
|
||||
"text": "PHP > System Interactions > Configuration Files"
|
||||
},
|
||||
{
|
||||
"id": "lFoHoMRywCWa056ii5cKQ",
|
||||
"text": "PHP > Debugging Tools > Xdebug"
|
||||
},
|
||||
{
|
||||
"id": "KpQb5Zh3GUcbYUyXHvyu2",
|
||||
"text": "PHP > Debugging Tools > Zend Debugger"
|
||||
},
|
||||
{
|
||||
"id": "KMQqePqAjQ-ReDwHqeofx",
|
||||
"text": "PHP > Web Servers > Apache"
|
||||
},
|
||||
{
|
||||
"id": "aspZpACHEKOsi_Er5FYPY",
|
||||
"text": "PHP > Web Servers > Nginx"
|
||||
}
|
||||
];
|
||||
|
||||
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
||||
@@ -516,19 +416,11 @@ const openai = new OpenAI({
|
||||
|
||||
const prompt = `
|
||||
You are a helpful assistant that can help me generate content for a roadmap tree.
|
||||
User will give you roadmap topics in the form of "Parent > Child > Leaf". You need
|
||||
to generate content for the last node in the hierarchy in relation to the parents.
|
||||
Remember that you are explaining the topics for PHP showing what the given topic is
|
||||
with respect to PHP and giving a short code sample ONLY when required.
|
||||
You will be given a roadmap topic in the form of "Parent > Child > Leaf". You need
|
||||
to generate a single paragraph explaining the topic.
|
||||
Also, I hate it when you say "In the realm of..."
|
||||
or "In the context of..." or "..in the context of..." or "when we talk about..." or something
|
||||
similar.
|
||||
Content should be helpful and engaging for a technical audience.
|
||||
It can include things like (you can include more or less, depending on the topic):
|
||||
- Briefly explain the given topic in relation to PHP.
|
||||
- Code sample if applicable.
|
||||
- Add a link to PHP documentation
|
||||
The content should be a a single textual paragraph.
|
||||
IMPORTANT: Use simple and clear English. Avoid complex words and jargon when possible.
|
||||
Write in a way that is easy to understand. Use short sentences and common words.
|
||||
`;
|
||||
@@ -541,7 +433,7 @@ const prompt = `
|
||||
const generateContent = async (node) => {
|
||||
try {
|
||||
const content = await openai.chat.completions.create({
|
||||
model: 'gpt-4',
|
||||
model: 'o3',
|
||||
messages: [
|
||||
{ role: 'system', content: prompt },
|
||||
{
|
||||
|
||||
@@ -2,20 +2,22 @@ import { Menu } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { AITutorSidebar, type AITutorTab } from './AITutorSidebar';
|
||||
import { RoadmapLogoIcon } from '../ReactIcons/RoadmapLogo';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
type AITutorLayoutProps = {
|
||||
children: React.ReactNode;
|
||||
activeTab: AITutorTab;
|
||||
wrapperClassName?: string;
|
||||
};
|
||||
|
||||
export function AITutorLayout(props: AITutorLayoutProps) {
|
||||
const { children, activeTab } = props;
|
||||
const { children, activeTab, wrapperClassName } = props;
|
||||
|
||||
const [isSidebarFloating, setIsSidebarFloating] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row items-center justify-between border-b border-slate-200 px-4 py-3 lg:hidden">
|
||||
<div className="sticky top-0 z-10 flex flex-row items-center justify-between border-b border-slate-200 bg-white px-4 py-3 lg:hidden">
|
||||
<a href="/" className="flex flex-row items-center gap-1.5">
|
||||
<RoadmapLogoIcon className="size-6 text-gray-500" color="black" />
|
||||
</a>
|
||||
@@ -27,13 +29,18 @@ export function AITutorLayout(props: AITutorLayoutProps) {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-grow flex-row">
|
||||
<div className="flex flex-grow flex-row lg:h-screen">
|
||||
<AITutorSidebar
|
||||
onClose={() => setIsSidebarFloating(false)}
|
||||
isFloating={isSidebarFloating}
|
||||
activeTab={activeTab}
|
||||
/>
|
||||
<div className="flex flex-grow flex-col overflow-y-scroll bg-gray-100 p-3 lg:px-4 lg:py-4">
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-grow flex-col overflow-y-scroll bg-gray-100 p-3 lg:p-4',
|
||||
wrapperClassName,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import {
|
||||
BookOpen, Compass,
|
||||
Plus,
|
||||
Star,
|
||||
X,
|
||||
Zap
|
||||
} from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { BookOpen, Compass, Plus, Star, X, Zap } from 'lucide-react';
|
||||
import { AITutorLogo } from '../ReactIcons/AITutorLogo';
|
||||
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
||||
import { useIsPaidUser } from '../../queries/billing';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { useIsPaidUser } from '../../queries/billing';
|
||||
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
||||
import { AITutorLogo } from '../ReactIcons/AITutorLogo';
|
||||
|
||||
type AITutorSidebarProps = {
|
||||
isFloating: boolean;
|
||||
@@ -24,6 +30,12 @@ const sidebarItems = [
|
||||
href: '/ai/courses',
|
||||
icon: BookOpen,
|
||||
},
|
||||
// {
|
||||
// key: 'chat',
|
||||
// label: 'AI Chat',
|
||||
// href: '/ai/chat',
|
||||
// icon: Bot,
|
||||
// },
|
||||
{
|
||||
key: 'staff-picks',
|
||||
label: 'Staff Picks',
|
||||
|
||||
1
src/components/Analytics/Bluconic.astro
Normal file
1
src/components/Analytics/Bluconic.astro
Normal file
@@ -0,0 +1 @@
|
||||
<script async defer src='https://b174.roadmap.sh/script.js'></script>
|
||||
@@ -1,3 +1,5 @@
|
||||
import { httpPost } from '../../lib/query-http';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
gtag: any;
|
||||
@@ -20,6 +22,18 @@ declare global {
|
||||
*/
|
||||
window.fireEvent = (props) => {
|
||||
const { action, category, label, value, callback } = props;
|
||||
|
||||
if (['course', 'ai_tutor'].includes(category)) {
|
||||
const url = new URL(import.meta.env.PUBLIC_API_URL);
|
||||
url.pathname = '/_t';
|
||||
url.searchParams.set('action', action);
|
||||
url.searchParams.set('category', category);
|
||||
url.searchParams.set('label', label ?? '');
|
||||
url.searchParams.set('value', value ?? '');
|
||||
|
||||
httpPost(url.toString(), {}).catch(console.error);
|
||||
}
|
||||
|
||||
if (!window.gtag) {
|
||||
console.warn('Missing GTAG - Analytics disabled');
|
||||
return;
|
||||
|
||||
@@ -2,8 +2,9 @@ import type { FormEvent } from 'react';
|
||||
import { useId, useState } from 'react';
|
||||
import { httpPost } from '../../lib/http';
|
||||
import {
|
||||
COURSE_PURCHASE_PARAM, FIRST_LOGIN_PARAM,
|
||||
setAuthToken
|
||||
COURSE_PURCHASE_PARAM,
|
||||
FIRST_LOGIN_PARAM,
|
||||
setAuthToken,
|
||||
} from '../../lib/jwt';
|
||||
|
||||
type EmailLoginFormProps = {
|
||||
@@ -65,7 +66,11 @@ export function EmailLoginForm(props: EmailLoginFormProps) {
|
||||
const passwordFieldId = `form:${useId()}`;
|
||||
|
||||
return (
|
||||
<form className="w-full" onSubmit={handleFormSubmit}>
|
||||
<form
|
||||
className="w-full"
|
||||
onSubmit={handleFormSubmit}
|
||||
suppressHydrationWarning={true} // Hubspot adds data-* attributes which causes hydration errors
|
||||
>
|
||||
<label htmlFor={emailFieldId} className="sr-only">
|
||||
Email address
|
||||
</label>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AuthenticationForm } from './AuthenticationForm';
|
||||
|
||||
<Popup id='login-popup' title='' subtitle=''>
|
||||
<div class='mb-7 text-center'>
|
||||
<p class='mb-3 text-2xl font-semibold leading-5 text-slate-900'>
|
||||
<p class='mb-3 text-2xl leading-5 font-semibold text-slate-900'>
|
||||
Login or Signup
|
||||
</p>
|
||||
<p class='mt-2 text-sm leading-4 text-slate-600'>
|
||||
|
||||
@@ -108,8 +108,6 @@ export function BillingPage() {
|
||||
onClose={() => {
|
||||
setShowUpgradeModal(false);
|
||||
}}
|
||||
success="/account/billing?s=1"
|
||||
cancel="/account/billing"
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ export function UpdatePlanConfirmation(props: UpdatePlanConfirmationProps) {
|
||||
<h3 className="text-xl font-bold text-black">Subscription Update</h3>
|
||||
<p className="mt-2 text-balance text-gray-600">
|
||||
Your plan will be updated to the{' '}
|
||||
<b className="text-black">{planDetails.interval}</b> plan, and will
|
||||
be charged{' '}
|
||||
<b className="text-black">{planDetails.interval}</b> plan, and will be
|
||||
charged{' '}
|
||||
<b className="text-black">
|
||||
${selectedPrice.amount}/{selectedPrice.interval}
|
||||
</b>
|
||||
@@ -72,21 +72,21 @@ export function UpdatePlanConfirmation(props: UpdatePlanConfirmationProps) {
|
||||
|
||||
<div className="mt-6 grid grid-cols-2 gap-3">
|
||||
<button
|
||||
className="rounded-md border border-gray-200 py-2 text-sm font-semibold hover:bg-gray-50 transition-colors disabled:opacity-50"
|
||||
className="rounded-md border border-gray-200 py-2 text-sm font-semibold transition-colors hover:bg-gray-50 disabled:opacity-50"
|
||||
onClick={onCancel}
|
||||
disabled={isPending}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
className="flex items-center justify-center rounded-md bg-purple-600 py-2 text-sm font-semibold text-white hover:bg-purple-500 transition-colors disabled:opacity-50"
|
||||
className="flex items-center justify-center rounded-md bg-purple-600 py-2 text-sm font-semibold text-white transition-colors hover:bg-purple-500 disabled:opacity-50"
|
||||
disabled={isPending}
|
||||
onClick={() => {
|
||||
updatePlan({ priceId: selectedPrice.priceId });
|
||||
}}
|
||||
>
|
||||
{isPending && (
|
||||
<Loader2Icon className="size-4 animate-spin stroke-[2.5] mr-2" />
|
||||
<Loader2Icon className="mr-2 size-4 animate-spin stroke-[2.5]" />
|
||||
)}
|
||||
{!isPending && 'Confirm'}
|
||||
</button>
|
||||
|
||||
@@ -136,6 +136,14 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
|
||||
setSelectedPlan(currentPlan.interval);
|
||||
}, [currentPlan]);
|
||||
|
||||
useEffect(() => {
|
||||
window?.fireEvent({
|
||||
action: 'tutor_pricing',
|
||||
category: 'ai_tutor',
|
||||
label: 'Clicked Upgrade to Pro',
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
@@ -262,13 +270,35 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
|
||||
}
|
||||
onClick={() => {
|
||||
setSelectedPlan(plan.interval);
|
||||
|
||||
if (!currentPlanPriceId) {
|
||||
const currentUrlPath = window.location.pathname;
|
||||
createCheckoutSession({
|
||||
priceId: plan.priceId,
|
||||
success: success || `${currentUrlPath}?s=1`,
|
||||
cancel: cancel || `${currentUrlPath}?s=0`,
|
||||
const encodedCurrentUrlPath =
|
||||
encodeURIComponent(currentUrlPath);
|
||||
const successPage = `/thank-you?next=${encodedCurrentUrlPath}&s=1`;
|
||||
|
||||
window?.fireEvent({
|
||||
action: 'tutor_checkout',
|
||||
category: 'ai_tutor',
|
||||
label: 'Checkout Started',
|
||||
});
|
||||
|
||||
createCheckoutSession(
|
||||
{
|
||||
priceId: plan.priceId,
|
||||
success: success || successPage,
|
||||
cancel: cancel || `${currentUrlPath}?s=0`,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
window?.fireEvent({
|
||||
action: `tutor_checkout_${plan.interval === 'month' ? 'mo' : 'an'}`,
|
||||
category: 'ai_tutor',
|
||||
label: `${plan.interval} Plan Checkout Started`,
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
setIsUpdatingPlan(true);
|
||||
|
||||
@@ -30,11 +30,40 @@ export function VerifyUpgrade(props: VerifyUpgradeProps) {
|
||||
userBillingDetails.status === 'active' &&
|
||||
(newPriceId ? userBillingDetails.priceId === newPriceId : true)
|
||||
) {
|
||||
if (!newPriceId) {
|
||||
// it means that the user is subscribing for the first time
|
||||
// not changing the plan
|
||||
window?.fireEvent({
|
||||
action: `tutor_purchase_${userBillingDetails.interval === 'month' ? 'mo' : 'an'}`,
|
||||
category: 'ai_tutor',
|
||||
label: `${userBillingDetails.interval} Plan Purchased`,
|
||||
});
|
||||
}
|
||||
|
||||
deleteUrlParam('s');
|
||||
window.location.reload();
|
||||
}
|
||||
}, [userBillingDetails]);
|
||||
|
||||
useEffect(() => {
|
||||
// it means that the user is changing the plan
|
||||
// not subscribing for the first time
|
||||
if (newPriceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
window?.fireEvent({
|
||||
action: 'tutor_purchase',
|
||||
category: 'ai_tutor',
|
||||
label: 'Subscription Activated',
|
||||
});
|
||||
window?.fireEvent({
|
||||
action: 'tutor_ty',
|
||||
category: 'ai_tutor',
|
||||
label: 'Thank You Page Visited',
|
||||
});
|
||||
}, [newPriceId]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
// it's an unique modal, so we don't need to close it
|
||||
@@ -47,11 +76,11 @@ export function VerifyUpgrade(props: VerifyUpgradeProps) {
|
||||
<h3 className="text-xl font-bold text-black">Subscription Activated</h3>
|
||||
</div>
|
||||
|
||||
<p className="mt-2 text-balance text-center text-gray-600">
|
||||
<p className="mt-2 text-center text-balance text-gray-600">
|
||||
Your subscription has been activated successfully.
|
||||
</p>
|
||||
|
||||
<p className="mt-4 text-balance text-center text-gray-600">
|
||||
<p className="mt-4 text-center text-balance text-gray-600">
|
||||
It might take a minute for the changes to reflect. We will{' '}
|
||||
<b className="text-black">reload</b> the page for you.
|
||||
</p>
|
||||
|
||||
25
src/components/ChatEditor/ChatEditor.css
Normal file
25
src/components/ChatEditor/ChatEditor.css
Normal file
@@ -0,0 +1,25 @@
|
||||
.chat-editor .tiptap p.is-editor-empty:first-child::before {
|
||||
color: #adb5bd;
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.chat-editor .tiptap p:first-child.is-empty::before {
|
||||
color: #adb5bd;
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.chat-editor .tiptap [data-type='variable'] {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
padding: 2px 4px;
|
||||
border-radius: 8px;
|
||||
background-color: #f0f5ff;
|
||||
color: #2c5df1;
|
||||
}
|
||||
122
src/components/ChatEditor/ChatEditor.tsx
Normal file
122
src/components/ChatEditor/ChatEditor.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import './ChatEditor.css';
|
||||
|
||||
import {
|
||||
Editor,
|
||||
EditorContent,
|
||||
useEditor,
|
||||
type JSONContent,
|
||||
} from '@tiptap/react';
|
||||
import DocumentExtension from '@tiptap/extension-document';
|
||||
import ParagraphExtension from '@tiptap/extension-paragraph';
|
||||
import TextExtension from '@tiptap/extension-text';
|
||||
import Placeholder from '@tiptap/extension-placeholder';
|
||||
import { VariableExtension } from './VariableExtension/VariableExtension';
|
||||
import { variableSuggestion } from './VariableExtension/VariableSuggestion';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useEffect, type RefObject } from 'react';
|
||||
import { roadmapDetailsOptions } from '../../queries/roadmap';
|
||||
|
||||
const extensions = [
|
||||
DocumentExtension,
|
||||
ParagraphExtension,
|
||||
TextExtension,
|
||||
Placeholder.configure({
|
||||
placeholder: 'Ask AI anything about the roadmap...',
|
||||
}),
|
||||
VariableExtension.configure({
|
||||
suggestion: variableSuggestion(),
|
||||
}),
|
||||
];
|
||||
|
||||
const content = '<p></p>';
|
||||
|
||||
type ChatEditorProps = {
|
||||
editorRef: RefObject<Editor | null>;
|
||||
roadmapId: string;
|
||||
onSubmit: (content: JSONContent) => void;
|
||||
};
|
||||
|
||||
export function ChatEditor(props: ChatEditorProps) {
|
||||
const { roadmapId, onSubmit, editorRef } = props;
|
||||
|
||||
const { data: roadmapTreeData } = useQuery(
|
||||
roadmapTreeMappingOptions(roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const { data: roadmapDetailsData } = useQuery(
|
||||
roadmapDetailsOptions(roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const editor = useEditor({
|
||||
extensions,
|
||||
content,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: 'focus:outline-none w-full px-4 py-2 min-h-[40px]',
|
||||
},
|
||||
handleKeyDown(_, event) {
|
||||
if (!editor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.key === 'Enter' && !event.shiftKey) {
|
||||
// check if the variable suggestion list is focused
|
||||
// if it is, return false so the default behavior is not triggered
|
||||
const variableSuggestionList = document.getElementById(
|
||||
'variable-suggestion-list',
|
||||
);
|
||||
if (variableSuggestionList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
onSubmit(editor.getJSON());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.key === 'Enter' && event.shiftKey) {
|
||||
event.preventDefault();
|
||||
editor.commands.insertContent([
|
||||
{ type: 'text', text: ' ' },
|
||||
{ type: 'paragraph' },
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
onUpdate: ({ editor }) => {
|
||||
editorRef.current = editor;
|
||||
},
|
||||
onDestroy: () => {
|
||||
editorRef.current = null;
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor || !roadmapTreeData || !roadmapDetailsData) {
|
||||
return;
|
||||
}
|
||||
|
||||
editor.storage.variable.variables = roadmapTreeData.map((mapping) => {
|
||||
return {
|
||||
id: mapping.nodeId,
|
||||
// to remove the title of the roadmap
|
||||
// and only keep the path
|
||||
// e.g. "Roadmap > Topic > Subtopic" -> "Topic > Subtopic"
|
||||
label: mapping.text.split(' > ').slice(1).join(' > '),
|
||||
};
|
||||
});
|
||||
}, [editor, roadmapTreeData, roadmapDetailsData]);
|
||||
|
||||
return (
|
||||
<div className="chat-editor w-full py-1.5">
|
||||
<EditorContent editor={editor} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
import { mergeAttributes, Node } from '@tiptap/core';
|
||||
import { type DOMOutputSpec, Node as ProseMirrorNode } from '@tiptap/pm/model';
|
||||
import { PluginKey } from '@tiptap/pm/state';
|
||||
import Suggestion, { type SuggestionOptions } from '@tiptap/suggestion';
|
||||
|
||||
// See `addAttributes` below
|
||||
export interface VariableNodeAttrs {
|
||||
/**
|
||||
* The identifier for the selected item that was mentioned, stored as a `data-id`
|
||||
* attribute.
|
||||
*/
|
||||
id: string | null;
|
||||
/**
|
||||
* The label to be rendered by the editor as the displayed text for this mentioned
|
||||
* item, if provided. Stored as a `data-label` attribute. See `renderLabel`.
|
||||
*/
|
||||
label?: string | null;
|
||||
}
|
||||
|
||||
export type VariableOptions<
|
||||
SuggestionItem = any,
|
||||
Attrs extends Record<string, any> = VariableNodeAttrs,
|
||||
> = {
|
||||
/**
|
||||
* The HTML attributes for a mention node.
|
||||
* @default {}
|
||||
* @example { class: 'foo' }
|
||||
*/
|
||||
HTMLAttributes: Record<string, any>;
|
||||
|
||||
/**
|
||||
* A function to render the label of a mention.
|
||||
* @deprecated use renderText and renderHTML instead
|
||||
* @param props The render props
|
||||
* @returns The label
|
||||
* @example ({ options, node }) => `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`
|
||||
*/
|
||||
renderLabel?: (props: {
|
||||
options: VariableOptions<SuggestionItem, Attrs>;
|
||||
node: ProseMirrorNode;
|
||||
}) => string;
|
||||
|
||||
/**
|
||||
* A function to render the text of a mention.
|
||||
* @param props The render props
|
||||
* @returns The text
|
||||
* @example ({ options, node }) => `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`
|
||||
*/
|
||||
renderText: (props: {
|
||||
options: VariableOptions<SuggestionItem, Attrs>;
|
||||
node: ProseMirrorNode;
|
||||
}) => string;
|
||||
|
||||
/**
|
||||
* A function to render the HTML of a mention.
|
||||
* @param props The render props
|
||||
* @returns The HTML as a ProseMirror DOM Output Spec
|
||||
* @example ({ options, node }) => ['span', { 'data-type': 'mention' }, `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`]
|
||||
*/
|
||||
renderHTML: (props: {
|
||||
options: VariableOptions<SuggestionItem, Attrs>;
|
||||
node: ProseMirrorNode;
|
||||
}) => DOMOutputSpec;
|
||||
|
||||
/**
|
||||
* Whether to delete the trigger character with backspace.
|
||||
* @default false
|
||||
*/
|
||||
deleteTriggerWithBackspace: boolean;
|
||||
|
||||
/**
|
||||
* The suggestion options.
|
||||
* @default {}
|
||||
* @example { char: '@', pluginKey: MentionPluginKey, command: ({ editor, range, props }) => { ... } }
|
||||
*/
|
||||
suggestion: Omit<SuggestionOptions<SuggestionItem, Attrs>, 'editor'>;
|
||||
};
|
||||
|
||||
export type VariableType = {
|
||||
id: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type VariableStorage = {
|
||||
variables: VariableType[];
|
||||
};
|
||||
|
||||
/**
|
||||
* The plugin key for the variable plugin.
|
||||
* @default 'variable'
|
||||
*/
|
||||
export const VariablePluginKey = new PluginKey('variable');
|
||||
|
||||
export const VariableExtension = Node.create<VariableOptions, VariableStorage>({
|
||||
name: 'variable',
|
||||
|
||||
priority: 101,
|
||||
|
||||
addStorage() {
|
||||
return {
|
||||
variables: [],
|
||||
};
|
||||
},
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
HTMLAttributes: {},
|
||||
renderText({ options, node }) {
|
||||
return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`;
|
||||
},
|
||||
deleteTriggerWithBackspace: false,
|
||||
renderHTML({ options, node }) {
|
||||
return [
|
||||
'span',
|
||||
mergeAttributes(this.HTMLAttributes, options.HTMLAttributes),
|
||||
`${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`,
|
||||
];
|
||||
},
|
||||
suggestion: {
|
||||
char: '@',
|
||||
pluginKey: VariablePluginKey,
|
||||
command: ({ editor, range, props }) => {
|
||||
// increase range.to by one when the next node is of type "text"
|
||||
// and starts with a space character
|
||||
const nodeAfter = editor.view.state.selection.$to.nodeAfter;
|
||||
const overrideSpace = nodeAfter?.text?.startsWith(' ');
|
||||
|
||||
if (overrideSpace) {
|
||||
range.to += 1;
|
||||
}
|
||||
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.insertContentAt(range, [
|
||||
{
|
||||
type: this.name,
|
||||
attrs: props,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: ' ',
|
||||
},
|
||||
])
|
||||
.run();
|
||||
|
||||
// get reference to `window` object from editor element, to support cross-frame JS usage
|
||||
editor.view.dom.ownerDocument.defaultView
|
||||
?.getSelection()
|
||||
?.collapseToEnd();
|
||||
},
|
||||
allow: ({ state, range }) => {
|
||||
const $from = state.doc.resolve(range.from);
|
||||
const type = state.schema.nodes[this.name];
|
||||
const allow = !!$from.parent.type.contentMatch.matchType(type);
|
||||
|
||||
return allow;
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
group: 'inline',
|
||||
|
||||
inline: true,
|
||||
|
||||
selectable: false,
|
||||
|
||||
atom: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
id: {
|
||||
default: null,
|
||||
parseHTML: (element) => element.getAttribute('data-id'),
|
||||
renderHTML: (attributes) => {
|
||||
if (!attributes.id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
'data-id': attributes.id,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
label: {
|
||||
default: null,
|
||||
parseHTML: (element) => element.getAttribute('data-label'),
|
||||
renderHTML: (attributes) => {
|
||||
if (!attributes.label) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
'data-label': attributes.label,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: `span[data-type="${this.name}"]`,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ node, HTMLAttributes }) {
|
||||
if (this.options.renderLabel !== undefined) {
|
||||
console.warn(
|
||||
'renderLabel is deprecated use renderText and renderHTML instead',
|
||||
);
|
||||
return [
|
||||
'span',
|
||||
mergeAttributes(
|
||||
{ 'data-type': this.name },
|
||||
this.options.HTMLAttributes,
|
||||
HTMLAttributes,
|
||||
),
|
||||
this.options.renderLabel({
|
||||
options: this.options,
|
||||
node,
|
||||
}),
|
||||
];
|
||||
}
|
||||
const mergedOptions = { ...this.options };
|
||||
|
||||
mergedOptions.HTMLAttributes = mergeAttributes(
|
||||
{ 'data-type': this.name },
|
||||
this.options.HTMLAttributes,
|
||||
HTMLAttributes,
|
||||
);
|
||||
const html = this.options.renderHTML({
|
||||
options: mergedOptions,
|
||||
node,
|
||||
});
|
||||
|
||||
if (typeof html === 'string') {
|
||||
return [
|
||||
'span',
|
||||
mergeAttributes(
|
||||
{ 'data-type': this.name },
|
||||
this.options.HTMLAttributes,
|
||||
HTMLAttributes,
|
||||
),
|
||||
html,
|
||||
];
|
||||
}
|
||||
return html;
|
||||
},
|
||||
|
||||
renderText({ node }) {
|
||||
if (this.options.renderLabel !== undefined) {
|
||||
console.warn(
|
||||
'renderLabel is deprecated use renderText and renderHTML instead',
|
||||
);
|
||||
return this.options.renderLabel({
|
||||
options: this.options,
|
||||
node,
|
||||
});
|
||||
}
|
||||
return this.options.renderText({
|
||||
options: this.options,
|
||||
node,
|
||||
});
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
Backspace: () =>
|
||||
this.editor.commands.command(({ tr, state }) => {
|
||||
let isVariable = false;
|
||||
const { selection } = state;
|
||||
const { empty, anchor } = selection;
|
||||
|
||||
if (!empty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
|
||||
if (node.type.name === this.name) {
|
||||
isVariable = true;
|
||||
tr.insertText(
|
||||
this.options.deleteTriggerWithBackspace
|
||||
? ''
|
||||
: this.options.suggestion.char || '',
|
||||
pos,
|
||||
pos + node.nodeSize,
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return isVariable;
|
||||
}),
|
||||
};
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
Suggestion({
|
||||
editor: this.editor,
|
||||
...this.options.suggestion,
|
||||
}),
|
||||
];
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,175 @@
|
||||
import { ReactRenderer } from '@tiptap/react';
|
||||
import type { SuggestionOptions } from '@tiptap/suggestion';
|
||||
import tippy, { type GetReferenceClientRect } from 'tippy.js';
|
||||
|
||||
import {
|
||||
forwardRef,
|
||||
Fragment,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { cn } from '../../../lib/classname';
|
||||
import type { VariableStorage, VariableType } from './VariableExtension';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
|
||||
export type VariableListProps = {
|
||||
command: (variable: VariableType) => void;
|
||||
items: VariableType[];
|
||||
} & SuggestionOptions;
|
||||
|
||||
export const VariableList = forwardRef((props: VariableListProps, ref) => {
|
||||
const { items, command } = props;
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
|
||||
const selectItem = (index: number) => {
|
||||
const item = props.items[index];
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
command(item);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedIndex(0);
|
||||
}, [items]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
onKeyDown: ({ event }: { event: KeyboardEvent }) => {
|
||||
if (event.key === 'ArrowUp') {
|
||||
setSelectedIndex((selectedIndex + items.length - 1) % items.length);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
setSelectedIndex((selectedIndex + 1) % items.length);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
selectItem(selectedIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<div
|
||||
id="variable-suggestion-list"
|
||||
className="flex max-w-[300px] flex-col gap-0.5 overflow-auto rounded-lg border border-gray-200 bg-white p-1 shadow-sm"
|
||||
>
|
||||
{items.length ? (
|
||||
items.map((item, index) => {
|
||||
const labelParts = item?.label.split('>');
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
'flex items-center gap-1 truncate rounded-md p-1 px-1.5 text-left text-sm hover:bg-gray-100',
|
||||
index === selectedIndex && 'bg-gray-100',
|
||||
)}
|
||||
key={index}
|
||||
onClick={() => selectItem(index)}
|
||||
>
|
||||
{labelParts.map((labelPart, counter) => {
|
||||
const isLast = counter === labelParts.length - 1;
|
||||
return (
|
||||
<Fragment key={counter}>
|
||||
<span
|
||||
className={cn({
|
||||
'text-gray-400': !isLast,
|
||||
'text-gray-900': isLast,
|
||||
})}
|
||||
>
|
||||
{labelPart.trim()}
|
||||
</span>
|
||||
{!isLast && (
|
||||
<ChevronRight className="inline size-3 flex-shrink-0 stroke-[1.5] text-gray-400" />
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</button>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div className="rounded-md p-1 px-1.5 text-left text-sm">No result</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
VariableList.displayName = 'VariableList';
|
||||
|
||||
export function variableSuggestion(): Omit<SuggestionOptions, 'editor'> {
|
||||
return {
|
||||
items: ({ editor, query }) => {
|
||||
const storage = editor.storage.variable as VariableStorage;
|
||||
|
||||
return storage.variables
|
||||
.filter((variable) =>
|
||||
variable?.label?.toLowerCase().includes(query.toLowerCase()),
|
||||
)
|
||||
.slice(0, 5);
|
||||
},
|
||||
|
||||
render: () => {
|
||||
let component: ReactRenderer<any>;
|
||||
let popup: InstanceType<any> | null = null;
|
||||
|
||||
return {
|
||||
onStart: (props) => {
|
||||
component = new ReactRenderer(VariableList, {
|
||||
props,
|
||||
editor: props.editor,
|
||||
});
|
||||
|
||||
if (!props.clientRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
popup = tippy('body', {
|
||||
getReferenceClientRect: props.clientRect as GetReferenceClientRect,
|
||||
appendTo: () => document.body,
|
||||
content: component.element,
|
||||
showOnCreate: true,
|
||||
interactive: true,
|
||||
trigger: 'manual',
|
||||
placement: 'top-start',
|
||||
});
|
||||
},
|
||||
|
||||
onUpdate(props) {
|
||||
component.updateProps(props);
|
||||
|
||||
if (!props.clientRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
popup[0].setProps({
|
||||
getReferenceClientRect: props.clientRect,
|
||||
});
|
||||
},
|
||||
|
||||
onKeyDown(props) {
|
||||
if (props.event.key === 'Escape') {
|
||||
popup[0].hide();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return component.ref?.onKeyDown(props);
|
||||
},
|
||||
|
||||
onExit() {
|
||||
popup[0].destroy();
|
||||
component.destroy();
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -118,6 +118,7 @@ export function CustomRoadmap(props: CustomRoadmapProps) {
|
||||
resourceId={roadmap!._id}
|
||||
resourceTitle={roadmap!.title}
|
||||
resourceType="roadmap"
|
||||
renderer='editor'
|
||||
isEmbed={isEmbed}
|
||||
canSubmitContribution={false}
|
||||
/>
|
||||
|
||||
@@ -36,7 +36,9 @@ export function EditorRoadmap(props: EditorRoadmapProps) {
|
||||
|
||||
const { response, error } = await httpGet<
|
||||
Omit<RoadmapRendererProps, 'resourceId'>
|
||||
>(`/${switchRoadmapId || resourceId}.json`);
|
||||
>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${switchRoadmapId || resourceId}`,
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { GuideFileType, GuideFrontmatter } from '../../lib/guide';
|
||||
import { type QuestionGroupType } from '../../lib/question-group';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export interface GuideListItemProps {
|
||||
guide: GuideFileType | QuestionGroupType;
|
||||
@@ -27,6 +28,11 @@ export function GuideListItem(props: GuideListItemProps) {
|
||||
guideType = (frontmatter as GuideFrontmatter).type;
|
||||
}
|
||||
|
||||
// Check if article is within the last 15 days
|
||||
const isNew = frontmatter.date
|
||||
? dayjs().diff(dayjs(frontmatter.date), 'day') < 15
|
||||
: false;
|
||||
|
||||
return (
|
||||
<a
|
||||
className="text-md group block flex items-center justify-between border-b py-2 text-gray-600 no-underline hover:text-blue-600"
|
||||
@@ -35,19 +41,17 @@ export function GuideListItem(props: GuideListItemProps) {
|
||||
<span className="text-sm transition-transform group-hover:translate-x-2 md:text-base">
|
||||
{frontmatter.title}
|
||||
|
||||
{frontmatter.isNew && (
|
||||
<span className="ml-2.5 rounded-xs bg-green-300 px-1.5 py-0.5 text-xs font-medium uppercase text-green-900">
|
||||
{isNew && (
|
||||
<span className="ml-2.5 rounded-xs bg-green-300 px-1.5 py-0.5 text-xs font-medium text-green-900 uppercase">
|
||||
New
|
||||
<span className="hidden sm:inline">
|
||||
·
|
||||
{new Date(frontmatter.date || '').toLocaleString('default', {
|
||||
month: 'long',
|
||||
})}
|
||||
{frontmatter.date ? dayjs(frontmatter.date).format('MMMM') : ''}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="hidden text-xs capitalize text-gray-500 sm:block">
|
||||
<span className="hidden text-xs text-gray-500 capitalize sm:block">
|
||||
{guideType}
|
||||
</span>
|
||||
|
||||
|
||||
@@ -67,14 +67,13 @@ import Icon from './AstroIcon.astro';
|
||||
<span class='mx-1.5'>·</span>
|
||||
<a href='/privacy' class='hover:text-white'>Privacy</a>
|
||||
<span class='mx-1.5'>·</span>
|
||||
<a href='/advertise' class='hover:text-white'>Advertise</a>
|
||||
<span class='mx-1.5'>·</span>
|
||||
<a
|
||||
aria-label='Write us an email'
|
||||
href='mailto:info@roadmap.sh'
|
||||
aria-label='Follow on LinkedIn'
|
||||
href='https://www.linkedin.com/company/roadmapsh'
|
||||
class='hover:text-white'
|
||||
target='_blank'
|
||||
>
|
||||
<AstroIcon icon='letter' class='inline-block h-5 w-5' />
|
||||
<AstroIcon icon='linkedin-2' class='inline-block h-5 w-5 fill-current' />
|
||||
</a>
|
||||
<a
|
||||
aria-label='Subscribe to YouTube channel'
|
||||
@@ -95,6 +94,17 @@ import Icon from './AstroIcon.astro';
|
||||
class='inline-block h-5 w-5 fill-current'
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
aria-label='Follow on Blusky'
|
||||
href='http://roadmapsh.bsky.social/'
|
||||
target='_blank'
|
||||
class='ml-2 hover:text-white'
|
||||
>
|
||||
<AstroIcon
|
||||
icon='blusky'
|
||||
class='inline-block h-5 w-5 fill-current'
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -51,6 +51,14 @@ export function AICourse(props: AICourseProps) {
|
||||
setCustomInstructions(fineTuneData.customInstructions);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window?.fireEvent({
|
||||
action: 'tutor_user',
|
||||
category: 'ai_tutor',
|
||||
label: 'Visited AI Course Page',
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter' && keyword.trim()) {
|
||||
onSubmit();
|
||||
|
||||
@@ -399,7 +399,7 @@ type AIChatCardProps = {
|
||||
html?: string;
|
||||
};
|
||||
|
||||
function AIChatCard(props: AIChatCardProps) {
|
||||
export function AIChatCard(props: AIChatCardProps) {
|
||||
const { role, content, html: defaultHtml } = props;
|
||||
|
||||
const html = useMemo(() => {
|
||||
|
||||
@@ -56,7 +56,7 @@ export function FineTuneCourse(props: FineTuneCourseProps) {
|
||||
return (
|
||||
<div className="mt-0 flex flex-col">
|
||||
<Question
|
||||
label="Tell us about your self"
|
||||
label="Tell us about yourself"
|
||||
placeholder="e.g. I am a frontend developer and have good knowledge of HTML, CSS, and JavaScript."
|
||||
value={about}
|
||||
onChange={setAbout}
|
||||
|
||||
@@ -7,7 +7,6 @@ import { generateCourse } from '../../helper/generate-ai-course';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getAiCourseOptions } from '../../queries/ai-course';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { useAuth } from '../../hooks/use-auth';
|
||||
|
||||
type GenerateAICourseProps = {};
|
||||
|
||||
@@ -21,8 +20,8 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const currentUser = useAuth();
|
||||
|
||||
const [creatorId, setCreatorId] = useState('');
|
||||
const [courseId, setCourseId] = useState('');
|
||||
const [courseSlug, setCourseSlug] = useState('');
|
||||
const [course, setCourse] = useState<AiCourse>({
|
||||
@@ -54,6 +53,7 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||
const params = getUrlParams();
|
||||
const paramsTerm = params?.term;
|
||||
const paramsDifficulty = params?.difficulty;
|
||||
const paramsSrc = params?.src || 'search';
|
||||
if (!paramsTerm || !paramsDifficulty) {
|
||||
return;
|
||||
}
|
||||
@@ -87,6 +87,7 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||
instructions: paramsCustomInstructions,
|
||||
goal: paramsGoal,
|
||||
about: paramsAbout,
|
||||
src: paramsSrc,
|
||||
});
|
||||
}, [term, difficulty]);
|
||||
|
||||
@@ -98,9 +99,18 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||
about?: string;
|
||||
isForce?: boolean;
|
||||
prompt?: string;
|
||||
src?: string;
|
||||
}) => {
|
||||
const { term, difficulty, isForce, prompt, instructions, goal, about } =
|
||||
options;
|
||||
const {
|
||||
term,
|
||||
difficulty,
|
||||
isForce,
|
||||
prompt,
|
||||
instructions,
|
||||
goal,
|
||||
about,
|
||||
src,
|
||||
} = options;
|
||||
|
||||
if (!isLoggedIn()) {
|
||||
window.location.href = '/ai';
|
||||
@@ -113,6 +123,7 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||
slug: courseSlug,
|
||||
onCourseIdChange: setCourseId,
|
||||
onCourseSlugChange: setCourseSlug,
|
||||
onCreatorIdChange: setCreatorId,
|
||||
onCourseChange: setCourse,
|
||||
onLoadingChange: setIsLoading,
|
||||
onError: setError,
|
||||
@@ -121,6 +132,7 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||
about,
|
||||
isForce,
|
||||
prompt,
|
||||
src,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -152,7 +164,7 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||
return (
|
||||
<AICourseContent
|
||||
courseSlug={courseSlug}
|
||||
creatorId={currentUser?.id}
|
||||
creatorId={creatorId}
|
||||
course={course}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { NavigationDropdown } from '../NavigationDropdown';
|
||||
import { RoadmapDropdownMenu } from '../RoadmapDropdownMenu/RoadmapDropdownMenu';
|
||||
import { AccountDropdown } from './AccountDropdown';
|
||||
import { CourseAnnouncement } from '../SQLCourse/CourseAnnouncement';
|
||||
import { Fragment } from 'react';
|
||||
---
|
||||
|
||||
<div class='bg-slate-900 py-5 text-white sm:py-8'>
|
||||
|
||||
@@ -27,7 +27,7 @@ const discordInfo = await getDiscordInfo();
|
||||
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 mb-4 sm:mb-0'
|
||||
>
|
||||
<OpenSourceStat text='GitHub Stars' value={starCount} secondaryValue={repoRank} />
|
||||
<OpenSourceStat text='Registered Users' value={'+1.5M'} secondaryValue="+90k" />
|
||||
<OpenSourceStat text='Registered Users' value={'+2.1M'} secondaryValue="+90k" />
|
||||
<OpenSourceStat
|
||||
text='Discord Members'
|
||||
value={discordInfo.totalFormatted}
|
||||
|
||||
420
src/components/Premium/PremiumPage.tsx
Normal file
420
src/components/Premium/PremiumPage.tsx
Normal file
@@ -0,0 +1,420 @@
|
||||
import {
|
||||
Brain,
|
||||
Bot,
|
||||
Book,
|
||||
Star,
|
||||
Rocket,
|
||||
CheckCircle2,
|
||||
Zap,
|
||||
Clock,
|
||||
Crown,
|
||||
Users2,
|
||||
Wand2,
|
||||
Play,
|
||||
GitPullRequest,
|
||||
} from 'lucide-react';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
interface FeatureCardProps {
|
||||
title: string;
|
||||
description: string;
|
||||
Icon: LucideIcon;
|
||||
duration?: string;
|
||||
}
|
||||
|
||||
function FeatureCard({
|
||||
title,
|
||||
description,
|
||||
Icon,
|
||||
duration = '2:30',
|
||||
}: FeatureCardProps) {
|
||||
return (
|
||||
<div className="rounded-lg border border-slate-700 bg-slate-800/50 p-8 transition-colors hover:border-blue-400">
|
||||
<div className="group relative mb-6 aspect-video w-full overflow-hidden rounded-lg bg-slate-900/50">
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-white/10 backdrop-blur-sm">
|
||||
<Play className="h-6 w-6 text-white" strokeWidth={2} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute right-2 bottom-2 rounded bg-black/60 px-2 py-1 text-xs text-white backdrop-blur-sm">
|
||||
{duration}
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="mb-2 text-lg font-bold text-white">{title}</h3>
|
||||
<p className="leading-relaxed text-slate-400">{description}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StarRating() {
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<Star key={i} className="h-4 w-4 fill-current text-yellow-400" />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Testimonial({
|
||||
name,
|
||||
role,
|
||||
content,
|
||||
}: {
|
||||
name: string;
|
||||
role: string;
|
||||
content: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col rounded-lg border border-slate-700 bg-slate-800/50 p-6">
|
||||
<StarRating />
|
||||
<p className="mt-4 mb-auto leading-relaxed text-slate-400">{content}</p>
|
||||
<div className="mt-4">
|
||||
<div className="font-medium text-white">{name}</div>
|
||||
<div className="text-sm text-slate-500">{role}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface StatsItemProps {
|
||||
icon: LucideIcon;
|
||||
text: string;
|
||||
}
|
||||
|
||||
function StatsItem(props: StatsItemProps) {
|
||||
const Icon = props.icon;
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<Icon className="h-6 w-6 text-purple-500" strokeWidth={1.5} />
|
||||
<span className="text-gray-300">{props.text}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface CredibilityItemProps {
|
||||
icon: LucideIcon;
|
||||
iconClassName: string;
|
||||
value: string;
|
||||
label: string;
|
||||
subLabel: string;
|
||||
}
|
||||
|
||||
function CredibilityItem(props: CredibilityItemProps) {
|
||||
const Icon = props.icon;
|
||||
return (
|
||||
<div className="group flex flex-col items-center text-center">
|
||||
<div className="mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-slate-900/50">
|
||||
<Icon
|
||||
className={cn(`h-6 w-6`, props.iconClassName)}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-3xl font-bold text-white">{props.value}</div>
|
||||
<div className="mt-3 text-sm font-medium text-slate-400">
|
||||
{props.label}
|
||||
</div>
|
||||
<div className="mt-1.5 text-xs text-slate-500">{props.subLabel}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function PremiumPage() {
|
||||
const handleUpgrade = () => {
|
||||
alert('Upgrade functionality coming soon!');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-slate-900 to-black">
|
||||
<div className="mx-auto max-w-4xl px-4 py-20">
|
||||
{/* Hero Section */}
|
||||
<div className="mx-auto mb-20 max-w-4xl">
|
||||
<div className="text-center">
|
||||
<div className="mb-8 inline-flex items-center gap-2 rounded-full bg-slate-800/50 px-4 py-2 text-blue-400">
|
||||
<Zap className="h-4 w-4" />
|
||||
<span className="text-sm font-medium">
|
||||
Unlock All Premium Features
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="mb-6 text-4xl font-bold text-white md:text-5xl">
|
||||
Learn Faster with AI
|
||||
</h1>
|
||||
<p className="mb-8 text-lg text-balance text-slate-400 md:text-xl">
|
||||
Generate unlimited courses about any topic, get career guidance
|
||||
and instant answers from AI, test your skills and more
|
||||
</p>
|
||||
<button
|
||||
onClick={handleUpgrade}
|
||||
className="group mx-auto block rounded-2xl bg-gradient-to-b from-indigo-600 to-indigo-700 px-8 py-4 shadow-lg transition-all hover:-translate-y-0.5 hover:shadow-xl hover:shadow-indigo-500/25"
|
||||
>
|
||||
<div className="flex items-center justify-center gap-3 text-lg">
|
||||
<span className="font-medium text-indigo-100">
|
||||
Upgrade for just
|
||||
</span>
|
||||
<span className="font-bold text-white">$10/month</span>
|
||||
<span className="text-white transition-transform duration-200 group-hover:translate-x-1">
|
||||
→
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
<p className="mt-5 flex items-center justify-center gap-2 text-sm">
|
||||
<Clock className="h-3 w-3 text-white" />
|
||||
<span className="text-indigo-200">
|
||||
2 months free with yearly plan
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Section */}
|
||||
<div className="mb-20 flex flex-wrap items-center justify-center gap-x-10 gap-y-8">
|
||||
<StatsItem icon={Users2} text="+100K Learners" />
|
||||
<StatsItem icon={Bot} text="+135K Roadmaps" />
|
||||
<StatsItem icon={Book} text="+90K Courses" />
|
||||
<StatsItem icon={Wand2} text="+1M AI Chats" />
|
||||
</div>
|
||||
|
||||
{/* Testimonials */}
|
||||
<div className="-mx-4 mb-20 md:-mx-8 lg:-mx-16 xl:-mx-68">
|
||||
<h2 className="mb-12 text-center text-3xl font-bold text-white">
|
||||
What others are saying
|
||||
</h2>
|
||||
<div className="grid gap-6 px-4 md:grid-cols-4 md:px-8 lg:px-16">
|
||||
<Testimonial
|
||||
name="Gourav Khunger"
|
||||
role="Full Stack Developer"
|
||||
content="The AI tutor is absolutely brilliant! It's like having a senior developer available 24/7 to answer my questions."
|
||||
/>
|
||||
<Testimonial
|
||||
name="Meabed"
|
||||
role="Tech Lead"
|
||||
content="The personalized learning paths and premium resources have helped my entire team stay up-to-date with the latest tech."
|
||||
/>
|
||||
<Testimonial
|
||||
name="Mohsin Aheer"
|
||||
role="Software Engineer"
|
||||
content="The interactive exercises and real-world scenarios have significantly improved my problem-solving skills."
|
||||
/>
|
||||
<Testimonial
|
||||
name="Sarah Chen"
|
||||
role="Frontend Developer"
|
||||
content="The AI-powered code reviews have been invaluable. I've learned so many best practices and modern patterns."
|
||||
/>
|
||||
<Testimonial
|
||||
name="Alex Rodriguez"
|
||||
role="DevOps Engineer"
|
||||
content="Premium resources helped me master cloud architecture. The roadmaps are incredibly detailed and practical."
|
||||
/>
|
||||
<Testimonial
|
||||
name="Priya Sharma"
|
||||
role="Backend Developer"
|
||||
content="Worth every penny! The AI assistant helped me solve complex problems and understand advanced concepts quickly."
|
||||
/>
|
||||
<Testimonial
|
||||
name="James Wilson"
|
||||
role="Mobile Developer"
|
||||
content="The cross-platform development guides are exceptional. Helped me transition from native to React Native seamlessly."
|
||||
/>
|
||||
<Testimonial
|
||||
name="Emma Thompson"
|
||||
role="UI/UX Designer"
|
||||
content="As a designer learning to code, the visual learning paths and interactive tutorials made the journey much easier."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Features Grid */}
|
||||
<div className="mb-20">
|
||||
<h2 className="mb-12 text-center text-3xl font-bold text-white">
|
||||
Everything You Need to Succeed
|
||||
</h2>
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<FeatureCard
|
||||
Icon={Brain}
|
||||
title="AI Learning Assistant"
|
||||
description="Get instant answers and personalized guidance from our advanced AI tutor, available 24/7."
|
||||
/>
|
||||
<FeatureCard
|
||||
Icon={Bot}
|
||||
title="Custom Learning Paths"
|
||||
description="Follow AI-generated roadmaps tailored to your career goals and current skill level."
|
||||
/>
|
||||
<FeatureCard
|
||||
Icon={Crown}
|
||||
title="Premium Resources"
|
||||
description="Access exclusive learning materials, guides, and best practices curated by experts."
|
||||
/>
|
||||
<FeatureCard
|
||||
Icon={Clock}
|
||||
title="Time-Saving Tools"
|
||||
description="Save hours with AI-generated summaries and quick reference guides."
|
||||
/>
|
||||
<FeatureCard
|
||||
Icon={Book}
|
||||
title="Interactive Exercises"
|
||||
description="Practice with real-world scenarios and get instant feedback on your solutions."
|
||||
/>
|
||||
<FeatureCard
|
||||
Icon={Rocket}
|
||||
title="Career Acceleration"
|
||||
description="Get guidance on industry best practices and trending technologies."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Credibility Stats */}
|
||||
<div className="mb-20">
|
||||
<div className="rounded-xl border border-slate-700 bg-slate-800/50 p-8 md:p-12">
|
||||
<div className="mb-8 md:mb-12">
|
||||
<h2 className="text-3xl font-bold text-white md:text-4xl">
|
||||
The Platform Developers Trust
|
||||
</h2>
|
||||
<p className="mt-2 text-lg text-slate-400">
|
||||
Join millions of developers in their learning journey
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 md:grid-cols-4">
|
||||
<CredibilityItem
|
||||
icon={Star}
|
||||
iconClassName="text-yellow-400 fill-current"
|
||||
value="#6"
|
||||
label="Most Starred Project"
|
||||
subLabel="Among 200M+ Repositories"
|
||||
/>
|
||||
<CredibilityItem
|
||||
icon={Users2}
|
||||
iconClassName="text-blue-400"
|
||||
value="2.1M+"
|
||||
label="Active Developers"
|
||||
subLabel="Learning & Growing Daily"
|
||||
/>
|
||||
<CredibilityItem
|
||||
icon={Bot}
|
||||
iconClassName="text-indigo-400"
|
||||
value="37K+"
|
||||
label="Discord Members"
|
||||
subLabel="Active Community Support"
|
||||
/>
|
||||
<CredibilityItem
|
||||
icon={GitPullRequest}
|
||||
iconClassName="text-purple-400"
|
||||
value="1000+"
|
||||
label="Contributors"
|
||||
subLabel="Community Driven Project"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pricing Section */}
|
||||
<div className="mb-20">
|
||||
<h2 className="mb-12 text-center text-3xl font-bold text-white">
|
||||
Choose Your Plan
|
||||
</h2>
|
||||
<div className="mx-auto grid max-w-5xl gap-8 md:grid-cols-2">
|
||||
<div className="rounded-xl border border-slate-700 bg-slate-800/50 p-8">
|
||||
<h3 className="mb-4 text-2xl font-bold text-white">Monthly</h3>
|
||||
<div className="mb-6">
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-5xl font-bold text-white">$10</span>
|
||||
<span className="text-slate-400">/month</span>
|
||||
</div>
|
||||
<p className="mt-2 text-slate-400">
|
||||
Perfect for continuous learning
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleUpgrade}
|
||||
className="mb-8 w-full rounded-lg bg-blue-600 px-6 py-3 font-medium text-white transition-colors hover:bg-blue-700"
|
||||
>
|
||||
Start Monthly Plan
|
||||
</button>
|
||||
<ul className="space-y-4 text-slate-300">
|
||||
{[
|
||||
'AI Learning Assistant',
|
||||
'Personalized Learning Paths',
|
||||
'Interactive Exercises',
|
||||
'Premium Resources',
|
||||
].map((feature) => (
|
||||
<li key={feature} className="flex items-start">
|
||||
<CheckCircle2
|
||||
className="mt-0.5 mr-3 h-5 w-5 flex-shrink-0 text-blue-400"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="relative rounded-xl border-2 border-blue-400 bg-slate-800/50 p-8">
|
||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2 transform">
|
||||
<span className="rounded-full bg-blue-600 px-4 py-1 text-sm font-medium text-white">
|
||||
Most Popular
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="mb-4 text-2xl font-bold text-white">Yearly</h3>
|
||||
<div className="mb-6">
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-5xl font-bold text-white">$100</span>
|
||||
<span className="text-slate-400">/year</span>
|
||||
</div>
|
||||
<p className="mt-2 font-medium text-blue-400">
|
||||
Save $20 (2 months free)
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleUpgrade}
|
||||
className="mb-8 w-full rounded-lg bg-blue-600 px-6 py-3 font-medium text-white transition-colors hover:bg-blue-700"
|
||||
>
|
||||
Start Yearly Plan
|
||||
</button>
|
||||
<ul className="space-y-4 text-slate-300">
|
||||
{[
|
||||
'Everything in Monthly',
|
||||
'Priority Support',
|
||||
'Early Access Features',
|
||||
'Team Collaboration Tools',
|
||||
'Advanced Analytics',
|
||||
].map((feature) => (
|
||||
<li key={feature} className="flex items-start">
|
||||
<CheckCircle2
|
||||
className="mt-0.5 mr-3 h-5 w-5 flex-shrink-0 text-blue-400"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Final CTA */}
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<h2 className="mb-4 text-3xl font-bold text-white">
|
||||
Not Ready to Commit Yet?
|
||||
</h2>
|
||||
<p className="mb-8 text-lg text-slate-400">
|
||||
Try our AI features for free and experience the power of AI-assisted
|
||||
learning before upgrading.
|
||||
</p>
|
||||
<a
|
||||
href="/ai"
|
||||
className="group inline-flex items-center gap-3 rounded-full bg-slate-800/50 px-6 py-3 text-blue-400 ring-1 ring-slate-700/50 transition-all hover:bg-slate-800 hover:text-blue-300 hover:ring-blue-400/50"
|
||||
>
|
||||
<Bot className="h-5 w-5 transition-transform group-hover:scale-110" />
|
||||
<span className="text-lg font-medium">
|
||||
Try AI Features for Free
|
||||
</span>
|
||||
<span className="transform transition-transform group-hover:translate-x-1">
|
||||
→
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
---
|
||||
import { getGuideTableOfContent, type HeadingGroupType } from '../../lib/guide';
|
||||
import { markdownToHtml } from '../../lib/markdown';
|
||||
import { type QuestionGroupType } from '../../lib/question-group';
|
||||
import {
|
||||
type QuestionGroupType,
|
||||
type QuestionType,
|
||||
} from '../../lib/question-group';
|
||||
import { slugify } from '../../lib/slugger';
|
||||
import { RelatedGuides } from '../Guide/RelatedGuides';
|
||||
import MarkdownFile from '../MarkdownFile.astro';
|
||||
import { TableOfContent } from '../TableOfContent/TableOfContent';
|
||||
@@ -13,8 +17,27 @@ interface Props {
|
||||
|
||||
const { questionGroup } = Astro.props;
|
||||
|
||||
const { frontmatter: guideFrontmatter, author } = questionGroup;
|
||||
|
||||
// Group questions by topics
|
||||
const questionsGroupedByTopics = questionGroup.questions.reduce(
|
||||
(acc, question) => {
|
||||
question.topics?.forEach((topic) => {
|
||||
acc[topic] = [...(acc[topic] || []), question];
|
||||
});
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, QuestionType[]>,
|
||||
);
|
||||
|
||||
// Get all unique topics
|
||||
const questionTopics = Object.keys(questionsGroupedByTopics);
|
||||
const topicsList = Array.from(
|
||||
new Set(['Beginner', 'Intermediate', 'Advanced', ...questionTopics]),
|
||||
).filter((topic) => questionTopics.includes(topic));
|
||||
|
||||
const allHeadings = questionGroup.getHeadings();
|
||||
const tableOfContent: HeadingGroupType[] = [
|
||||
let tableOfContent: HeadingGroupType[] = [
|
||||
...getGuideTableOfContent(allHeadings),
|
||||
{
|
||||
depth: 2,
|
||||
@@ -24,36 +47,33 @@ const tableOfContent: HeadingGroupType[] = [
|
||||
},
|
||||
{
|
||||
depth: 2,
|
||||
children: [
|
||||
{
|
||||
children: topicsList.map((topic) => {
|
||||
let topicText = topic;
|
||||
let topicSlug = slugify(topic);
|
||||
if (topic === 'beginner') {
|
||||
topicText = 'Beginner Level';
|
||||
topicSlug = 'beginner-level';
|
||||
} else if (topic === 'intermediate') {
|
||||
topicText = 'Intermediate Level';
|
||||
topicSlug = 'intermediate-level';
|
||||
} else if (topic === 'advanced') {
|
||||
topicText = 'Advanced Level';
|
||||
topicSlug = 'advanced-level';
|
||||
}
|
||||
|
||||
return {
|
||||
depth: 2,
|
||||
title: 'Beginner Level',
|
||||
children: [],
|
||||
slug: 'beginner-level',
|
||||
text: 'Beginner Level',
|
||||
} as HeadingGroupType,
|
||||
{
|
||||
depth: 2,
|
||||
title: 'Intermediate Level',
|
||||
children: [],
|
||||
slug: 'intermediate-level',
|
||||
text: 'Intermediate Level',
|
||||
} as HeadingGroupType,
|
||||
{
|
||||
depth: 2,
|
||||
title: 'Advanced Level',
|
||||
children: [],
|
||||
slug: 'advanced-level',
|
||||
text: 'Advanced Level',
|
||||
} as HeadingGroupType,
|
||||
],
|
||||
slug: topicSlug,
|
||||
text: topicText,
|
||||
};
|
||||
}),
|
||||
slug: 'questions-list',
|
||||
text: 'Questions List',
|
||||
},
|
||||
];
|
||||
|
||||
const showTableOfContent = tableOfContent.length > 0;
|
||||
const { frontmatter: guideFrontmatter, author } = questionGroup;
|
||||
---
|
||||
|
||||
<article class='lg:grid lg:max-w-full lg:grid-cols-[1fr_minmax(0,700px)_1fr]'>
|
||||
@@ -79,7 +99,7 @@ const { frontmatter: guideFrontmatter, author } = questionGroup;
|
||||
]}
|
||||
>
|
||||
<MarkdownFile>
|
||||
<h1 class='mb-3 text-balance text-4xl font-bold'>
|
||||
<h1 class='mb-3 text-4xl font-bold text-balance'>
|
||||
{guideFrontmatter.title}
|
||||
</h1>
|
||||
{
|
||||
@@ -92,7 +112,7 @@ const { frontmatter: guideFrontmatter, author } = questionGroup;
|
||||
<img
|
||||
alt={author.frontmatter.name}
|
||||
src={author.frontmatter.imageUrl}
|
||||
class='mb-0 mr-2 inline h-5 w-5 rounded-full'
|
||||
class='mr-2 mb-0 inline h-5 w-5 rounded-full'
|
||||
/>
|
||||
{author.frontmatter.name}
|
||||
</a>
|
||||
@@ -129,31 +149,30 @@ const { frontmatter: guideFrontmatter, author } = questionGroup;
|
||||
</p>
|
||||
|
||||
{
|
||||
['beginner', 'intermediate', 'advanced'].map((questionLevel) => (
|
||||
Object.keys(questionsGroupedByTopics).map((questionLevel) => (
|
||||
<div class='mb-5'>
|
||||
<h3 id={`${questionLevel}-level`} class='mb-0 capitalize'>
|
||||
{questionLevel} Level
|
||||
<h3 id={slugify(questionLevel)} class='mb-0 capitalize'>
|
||||
{questionLevel}{' '}
|
||||
{['Beginner', 'Intermediate', 'Advanced'].includes(questionLevel)
|
||||
? 'Level'
|
||||
: ''}
|
||||
</h3>
|
||||
{questionGroup.questions
|
||||
.filter((q) => {
|
||||
return q?.topics
|
||||
?.map((t) => t.toLowerCase())
|
||||
.includes(questionLevel);
|
||||
})
|
||||
.map((q) => (
|
||||
<div class='mb-5'>
|
||||
<h4>{q.question}</h4>
|
||||
<div set:html={markdownToHtml(q.answer, false)} />
|
||||
</div>
|
||||
))}
|
||||
{questionsGroupedByTopics[questionLevel].map((q) => (
|
||||
<div class='mb-5'>
|
||||
<h4>{q.question}</h4>
|
||||
<div set:html={markdownToHtml(q.answer, false)} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
{questionGroup.ending && (
|
||||
<div class='mb-5'>
|
||||
<div set:html={markdownToHtml(questionGroup.ending, false)} />
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
questionGroup.ending && (
|
||||
<div class='mb-5'>
|
||||
<div set:html={markdownToHtml(questionGroup.ending, false)} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</MarkdownFile>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
48
src/components/RoadmapAIChat/AIChatActionButtons.tsx
Normal file
48
src/components/RoadmapAIChat/AIChatActionButtons.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { SettingsIcon, Trash2, type LucideIcon } from 'lucide-react';
|
||||
|
||||
type AIChatActionButtonProps = {
|
||||
icon: LucideIcon;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
function AIChatActionButton(props: AIChatActionButtonProps) {
|
||||
const { icon: Icon, label, onClick } = props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className="flex items-center gap-1 rounded-md border border-gray-200 px-2 py-1.5 text-xs hover:bg-gray-100"
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon className="size-3" />
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
type AIChatActionButtonsProps = {
|
||||
onTellUsAboutYourSelf: () => void;
|
||||
onClearChat: () => void;
|
||||
messageCount: number;
|
||||
};
|
||||
|
||||
export function AIChatActionButtons(props: AIChatActionButtonsProps) {
|
||||
const { onTellUsAboutYourSelf, onClearChat, messageCount } = props;
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 px-4 pt-2">
|
||||
<AIChatActionButton
|
||||
icon={SettingsIcon}
|
||||
label="Tell us about your self"
|
||||
onClick={onTellUsAboutYourSelf}
|
||||
/>
|
||||
{messageCount > 0 && (
|
||||
<AIChatActionButton
|
||||
icon={Trash2}
|
||||
label="Clear chat"
|
||||
onClick={onClearChat}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
68
src/components/RoadmapAIChat/ChatRoadmapRenderer.css
Normal file
68
src/components/RoadmapAIChat/ChatRoadmapRenderer.css
Normal file
@@ -0,0 +1,68 @@
|
||||
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[data-type='link-item'],
|
||||
svg > g[data-type='button'],
|
||||
svg > g[data-type='resourceButton'],
|
||||
svg > g[data-type='todo-checkbox'],
|
||||
svg > g[data-type='todo'],
|
||||
svg > g[data-type='checklist'] > g[data-type='checklist-item'] > rect {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg > g[data-type='topic']:hover > rect {
|
||||
fill: var(--hover-color);
|
||||
}
|
||||
|
||||
svg > g[data-type='subtopic']:hover > rect {
|
||||
fill: var(--hover-color);
|
||||
}
|
||||
svg g[data-type='button']:hover,
|
||||
svg g[data-type='link-item']:hover,
|
||||
svg g[data-type='resourceButton']:hover,
|
||||
svg g[data-type='todo-checkbox']:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
svg g[data-type='checklist'] > g[data-type='checklist-item'] > rect:hover {
|
||||
fill: #cbcbcb !important;
|
||||
}
|
||||
|
||||
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 .done text[fill='#ffffff'] {
|
||||
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;
|
||||
}
|
||||
263
src/components/RoadmapAIChat/ChatRoadmapRenderer.tsx
Normal file
263
src/components/RoadmapAIChat/ChatRoadmapRenderer.tsx
Normal file
@@ -0,0 +1,263 @@
|
||||
import './ChatRoadmapRenderer.css';
|
||||
|
||||
import { lazy, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
renderResourceProgress,
|
||||
updateResourceProgress,
|
||||
type ResourceProgressType,
|
||||
renderTopicProgress,
|
||||
refreshProgressCounters,
|
||||
} from '../../lib/resource-progress';
|
||||
import { pageProgressMessage } from '../../stores/page';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import type { Edge, Node } from '@roadmapsh/editor';
|
||||
import { slugify } from '../../lib/slugger';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { userResourceProgressOptions } from '../../queries/resource-progress';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { TopicResourcesModal } from './TopicResourcesModal';
|
||||
|
||||
const Renderer = lazy(() =>
|
||||
import('@roadmapsh/editor').then((mod) => ({
|
||||
default: mod.Renderer,
|
||||
})),
|
||||
);
|
||||
|
||||
type RoadmapNodeDetails = {
|
||||
nodeId: string;
|
||||
nodeType: string;
|
||||
targetGroup: SVGElement;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
function getNodeDetails(svgElement: SVGElement): RoadmapNodeDetails | null {
|
||||
const targetGroup = (svgElement?.closest('g') as SVGElement) || {};
|
||||
|
||||
const nodeId = targetGroup?.dataset?.nodeId;
|
||||
const nodeType = targetGroup?.dataset?.type;
|
||||
const title = targetGroup?.dataset?.title;
|
||||
|
||||
if (!nodeId || !nodeType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { nodeId, nodeType, targetGroup, title };
|
||||
}
|
||||
|
||||
const allowedNodeTypes = [
|
||||
'topic',
|
||||
'subtopic',
|
||||
'button',
|
||||
'link-item',
|
||||
'resourceButton',
|
||||
'todo',
|
||||
'todo-checkbox',
|
||||
'checklist-item',
|
||||
];
|
||||
|
||||
export type ChatRoadmapRendererProps = {
|
||||
roadmapId: string;
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
|
||||
onSelectTopic: (topicId: string, topicTitle: string) => void;
|
||||
};
|
||||
|
||||
export function ChatRoadmapRenderer(props: ChatRoadmapRendererProps) {
|
||||
const { roadmapId, nodes = [], edges = [], onSelectTopic } = props;
|
||||
const roadmapRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const { data: userResourceProgressData } = useQuery(
|
||||
userResourceProgressOptions('roadmap', roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
async function updateTopicStatus(
|
||||
topicId: string,
|
||||
newStatus: ResourceProgressType,
|
||||
) {
|
||||
pageProgressMessage.set('Updating progress');
|
||||
updateResourceProgress(
|
||||
{
|
||||
resourceId: roadmapId,
|
||||
resourceType: 'roadmap',
|
||||
topicId,
|
||||
},
|
||||
newStatus,
|
||||
)
|
||||
.then(() => {
|
||||
renderTopicProgress(topicId, newStatus);
|
||||
queryClient.invalidateQueries(
|
||||
userResourceProgressOptions('roadmap', roadmapId),
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('Something went wrong, please try again.');
|
||||
console.error(err);
|
||||
})
|
||||
.finally(() => {
|
||||
pageProgressMessage.set('');
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const handleSvgClick = useCallback((e: MouseEvent) => {
|
||||
const target = e.target as SVGElement;
|
||||
const { nodeId, nodeType, targetGroup, title } =
|
||||
getNodeDetails(target) || {};
|
||||
|
||||
if (!nodeId || !nodeType || !allowedNodeTypes.includes(nodeType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
nodeType === 'button' ||
|
||||
nodeType === 'link-item' ||
|
||||
nodeType === 'resourceButton'
|
||||
) {
|
||||
const link = targetGroup?.dataset?.link || '';
|
||||
const isExternalLink = link.startsWith('http');
|
||||
if (isExternalLink) {
|
||||
window.open(link, '_blank');
|
||||
} else {
|
||||
window.location.href = link;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const isCurrentStatusLearning = targetGroup?.classList.contains('learning');
|
||||
const isCurrentStatusSkipped = targetGroup?.classList.contains('skipped');
|
||||
|
||||
if (nodeType === 'todo-checkbox') {
|
||||
e.preventDefault();
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
const newStatus = targetGroup?.classList.contains('done')
|
||||
? 'pending'
|
||||
: 'done';
|
||||
updateTopicStatus(nodeId, newStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.shiftKey) {
|
||||
e.preventDefault();
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
updateTopicStatus(
|
||||
nodeId,
|
||||
isCurrentStatusLearning ? 'pending' : 'learning',
|
||||
);
|
||||
return;
|
||||
} else if (e.altKey) {
|
||||
e.preventDefault();
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
updateTopicStatus(nodeId, isCurrentStatusSkipped ? 'pending' : 'skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
// for the click on rect of checklist-item
|
||||
if (nodeType === 'checklist-item' && target.tagName === 'rect') {
|
||||
e.preventDefault();
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
const newStatus = targetGroup?.classList.contains('done')
|
||||
? 'pending'
|
||||
: 'done';
|
||||
updateTopicStatus(nodeId, newStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
// we don't have the topic popup for checklist-item
|
||||
if (nodeType === 'checklist-item') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!title || !nodeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
onSelectTopic(nodeId, title);
|
||||
}, []);
|
||||
|
||||
const handleSvgRightClick = useCallback((e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const target = e.target as SVGElement;
|
||||
const { nodeId, nodeType, targetGroup } = getNodeDetails(target) || {};
|
||||
if (!nodeId || !nodeType || !allowedNodeTypes.includes(nodeType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodeType === 'button') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
const isCurrentStatusDone = targetGroup?.classList.contains('done');
|
||||
updateTopicStatus(nodeId, isCurrentStatusDone ? 'pending' : 'done');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!roadmapRef?.current) {
|
||||
return;
|
||||
}
|
||||
roadmapRef?.current?.addEventListener('click', handleSvgClick);
|
||||
roadmapRef?.current?.addEventListener('contextmenu', handleSvgRightClick);
|
||||
|
||||
return () => {
|
||||
roadmapRef?.current?.removeEventListener('click', handleSvgClick);
|
||||
roadmapRef?.current?.removeEventListener(
|
||||
'contextmenu',
|
||||
handleSvgRightClick,
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Renderer
|
||||
ref={roadmapRef}
|
||||
roadmap={{ nodes, edges }}
|
||||
onRendered={() => {
|
||||
roadmapRef.current?.setAttribute('data-renderer', 'editor');
|
||||
|
||||
if (!userResourceProgressData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { done, learning, skipped } = userResourceProgressData;
|
||||
done.forEach((topicId) => {
|
||||
renderTopicProgress(topicId, 'done');
|
||||
});
|
||||
|
||||
learning.forEach((topicId) => {
|
||||
renderTopicProgress(topicId, 'learning');
|
||||
});
|
||||
|
||||
skipped.forEach((topicId) => {
|
||||
renderTopicProgress(topicId, 'skipped');
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
141
src/components/RoadmapAIChat/RoadmapAIChat.css
Normal file
141
src/components/RoadmapAIChat/RoadmapAIChat.css
Normal file
@@ -0,0 +1,141 @@
|
||||
.prose ul li > code,
|
||||
.prose ol li > code,
|
||||
p code,
|
||||
a > code,
|
||||
strong > code,
|
||||
em > code,
|
||||
h1 > code,
|
||||
h2 > code,
|
||||
h3 > code {
|
||||
background: #ebebeb !important;
|
||||
color: currentColor !important;
|
||||
font-size: 14px;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
.course-ai-content.course-content.prose ul li > code,
|
||||
.course-ai-content.course-content.prose ol li > code,
|
||||
.course-ai-content.course-content.prose p code,
|
||||
.course-ai-content.course-content.prose a > code,
|
||||
.course-ai-content.course-content.prose strong > code,
|
||||
.course-ai-content.course-content.prose em > code,
|
||||
.course-ai-content.course-content.prose h1 > code,
|
||||
.course-ai-content.course-content.prose h2 > code,
|
||||
.course-ai-content.course-content.prose h3 > code,
|
||||
.course-notes-content.prose ul li > code,
|
||||
.course-notes-content.prose ol li > code,
|
||||
.course-notes-content.prose p code,
|
||||
.course-notes-content.prose a > code,
|
||||
.course-notes-content.prose strong > code,
|
||||
.course-notes-content.prose em > code,
|
||||
.course-notes-content.prose h1 > code,
|
||||
.course-notes-content.prose h2 > code,
|
||||
.course-notes-content.prose h3 > code {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.course-ai-content pre {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.course-ai-content pre::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.course-ai-content pre,
|
||||
.course-notes-content pre {
|
||||
overflow: scroll;
|
||||
font-size: 15px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.prose ul li > code:before,
|
||||
p > code:before,
|
||||
.prose ul li > code:after,
|
||||
.prose ol li > code:before,
|
||||
p > code:before,
|
||||
.prose ol li > code:after,
|
||||
.course-content h1 > code:after,
|
||||
.course-content h1 > code:before,
|
||||
.course-content h2 > code:after,
|
||||
.course-content h2 > code:before,
|
||||
.course-content h3 > code:after,
|
||||
.course-content h3 > code:before,
|
||||
.course-content h4 > code:after,
|
||||
.course-content h4 > code:before,
|
||||
p > code:after,
|
||||
a > code:after,
|
||||
a > code:before {
|
||||
content: '' !important;
|
||||
}
|
||||
|
||||
.course-content.prose ul li > code,
|
||||
.course-content.prose ol li > code,
|
||||
.course-content p code,
|
||||
.course-content a > code,
|
||||
.course-content strong > code,
|
||||
.course-content em > code,
|
||||
.course-content h1 > code,
|
||||
.course-content h2 > code,
|
||||
.course-content h3 > code,
|
||||
.course-content table code {
|
||||
background: #f4f4f5 !important;
|
||||
border: 1px solid #282a36 !important;
|
||||
color: #282a36 !important;
|
||||
padding: 2px 4px;
|
||||
border-radius: 5px;
|
||||
font-size: 16px !important;
|
||||
white-space: pre;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.course-content blockquote {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.course-content.prose blockquote h1,
|
||||
.course-content.prose blockquote h2,
|
||||
.course-content.prose blockquote h3,
|
||||
.course-content.prose blockquote h4 {
|
||||
font-style: normal;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.course-content.prose ul li > code:before,
|
||||
.course-content p > code:before,
|
||||
.course-content.prose ul li > code:after,
|
||||
.course-content p > code:after,
|
||||
.course-content h2 > code:after,
|
||||
.course-content h2 > code:before,
|
||||
.course-content table code:before,
|
||||
.course-content table code:after,
|
||||
.course-content a > code:after,
|
||||
.course-content a > code:before,
|
||||
.course-content h2 code:after,
|
||||
.course-content h2 code:before,
|
||||
.course-content h2 code:after,
|
||||
.course-content h2 code:before {
|
||||
content: '' !important;
|
||||
}
|
||||
|
||||
.course-content table {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid black;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.course-content table td,
|
||||
.course-content table th {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.course-ai-content .chat-variable {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
padding: 2px 4px;
|
||||
border-radius: 8px;
|
||||
background-color: #f0f5ff;
|
||||
color: #2c5df1;
|
||||
}
|
||||
731
src/components/RoadmapAIChat/RoadmapAIChat.tsx
Normal file
731
src/components/RoadmapAIChat/RoadmapAIChat.tsx
Normal file
@@ -0,0 +1,731 @@
|
||||
import './RoadmapAIChat.css';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { roadmapJSONOptions } from '../../queries/roadmap';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import {
|
||||
Fragment,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
Bot,
|
||||
Frown,
|
||||
Loader2Icon,
|
||||
LockIcon,
|
||||
PauseCircleIcon,
|
||||
SendIcon,
|
||||
} from 'lucide-react';
|
||||
import { ChatEditor } from '../ChatEditor/ChatEditor';
|
||||
import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree';
|
||||
import { type AllowedAIChatRole } from '../GenerateCourse/AICourseLessonChat';
|
||||
import { isLoggedIn, removeAuthToken } from '../../lib/jwt';
|
||||
import type { JSONContent, Editor } from '@tiptap/core';
|
||||
import { flushSync } from 'react-dom';
|
||||
import { getAiCourseLimitOptions } from '../../queries/ai-course';
|
||||
import { readStream } from '../../lib/ai';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { userResourceProgressOptions } from '../../queries/resource-progress';
|
||||
import { ChatRoadmapRenderer } from './ChatRoadmapRenderer';
|
||||
import {
|
||||
renderMessage,
|
||||
type MessagePartRenderer,
|
||||
} from '../../lib/render-chat-message';
|
||||
import { RoadmapAIChatCard } from './RoadmapAIChatCard';
|
||||
import { UserProgressList } from './UserProgressList';
|
||||
import { UserProgressActionList } from './UserProgressActionList';
|
||||
import { RoadmapTopicList } from './RoadmapTopicList';
|
||||
import { ShareResourceLink } from './ShareResourceLink';
|
||||
import { RoadmapRecommendations } from './RoadmapRecommendations';
|
||||
import { RoadmapAIChatHeader } from './RoadmapAIChatHeader';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
||||
import { billingDetailsOptions } from '../../queries/billing';
|
||||
import { TopicDetail } from '../TopicDetail/TopicDetail';
|
||||
import { slugify } from '../../lib/slugger';
|
||||
import { AIChatActionButtons } from './AIChatActionButtons';
|
||||
import { cn } from '../../lib/classname';
|
||||
import {
|
||||
getTailwindScreenDimension,
|
||||
type TailwindScreenDimensions,
|
||||
} from '../../lib/is-mobile';
|
||||
import { ChatPersona } from '../UserPersona/ChatPersona';
|
||||
import { userPersonaOptions } from '../../queries/user-persona';
|
||||
import { UpdatePersonaModal } from '../UserPersona/UpdatePersonaModal';
|
||||
import { lockBodyScroll } from '../../lib/dom';
|
||||
import { TutorIntroMessage } from './TutorIntroMessage';
|
||||
|
||||
export type RoamdapAIChatHistoryType = {
|
||||
role: AllowedAIChatRole;
|
||||
isDefault?: boolean;
|
||||
|
||||
// these two will be used only into the backend
|
||||
// for transforming the raw message into the final message
|
||||
content?: string;
|
||||
json?: JSONContent;
|
||||
|
||||
// these two will be used only into the frontend
|
||||
// for rendering the message
|
||||
html?: string;
|
||||
jsx?: React.ReactNode;
|
||||
};
|
||||
|
||||
export type RoadmapAIChatTab = 'chat' | 'topic';
|
||||
|
||||
type RoadmapAIChatProps = {
|
||||
roadmapId: string;
|
||||
};
|
||||
|
||||
export function RoadmapAIChat(props: RoadmapAIChatProps) {
|
||||
const { roadmapId } = props;
|
||||
|
||||
const toast = useToast();
|
||||
const editorRef = useRef<Editor | null>(null);
|
||||
const scrollareaRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [deviceType, setDeviceType] = useState<TailwindScreenDimensions>();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setDeviceType(getTailwindScreenDimension());
|
||||
}, []);
|
||||
|
||||
const [isChatMobileVisible, setIsChatMobileVisible] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
||||
const [selectedTopicId, setSelectedTopicId] = useState<string | null>(null);
|
||||
const [selectedTopicTitle, setSelectedTopicTitle] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [activeTab, setActiveTab] = useState<RoadmapAIChatTab>('chat');
|
||||
|
||||
const [aiChatHistory, setAiChatHistory] = useState<
|
||||
RoamdapAIChatHistoryType[]
|
||||
>([]);
|
||||
const [isStreamingMessage, setIsStreamingMessage] = useState(false);
|
||||
const [streamedMessage, setStreamedMessage] =
|
||||
useState<React.ReactNode | null>(null);
|
||||
const [showUpdatePersonaModal, setShowUpdatePersonaModal] = useState(false);
|
||||
|
||||
const { data: roadmapDetail, error: roadmapDetailError } = useQuery(
|
||||
roadmapJSONOptions(roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
const { data: roadmapTreeData, isLoading: roadmapTreeLoading } = useQuery(
|
||||
roadmapTreeMappingOptions(roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const { isLoading: userResourceProgressLoading } = useQuery(
|
||||
userResourceProgressOptions('roadmap', roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const { data: tokenUsage, isLoading: isTokenUsageLoading } = useQuery(
|
||||
getAiCourseLimitOptions(),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const { data: userBillingDetails, isLoading: isBillingDetailsLoading } =
|
||||
useQuery(billingDetailsOptions(), queryClient);
|
||||
|
||||
const { data: userPersona, isLoading: isUserPersonaLoading } = useQuery(
|
||||
userPersonaOptions(roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
lockBodyScroll(isChatMobileVisible);
|
||||
}, [isChatMobileVisible]);
|
||||
|
||||
const isLimitExceeded = (tokenUsage?.used || 0) >= (tokenUsage?.limit || 0);
|
||||
const isPaidUser = userBillingDetails?.status === 'active';
|
||||
|
||||
const roadmapContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!roadmapDetail || !roadmapContainerRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
roadmapContainerRef.current.replaceChildren(roadmapDetail.svg);
|
||||
}, [roadmapDetail]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!roadmapTreeData || !roadmapDetail || isUserPersonaLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
}, [roadmapTreeData, roadmapDetail, isUserPersonaLoading]);
|
||||
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
const handleChatSubmit = (json: JSONContent) => {
|
||||
if (
|
||||
!json ||
|
||||
isStreamingMessage ||
|
||||
!isLoggedIn() ||
|
||||
isLoading ||
|
||||
abortControllerRef.current
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
abortControllerRef.current = new AbortController();
|
||||
|
||||
const html = htmlFromTiptapJSON(json);
|
||||
const newMessages: RoamdapAIChatHistoryType[] = [
|
||||
...aiChatHistory,
|
||||
{
|
||||
role: 'user',
|
||||
json,
|
||||
html,
|
||||
},
|
||||
];
|
||||
|
||||
flushSync(() => {
|
||||
setAiChatHistory(newMessages);
|
||||
editorRef.current?.commands.setContent('<p></p>');
|
||||
});
|
||||
|
||||
scrollToBottom();
|
||||
completeAITutorChat(newMessages, abortControllerRef.current);
|
||||
};
|
||||
|
||||
const scrollToBottom = useCallback(() => {
|
||||
scrollareaRef.current?.scrollTo({
|
||||
top: scrollareaRef.current.scrollHeight,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}, [scrollareaRef]);
|
||||
|
||||
const handleSelectTopic = useCallback(
|
||||
(topicId: string, topicTitle: string) => {
|
||||
flushSync(() => {
|
||||
setSelectedTopicId(topicId);
|
||||
setSelectedTopicTitle(topicTitle);
|
||||
setActiveTab('topic');
|
||||
|
||||
if (['sm', 'md', 'lg', 'xl'].includes(deviceType || 'xl')) {
|
||||
setIsChatMobileVisible(true);
|
||||
}
|
||||
});
|
||||
|
||||
const topicWithSlug = slugify(topicTitle) + '@' + topicId;
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('roadmap.node.click', {
|
||||
detail: {
|
||||
resourceType: 'roadmap',
|
||||
resourceId: roadmapId,
|
||||
topicId: topicWithSlug,
|
||||
isCustomResource: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
[roadmapId, deviceType],
|
||||
);
|
||||
|
||||
const totalTopicCount = useMemo(() => {
|
||||
const allowedTypes = ['topic', 'subtopic', 'todo'];
|
||||
return (
|
||||
roadmapDetail?.json?.nodes.filter((node) =>
|
||||
allowedTypes.includes(node.type || ''),
|
||||
).length ?? 0
|
||||
);
|
||||
}, [roadmapDetail]);
|
||||
|
||||
const renderer: Record<string, MessagePartRenderer> = useMemo(() => {
|
||||
return {
|
||||
'user-progress': () => {
|
||||
return (
|
||||
<UserProgressList
|
||||
totalTopicCount={totalTopicCount}
|
||||
roadmapId={roadmapId}
|
||||
/>
|
||||
);
|
||||
},
|
||||
'update-progress': (options) => {
|
||||
return <UserProgressActionList roadmapId={roadmapId} {...options} />;
|
||||
},
|
||||
'roadmap-topics': (options) => {
|
||||
return (
|
||||
<RoadmapTopicList
|
||||
roadmapId={roadmapId}
|
||||
onTopicClick={(topicId, text) => {
|
||||
const title = text.split(' > ').pop();
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleSelectTopic(topicId, title);
|
||||
}}
|
||||
{...options}
|
||||
/>
|
||||
);
|
||||
},
|
||||
'resource-progress-link': () => {
|
||||
return <ShareResourceLink roadmapId={roadmapId} />;
|
||||
},
|
||||
'roadmap-recommendations': (options) => {
|
||||
return <RoadmapRecommendations roadmapId={roadmapId} {...options} />;
|
||||
},
|
||||
};
|
||||
}, [roadmapId, handleSelectTopic, totalTopicCount]);
|
||||
|
||||
const completeAITutorChat = async (
|
||||
messages: RoamdapAIChatHistoryType[],
|
||||
abortController?: AbortController,
|
||||
) => {
|
||||
try {
|
||||
setIsStreamingMessage(true);
|
||||
|
||||
const response = await fetch(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-chat-roadmap`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
signal: abortController?.signal,
|
||||
body: JSON.stringify({
|
||||
roadmapId,
|
||||
messages: messages.slice(-10),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
toast.error(data?.message || 'Something went wrong');
|
||||
setAiChatHistory([...messages].slice(0, messages.length - 1));
|
||||
setIsStreamingMessage(false);
|
||||
|
||||
if (data.status === 401) {
|
||||
removeAuthToken();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
queryClient.invalidateQueries(getAiCourseLimitOptions());
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
|
||||
if (!reader) {
|
||||
setIsStreamingMessage(false);
|
||||
toast.error('Something went wrong');
|
||||
return;
|
||||
}
|
||||
|
||||
await readStream(reader, {
|
||||
onStream: async (content) => {
|
||||
if (abortController?.signal.aborted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jsx = await renderMessage(content, renderer, {
|
||||
isLoading: true,
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
setStreamedMessage(jsx);
|
||||
});
|
||||
|
||||
scrollToBottom();
|
||||
},
|
||||
onStreamEnd: async (content) => {
|
||||
if (abortController?.signal.aborted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jsx = await renderMessage(content, renderer, {
|
||||
isLoading: false,
|
||||
});
|
||||
const newMessages: RoamdapAIChatHistoryType[] = [
|
||||
...messages,
|
||||
{
|
||||
role: 'assistant',
|
||||
content,
|
||||
jsx,
|
||||
},
|
||||
];
|
||||
|
||||
flushSync(() => {
|
||||
setStreamedMessage(null);
|
||||
setIsStreamingMessage(false);
|
||||
setAiChatHistory(newMessages);
|
||||
});
|
||||
|
||||
queryClient.invalidateQueries(getAiCourseLimitOptions());
|
||||
scrollToBottom();
|
||||
},
|
||||
});
|
||||
|
||||
setIsStreamingMessage(false);
|
||||
abortControllerRef.current = null;
|
||||
} catch (error) {
|
||||
setIsStreamingMessage(false);
|
||||
setStreamedMessage(null);
|
||||
abortControllerRef.current = null;
|
||||
|
||||
if (abortController?.signal.aborted) {
|
||||
return;
|
||||
}
|
||||
toast.error('Something went wrong');
|
||||
}
|
||||
};
|
||||
|
||||
const handleAbort = () => {
|
||||
abortControllerRef.current?.abort();
|
||||
abortControllerRef.current = null;
|
||||
setIsStreamingMessage(false);
|
||||
setStreamedMessage(null);
|
||||
setAiChatHistory([...aiChatHistory].slice(0, aiChatHistory.length - 1));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, []);
|
||||
|
||||
if (roadmapDetailError) {
|
||||
return (
|
||||
<div className="flex flex-grow flex-col items-center justify-center">
|
||||
<Frown className="mb-4 size-16" />
|
||||
<h1 className="mb-2 text-2xl font-bold">There was an error</h1>
|
||||
<p className="max-w-sm text-balance text-gray-500">
|
||||
{roadmapDetailError.message}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const isDataLoading =
|
||||
isLoading ||
|
||||
roadmapTreeLoading ||
|
||||
userResourceProgressLoading ||
|
||||
isTokenUsageLoading ||
|
||||
isBillingDetailsLoading ||
|
||||
isUserPersonaLoading;
|
||||
|
||||
const shouldShowChatPersona =
|
||||
!isLoading && !isUserPersonaLoading && !userPersona && isLoggedIn();
|
||||
|
||||
return (
|
||||
<div className="flex flex-grow flex-row">
|
||||
<div className="relative h-full flex-grow overflow-y-scroll">
|
||||
{showUpgradeModal && (
|
||||
<UpgradeAccountModal onClose={() => setShowUpgradeModal(false)} />
|
||||
)}
|
||||
|
||||
{showUpdatePersonaModal && (
|
||||
<UpdatePersonaModal
|
||||
roadmapId={roadmapId}
|
||||
onClose={() => setShowUpdatePersonaModal(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 flex h-full w-full items-center justify-center">
|
||||
<Loader2Icon className="size-6 animate-spin stroke-[2.5]" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{roadmapDetail?.json && !isLoading && (
|
||||
<div className="relative mx-auto max-w-[968px] px-4 pb-28 xl:pb-0">
|
||||
<ChatRoadmapRenderer
|
||||
roadmapId={roadmapId}
|
||||
nodes={roadmapDetail?.json.nodes}
|
||||
edges={roadmapDetail?.json.edges}
|
||||
onSelectTopic={handleSelectTopic}
|
||||
/>
|
||||
|
||||
{/* floating chat button */}
|
||||
{!isChatMobileVisible && (
|
||||
<div className="fixed bottom-4 left-1/2 z-50 block -translate-x-1/2 xl:hidden">
|
||||
<button
|
||||
onClick={() => {
|
||||
setActiveTab('chat');
|
||||
setIsChatMobileVisible(true);
|
||||
}}
|
||||
className="relative w-max overflow-hidden rounded-full bg-stone-900 px-4 py-2 text-center text-white shadow-2xl hover:bg-stone-800"
|
||||
>
|
||||
<span className="relative z-20 flex items-center gap-2 text-sm">
|
||||
<Bot className="size-5 text-yellow-400" />
|
||||
<span>Chat with Roadmap</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isChatMobileVisible && (
|
||||
<div
|
||||
onClick={() => {
|
||||
setIsChatMobileVisible(false);
|
||||
}}
|
||||
className="fixed inset-0 z-50 bg-black/50"
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'h-full flex-grow flex-col border-l border-gray-200 bg-white',
|
||||
{
|
||||
'relative hidden max-w-[40%] xl:flex': !isChatMobileVisible,
|
||||
'fixed inset-y-0 right-0 z-50 w-full max-w-[520px]':
|
||||
isChatMobileVisible,
|
||||
flex: isChatMobileVisible,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<RoadmapAIChatHeader
|
||||
isLoading={isDataLoading}
|
||||
onLogin={() => {
|
||||
showLoginPopup();
|
||||
}}
|
||||
onUpgrade={() => {
|
||||
setShowUpgradeModal(true);
|
||||
}}
|
||||
activeTab={activeTab}
|
||||
onTabChange={(tab) => {
|
||||
setActiveTab(tab);
|
||||
if (tab === 'topic' && selectedTopicId && selectedTopicTitle) {
|
||||
handleSelectTopic(selectedTopicId, selectedTopicTitle);
|
||||
}
|
||||
}}
|
||||
onCloseTopic={() => {
|
||||
setSelectedTopicId(null);
|
||||
setSelectedTopicTitle(null);
|
||||
setActiveTab('chat');
|
||||
}}
|
||||
onCloseChat={() => {
|
||||
setIsChatMobileVisible(false);
|
||||
setActiveTab('chat');
|
||||
}}
|
||||
selectedTopicId={selectedTopicId}
|
||||
/>
|
||||
|
||||
{activeTab === 'topic' && selectedTopicId && (
|
||||
<TopicDetail
|
||||
resourceId={selectedTopicId}
|
||||
resourceType="roadmap"
|
||||
renderer="editor"
|
||||
defaultActiveTab="content"
|
||||
hasUpgradeButtons={false}
|
||||
canSubmitContribution={false}
|
||||
wrapperClassName="grow flex flex-col overflow-y-auto"
|
||||
bodyClassName="static mx-auto h-auto grow sm:max-w-full sm:p-4"
|
||||
overlayClassName="hidden"
|
||||
closeButtonClassName="hidden"
|
||||
onClose={() => {
|
||||
setSelectedTopicId(null);
|
||||
setSelectedTopicTitle(null);
|
||||
setActiveTab('chat');
|
||||
}}
|
||||
shouldCloseOnBackdropClick={false}
|
||||
shouldCloseOnEscape={false}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeTab === 'chat' && (
|
||||
<>
|
||||
<div className="relative grow overflow-y-auto" ref={scrollareaRef}>
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 flex h-full w-full items-center justify-center">
|
||||
<div className="flex items-center gap-2 rounded-lg border border-gray-200 bg-white p-1.5 px-3 text-sm text-gray-500">
|
||||
<Loader2Icon className="size-4 animate-spin stroke-[2.5]" />
|
||||
<span>Loading Roadmap</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{shouldShowChatPersona && !isLoading && (
|
||||
<ChatPersona roadmapId={roadmapId} />
|
||||
)}
|
||||
|
||||
{!isLoading && !shouldShowChatPersona && (
|
||||
<div className="absolute inset-0 flex flex-col">
|
||||
<div className="relative flex grow flex-col justify-end">
|
||||
<div className="flex flex-col justify-end gap-2 px-3 py-2">
|
||||
<RoadmapAIChatCard
|
||||
role="assistant"
|
||||
jsx={
|
||||
<TutorIntroMessage roadmap={roadmapDetail?.json!} />
|
||||
}
|
||||
isIntro
|
||||
/>
|
||||
|
||||
{aiChatHistory.map((chat, index) => {
|
||||
return (
|
||||
<Fragment key={`chat-${index}`}>
|
||||
<RoadmapAIChatCard {...chat} />
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
|
||||
{isStreamingMessage && !streamedMessage && (
|
||||
<RoadmapAIChatCard
|
||||
role="assistant"
|
||||
html="Thinking..."
|
||||
/>
|
||||
)}
|
||||
|
||||
{streamedMessage && (
|
||||
<RoadmapAIChatCard
|
||||
role="assistant"
|
||||
jsx={streamedMessage}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!isLoading && !shouldShowChatPersona && (
|
||||
<div className="flex flex-col border-t border-gray-200">
|
||||
{!isLimitExceeded && (
|
||||
<AIChatActionButtons
|
||||
onTellUsAboutYourSelf={() => {
|
||||
setShowUpdatePersonaModal(true);
|
||||
}}
|
||||
messageCount={aiChatHistory.length}
|
||||
onClearChat={() => {
|
||||
setAiChatHistory([]);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="relative flex items-start text-sm">
|
||||
<ChatEditor
|
||||
editorRef={editorRef}
|
||||
roadmapId={roadmapId}
|
||||
onSubmit={(content) => {
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isStreamingMessage ||
|
||||
abortControllerRef.current ||
|
||||
!isLoggedIn() ||
|
||||
isDataLoading ||
|
||||
isEmptyContent(content)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleChatSubmit(content);
|
||||
}}
|
||||
/>
|
||||
|
||||
{isLimitExceeded && isLoggedIn() && (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center gap-2 bg-black text-white">
|
||||
<LockIcon
|
||||
className="size-4 cursor-not-allowed"
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
<p className="cursor-not-allowed">
|
||||
Limit reached for today
|
||||
{isPaidUser ? '. Please wait until tomorrow.' : ''}
|
||||
</p>
|
||||
{!isPaidUser && (
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowUpgradeModal(true);
|
||||
}}
|
||||
className="rounded-md bg-white px-2 py-1 text-xs font-medium text-black hover:bg-gray-300"
|
||||
>
|
||||
Upgrade for more
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
className="flex aspect-square size-[36px] items-center justify-center p-2 text-zinc-500 hover:text-black disabled:cursor-not-allowed disabled:opacity-50"
|
||||
onClick={(e) => {
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isStreamingMessage || abortControllerRef.current) {
|
||||
handleAbort();
|
||||
return;
|
||||
}
|
||||
|
||||
const json = editorRef.current?.getJSON();
|
||||
if (!json || isEmptyContent(json)) {
|
||||
toast.error('Please enter a message');
|
||||
return;
|
||||
}
|
||||
|
||||
handleChatSubmit(json);
|
||||
}}
|
||||
>
|
||||
{isStreamingMessage ? (
|
||||
<PauseCircleIcon className="size-4 stroke-[2.5]" />
|
||||
) : (
|
||||
<SendIcon className="size-4 stroke-[2.5]" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function isEmptyContent(content: JSONContent) {
|
||||
if (!content) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// because they wrap the content in type doc
|
||||
const firstContent = content.content?.[0];
|
||||
if (!firstContent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
firstContent.type === 'paragraph' &&
|
||||
(!firstContent?.content || firstContent?.content?.length === 0)
|
||||
);
|
||||
}
|
||||
|
||||
export function htmlFromTiptapJSON(json: JSONContent) {
|
||||
const content = json.content;
|
||||
|
||||
let text = '';
|
||||
for (const child of content || []) {
|
||||
switch (child.type) {
|
||||
case 'text':
|
||||
text += child.text;
|
||||
break;
|
||||
case 'paragraph':
|
||||
text += `<p>${htmlFromTiptapJSON(child)}</p>`;
|
||||
break;
|
||||
case 'variable':
|
||||
const label = child?.attrs?.label || '';
|
||||
text += `<span class="chat-variable">${label}</span>`;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
46
src/components/RoadmapAIChat/RoadmapAIChatCard.tsx
Normal file
46
src/components/RoadmapAIChat/RoadmapAIChatCard.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { RoamdapAIChatHistoryType } from './RoadmapAIChat';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { BotIcon, User2Icon } from 'lucide-react';
|
||||
|
||||
type RoadmapAIChatCardProps = RoamdapAIChatHistoryType & {
|
||||
isIntro?: boolean;
|
||||
};
|
||||
|
||||
export function RoadmapAIChatCard(props: RoadmapAIChatCardProps) {
|
||||
const { role, html, jsx, isIntro = false } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col rounded-lg',
|
||||
role === 'user' ? 'bg-gray-300/30' : 'bg-yellow-500/30',
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-2.5 p-3">
|
||||
<div
|
||||
className={cn(
|
||||
'flex size-6 shrink-0 items-center justify-center rounded-full',
|
||||
role === 'user'
|
||||
? 'bg-gray-200 text-black'
|
||||
: 'bg-yellow-400 text-black',
|
||||
)}
|
||||
>
|
||||
{role === 'user' ? (
|
||||
<User2Icon className="size-4 stroke-[2.5]" />
|
||||
) : (
|
||||
<BotIcon className="size-4 stroke-[2.5]" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!!jsx && jsx}
|
||||
|
||||
{!!html && (
|
||||
<div
|
||||
className="course-content course-ai-content prose prose-sm mt-0.5 w-full max-w-[calc(100%-38px)] overflow-hidden text-sm"
|
||||
dangerouslySetInnerHTML={{ __html: html }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
178
src/components/RoadmapAIChat/RoadmapAIChatHeader.tsx
Normal file
178
src/components/RoadmapAIChat/RoadmapAIChatHeader.tsx
Normal file
@@ -0,0 +1,178 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getAiCourseLimitOptions } from '../../queries/ai-course';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { billingDetailsOptions } from '../../queries/billing';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { BookIcon, BotIcon, GiftIcon, XIcon } from 'lucide-react';
|
||||
import type { RoadmapAIChatTab } from './RoadmapAIChat';
|
||||
import { useState } from 'react';
|
||||
import { getPercentage } from '../../lib/number';
|
||||
import { AILimitsPopup } from '../GenerateCourse/AILimitsPopup';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { useKeydown } from '../../hooks/use-keydown';
|
||||
|
||||
type RoadmapAIChatHeaderProps = {
|
||||
isLoading: boolean;
|
||||
|
||||
onLogin: () => void;
|
||||
onUpgrade: () => void;
|
||||
|
||||
onCloseChat: () => void;
|
||||
|
||||
activeTab: RoadmapAIChatTab;
|
||||
onTabChange: (tab: RoadmapAIChatTab) => void;
|
||||
onCloseTopic: () => void;
|
||||
selectedTopicId: string | null;
|
||||
};
|
||||
|
||||
type TabButtonProps = {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
isActive: boolean;
|
||||
onClick: () => void;
|
||||
showBorder?: boolean;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
function TabButton(props: TabButtonProps) {
|
||||
const { icon, label, isActive, onClick, onClose } = props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
'flex h-full flex-shrink-0 items-center gap-2 px-4 text-sm',
|
||||
isActive && 'bg-gray-100',
|
||||
onClose && 'pr-2 pl-4',
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{icon}
|
||||
<span className="hidden sm:block">{label}</span>
|
||||
|
||||
{onClose && (
|
||||
<span
|
||||
role="button"
|
||||
className="ml-1 rounded-lg p-1 text-gray-500 hover:bg-gray-200"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<XIcon className="size-4 shrink-0" strokeWidth={2.5} />
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function RoadmapAIChatHeader(props: RoadmapAIChatHeaderProps) {
|
||||
const {
|
||||
onLogin,
|
||||
onUpgrade,
|
||||
isLoading: isDataLoading,
|
||||
onCloseChat,
|
||||
|
||||
activeTab,
|
||||
onTabChange,
|
||||
onCloseTopic,
|
||||
selectedTopicId,
|
||||
} = props;
|
||||
|
||||
const [showAILimitsPopup, setShowAILimitsPopup] = useState(false);
|
||||
const { data: tokenUsage } = useQuery(getAiCourseLimitOptions(), queryClient);
|
||||
|
||||
const { data: userBillingDetails } = useQuery(
|
||||
billingDetailsOptions(),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
useKeydown('Escape', onCloseChat);
|
||||
|
||||
const isLimitExceeded = (tokenUsage?.used || 0) >= (tokenUsage?.limit || 0);
|
||||
const isPaidUser = userBillingDetails?.status === 'active';
|
||||
|
||||
const usagePercentage = getPercentage(
|
||||
tokenUsage?.used || 0,
|
||||
tokenUsage?.limit || 0,
|
||||
);
|
||||
|
||||
const handleCreditsClick = () => {
|
||||
if (!isLoggedIn()) {
|
||||
onLogin();
|
||||
return;
|
||||
}
|
||||
setShowAILimitsPopup(true);
|
||||
};
|
||||
|
||||
const handleUpgradeClick = () => {
|
||||
if (!isLoggedIn()) {
|
||||
onLogin();
|
||||
return;
|
||||
}
|
||||
onUpgrade();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{showAILimitsPopup && (
|
||||
<AILimitsPopup
|
||||
onClose={() => setShowAILimitsPopup(false)}
|
||||
onUpgrade={() => {
|
||||
setShowAILimitsPopup(false);
|
||||
onUpgrade();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex h-[46px] flex-shrink-0 items-center justify-between border-b border-gray-200 text-sm">
|
||||
<div className="flex h-full items-center">
|
||||
<TabButton
|
||||
icon={<BotIcon className="size-4 shrink-0 text-black" />}
|
||||
label="AI Chat"
|
||||
isActive={activeTab === 'chat' && !!selectedTopicId}
|
||||
onClick={() => onTabChange('chat')}
|
||||
/>
|
||||
|
||||
{(activeTab === 'topic' || selectedTopicId) && (
|
||||
<TabButton
|
||||
icon={<BookIcon className="size-4 shrink-0 text-black" />}
|
||||
label="Topic"
|
||||
isActive={activeTab === 'topic' && !!selectedTopicId}
|
||||
onClick={() => onTabChange('topic')}
|
||||
onClose={onCloseTopic}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!isDataLoading && isLoggedIn() && (
|
||||
<div className="flex gap-1.5 pr-4">
|
||||
{!isPaidUser && (
|
||||
<>
|
||||
<button
|
||||
className="hidden rounded-md bg-gray-200 px-2 py-1 text-sm hover:bg-gray-300 2xl:block"
|
||||
onClick={handleCreditsClick}
|
||||
>
|
||||
<span className="font-medium">{usagePercentage}%</span> limit
|
||||
used
|
||||
</button>
|
||||
<button
|
||||
className="flex items-center gap-1 rounded-md bg-yellow-400 px-2 py-1 text-sm text-black hover:bg-yellow-500"
|
||||
onClick={handleUpgradeClick}
|
||||
>
|
||||
<GiftIcon className="size-4" />
|
||||
Upgrade
|
||||
</button>
|
||||
<button
|
||||
className="hidden items-center gap-1 rounded-md bg-gray-200 px-2 py-1 text-sm text-black hover:bg-gray-300 max-xl:flex"
|
||||
onClick={onCloseChat}
|
||||
>
|
||||
<XIcon className="size-3.5" strokeWidth={2.5} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
79
src/components/RoadmapAIChat/RoadmapRecommendations.tsx
Normal file
79
src/components/RoadmapAIChat/RoadmapRecommendations.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { useMemo } from 'react';
|
||||
import { listBuiltInRoadmaps } from '../../queries/roadmap';
|
||||
import { SquareArrowOutUpRightIcon } from 'lucide-react';
|
||||
|
||||
type RoadmapSlugListType = {
|
||||
roadmapSlug: string;
|
||||
};
|
||||
|
||||
function parseRoadmapSlugList(content: string): RoadmapSlugListType[] {
|
||||
const items: RoadmapSlugListType[] = [];
|
||||
|
||||
const roadmapSlugListRegex = /<roadmap-slug>.*?<\/roadmap-slug>/gs;
|
||||
const roadmapSlugListItems = content.match(roadmapSlugListRegex);
|
||||
if (!roadmapSlugListItems) {
|
||||
return items;
|
||||
}
|
||||
|
||||
for (const roadmapSlugListItem of roadmapSlugListItems) {
|
||||
const roadmapSlugRegex = /<roadmap-slug>(.*?)<\/roadmap-slug>/;
|
||||
const roadmapSlug = roadmapSlugListItem
|
||||
.match(roadmapSlugRegex)?.[1]
|
||||
?.trim();
|
||||
if (!roadmapSlug) {
|
||||
continue;
|
||||
}
|
||||
|
||||
items.push({
|
||||
roadmapSlug,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
type RoadmapRecommendationsProps = {
|
||||
roadmapId: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
export function RoadmapRecommendations(props: RoadmapRecommendationsProps) {
|
||||
const { content } = props;
|
||||
|
||||
const roadmapSlugListItems = parseRoadmapSlugList(content);
|
||||
|
||||
const { data: roadmaps } = useQuery(listBuiltInRoadmaps(), queryClient);
|
||||
|
||||
const progressItemWithText = useMemo(() => {
|
||||
return roadmapSlugListItems.map((item) => {
|
||||
const roadmap = roadmaps?.find(
|
||||
(mapping) => mapping.id === item.roadmapSlug,
|
||||
);
|
||||
|
||||
return {
|
||||
...item,
|
||||
title: roadmap?.title,
|
||||
};
|
||||
});
|
||||
}, [roadmapSlugListItems, roadmaps]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative my-6 flex flex-wrap gap-1 first:mt-0 last:mb-0">
|
||||
{progressItemWithText.map((item) => (
|
||||
<a
|
||||
href={`/ai/chat/${item.roadmapSlug}`}
|
||||
target="_blank"
|
||||
key={item.roadmapSlug}
|
||||
className="group flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-2.5 py-1.5 text-left text-sm text-gray-700 transition-all hover:border-gray-300 hover:bg-gray-50 hover:text-gray-900 active:bg-gray-100"
|
||||
>
|
||||
{item.title}
|
||||
<SquareArrowOutUpRightIcon className="size-3.5 text-gray-400 transition-transform group-hover:text-gray-600" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
99
src/components/RoadmapAIChat/RoadmapTopicList.tsx
Normal file
99
src/components/RoadmapAIChat/RoadmapTopicList.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { Fragment, useMemo } from 'react';
|
||||
import { ChevronRightIcon } from 'lucide-react';
|
||||
|
||||
type TopicListType = {
|
||||
topicId: string;
|
||||
};
|
||||
|
||||
function parseTopicList(content: string): TopicListType[] {
|
||||
const items: TopicListType[] = [];
|
||||
|
||||
const topicListRegex = /<topic-id>.*?<\/topic-id>/gs;
|
||||
const topicListItems = content.match(topicListRegex);
|
||||
if (!topicListItems) {
|
||||
return items;
|
||||
}
|
||||
|
||||
for (const topicListItem of topicListItems) {
|
||||
const topicIdRegex = /<topic-id>(.*?)<\/topic-id>/;
|
||||
const topicId = topicListItem.match(topicIdRegex)?.[1]?.trim();
|
||||
if (!topicId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
items.push({
|
||||
topicId,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
type RoadmapTopicListProps = {
|
||||
roadmapId: string;
|
||||
content: string;
|
||||
onTopicClick?: (topicId: string, topicTitle: string) => void;
|
||||
};
|
||||
|
||||
export function RoadmapTopicList(props: RoadmapTopicListProps) {
|
||||
const { roadmapId, content, onTopicClick } = props;
|
||||
|
||||
const topicListItems = parseTopicList(content);
|
||||
|
||||
const { data: roadmapTreeData } = useQuery(
|
||||
roadmapTreeMappingOptions(roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const progressItemWithText = useMemo(() => {
|
||||
return topicListItems.map((item) => {
|
||||
const roadmapTreeItem = roadmapTreeData?.find(
|
||||
(mapping) => mapping.nodeId === item.topicId,
|
||||
);
|
||||
|
||||
return {
|
||||
...item,
|
||||
text: (roadmapTreeItem?.text || item.topicId)
|
||||
?.split(' > ')
|
||||
.slice(1)
|
||||
.join(' > '),
|
||||
};
|
||||
});
|
||||
}, [topicListItems, roadmapTreeData]);
|
||||
|
||||
return (
|
||||
<div className="relative my-6 flex flex-wrap gap-1 first:mt-0 last:mb-0">
|
||||
{progressItemWithText.map((item) => {
|
||||
const labelParts = item.text.split(' > ');
|
||||
const labelPartCount = labelParts.length;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={item.topicId}
|
||||
className="collapse-if-empty flex items-center gap-1 rounded-lg border border-gray-200 bg-white p-1 px-2 text-left text-sm hover:bg-gray-50"
|
||||
onClick={() => {
|
||||
onTopicClick?.(item.topicId, item.text);
|
||||
}}
|
||||
>
|
||||
{labelParts.map((part, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<span>{part}</span>
|
||||
{index < labelPartCount - 1 && (
|
||||
<ChevronRightIcon
|
||||
className="size-3 text-gray-400"
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
46
src/components/RoadmapAIChat/ShareResourceLink.tsx
Normal file
46
src/components/RoadmapAIChat/ShareResourceLink.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { ShareIcon } from 'lucide-react';
|
||||
import { useAuth } from '../../hooks/use-auth';
|
||||
import { useCopyText } from '../../hooks/use-copy-text';
|
||||
import { CheckIcon } from '../ReactIcons/CheckIcon';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
type ShareResourceLinkProps = {
|
||||
roadmapId: string;
|
||||
};
|
||||
|
||||
export function ShareResourceLink(props: ShareResourceLinkProps) {
|
||||
const { roadmapId } = props;
|
||||
const user = useAuth();
|
||||
const { copyText, isCopied } = useCopyText();
|
||||
|
||||
const handleShareResourceLink = () => {
|
||||
const url = `${import.meta.env.PUBLIC_APP_URL}/${roadmapId}?s=${user?.id}`;
|
||||
copyText(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative my-6 flex flex-wrap gap-1 first:mt-0 last:mb-0">
|
||||
<button
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 rounded-lg border border-gray-200 bg-white p-1 px-1.5 text-left text-sm',
|
||||
isCopied && 'text-green-500',
|
||||
)}
|
||||
onClick={handleShareResourceLink}
|
||||
>
|
||||
{!isCopied && (
|
||||
<>
|
||||
<ShareIcon className="h-4 w-4" />
|
||||
Share Progress
|
||||
</>
|
||||
)}
|
||||
|
||||
{isCopied && (
|
||||
<>
|
||||
<CheckIcon additionalClasses="h-4 w-4" />
|
||||
Copied
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
88
src/components/RoadmapAIChat/TopicResourcesModal.tsx
Normal file
88
src/components/RoadmapAIChat/TopicResourcesModal.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Modal } from '../Modal';
|
||||
import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { roadmapContentOptions } from '../../queries/roadmap';
|
||||
import { ModalLoader } from '../UserProgress/ModalLoader';
|
||||
import { TopicDetailLink } from '../TopicDetail/TopicDetailLink';
|
||||
import { Spinner } from '../ReactIcons/Spinner';
|
||||
import { ErrorIcon } from '../ReactIcons/ErrorIcon';
|
||||
import { markdownToHtml } from '../../lib/markdown';
|
||||
|
||||
type TopicResourcesModalProps = {
|
||||
roadmapId: string;
|
||||
topicId: string;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export function TopicResourcesModal(props: TopicResourcesModalProps) {
|
||||
const { roadmapId, topicId, onClose } = props;
|
||||
|
||||
const {
|
||||
data: roadmapContentData,
|
||||
isLoading: isLoadingRoadmapContent,
|
||||
error,
|
||||
} = useQuery(roadmapContentOptions(roadmapId), queryClient);
|
||||
|
||||
const topicContent = roadmapContentData?.[topicId];
|
||||
const links = topicContent?.links || [];
|
||||
|
||||
return (
|
||||
<Modal onClose={onClose} wrapperClassName="max-w-lg">
|
||||
{!isLoadingRoadmapContent && !error && topicContent && (
|
||||
<div className="p-4">
|
||||
<h2 className="text-xl font-bold">{topicContent?.title}</h2>
|
||||
|
||||
<div
|
||||
className="course-content course-ai-content prose prose-sm mt-1 max-w-full overflow-hidden text-sm"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: markdownToHtml(topicContent.description, true),
|
||||
}}
|
||||
/>
|
||||
|
||||
{links.length > 0 && (
|
||||
<ul className="mt-4 space-y-1">
|
||||
{links.map((link, index) => {
|
||||
return (
|
||||
<li key={`${link.url}-${index}`}>
|
||||
<TopicDetailLink
|
||||
url={link.url}
|
||||
type={link.type}
|
||||
title={link.title}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(isLoadingRoadmapContent || error || !topicContent) && (
|
||||
<div className="rounded-lg bg-white p-5">
|
||||
<div className="flex items-center">
|
||||
{isLoadingRoadmapContent && (
|
||||
<>
|
||||
<Spinner className="h-6 w-6" isDualRing={false} />
|
||||
<span className="ml-3 text-lg font-semibold">
|
||||
Loading Topic Resources...
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
{(error || !topicContent) && !isLoadingRoadmapContent && (
|
||||
<>
|
||||
<ErrorIcon additionalClasses="h-6 w-6 text-red-500" />
|
||||
<span className="ml-3 text-lg font-semibold">
|
||||
{!topicContent
|
||||
? 'No resources found'
|
||||
: (error?.message ?? 'Something went wrong')}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
100
src/components/RoadmapAIChat/TutorIntroMessage.tsx
Normal file
100
src/components/RoadmapAIChat/TutorIntroMessage.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import type { RoadmapJSON } from '../../queries/roadmap';
|
||||
|
||||
type TutorIntroMessageProps = {
|
||||
roadmap: RoadmapJSON;
|
||||
};
|
||||
|
||||
export function TutorIntroMessage(props: TutorIntroMessageProps) {
|
||||
const { roadmap } = props;
|
||||
|
||||
const topicNodes = roadmap.nodes.filter((node) => node.type === 'topic');
|
||||
|
||||
const firstTopicNode = topicNodes[0];
|
||||
const firstTopicTitle = firstTopicNode?.data?.label || 'XYZ';
|
||||
|
||||
const secondTopicNode = topicNodes[1];
|
||||
const secondTopicTitle = secondTopicNode?.data?.label || 'XYZ';
|
||||
|
||||
const capabilities = [
|
||||
{
|
||||
icon: '📚',
|
||||
title: 'Learn concepts:',
|
||||
description: 'Ask me about any topics on the roadmap',
|
||||
examples:
|
||||
'"Explain what React hooks are" or "How does async/await work?"',
|
||||
},
|
||||
{
|
||||
icon: '📊',
|
||||
title: 'Track progress:',
|
||||
description: 'Mark topics as done, learning, or skipped',
|
||||
examples: `"Mark ${firstTopicTitle} as done" or "Show my overall progress"`,
|
||||
},
|
||||
{
|
||||
icon: '🎯',
|
||||
title: 'Recommendations:',
|
||||
description: 'Find what to learn next or explore other roadmaps',
|
||||
examples: `"What should I learn next?" or "Recommend roadmaps for backend development"`,
|
||||
},
|
||||
{
|
||||
icon: '🔍',
|
||||
title: 'Find resources:',
|
||||
description: 'Get learning materials for specific topics',
|
||||
examples: `"Show me resources for learning ${secondTopicTitle}"`,
|
||||
},
|
||||
{
|
||||
icon: '🔗',
|
||||
title: 'Share progress:',
|
||||
description: 'Get a link to share your learning progress',
|
||||
examples: '"Give me my shareable progress link"',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-2 text-sm text-gray-700">
|
||||
<div className="flex items-start gap-3">
|
||||
<div>
|
||||
<h3 className="mb-2 font-medium text-gray-900">
|
||||
Hi! I'm your AI learning assistant 👋
|
||||
</h3>
|
||||
<p className="mb-3">
|
||||
I'm here to guide you through your learning journey on this roadmap.
|
||||
I can help you understand concepts, track your progress, and provide
|
||||
personalized learning advice.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h4 className="font-medium text-gray-900">
|
||||
Here's what I can help you with:
|
||||
</h4>
|
||||
|
||||
<div className="space-y-3">
|
||||
{capabilities.map((capability, index) => (
|
||||
<div key={index} className="flex items-start gap-2">
|
||||
<span className={`font-medium`}>{capability.icon}</span>
|
||||
<div>
|
||||
<span className="font-medium text-black">
|
||||
{capability.title}
|
||||
</span>{' '}
|
||||
{capability.description}
|
||||
<div className="mt-1 text-xs text-gray-600">
|
||||
Try: {capability.examples}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 rounded-lg bg-gray-50 p-3">
|
||||
<p className="text-xs text-black">
|
||||
<span className="font-medium">Tip:</span> I can see your current
|
||||
progress on the roadmap, so my advice will be personalized to your
|
||||
learning journey. Just ask me anything about the topics you see on the
|
||||
roadmap!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
332
src/components/RoadmapAIChat/UserProgressActionList.tsx
Normal file
332
src/components/RoadmapAIChat/UserProgressActionList.tsx
Normal file
@@ -0,0 +1,332 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { Fragment, useMemo, useState } from 'react';
|
||||
import { renderTopicProgress } from '../../lib/resource-progress';
|
||||
import { updateResourceProgress } from '../../lib/resource-progress';
|
||||
import { pageProgressMessage } from '../../stores/page';
|
||||
import type { ResourceProgressType } from '../../lib/resource-progress';
|
||||
import { userResourceProgressOptions } from '../../queries/resource-progress';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { Check, ChevronRightIcon, Loader2Icon } from 'lucide-react';
|
||||
import { CheckIcon } from '../ReactIcons/CheckIcon';
|
||||
import { httpPost } from '../../lib/query-http';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
type UpdateUserProgress = {
|
||||
id: string;
|
||||
action: 'done' | 'learning' | 'skipped' | 'pending';
|
||||
};
|
||||
|
||||
function parseUserProgress(content: string): UpdateUserProgress[] {
|
||||
const items: UpdateUserProgress[] = [];
|
||||
|
||||
const progressRegex = /<update-progress-item>.*?<\/update-progress-item>/gs;
|
||||
const progressItems = content.match(progressRegex);
|
||||
if (!progressItems) {
|
||||
return items;
|
||||
}
|
||||
|
||||
for (const progressItem of progressItems) {
|
||||
const progressItemRegex = /<topic-id>(.*?)<\/topic-id>/;
|
||||
const topicId = progressItem.match(progressItemRegex)?.[1]?.trim();
|
||||
const topicActionRegex = /<topic-action>(.*?)<\/topic-action>/;
|
||||
const topicAction = progressItem
|
||||
.match(topicActionRegex)?.[1]
|
||||
.trim()
|
||||
?.toLowerCase();
|
||||
|
||||
if (!topicId || !topicAction) {
|
||||
continue;
|
||||
}
|
||||
|
||||
items.push({
|
||||
id: topicId,
|
||||
action: topicAction as UpdateUserProgress['action'],
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
type BulkUpdateResourceProgressBody = {
|
||||
done: string[];
|
||||
learning: string[];
|
||||
skipped: string[];
|
||||
pending: string[];
|
||||
};
|
||||
|
||||
type BulkUpdateResourceProgressResponse = {
|
||||
done: string[];
|
||||
learning: string[];
|
||||
skipped: string[];
|
||||
};
|
||||
|
||||
type UserProgressActionListProps = {
|
||||
roadmapId: string;
|
||||
content: string;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
export function UserProgressActionList(props: UserProgressActionListProps) {
|
||||
const { roadmapId, content, isLoading = false } = props;
|
||||
|
||||
const toast = useToast();
|
||||
const updateUserProgress = parseUserProgress(content);
|
||||
|
||||
const { data: roadmapTreeData } = useQuery(
|
||||
roadmapTreeMappingOptions(roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const {
|
||||
mutate: bulkUpdateResourceProgress,
|
||||
isPending: isBulkUpdating,
|
||||
isSuccess: isBulkUpdateSuccess,
|
||||
} = useMutation(
|
||||
{
|
||||
mutationFn: (body: BulkUpdateResourceProgressBody) => {
|
||||
return httpPost<BulkUpdateResourceProgressResponse>(
|
||||
`/v1-bulk-update-resource-progress/${roadmapId}`,
|
||||
body,
|
||||
);
|
||||
},
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries(
|
||||
userResourceProgressOptions('roadmap', roadmapId),
|
||||
);
|
||||
},
|
||||
onSettled: () => {
|
||||
pageProgressMessage.set('');
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(
|
||||
error?.message ?? 'Something went wrong, please try again.',
|
||||
);
|
||||
},
|
||||
},
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const progressItemWithText = useMemo(() => {
|
||||
return updateUserProgress.map((item) => {
|
||||
const roadmapTreeItem = roadmapTreeData?.find(
|
||||
(mapping) => mapping.nodeId === item.id,
|
||||
);
|
||||
|
||||
return {
|
||||
...item,
|
||||
text: (roadmapTreeItem?.text || item.id)
|
||||
?.split(' > ')
|
||||
.slice(1)
|
||||
.join(' > '),
|
||||
};
|
||||
});
|
||||
}, [updateUserProgress, roadmapTreeData]);
|
||||
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
const itemCountToShow = 4;
|
||||
const itemsToShow = showAll
|
||||
? progressItemWithText
|
||||
: progressItemWithText.slice(0, itemCountToShow);
|
||||
|
||||
const hasMoreItemsToShow = progressItemWithText.length > itemCountToShow;
|
||||
|
||||
return (
|
||||
<div className="relative my-6 w-full first:mt-0 last:mb-0">
|
||||
<div className="relative flex flex-col gap-0.5">
|
||||
{itemsToShow.map((item) => (
|
||||
<ProgressItem
|
||||
key={item.id}
|
||||
roadmapId={roadmapId}
|
||||
topicId={item.id}
|
||||
text={item.text}
|
||||
action={item.action}
|
||||
isStreaming={isLoading}
|
||||
isBulkUpdating={isBulkUpdating}
|
||||
isBulkUpdateSuccess={isBulkUpdateSuccess}
|
||||
/>
|
||||
))}
|
||||
|
||||
{hasMoreItemsToShow && (
|
||||
<div className="relative mt-1 flex items-center justify-between gap-2">
|
||||
<button
|
||||
className="z-50 flex items-center gap-1 rounded-md bg-gray-400 px-2 py-1 text-xs font-medium text-white hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-70"
|
||||
onClick={() => setShowAll(!showAll)}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading && (
|
||||
<>
|
||||
<Loader2Icon className="size-3 animate-spin" />
|
||||
{progressItemWithText.length} loaded ..
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isLoading && (
|
||||
<>
|
||||
{showAll
|
||||
? '- Show Less'
|
||||
: `+ Show ${progressItemWithText.length - itemCountToShow} More`}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="z-50 flex items-center gap-1 rounded-md bg-green-600 px-2 py-1 text-xs font-medium text-white hover:bg-green-700 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-70"
|
||||
disabled={isBulkUpdating || isLoading}
|
||||
onClick={() => {
|
||||
const done = updateUserProgress
|
||||
.filter((item) => item.action === 'done')
|
||||
.map((item) => item.id);
|
||||
const learning = updateUserProgress
|
||||
.filter((item) => item.action === 'learning')
|
||||
.map((item) => item.id);
|
||||
const skipped = updateUserProgress
|
||||
.filter((item) => item.action === 'skipped')
|
||||
.map((item) => item.id);
|
||||
const pending = updateUserProgress
|
||||
.filter((item) => item.action === 'pending')
|
||||
.map((item) => item.id);
|
||||
|
||||
bulkUpdateResourceProgress({
|
||||
done,
|
||||
learning,
|
||||
skipped,
|
||||
pending,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{isBulkUpdating && (
|
||||
<Loader2Icon className="size-3 animate-spin" />
|
||||
)}
|
||||
{!isBulkUpdating && <CheckIcon additionalClasses="size-3" />}
|
||||
Apply All
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type ProgressItemProps = {
|
||||
roadmapId: string;
|
||||
topicId: string;
|
||||
text: string;
|
||||
action: UpdateUserProgress['action'];
|
||||
isStreaming: boolean;
|
||||
isBulkUpdating: boolean;
|
||||
isBulkUpdateSuccess: boolean;
|
||||
};
|
||||
|
||||
function ProgressItem(props: ProgressItemProps) {
|
||||
const {
|
||||
roadmapId,
|
||||
topicId,
|
||||
text,
|
||||
action,
|
||||
isStreaming,
|
||||
isBulkUpdating,
|
||||
isBulkUpdateSuccess,
|
||||
} = props;
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const {
|
||||
mutate: updateTopicStatus,
|
||||
isSuccess,
|
||||
isPending: isUpdating,
|
||||
} = useMutation(
|
||||
{
|
||||
mutationFn: (action: ResourceProgressType) => {
|
||||
return updateResourceProgress(
|
||||
{
|
||||
resourceId: roadmapId,
|
||||
resourceType: 'roadmap',
|
||||
topicId,
|
||||
},
|
||||
action,
|
||||
);
|
||||
},
|
||||
onMutate: () => {},
|
||||
onSuccess: () => {
|
||||
renderTopicProgress(topicId, action);
|
||||
},
|
||||
onError: () => {
|
||||
toast.error('Something went wrong, please try again.');
|
||||
},
|
||||
onSettled: () => {
|
||||
pageProgressMessage.set('');
|
||||
return queryClient.invalidateQueries(
|
||||
userResourceProgressOptions('roadmap', roadmapId),
|
||||
);
|
||||
},
|
||||
},
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const textParts = text.split(' > ');
|
||||
const lastIndex = textParts.length - 1;
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[40px] items-center justify-between gap-2 rounded-lg border border-gray-200 bg-white py-1 pr-1 pl-3">
|
||||
<span className="flex items-center gap-1 truncate text-sm text-gray-500">
|
||||
{textParts.map((part, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
{part}
|
||||
{index !== lastIndex && (
|
||||
<span className="text-gray-500">
|
||||
<ChevronRightIcon className="size-3 shrink-0" />{' '}
|
||||
</span>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
{!isSuccess && !isBulkUpdateSuccess && (
|
||||
<>
|
||||
{!isStreaming && (
|
||||
<button
|
||||
className={cn(
|
||||
`flex shrink-0 items-center gap-1.5 rounded-md border border-gray-200 px-2 py-1 text-xs disabled:pointer-events-none disabled:opacity-40`,
|
||||
{
|
||||
'bg-green-100 hover:border-green-300 hover:bg-green-200':
|
||||
action === 'done',
|
||||
'bg-yellow-100 hover:border-yellow-300 hover:bg-yellow-200':
|
||||
action === 'learning',
|
||||
'bg-gray-800 text-white hover:border-black hover:bg-black':
|
||||
action === 'skipped',
|
||||
'bg-gray-100 hover:border-gray-300 hover:bg-gray-200':
|
||||
action === 'pending',
|
||||
},
|
||||
)}
|
||||
onClick={() => updateTopicStatus(action)}
|
||||
disabled={isStreaming || isUpdating || isBulkUpdating}
|
||||
>
|
||||
{(isUpdating || isBulkUpdating) && (
|
||||
<Loader2Icon className="size-4 animate-spin" />
|
||||
)}
|
||||
{!isUpdating && !isBulkUpdating && (
|
||||
<>
|
||||
<Check strokeWidth={3} className="size-3" />
|
||||
Mark it as {action}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
{isStreaming && (
|
||||
<span className="flex size-[30px] items-center justify-center text-gray-300">
|
||||
<Loader2Icon className="size-4 animate-spin" />
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{(isSuccess || isBulkUpdateSuccess) && (
|
||||
<span className="flex size-[30px] items-center justify-center text-green-500">
|
||||
<CheckIcon additionalClasses="size-4" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
58
src/components/RoadmapAIChat/UserProgressList.tsx
Normal file
58
src/components/RoadmapAIChat/UserProgressList.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { userResourceProgressOptions } from '../../queries/resource-progress';
|
||||
import { getPercentage } from '../../lib/number';
|
||||
|
||||
type UserProgressListProps = {
|
||||
totalTopicCount: number;
|
||||
roadmapId: string;
|
||||
};
|
||||
|
||||
export function UserProgressList(props: UserProgressListProps) {
|
||||
const { totalTopicCount, roadmapId } = props;
|
||||
|
||||
const { data: userResourceProgressData } = useQuery(
|
||||
userResourceProgressOptions('roadmap', roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const doneCount = userResourceProgressData?.done?.length ?? 0;
|
||||
const skippedCount = userResourceProgressData?.skipped?.length ?? 0;
|
||||
|
||||
const totalFinished = doneCount + skippedCount;
|
||||
const progressPercentage = getPercentage(totalFinished, totalTopicCount);
|
||||
|
||||
return (
|
||||
<div className="relative my-6 flex flex-col gap-3 rounded-xl border border-gray-200 bg-white p-4 first:mt-0 last:mb-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium text-gray-600">Progress</span>
|
||||
<span className="rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700">
|
||||
{progressPercentage}%
|
||||
</span>
|
||||
</div>
|
||||
<span className="hidden text-sm font-medium text-gray-600 md:block">
|
||||
{totalFinished} / {totalTopicCount} topics
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative h-2 w-full overflow-hidden rounded-full bg-gray-100">
|
||||
<div
|
||||
className="absolute inset-0 bg-gradient-to-r from-green-500 to-green-600 transition-all duration-300"
|
||||
style={{ width: `${progressPercentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 text-xs text-gray-500">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="h-2 w-2 rounded-full bg-green-500" />
|
||||
<span>Completed: {doneCount}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="h-2 w-2 rounded-full bg-gray-400" />
|
||||
<span>Skipped: {skippedCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,19 +2,20 @@
|
||||
import {
|
||||
ArrowLeftIcon,
|
||||
BookOpenIcon,
|
||||
Bot,
|
||||
FolderKanbanIcon,
|
||||
MapIcon,
|
||||
MessageCircle,
|
||||
} from 'lucide-react';
|
||||
import { TabLink } from './TabLink';
|
||||
import LoginPopup from './AuthenticationFlow/LoginPopup.astro';
|
||||
import { ScheduleButton } from './Schedule/ScheduleButton';
|
||||
import ProgressHelpPopup from './ProgressHelpPopup.astro';
|
||||
import { MarkFavorite } from './FeaturedItems/MarkFavorite';
|
||||
import { type RoadmapFrontmatter } from '../lib/roadmap';
|
||||
import { ShareRoadmapButton } from './ShareRoadmapButton';
|
||||
import LoginPopup from './AuthenticationFlow/LoginPopup.astro';
|
||||
import { DownloadRoadmapButton } from './DownloadRoadmapButton';
|
||||
import { CourseAnnouncement } from './SQLCourse/CourseAnnouncement';
|
||||
import { MarkFavorite } from './FeaturedItems/MarkFavorite';
|
||||
import ProgressHelpPopup from './ProgressHelpPopup.astro';
|
||||
import { ScheduleButton } from './Schedule/ScheduleButton';
|
||||
import { ShareRoadmapButton } from './ShareRoadmapButton';
|
||||
import { TabLink } from './TabLink';
|
||||
|
||||
export interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
@@ -29,6 +30,7 @@ export interface Props {
|
||||
hasSearch?: boolean;
|
||||
projectCount?: number;
|
||||
coursesCount?: number;
|
||||
hasAIChat?: boolean;
|
||||
question?: RoadmapFrontmatter['question'];
|
||||
hasTopics?: boolean;
|
||||
isForkable?: boolean;
|
||||
@@ -43,6 +45,7 @@ const {
|
||||
isUpcoming = false,
|
||||
note,
|
||||
hasTopics = false,
|
||||
hasAIChat = false,
|
||||
projectCount = 0,
|
||||
question,
|
||||
activeTab = 'roadmap',
|
||||
@@ -125,7 +128,7 @@ const hasProjects = projectCount > 0;
|
||||
{
|
||||
(
|
||||
<div class='flex justify-between gap-2 sm:gap-0'>
|
||||
<div class='relative top-px flex gap-1 sm:gap-3'>
|
||||
<div class='relative top-px flex gap-1 sm:gap-2'>
|
||||
<TabLink
|
||||
url={`/${roadmapId}`}
|
||||
icon={MapIcon}
|
||||
@@ -147,6 +150,16 @@ const hasProjects = projectCount > 0;
|
||||
icon={BookOpenIcon}
|
||||
text='Courses'
|
||||
isActive={activeTab === 'courses'}
|
||||
className='hidden md:flex'
|
||||
/>
|
||||
)}
|
||||
{hasAIChat && (
|
||||
<TabLink
|
||||
url={`/${roadmapId}/ai`}
|
||||
icon={Bot}
|
||||
text='AI Mentor'
|
||||
mobileText='AI'
|
||||
isActive={false}
|
||||
badgeText='New'
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -388,6 +388,11 @@ const groups: GroupType[] = [
|
||||
link: '/ai-engineer',
|
||||
type: 'role',
|
||||
},
|
||||
{
|
||||
title: 'AI Agents',
|
||||
link: '/ai-agents',
|
||||
type: 'role',
|
||||
},
|
||||
{
|
||||
title: 'AI Red Teaming',
|
||||
link: '/ai-red-teaming',
|
||||
|
||||
@@ -161,9 +161,12 @@ export function BuyButton(props: BuyButtonProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
const encodedCourseSlug = encodeURIComponent(`/courses/${SQL_COURSE_SLUG}`);
|
||||
const successUrl = `/thank-you?next=${encodedCourseSlug}`;
|
||||
|
||||
createCheckoutSession({
|
||||
courseId: SQL_COURSE_SLUG,
|
||||
success: `/courses/${SQL_COURSE_SLUG}?${COURSE_PURCHASE_SUCCESS_PARAM}=1`,
|
||||
success: successUrl,
|
||||
cancel: `/courses/${SQL_COURSE_SLUG}?${COURSE_PURCHASE_SUCCESS_PARAM}=0`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,10 +2,22 @@ import { Database, X } from 'lucide-react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
const HIDE_ANNOUNCEMENT_END_KEY = '__course_announcement_closed_at__';
|
||||
|
||||
export function CourseAnnouncement() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const hiddenEndAt = Number(
|
||||
localStorage.getItem(HIDE_ANNOUNCEMENT_END_KEY) || '0',
|
||||
);
|
||||
|
||||
// only show if the closed at passed 14 days ago
|
||||
const shouldShow = hiddenEndAt < Date.now();
|
||||
if (!shouldShow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => setIsVisible(true), 5000);
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
@@ -14,7 +26,7 @@ export function CourseAnnouncement() {
|
||||
<div
|
||||
className={cn(
|
||||
'sticky top-0 z-91 h-0 overflow-hidden transition-[height] duration-300',
|
||||
isVisible ? 'sm:h-[36px] h-[30px]' : 'h-0',
|
||||
isVisible ? 'h-[30px] sm:h-[36px]' : 'h-0',
|
||||
)}
|
||||
>
|
||||
<a href="/courses/sql" className="flex items-center bg-yellow-400 py-1.5">
|
||||
@@ -26,10 +38,12 @@ export function CourseAnnouncement() {
|
||||
</span>
|
||||
<span className="block sm:hidden">Announcing our SQL course</span>
|
||||
</span>
|
||||
<span className={cn(
|
||||
"items-center gap-1.5 rounded-full bg-black px-2 py-0.5 text-xs font-medium uppercase tracking-wide text-white hover:bg-zinc-800 sm:px-3 sm:py-1",
|
||||
isVisible && "animate-wiggle [animation-delay:0.25s]"
|
||||
)}>
|
||||
<span
|
||||
className={cn(
|
||||
'items-center gap-1.5 rounded-full bg-black px-2 py-0.5 text-xs font-medium tracking-wide text-white uppercase hover:bg-zinc-800 sm:px-3 sm:py-1',
|
||||
isVisible && 'animate-wiggle [animation-delay:0.25s]',
|
||||
)}
|
||||
>
|
||||
<span className="mr-1.5 hidden sm:inline">Start Learning</span>
|
||||
<span className="mr-1.5 inline sm:hidden">Visit</span>
|
||||
<span className="">→</span>
|
||||
@@ -38,9 +52,16 @@ export function CourseAnnouncement() {
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-3.5 top-1/2 -translate-y-1/2 rounded-lg px-1.5 py-1.5 text-gray-500 hover:bg-yellow-500 hover:text-gray-700"
|
||||
className="absolute top-1/2 right-3.5 -translate-y-1/2 rounded-lg px-1.5 py-1.5 text-gray-500 hover:bg-yellow-500 hover:text-gray-700"
|
||||
onClick={(e) => {
|
||||
setIsVisible(false);
|
||||
|
||||
// 14 days from now
|
||||
const fourteenDaysFromNow = Date.now() + 1000 * 60 * 60 * 24 * 14;
|
||||
localStorage.setItem(
|
||||
HIDE_ANNOUNCEMENT_END_KEY,
|
||||
String(fourteenDaysFromNow),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
|
||||
30
src/components/SelectNative.tsx
Normal file
30
src/components/SelectNative.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ChevronDownIcon } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '../lib/classname';
|
||||
|
||||
export function SelectNative(props: React.ComponentProps<'select'>) {
|
||||
const { className, children, ...rest } = props;
|
||||
return (
|
||||
<div className="relative flex">
|
||||
<select
|
||||
data-slot="select-native"
|
||||
className={cn(
|
||||
'peer inline-flex w-full cursor-pointer appearance-none items-center rounded-lg border border-gray-200 text-sm text-black outline-none focus-visible:border-gray-500 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 has-[option[disabled]:checked]:text-gray-500 aria-invalid:border-red-500 aria-invalid:ring-red-500/20 dark:aria-invalid:ring-red-500/40',
|
||||
props.multiple
|
||||
? '[&_option:checked]:bg-accent py-1 *:px-3 *:py-1'
|
||||
: 'h-9 ps-3 pe-8',
|
||||
className,
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</select>
|
||||
{!props.multiple && (
|
||||
<span className="pointer-events-none absolute inset-y-0 end-0 flex h-full w-9 items-center justify-center text-gray-500/80 peer-disabled:opacity-50 peer-aria-invalid:text-red-500/80">
|
||||
<ChevronDownIcon size={16} aria-hidden="true" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { cn } from '../lib/classname.ts';
|
||||
type TabLinkProps = {
|
||||
icon: LucideIcon;
|
||||
text: string;
|
||||
mobileText?: string;
|
||||
isActive: boolean;
|
||||
isExternal?: boolean;
|
||||
badgeText?: string;
|
||||
@@ -19,6 +20,7 @@ export function TabLink(props: TabLinkProps) {
|
||||
isExternal = false,
|
||||
url,
|
||||
text,
|
||||
mobileText,
|
||||
isActive,
|
||||
hideTextOnMobile = false,
|
||||
className: additionalClassName = '',
|
||||
@@ -75,7 +77,8 @@ export function TabLink(props: TabLinkProps) {
|
||||
className={className}
|
||||
>
|
||||
<Icon className="h-4 w-4 shrink-0" />
|
||||
<span className={textClass}>{text}</span>
|
||||
<span className={cn(textClass, 'hidden sm:inline')}>{text}</span>
|
||||
<span className={cn(textClass, 'inline sm:hidden')}>{mobileText || text}</span>
|
||||
{badgeNode}
|
||||
</a>
|
||||
);
|
||||
|
||||
@@ -62,15 +62,6 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
const toast = useToast();
|
||||
const [renderer, setRenderer] = useState<PageType['renderer']>('balsamiq');
|
||||
|
||||
let resourceJsonUrl = import.meta.env.DEV
|
||||
? 'http://localhost:3000'
|
||||
: 'https://roadmap.sh';
|
||||
if (resourceType === 'roadmap') {
|
||||
resourceJsonUrl += `/${resourceId}.json`;
|
||||
} else {
|
||||
resourceJsonUrl += `/best-practices/${resourceId}.json`;
|
||||
}
|
||||
|
||||
async function getMemberProgress(
|
||||
teamId: string,
|
||||
memberId: string,
|
||||
@@ -92,7 +83,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
return response;
|
||||
}
|
||||
|
||||
async function renderResource(jsonUrl: string) {
|
||||
async function renderResource() {
|
||||
const page = await getResourceMeta(resourceType, resourceId);
|
||||
if (!page) {
|
||||
toast.error('Resource not found');
|
||||
@@ -102,11 +93,22 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
const renderer = page.renderer || 'balsamiq';
|
||||
setRenderer(renderer);
|
||||
|
||||
const res = await fetch(jsonUrl, {});
|
||||
let resourceJsonUrl = import.meta.env.DEV
|
||||
? 'http://localhost:3000'
|
||||
: 'https://roadmap.sh';
|
||||
if (resourceType === 'roadmap' && renderer === 'balsamiq') {
|
||||
resourceJsonUrl += `/${resourceId}.json`;
|
||||
} else if (resourceType === 'roadmap' && renderer === 'editor') {
|
||||
resourceJsonUrl = `${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${resourceId}`;
|
||||
} else {
|
||||
resourceJsonUrl += `/best-practices/${resourceId}.json`;
|
||||
}
|
||||
|
||||
const res = await fetch(resourceJsonUrl, {});
|
||||
const json = await res.json();
|
||||
const svg =
|
||||
renderer === 'editor'
|
||||
? await renderFlowJSON(json as any)
|
||||
? await renderFlowJSON(json)
|
||||
: await wireframeJSONToSVG(json, {
|
||||
fontURL: '/fonts/balsamiq.woff2',
|
||||
});
|
||||
@@ -129,19 +131,13 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!containerEl.current ||
|
||||
!resourceJsonUrl ||
|
||||
!resourceId ||
|
||||
!resourceType ||
|
||||
!teamId
|
||||
) {
|
||||
if (!containerEl.current || !resourceId || !resourceType || !teamId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
Promise.all([
|
||||
renderResource(resourceJsonUrl),
|
||||
renderResource(),
|
||||
getMemberProgress(teamId, member._id, resourceType, resourceId),
|
||||
])
|
||||
.then(([_, memberProgress = {}]) => {
|
||||
@@ -276,7 +272,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
}, [member]);
|
||||
|
||||
return (
|
||||
<div className="fixed left-0 right-0 top-0 z-100 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
|
||||
<div className="fixed top-0 right-0 left-0 z-100 h-full items-center justify-center overflow-x-hidden overflow-y-auto overscroll-contain bg-black/50">
|
||||
<div
|
||||
id={renderer === 'editor' ? undefined : 'customized-roadmap'}
|
||||
className="relative mx-auto h-full w-full max-w-4xl p-4 md:h-auto"
|
||||
@@ -304,14 +300,14 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
<div className="flex w-full justify-center">
|
||||
<Spinner
|
||||
isDualRing={false}
|
||||
className="mb-4 mt-2 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
||||
className="mt-2 mb-4 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`absolute right-2.5 top-3 ml-auto inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:text-gray-900 lg:hidden ${
|
||||
className={`absolute top-3 right-2.5 ml-auto inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:text-gray-900 lg:hidden ${
|
||||
isCurrentUser ? 'hover:bg-gray-800' : 'hover:bg-gray-100'
|
||||
}`}
|
||||
onClick={onClose}
|
||||
|
||||
100
src/components/ThankYou/ThankYouPage.tsx
Normal file
100
src/components/ThankYou/ThankYouPage.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getUrlParams } from '../../lib/browser';
|
||||
import { Spinner } from '../ReactIcons/Spinner';
|
||||
import { VerifyUpgrade } from '../Billing/VerifyUpgrade';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
|
||||
export function ThankYouPage() {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [nextPage, setNextPage] = useState<string | null>(null);
|
||||
const [shouldVerifyUpgrade, setShouldVerifyUpgrade] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const params = getUrlParams();
|
||||
const next = params?.next;
|
||||
const shouldVerifyUpgrade = params?.s === '1';
|
||||
if (!next) {
|
||||
window.location.href = '/';
|
||||
return;
|
||||
}
|
||||
|
||||
let decodedNextPage = decodeURIComponent(next);
|
||||
|
||||
if (decodedNextPage === '/courses/sql') {
|
||||
decodedNextPage = `${import.meta.env.PUBLIC_COURSE_APP_URL}/sql`;
|
||||
|
||||
window?.fireEvent({
|
||||
action: 'sql_course_purchase_complete',
|
||||
category: 'course',
|
||||
label: 'SQL Course Purchase Completed',
|
||||
});
|
||||
}
|
||||
|
||||
setNextPage(decodedNextPage);
|
||||
setIsLoading(false);
|
||||
setShouldVerifyUpgrade(shouldVerifyUpgrade);
|
||||
}, []);
|
||||
|
||||
const pageType = nextPage?.startsWith('/courses/')
|
||||
? 'course'
|
||||
: nextPage?.startsWith('/ai')
|
||||
? 'ai-tutor'
|
||||
: 'other';
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex flex-grow flex-col items-center justify-center py-20">
|
||||
<Spinner isDualRing={false} className="mb-5 h-7 w-7" />
|
||||
<p className="mb-1 text-xl font-medium">Please wait</p>
|
||||
<p className="text-gray-500">This may take a few seconds</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{shouldVerifyUpgrade && <VerifyUpgrade />}
|
||||
|
||||
<div className="flex flex-grow flex-col items-center justify-center px-4">
|
||||
<div className="flex max-w-2xl flex-col items-center text-center">
|
||||
<img
|
||||
src="/images/gifs/party-popper.gif"
|
||||
alt="Thank you"
|
||||
className="relative left-6 mb-6 aspect-square w-24"
|
||||
/>
|
||||
|
||||
<h1 className="mb-3 text-4xl font-bold text-gray-800 md:text-5xl">
|
||||
Thank you!
|
||||
</h1>
|
||||
|
||||
<p className="mb-8 text-lg text-gray-600 max-w-lg text-balance">
|
||||
Your transaction was successful and your access has been activated.
|
||||
</p>
|
||||
|
||||
{nextPage && (
|
||||
<a
|
||||
href={nextPage}
|
||||
className="group flex items-center gap-2 rounded-lg bg-purple-500 px-5 py-2.5 font-medium text-white transition-all hover:bg-blue-600"
|
||||
>
|
||||
{pageType === 'course'
|
||||
? 'Continue to Course'
|
||||
: pageType === 'ai-tutor'
|
||||
? 'Continue to AI Tutor'
|
||||
: 'Continue'}
|
||||
<ChevronRight className="h-4 w-4 transition-transform group-hover:translate-x-1" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-12 flex gap-4 text-sm text-gray-500">
|
||||
<a href="/terms" className="hover:text-gray-800">
|
||||
Terms of Use
|
||||
</a>
|
||||
<a href="/privacy" className="hover:text-gray-800">
|
||||
Privacy Policy
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
60
src/components/TopicDetail/CreateCourseModal.tsx
Normal file
60
src/components/TopicDetail/CreateCourseModal.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { WandSparkles } from 'lucide-react';
|
||||
import { Modal } from '../Modal';
|
||||
import { useState } from 'react';
|
||||
|
||||
type CreateCourseModalProps = {
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export function CreateCourseModal(props: CreateCourseModalProps) {
|
||||
const { onClose } = props;
|
||||
|
||||
const [subject, setSubject] = useState('');
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onClose={onClose}
|
||||
wrapperClassName="h-auto mt-20"
|
||||
overlayClassName="items-start"
|
||||
bodyClassName="p-1.5"
|
||||
>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target as HTMLFormElement);
|
||||
const subject = formData.get('subject');
|
||||
|
||||
window.location.href = `/ai/search?term=${subject}&difficulty=beginner&src=topic`;
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<label
|
||||
className="mb-2.5 ml-1 inline-block text-sm leading-none"
|
||||
htmlFor="subject"
|
||||
>
|
||||
Ask AI to Teach You
|
||||
</label>
|
||||
<div className="relative flex items-center gap-2 overflow-hidden">
|
||||
<input
|
||||
id="subject"
|
||||
type="text"
|
||||
className="w-full bg-white p-2.5 pr-8 text-sm focus:outline-hidden"
|
||||
placeholder="Enter a topic to learn"
|
||||
name="subject"
|
||||
autoFocus={true}
|
||||
value={subject}
|
||||
onChange={(e) => setSubject(e.target.value)}
|
||||
/>
|
||||
|
||||
<button
|
||||
disabled={!subject.trim()}
|
||||
className="flex h-full disabled:opacity-40 items-center justify-center gap-2 rounded-md bg-black px-3 py-1 text-sm text-white hover:opacity-80"
|
||||
>
|
||||
Generate
|
||||
<WandSparkles className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
54
src/components/TopicDetail/PredefinedActionGroup.tsx
Normal file
54
src/components/TopicDetail/PredefinedActionGroup.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import { useState, useRef } from 'react';
|
||||
import { useOutsideClick } from '../../hooks/use-outside-click';
|
||||
import {
|
||||
type PredefinedActionType,
|
||||
PredefinedActionButton,
|
||||
} from './PredefinedActions';
|
||||
|
||||
type PredefinedActionGroupProps = {
|
||||
label: string;
|
||||
icon: LucideIcon;
|
||||
actions: PredefinedActionType[];
|
||||
onSelect: (action: PredefinedActionType) => void;
|
||||
};
|
||||
|
||||
export function PredefinedActionGroup(props: PredefinedActionGroupProps) {
|
||||
const { label, icon: Icon, actions, onSelect } = props;
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useOutsideClick(containerRef, () => {
|
||||
setIsOpen(false);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="relative" ref={containerRef}>
|
||||
<PredefinedActionButton
|
||||
label={label}
|
||||
icon={Icon}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
isGroup={true}
|
||||
/>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute top-full left-0 z-20 mt-1 divide-y overflow-hidden rounded-md border border-gray-200 bg-white p-0">
|
||||
{actions.map((action) => {
|
||||
return (
|
||||
<PredefinedActionButton
|
||||
key={action.label}
|
||||
{...action}
|
||||
className="py-2 pl-2.5 pr-5 w-full rounded-none bg-transparent hover:bg-gray-200"
|
||||
onClick={() => {
|
||||
onSelect(action);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
144
src/components/TopicDetail/PredefinedActions.tsx
Normal file
144
src/components/TopicDetail/PredefinedActions.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
import {
|
||||
BabyIcon,
|
||||
BookOpenTextIcon,
|
||||
BrainIcon,
|
||||
ChevronDownIcon,
|
||||
ListIcon,
|
||||
NotebookPenIcon,
|
||||
PencilLine,
|
||||
Star,
|
||||
type LucideIcon
|
||||
} from 'lucide-react';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { PredefinedActionGroup } from './PredefinedActionGroup';
|
||||
|
||||
export type PredefinedActionType = {
|
||||
icon: LucideIcon;
|
||||
label: string;
|
||||
prompt?: string;
|
||||
children?: PredefinedActionType[];
|
||||
};
|
||||
|
||||
export const actions: PredefinedActionType[] = [
|
||||
{
|
||||
icon: BookOpenTextIcon,
|
||||
label: 'Explain',
|
||||
children: [
|
||||
{
|
||||
icon: NotebookPenIcon,
|
||||
label: 'Explain the topic',
|
||||
prompt: 'Explain this topic in detail and include examples',
|
||||
},
|
||||
{
|
||||
icon: ListIcon,
|
||||
label: 'List the key points',
|
||||
prompt: 'List the key points to remember from this topic',
|
||||
},
|
||||
{
|
||||
icon: PencilLine,
|
||||
label: 'Summarize the topic',
|
||||
prompt:
|
||||
'Briefly explain the topic in a few sentences. Treat it as a brief answer to an interview question. Your response should just be the answer to the question, nothing else.',
|
||||
},
|
||||
{
|
||||
icon: BabyIcon,
|
||||
label: 'Explain like I am five',
|
||||
prompt: 'Explain this topic like I am a 5 years old',
|
||||
},
|
||||
{
|
||||
icon: Star,
|
||||
label: 'Why is it important?',
|
||||
prompt:
|
||||
'Why is this topic important? What are the real world applications (only add if appropriate)?',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: BrainIcon,
|
||||
label: 'Test my Knowledge',
|
||||
prompt:
|
||||
"Act as an interviewer and test my understanding of this topic. Ask me a single question at a time and evaluate my answer. Question number should be bold. After evaluating my answer, immediately proceed to the next question without asking if I'm ready or want another question. Continue asking questions until I explicitly tell you to stop.",
|
||||
},
|
||||
];
|
||||
|
||||
export const promptLabelMapping = actions.reduce(
|
||||
(acc, action) => {
|
||||
if (action.prompt) {
|
||||
acc[action.prompt] = action.label;
|
||||
}
|
||||
|
||||
if (action.children) {
|
||||
action.children.forEach((child) => {
|
||||
if (child.prompt) {
|
||||
acc[child.prompt] = child.label;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
|
||||
type PredefinedActionsProps = {
|
||||
onSelect: (action: PredefinedActionType) => void;
|
||||
};
|
||||
|
||||
export function PredefinedActions(props: PredefinedActionsProps) {
|
||||
const { onSelect } = props;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2 border-gray-200 px-3 py-1 text-sm">
|
||||
{actions.map((action) => {
|
||||
if (!action.children) {
|
||||
return (
|
||||
<PredefinedActionButton
|
||||
key={action.label}
|
||||
icon={action.icon}
|
||||
label={action.label}
|
||||
onClick={() => {
|
||||
onSelect(action);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PredefinedActionGroup
|
||||
key={action.label}
|
||||
label={action.label}
|
||||
icon={action.icon}
|
||||
actions={action.children}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type PredefinedActionButtonProps = {
|
||||
label: string;
|
||||
icon?: LucideIcon;
|
||||
onClick: () => void;
|
||||
isGroup?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function PredefinedActionButton(props: PredefinedActionButtonProps) {
|
||||
const { label, icon: Icon, onClick, isGroup = false, className } = props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
'flex shrink-0 items-center gap-1.5 rounded-md bg-gray-200 px-2 py-1 text-sm whitespace-nowrap hover:bg-gray-300',
|
||||
className,
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{Icon && <Icon className="mr-1 size-3.5" />}
|
||||
{label}
|
||||
{isGroup && <ChevronDownIcon className="size-3.5" />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export function ResourceListSeparator(props: ResourceSeparatorProps) {
|
||||
{Icon && <Icon className="inline-block h-3 w-3 fill-current" />}
|
||||
{text}
|
||||
</span>
|
||||
<hr className="absolute inset-x-0 grow border-current" />
|
||||
<span className="absolute inset-x-0 h-px w-full grow bg-current" />
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
updateResourceProgress as updateResourceProgressApi,
|
||||
} from '../../lib/resource-progress';
|
||||
import { pageProgressMessage } from '../../stores/page';
|
||||
import { TopicProgressButton } from './TopicProgressButton';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import type {
|
||||
@@ -22,26 +21,28 @@ import type {
|
||||
RoadmapContentDocument,
|
||||
} from '../CustomRoadmap/CustomRoadmap';
|
||||
import { markdownToHtml, sanitizeMarkdown } from '../../lib/markdown';
|
||||
import { Ban, Coins, FileText, HeartHandshake, Star, X } from 'lucide-react';
|
||||
import { Ban, FileText, HeartHandshake, Star, X } from 'lucide-react';
|
||||
import { getUrlParams, parseUrl } from '../../lib/browser';
|
||||
import { Spinner } from '../ReactIcons/Spinner';
|
||||
import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx';
|
||||
import { GoogleIcon } from '../ReactIcons/GoogleIcon.tsx';
|
||||
import { YouTubeIcon } from '../ReactIcons/YouTubeIcon.tsx';
|
||||
import { resourceTitleFromId } from '../../lib/roadmap.ts';
|
||||
import {
|
||||
resourceTitleFromId,
|
||||
type AllowedRoadmapRenderer,
|
||||
} from '../../lib/roadmap.ts';
|
||||
import { lockBodyScroll } from '../../lib/dom.ts';
|
||||
import { TopicDetailLink } from './TopicDetailLink.tsx';
|
||||
import { ResourceListSeparator } from './ResourceListSeparator.tsx';
|
||||
import { PaidResourceDisclaimer } from './PaidResourceDisclaimer.tsx';
|
||||
|
||||
type TopicDetailProps = {
|
||||
resourceId?: string;
|
||||
resourceTitle?: string;
|
||||
resourceType?: ResourceType;
|
||||
|
||||
isEmbed?: boolean;
|
||||
canSubmitContribution: boolean;
|
||||
};
|
||||
import {
|
||||
TopicDetailsTabs,
|
||||
type AllowedTopicDetailsTabs,
|
||||
} from './TopicDetailsTabs.tsx';
|
||||
import { TopicDetailAI } from './TopicDetailAI.tsx';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
import type { AIChatHistoryType } from '../GenerateCourse/AICourseLessonChat.tsx';
|
||||
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal.tsx';
|
||||
import { TopicProgressButton } from './TopicProgressButton.tsx';
|
||||
import { CreateCourseModal } from './CreateCourseModal.tsx';
|
||||
|
||||
type PaidResourceType = {
|
||||
_id?: string;
|
||||
@@ -53,6 +54,14 @@ type PaidResourceType = {
|
||||
|
||||
const paidResourcesCache: Record<string, PaidResourceType[]> = {};
|
||||
|
||||
export const defaultChatHistory: AIChatHistoryType[] = [
|
||||
{
|
||||
role: 'assistant',
|
||||
content: 'Hey, I am your AI instructor. How can I help you today? 🤖',
|
||||
isDefault: true,
|
||||
},
|
||||
];
|
||||
|
||||
async function fetchRoadmapPaidResources(roadmapId: string) {
|
||||
if (paidResourcesCache[roadmapId]) {
|
||||
return paidResourcesCache[roadmapId];
|
||||
@@ -74,12 +83,41 @@ async function fetchRoadmapPaidResources(roadmapId: string) {
|
||||
|
||||
const PAID_RESOURCE_DISCLAIMER_HIDDEN = 'paid-resource-disclaimer-hidden';
|
||||
|
||||
type TopicDetailProps = {
|
||||
resourceId?: string;
|
||||
resourceType?: ResourceType;
|
||||
renderer?: AllowedRoadmapRenderer;
|
||||
defaultActiveTab?: AllowedTopicDetailsTabs;
|
||||
|
||||
hasUpgradeButtons?: boolean;
|
||||
|
||||
isEmbed?: boolean;
|
||||
canSubmitContribution: boolean;
|
||||
|
||||
wrapperClassName?: string;
|
||||
bodyClassName?: string;
|
||||
overlayClassName?: string;
|
||||
closeButtonClassName?: string;
|
||||
onClose?: () => void;
|
||||
shouldCloseOnBackdropClick?: boolean;
|
||||
shouldCloseOnEscape?: boolean;
|
||||
};
|
||||
|
||||
export function TopicDetail(props: TopicDetailProps) {
|
||||
const {
|
||||
hasUpgradeButtons = true,
|
||||
canSubmitContribution,
|
||||
resourceId: defaultResourceId,
|
||||
isEmbed = false,
|
||||
resourceTitle,
|
||||
renderer = 'balsamiq',
|
||||
wrapperClassName,
|
||||
bodyClassName,
|
||||
overlayClassName,
|
||||
closeButtonClassName,
|
||||
onClose,
|
||||
shouldCloseOnBackdropClick = true,
|
||||
shouldCloseOnEscape = true,
|
||||
defaultActiveTab = 'content',
|
||||
} = props;
|
||||
|
||||
const [hasEnoughLinks, setHasEnoughLinks] = useState(false);
|
||||
@@ -93,6 +131,15 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
const [topicTitle, setTopicTitle] = useState('');
|
||||
const [topicHtmlTitle, setTopicHtmlTitle] = useState('');
|
||||
const [links, setLinks] = useState<RoadmapContentDocument['links']>([]);
|
||||
const [activeTab, setActiveTab] =
|
||||
useState<AllowedTopicDetailsTabs>(defaultActiveTab);
|
||||
const [aiChatHistory, setAiChatHistory] =
|
||||
useState<AIChatHistoryType[]>(defaultChatHistory);
|
||||
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
||||
const [isCustomResource, setIsCustomResource] = useState(false);
|
||||
|
||||
const [showSubjectSearchModal, setShowSubjectSearchModal] = useState(false);
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const [showPaidResourceDisclaimer, setShowPaidResourceDisclaimer] =
|
||||
@@ -108,14 +155,21 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
const [resourceType, setResourceType] = useState<ResourceType>('roadmap');
|
||||
const [paidResources, setPaidResources] = useState<PaidResourceType[]>([]);
|
||||
|
||||
// Close the topic detail when user clicks outside the topic detail
|
||||
useOutsideClick(topicRef, () => {
|
||||
const handleClose = () => {
|
||||
onClose?.();
|
||||
setIsActive(false);
|
||||
});
|
||||
setShowUpgradeModal(false);
|
||||
setAiChatHistory(defaultChatHistory);
|
||||
setActiveTab('content');
|
||||
setShowSubjectSearchModal(false);
|
||||
};
|
||||
|
||||
useKeydown('Escape', () => {
|
||||
setIsActive(false);
|
||||
});
|
||||
// Close the topic detail when user clicks outside the topic detail
|
||||
useOutsideClick(
|
||||
topicRef,
|
||||
shouldCloseOnBackdropClick ? handleClose : undefined,
|
||||
);
|
||||
useKeydown('Escape', shouldCloseOnEscape ? handleClose : undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (resourceType !== 'roadmap' || !defaultResourceId) {
|
||||
@@ -179,6 +233,7 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
setTopicId(topicId);
|
||||
setResourceType(resourceType);
|
||||
setResourceId(resourceId);
|
||||
setIsCustomResource(isCustomResource);
|
||||
|
||||
const topicPartial = topicId.replaceAll(':', '/');
|
||||
let topicUrl =
|
||||
@@ -283,12 +338,17 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
}
|
||||
|
||||
topicHtml = topicDom.body.innerHTML;
|
||||
const topicHasContent = otherElems.length > 0;
|
||||
|
||||
setLinks(listLinks);
|
||||
setHasContent(otherElems.length > 0);
|
||||
setHasContent(topicHasContent);
|
||||
setContributionUrl(contributionUrl);
|
||||
setHasEnoughLinks(links.length >= 3);
|
||||
setTopicHtmlTitle(titleElem?.textContent || '');
|
||||
|
||||
if (!topicHasContent && renderer === 'editor') {
|
||||
setActiveTab('ai');
|
||||
}
|
||||
} else {
|
||||
setLinks((response as RoadmapContentDocument)?.links || []);
|
||||
setTopicTitle((response as RoadmapContentDocument)?.title || '');
|
||||
@@ -311,7 +371,9 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isActive) topicRef?.current?.focus();
|
||||
if (isActive) {
|
||||
topicRef?.current?.focus();
|
||||
}
|
||||
|
||||
lockBodyScroll(isActive);
|
||||
}, [isActive]);
|
||||
@@ -320,12 +382,6 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const resourceTitleForSearch = resourceTitle
|
||||
?.toLowerCase()
|
||||
?.replace(/\s+?roadmap/gi, '');
|
||||
const googleSearchUrl = `https://www.google.com/search?q=${encodeURIComponent(topicHtmlTitle?.toLowerCase())} guide for ${encodeURIComponent(resourceTitleForSearch || '')}`;
|
||||
const youtubeSearchUrl = `https://www.youtube.com/results?search_query=${encodeURIComponent(topicHtmlTitle?.toLowerCase())} for ${encodeURIComponent(resourceTitleForSearch || '')}`;
|
||||
|
||||
const tnsLink =
|
||||
'https://thenewstack.io/devops/?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Topic';
|
||||
|
||||
@@ -335,19 +391,28 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
return resource.topicIds.includes(normalizedTopicId);
|
||||
});
|
||||
|
||||
const hasPaidScrimbaLinks = paidResourcesForTopic.some(
|
||||
(resource) => resource?.url?.toLowerCase().indexOf('scrimba') !== -1,
|
||||
);
|
||||
const shouldShowAiTab = !isCustomResource && resourceType === 'roadmap';
|
||||
|
||||
return (
|
||||
<div className={'relative z-92'}>
|
||||
<div className={cn('relative z-92', wrapperClassName)}>
|
||||
<div
|
||||
ref={topicRef}
|
||||
tabIndex={0}
|
||||
className="fixed right-0 top-0 z-40 flex h-screen w-full flex-col overflow-y-auto bg-white p-4 focus:outline-0 sm:max-w-[600px] sm:p-6"
|
||||
className={cn(
|
||||
'fixed top-0 right-0 z-40 flex h-screen w-full flex-col overflow-y-auto bg-white p-4 focus:outline-0 sm:max-w-[600px] sm:p-6',
|
||||
bodyClassName,
|
||||
)}
|
||||
>
|
||||
{showUpgradeModal && (
|
||||
<UpgradeAccountModal onClose={() => setShowUpgradeModal(false)} />
|
||||
)}
|
||||
|
||||
{showSubjectSearchModal && (
|
||||
<CreateCourseModal onClose={() => setShowSubjectSearchModal(false)} />
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<div className="flex h-full w-full justify-center">
|
||||
<Spinner
|
||||
outerFill="#d1d5db"
|
||||
className="h-6 w-6 sm:h-8 sm:w-8"
|
||||
@@ -359,256 +424,241 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
|
||||
{!isContributing && !isLoading && !error && (
|
||||
<>
|
||||
<div className="flex-1">
|
||||
{/* Actions for the topic */}
|
||||
<div className="mb-2">
|
||||
{!isEmbed && (
|
||||
<TopicProgressButton
|
||||
topicId={
|
||||
topicId.indexOf('@') !== -1
|
||||
? topicId.split('@')[1]
|
||||
: topicId
|
||||
<div
|
||||
className={cn('flex-1', {
|
||||
'flex flex-col': activeTab === 'ai',
|
||||
})}
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
{shouldShowAiTab && (
|
||||
<TopicDetailsTabs
|
||||
activeTab={activeTab}
|
||||
setActiveTab={setActiveTab}
|
||||
hasAITutor={renderer === 'editor'}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={cn('flex flex-grow justify-end gap-1', {
|
||||
'justify-between': !shouldShowAiTab,
|
||||
})}
|
||||
>
|
||||
{!isEmbed && (
|
||||
<TopicProgressButton
|
||||
topicId={
|
||||
topicId.indexOf('@') !== -1
|
||||
? topicId.split('@')[1]
|
||||
: topicId
|
||||
}
|
||||
dropdownClassName={
|
||||
!shouldShowAiTab ? 'left-0' : 'right-0'
|
||||
}
|
||||
resourceId={resourceId}
|
||||
resourceType={resourceType}
|
||||
onClose={() => null}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
id="close-topic"
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 rounded-lg bg-gray-200 px-1.5 py-1 text-xs text-black hover:bg-gray-300 hover:text-gray-900',
|
||||
closeButtonClassName,
|
||||
)}
|
||||
onClick={handleClose}
|
||||
>
|
||||
<X className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{activeTab === 'ai' && shouldShowAiTab && (
|
||||
<TopicDetailAI
|
||||
resourceId={resourceId}
|
||||
resourceType={resourceType}
|
||||
topicId={topicId}
|
||||
aiChatHistory={aiChatHistory}
|
||||
setAiChatHistory={setAiChatHistory}
|
||||
hasUpgradeButtons={hasUpgradeButtons}
|
||||
onUpgrade={() => setShowUpgradeModal(true)}
|
||||
onLogin={() => {
|
||||
handleClose();
|
||||
showLoginPopup();
|
||||
}}
|
||||
onShowSubjectSearchModal={() => {
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
resourceId={resourceId}
|
||||
resourceType={resourceType}
|
||||
onClose={() => {
|
||||
setIsActive(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
id="close-topic"
|
||||
className="absolute right-2.5 top-2.5 inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900"
|
||||
onClick={() => {
|
||||
setIsActive(false);
|
||||
setShowSubjectSearchModal(true);
|
||||
}}
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Topic Content */}
|
||||
{hasContent ? (
|
||||
<>
|
||||
<div className="prose prose-quoteless prose-h1:mb-2.5 prose-h1:mt-7 prose-h1:text-balance prose-h2:mb-3 prose-h2:mt-0 prose-h3:mb-[5px] prose-h3:mt-[10px] prose-p:mb-2 prose-p:mt-0 prose-blockquote:font-normal prose-blockquote:not-italic prose-blockquote:text-gray-700 prose-li:m-0 prose-li:mb-0.5">
|
||||
{topicTitle && <h1>{topicTitle}</h1>}
|
||||
<div
|
||||
id="topic-content"
|
||||
dangerouslySetInnerHTML={{ __html: topicHtml }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{!canSubmitContribution && (
|
||||
<div className="flex h-[calc(100%-38px)] flex-col items-center justify-center">
|
||||
<FileText className="h-16 w-16 text-gray-300" />
|
||||
<p className="mt-2 text-lg font-medium text-gray-500">
|
||||
Empty Content
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{canSubmitContribution && (
|
||||
<div className="mx-auto flex h-[calc(100%-38px)] max-w-[400px] flex-col items-center justify-center text-center">
|
||||
<HeartHandshake className="mb-2 h-16 w-16 text-gray-300" />
|
||||
<p className="text-lg font-semibold text-gray-900">
|
||||
Help us write this content
|
||||
</p>
|
||||
<p className="mb-3 mt-2 text-sm text-gray-500">
|
||||
Write a brief introduction to this topic and submit a
|
||||
link to a good article, podcast, video, or any other
|
||||
self-vetted resource that helped you understand this
|
||||
topic better.
|
||||
</p>
|
||||
<a
|
||||
href={contributionUrl}
|
||||
target={'_blank'}
|
||||
className="flex w-full items-center justify-center rounded-md bg-gray-800 p-2 text-sm text-white transition-colors hover:bg-black hover:text-white disabled:bg-green-200 disabled:text-black"
|
||||
>
|
||||
<GitHubIcon className="mr-2 inline-block h-4 w-4 text-white" />
|
||||
Help us Write this Content
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
/>
|
||||
)}
|
||||
|
||||
{links.length > 0 && (
|
||||
{activeTab === 'content' && (
|
||||
<>
|
||||
<ResourceListSeparator
|
||||
text="Free Resources"
|
||||
className="text-green-600"
|
||||
icon={HeartHandshake}
|
||||
/>
|
||||
<ul className="ml-3 mt-4 space-y-1">
|
||||
{links.map((link) => {
|
||||
return (
|
||||
<li key={link.id}>
|
||||
<TopicDetailLink
|
||||
url={link.url}
|
||||
type={link.type}
|
||||
title={link.title}
|
||||
onClick={() => {
|
||||
// if it is one of our roadmaps, we want to track the click
|
||||
if (canSubmitContribution) {
|
||||
const parsedUrl = parseUrl(link.url);
|
||||
|
||||
window.fireEvent({
|
||||
category: 'TopicResourceClick',
|
||||
action: `Click: ${parsedUrl.hostname}`,
|
||||
label: `${resourceType} / ${resourceId} / ${topicId} / ${link.url}`,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
|
||||
{paidResourcesForTopic.length > 0 && (
|
||||
<>
|
||||
<ResourceListSeparator text="Premium Resources" icon={Star} />
|
||||
|
||||
<ul className="ml-3 mt-3 space-y-1">
|
||||
{paidResourcesForTopic.map((resource) => {
|
||||
return (
|
||||
<li key={resource._id}>
|
||||
<TopicDetailLink
|
||||
url={resource.url}
|
||||
type={resource.type as any}
|
||||
title={resource.title}
|
||||
isPaid={true}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
{hasPaidScrimbaLinks && (
|
||||
<div className="relative -mb-1 ml-3 mt-4 rounded-md border border-yellow-300 bg-yellow-100 px-2.5 py-2 text-sm text-yellow-800">
|
||||
<div className="flex items-center gap-2">
|
||||
<Coins className="h-4 w-4 text-yellow-700" />
|
||||
<span>
|
||||
Scrimba is offering{' '}
|
||||
<span className={'font-semibold'}>20% off</span> on
|
||||
all courses for roadmap.sh users.
|
||||
</span>
|
||||
{hasContent ? (
|
||||
<>
|
||||
<div className="prose prose-quoteless prose-h1:mb-2.5 prose-h1:mt-7 prose-h1:text-balance prose-h2:mb-3 prose-h2:mt-0 prose-h3:mb-[5px] prose-h3:mt-[10px] prose-p:mb-2 prose-p:mt-0 prose-blockquote:font-normal prose-blockquote:not-italic prose-blockquote:text-gray-700 prose-li:m-0 prose-li:mb-0.5">
|
||||
{topicTitle && <h1>{topicTitle}</h1>}
|
||||
<div
|
||||
id="topic-content"
|
||||
dangerouslySetInnerHTML={{ __html: topicHtml }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{!canSubmitContribution && (
|
||||
<div className="flex h-[calc(100%-38px)] flex-col items-center justify-center">
|
||||
<FileText className="h-16 w-16 text-gray-300" />
|
||||
<p className="mt-2 text-lg font-medium text-gray-500">
|
||||
Empty Content
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{canSubmitContribution && (
|
||||
<div className="mx-auto flex h-[calc(100%-38px)] max-w-[400px] flex-col items-center justify-center text-center">
|
||||
<HeartHandshake className="mb-2 h-16 w-16 text-gray-300" />
|
||||
<p className="text-lg font-semibold text-gray-900">
|
||||
Help us write this content
|
||||
</p>
|
||||
<p className="mt-2 mb-3 text-sm text-gray-500">
|
||||
Write a brief introduction to this topic and submit
|
||||
a link to a good article, podcast, video, or any
|
||||
other self-vetted resource that helped you
|
||||
understand this topic better.
|
||||
</p>
|
||||
<a
|
||||
href={contributionUrl}
|
||||
target={'_blank'}
|
||||
className="flex w-full items-center justify-center rounded-md bg-gray-800 p-2 text-sm text-white transition-colors hover:bg-black hover:text-white disabled:bg-green-200 disabled:text-black"
|
||||
>
|
||||
<GitHubIcon className="mr-2 inline-block h-4 w-4 text-white" />
|
||||
Help us Write this Content
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{showPaidResourceDisclaimer && (
|
||||
<PaidResourceDisclaimer
|
||||
onClose={() => {
|
||||
localStorage.setItem(
|
||||
PAID_RESOURCE_DISCLAIMER_HIDDEN,
|
||||
'true',
|
||||
);
|
||||
setShowPaidResourceDisclaimer(false);
|
||||
}}
|
||||
/>
|
||||
{links.length > 0 && (
|
||||
<>
|
||||
<ResourceListSeparator
|
||||
text="Free Resources"
|
||||
className="text-green-600"
|
||||
icon={HeartHandshake}
|
||||
/>
|
||||
<ul className="mt-4 ml-3 space-y-1">
|
||||
{links.map((link) => {
|
||||
return (
|
||||
<li key={link.id}>
|
||||
<TopicDetailLink
|
||||
url={link.url}
|
||||
type={link.type}
|
||||
title={link.title}
|
||||
onClick={() => {
|
||||
// if it is one of our roadmaps, we want to track the click
|
||||
if (canSubmitContribution) {
|
||||
const parsedUrl = parseUrl(link.url);
|
||||
|
||||
window.fireEvent({
|
||||
category: 'TopicResourceClick',
|
||||
action: `Click: ${parsedUrl.hostname}`,
|
||||
label: `${resourceType} / ${resourceId} / ${topicId} / ${link.url}`,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
|
||||
{paidResourcesForTopic.length > 0 && (
|
||||
<>
|
||||
<ResourceListSeparator
|
||||
text="Premium Resources"
|
||||
icon={Star}
|
||||
/>
|
||||
|
||||
<ul className="mt-3 ml-3 space-y-1">
|
||||
{paidResourcesForTopic.map((resource) => {
|
||||
return (
|
||||
<li key={resource._id}>
|
||||
<TopicDetailLink
|
||||
url={resource.url}
|
||||
type={resource.type as any}
|
||||
title={resource.title}
|
||||
isPaid={true}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
{showPaidResourceDisclaimer && (
|
||||
<PaidResourceDisclaimer
|
||||
onClose={() => {
|
||||
localStorage.setItem(
|
||||
PAID_RESOURCE_DISCLAIMER_HIDDEN,
|
||||
'true',
|
||||
);
|
||||
setShowPaidResourceDisclaimer(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Contribution */}
|
||||
{canSubmitContribution &&
|
||||
!hasEnoughLinks &&
|
||||
contributionUrl &&
|
||||
hasContent && (
|
||||
<div className="mb-12 mt-3 border-t text-sm text-gray-400 sm:mt-12">
|
||||
<div className="mb-4 mt-3">
|
||||
<p className="">
|
||||
Find more resources using these pre-filled search
|
||||
queries:
|
||||
</p>
|
||||
<div className="mt-3 flex gap-2 text-gray-700">
|
||||
<a
|
||||
href={googleSearchUrl}
|
||||
target="_blank"
|
||||
className="flex items-center gap-2 rounded-md border border-gray-300 px-3 py-1.5 pl-2 text-xs hover:border-gray-700 hover:bg-gray-100"
|
||||
>
|
||||
<GoogleIcon className={'h-4 w-4'} />
|
||||
Google
|
||||
</a>
|
||||
<a
|
||||
href={youtubeSearchUrl}
|
||||
target="_blank"
|
||||
className="flex items-center gap-2 rounded-md border border-gray-300 px-3 py-1.5 pl-2 text-xs hover:border-gray-700 hover:bg-gray-100"
|
||||
>
|
||||
<YouTubeIcon className={'h-4 w-4 text-red-500'} />
|
||||
YouTube
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="mb-2 mt-2 leading-relaxed">
|
||||
This popup should be a brief introductory paragraph for
|
||||
the topic and a few links to good articles, videos, or any
|
||||
other self-vetted resources. Please consider submitting a
|
||||
PR to improve this content.
|
||||
</p>
|
||||
<a
|
||||
href={contributionUrl}
|
||||
target={'_blank'}
|
||||
className="flex w-full items-center justify-center rounded-md bg-gray-800 p-2 text-sm text-white transition-colors hover:bg-black hover:text-white disabled:bg-green-200 disabled:text-black"
|
||||
>
|
||||
<GitHubIcon className="mr-2 inline-block h-4 w-4 text-white" />
|
||||
Help us Improve this Content
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{resourceId === 'devops' && (
|
||||
<div className="mt-4">
|
||||
<a
|
||||
href={tnsLink}
|
||||
target="_blank"
|
||||
className="hidden rounded-md border bg-gray-200 px-2 py-2 text-sm hover:bg-gray-300 sm:block"
|
||||
>
|
||||
<span className="badge mr-1.5">Partner</span>
|
||||
Get the latest {resourceTitleFromId(resourceId)} news from our
|
||||
sister site{' '}
|
||||
<span className="font-medium underline underline-offset-1">
|
||||
TheNewStack.io
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={tnsLink}
|
||||
className="hidden rounded-md border bg-gray-200 px-2 py-1.5 text-sm hover:bg-gray-300 min-[390px]:block sm:hidden"
|
||||
onClick={() => {
|
||||
window.fireEvent({
|
||||
category: 'PartnerClick',
|
||||
action: 'TNS Redirect',
|
||||
label: 'Roadmap Topic / TNS Link',
|
||||
});
|
||||
}}
|
||||
>
|
||||
<span className="badge mr-1.5">Partner</span>
|
||||
Visit{' '}
|
||||
<span className="font-medium underline underline-offset-1">
|
||||
TheNewStack.io
|
||||
</span>{' '}
|
||||
for {resourceTitleFromId(resourceId)} news
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
{canSubmitContribution &&
|
||||
contributionUrl &&
|
||||
activeTab === 'content' &&
|
||||
hasContent && (
|
||||
<div className="mt-4">
|
||||
<a
|
||||
href={contributionUrl}
|
||||
target="_blank"
|
||||
className="hidden items-center justify-center rounded-md px-2 py-2 text-sm transition-all hover:bg-gray-200 sm:flex"
|
||||
>
|
||||
<GitHubIcon className="mr-2 inline-block h-4 w-4 text-current" />
|
||||
Help us Improve this Content
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={tnsLink}
|
||||
className="hidden rounded-md border bg-gray-200 px-2 py-1.5 text-sm hover:bg-gray-300 min-[390px]:block sm:hidden"
|
||||
onClick={() => {
|
||||
window.fireEvent({
|
||||
category: 'PartnerClick',
|
||||
action: 'TNS Redirect',
|
||||
label: 'Roadmap Topic / TNS Link',
|
||||
});
|
||||
}}
|
||||
>
|
||||
<span className="badge mr-1.5">Partner</span>
|
||||
Visit{' '}
|
||||
<span className="font-medium underline underline-offset-1">
|
||||
TheNewStack.io
|
||||
</span>{' '}
|
||||
for {resourceTitleFromId(resourceId)} news
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Error */}
|
||||
{!isContributing && !isLoading && error && (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
id="close-topic"
|
||||
className="absolute right-2.5 top-2.5 inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900"
|
||||
className="absolute top-2.5 right-2.5 inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900"
|
||||
onClick={() => {
|
||||
setIsActive(false);
|
||||
setIsContributing(false);
|
||||
@@ -623,7 +673,9 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="fixed inset-0 z-30 bg-gray-900/50"></div>
|
||||
<div
|
||||
className={cn('fixed inset-0 z-30 bg-gray-900/50', overlayClassName)}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
526
src/components/TopicDetail/TopicDetailAI.tsx
Normal file
526
src/components/TopicDetail/TopicDetailAI.tsx
Normal file
@@ -0,0 +1,526 @@
|
||||
import '../GenerateCourse/AICourseLessonChat.css';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useState, useRef, Fragment, useCallback, useEffect } from 'react';
|
||||
import { billingDetailsOptions } from '../../queries/billing';
|
||||
import { getAiCourseLimitOptions } from '../../queries/ai-course';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { isLoggedIn, removeAuthToken } from '../../lib/jwt';
|
||||
import {
|
||||
BotIcon,
|
||||
ChevronRightIcon,
|
||||
Gift,
|
||||
Loader2Icon,
|
||||
LockIcon, SendIcon, Trash2,
|
||||
WandSparkles
|
||||
} from 'lucide-react';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { cn } from '../../lib/classname';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { flushSync } from 'react-dom';
|
||||
import {
|
||||
AIChatCard,
|
||||
type AIChatHistoryType,
|
||||
} from '../GenerateCourse/AICourseLessonChat';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { readStream } from '../../lib/ai';
|
||||
import { markdownToHtmlWithHighlighting } from '../../lib/markdown';
|
||||
import type { ResourceType } from '../../lib/resource-progress';
|
||||
import { getPercentage } from '../../lib/number';
|
||||
import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree';
|
||||
import { defaultChatHistory } from './TopicDetail';
|
||||
import { AILimitsPopup } from '../GenerateCourse/AILimitsPopup';
|
||||
import { PredefinedActions, promptLabelMapping } from './PredefinedActions';
|
||||
|
||||
type TopicDetailAIProps = {
|
||||
resourceId: string;
|
||||
resourceType: ResourceType;
|
||||
topicId: string;
|
||||
|
||||
hasUpgradeButtons?: boolean;
|
||||
|
||||
aiChatHistory: AIChatHistoryType[];
|
||||
setAiChatHistory: (history: AIChatHistoryType[]) => void;
|
||||
|
||||
onUpgrade: () => void;
|
||||
onLogin: () => void;
|
||||
|
||||
onShowSubjectSearchModal: () => void;
|
||||
};
|
||||
|
||||
export function TopicDetailAI(props: TopicDetailAIProps) {
|
||||
const {
|
||||
aiChatHistory,
|
||||
setAiChatHistory,
|
||||
resourceId,
|
||||
resourceType,
|
||||
topicId,
|
||||
hasUpgradeButtons = true,
|
||||
onUpgrade,
|
||||
onLogin,
|
||||
onShowSubjectSearchModal,
|
||||
} = props;
|
||||
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const scrollareaRef = useRef<HTMLDivElement>(null);
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
|
||||
const sanitizedTopicId = topicId?.includes('@')
|
||||
? topicId?.split('@')?.[1]
|
||||
: topicId;
|
||||
|
||||
const toast = useToast();
|
||||
const [message, setMessage] = useState('');
|
||||
const [isStreamingMessage, setIsStreamingMessage] = useState(false);
|
||||
const [streamedMessage, setStreamedMessage] = useState('');
|
||||
const [showAILimitsPopup, setShowAILimitsPopup] = useState(false);
|
||||
const { data: tokenUsage, isLoading } = useQuery(
|
||||
getAiCourseLimitOptions(),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const { data: userBillingDetails, isLoading: isBillingDetailsLoading } =
|
||||
useQuery(billingDetailsOptions(), queryClient);
|
||||
|
||||
const { data: roadmapTreeMapping, isLoading: isRoadmapTreeMappingLoading } =
|
||||
useQuery(
|
||||
{
|
||||
...roadmapTreeMappingOptions(resourceId),
|
||||
select: (data) => {
|
||||
const node = data.find(
|
||||
(mapping) => mapping.nodeId === sanitizedTopicId,
|
||||
);
|
||||
return node;
|
||||
},
|
||||
},
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const isLimitExceeded = (tokenUsage?.used || 0) >= (tokenUsage?.limit || 0);
|
||||
const isPaidUser = userBillingDetails?.status === 'active';
|
||||
|
||||
const handleChatSubmit = (overrideMessage?: string) => {
|
||||
const trimmedMessage = (overrideMessage ?? message).trim();
|
||||
|
||||
if (
|
||||
!trimmedMessage ||
|
||||
isStreamingMessage ||
|
||||
!isLoggedIn() ||
|
||||
isLimitExceeded ||
|
||||
isLoading
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newMessages: AIChatHistoryType[] = [
|
||||
...aiChatHistory,
|
||||
{
|
||||
role: 'user',
|
||||
content: trimmedMessage,
|
||||
},
|
||||
];
|
||||
|
||||
flushSync(() => {
|
||||
setAiChatHistory(newMessages);
|
||||
setMessage('');
|
||||
});
|
||||
|
||||
scrollToBottom();
|
||||
completeAITutorChat(newMessages);
|
||||
};
|
||||
|
||||
const scrollToBottom = useCallback(() => {
|
||||
scrollareaRef.current?.scrollTo({
|
||||
top: scrollareaRef.current.scrollHeight,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}, [scrollareaRef]);
|
||||
|
||||
const completeAITutorChat = async (messages: AIChatHistoryType[]) => {
|
||||
try {
|
||||
setIsStreamingMessage(true);
|
||||
|
||||
const response = await fetch(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-topic-detail-chat`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
resourceId,
|
||||
resourceType,
|
||||
topicId: sanitizedTopicId,
|
||||
messages: messages.slice(-10),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
toast.error(data?.message || 'Something went wrong');
|
||||
setAiChatHistory([...messages].slice(0, messages.length - 1));
|
||||
setIsStreamingMessage(false);
|
||||
|
||||
if (data.status === 401) {
|
||||
removeAuthToken();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
queryClient.invalidateQueries(getAiCourseLimitOptions());
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
|
||||
if (!reader) {
|
||||
setIsStreamingMessage(false);
|
||||
toast.error('Something went wrong');
|
||||
return;
|
||||
}
|
||||
|
||||
await readStream(reader, {
|
||||
onStream: async (content) => {
|
||||
flushSync(() => {
|
||||
setStreamedMessage(content);
|
||||
});
|
||||
|
||||
scrollToBottom();
|
||||
},
|
||||
onStreamEnd: async (content) => {
|
||||
const newMessages: AIChatHistoryType[] = [
|
||||
...messages,
|
||||
{
|
||||
role: 'assistant',
|
||||
content,
|
||||
html: await markdownToHtmlWithHighlighting(content),
|
||||
},
|
||||
];
|
||||
|
||||
flushSync(() => {
|
||||
setStreamedMessage('');
|
||||
setIsStreamingMessage(false);
|
||||
setAiChatHistory(newMessages);
|
||||
});
|
||||
|
||||
queryClient.invalidateQueries(getAiCourseLimitOptions());
|
||||
scrollToBottom();
|
||||
},
|
||||
});
|
||||
|
||||
setIsStreamingMessage(false);
|
||||
} catch (error) {
|
||||
toast.error('Something went wrong');
|
||||
setIsStreamingMessage(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, []);
|
||||
|
||||
const isDataLoading =
|
||||
isLoading || isBillingDetailsLoading || isRoadmapTreeMappingLoading;
|
||||
const usagePercentage = getPercentage(
|
||||
tokenUsage?.used || 0,
|
||||
tokenUsage?.limit || 0,
|
||||
);
|
||||
const hasChatHistory = aiChatHistory.length > 1;
|
||||
const nodeTextParts = roadmapTreeMapping?.text?.split('>') || [];
|
||||
const hasSubjects =
|
||||
(roadmapTreeMapping?.subjects &&
|
||||
roadmapTreeMapping?.subjects?.length > 0) ||
|
||||
nodeTextParts.length > 1;
|
||||
|
||||
return (
|
||||
<div className="relative mt-4 flex grow flex-col overflow-hidden rounded-lg border border-gray-200">
|
||||
{isDataLoading && (
|
||||
<div className="absolute inset-0 z-20 flex items-center justify-center gap-2 bg-white text-black">
|
||||
<Loader2Icon className="size-8 animate-spin stroke-3 text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showAILimitsPopup && (
|
||||
<AILimitsPopup
|
||||
onClose={() => setShowAILimitsPopup(false)}
|
||||
onUpgrade={() => {
|
||||
setShowAILimitsPopup(false);
|
||||
onUpgrade();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{hasSubjects && (
|
||||
<div className="border-b border-gray-200 p-3">
|
||||
<h4 className="flex items-center gap-2 text-sm">
|
||||
Complete the following AI Tutor courses
|
||||
</h4>
|
||||
|
||||
<div className="mt-2.5 flex flex-wrap gap-1 text-sm">
|
||||
{roadmapTreeMapping?.subjects?.map((subject) => {
|
||||
return (
|
||||
<a
|
||||
key={subject}
|
||||
target="_blank"
|
||||
onClick={(e) => {
|
||||
if (!isLoggedIn()) {
|
||||
e.preventDefault();
|
||||
onLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLimitExceeded) {
|
||||
e.preventDefault();
|
||||
onUpgrade();
|
||||
return;
|
||||
}
|
||||
}}
|
||||
href={`/ai/search?term=${subject}&difficulty=beginner&src=topic`}
|
||||
className="flex items-center gap-1 gap-2 rounded-md border border-gray-300 bg-gray-100 px-2 py-1 hover:bg-gray-200 hover:text-black"
|
||||
>
|
||||
{subject}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
|
||||
{roadmapTreeMapping?.subjects?.length === 0 && (
|
||||
<a
|
||||
target="_blank"
|
||||
href={`/ai/search?term=${roadmapTreeMapping?.text}&difficulty=beginner&src=topic`}
|
||||
className="flex items-center gap-1 rounded-md border border-gray-300 bg-gray-100 px-2 py-1 hover:bg-gray-200 hover:text-black"
|
||||
>
|
||||
{nodeTextParts.map((text, index) => (
|
||||
<>
|
||||
<span key={text} className="flex items-center">
|
||||
{text}
|
||||
</span>
|
||||
|
||||
{index !== nodeTextParts.length - 1 && (
|
||||
<ChevronRightIcon className="h-3 w-3 text-gray-400" />
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</a>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={onShowSubjectSearchModal}
|
||||
className="flex items-center gap-1.5 rounded-md border border-dashed border-gray-300 bg-transparent px-2 py-1 text-gray-400 hover:border-solid hover:bg-gray-200 hover:text-black"
|
||||
>
|
||||
<WandSparkles className="h-3 w-3" />
|
||||
Learn another topic
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'flex min-h-[46px] items-center justify-between gap-2 border-gray-200 px-3 py-2 text-sm',
|
||||
)}
|
||||
>
|
||||
{hasSubjects && (
|
||||
<span className="flex items-center gap-2 text-sm">
|
||||
<BotIcon
|
||||
className="relative -top-[1px] size-4 shrink-0 text-black"
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
<span className="hidden sm:block">Chat with AI</span>
|
||||
<span className="block sm:hidden">AI Tutor</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{!hasSubjects && (
|
||||
<h4 className="flex items-center gap-2 text-base font-medium">
|
||||
<BotIcon
|
||||
className="relative -top-[1px] size-5 shrink-0 text-black"
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
AI Tutor
|
||||
</h4>
|
||||
)}
|
||||
|
||||
{!isDataLoading && (
|
||||
<div className="flex gap-1.5">
|
||||
{hasChatHistory && (
|
||||
<button
|
||||
className="rounded-md bg-white px-2 py-2 text-xs font-medium text-black hover:bg-gray-200"
|
||||
onClick={() => {
|
||||
setAiChatHistory(defaultChatHistory);
|
||||
}}
|
||||
>
|
||||
<Trash2 className="size-3.5" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!isPaidUser && hasUpgradeButtons && (
|
||||
<>
|
||||
<button
|
||||
className="hidden rounded-md bg-gray-200 px-2 py-1 text-sm hover:bg-gray-300 sm:block"
|
||||
onClick={() => {
|
||||
if (!isLoggedIn()) {
|
||||
onLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
setShowAILimitsPopup(true);
|
||||
}}
|
||||
>
|
||||
<span className="font-medium">{usagePercentage}%</span>{' '}
|
||||
credits used
|
||||
</button>
|
||||
<button
|
||||
className="flex items-center gap-1 rounded-md bg-yellow-400 px-2 py-1 text-sm text-black hover:bg-yellow-500"
|
||||
onClick={() => {
|
||||
if (!isLoggedIn()) {
|
||||
onLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
onUpgrade();
|
||||
}}
|
||||
>
|
||||
<Gift className="size-4" />
|
||||
Upgrade
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<PredefinedActions
|
||||
onSelect={(action) => {
|
||||
if (!isLoggedIn()) {
|
||||
onLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLimitExceeded) {
|
||||
onUpgrade();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!action?.prompt) {
|
||||
toast.error('Something went wrong');
|
||||
return;
|
||||
}
|
||||
|
||||
setMessage(action.prompt);
|
||||
handleChatSubmit(action.prompt);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="scrollbar-thumb-gray-300 scrollbar-track-transparent scrollbar-thin relative grow overflow-y-auto"
|
||||
ref={scrollareaRef}
|
||||
>
|
||||
<div className="absolute inset-0 flex flex-col">
|
||||
<div className="relative flex grow flex-col justify-end">
|
||||
<div className="flex flex-col justify-end gap-2 px-3 py-2">
|
||||
{aiChatHistory.map((chat, index) => {
|
||||
let content = chat.content;
|
||||
|
||||
if (chat.role === 'user' && promptLabelMapping[chat.content]) {
|
||||
content = promptLabelMapping[chat.content];
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment key={`chat-${index}`}>
|
||||
<AIChatCard
|
||||
role={chat.role}
|
||||
content={content}
|
||||
html={chat.html}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
|
||||
{isStreamingMessage && !streamedMessage && (
|
||||
<AIChatCard role="assistant" content="Thinking..." />
|
||||
)}
|
||||
|
||||
{streamedMessage && (
|
||||
<AIChatCard role="assistant" content={streamedMessage} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form
|
||||
ref={formRef}
|
||||
className="relative flex items-start border-t border-gray-200 text-sm"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleChatSubmit();
|
||||
}}
|
||||
>
|
||||
{isLimitExceeded && isLoggedIn() && (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center gap-2 bg-black text-white">
|
||||
<LockIcon className="size-4 cursor-not-allowed" strokeWidth={2.5} />
|
||||
<p className="cursor-not-allowed">
|
||||
Limit reached for today
|
||||
{isPaidUser ? '. Please wait until tomorrow.' : ''}
|
||||
</p>
|
||||
{!isPaidUser && (
|
||||
<button
|
||||
onClick={onUpgrade}
|
||||
className="rounded-md bg-white px-2 py-1 text-xs font-medium text-black hover:bg-gray-300"
|
||||
>
|
||||
Upgrade for more
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoggedIn() && (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center gap-2 bg-black text-white">
|
||||
<LockIcon className="size-4 cursor-not-allowed" strokeWidth={2.5} />
|
||||
<p className="cursor-not-allowed">Please login to continue</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
showLoginPopup();
|
||||
}}
|
||||
className="rounded-md bg-white px-2 py-1 text-xs font-medium text-black hover:bg-gray-300"
|
||||
>
|
||||
Login / Register
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isDataLoading && (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center gap-2 bg-black text-white">
|
||||
<Loader2Icon className="size-4 animate-spin" />
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<TextareaAutosize
|
||||
className={cn(
|
||||
'h-full min-h-[41px] grow resize-none bg-transparent px-4 py-2 focus:outline-hidden',
|
||||
)}
|
||||
placeholder="Ask AI anything about the lesson..."
|
||||
value={message}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
autoFocus={true}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleChatSubmit();
|
||||
}
|
||||
}}
|
||||
ref={textareaRef}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isStreamingMessage || isLimitExceeded}
|
||||
className="flex aspect-square size-[41px] items-center justify-center text-zinc-500 hover:text-black disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
<SendIcon className="size-4 stroke-[2.5]" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
import type { AllowedLinkTypes } from '../CustomRoadmap/CustomRoadmap.tsx';
|
||||
|
||||
const linkTypes: Record<AllowedLinkTypes, string> = {
|
||||
const linkTypes: Record<AllowedLinkTypes | string, string> = {
|
||||
article: 'bg-yellow-300',
|
||||
course: 'bg-green-400',
|
||||
opensource: 'bg-black text-white',
|
||||
@@ -18,6 +18,34 @@ const paidLinkTypes: Record<string, string> = {
|
||||
course: 'bg-yellow-300',
|
||||
};
|
||||
|
||||
type TopicLinkBadgeProps = {
|
||||
isPaid: boolean;
|
||||
discountText?: string;
|
||||
type: AllowedLinkTypes | string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function TopicLinkBadge(props: TopicLinkBadgeProps) {
|
||||
const { isPaid, type, className } = props;
|
||||
|
||||
const linkType = type === 'opensource' ? 'OpenSource' : type;
|
||||
const isDiscount = type.includes('% off');
|
||||
|
||||
return (
|
||||
<span className={cn('mr-2', className)}>
|
||||
<span
|
||||
className={cn(
|
||||
'inline-block rounded-sm px-1.5 py-0.5 text-xs capitalize no-underline',
|
||||
(isPaid ? paidLinkTypes[type] : linkTypes[type]) || 'bg-gray-200',
|
||||
isDiscount && 'bg-green-300',
|
||||
)}
|
||||
>
|
||||
{linkType}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
type TopicDetailLinkProps = {
|
||||
url: string;
|
||||
onClick?: () => void;
|
||||
@@ -29,7 +57,7 @@ type TopicDetailLinkProps = {
|
||||
export function TopicDetailLink(props: TopicDetailLinkProps) {
|
||||
const { url, onClick, type, title, isPaid = false } = props;
|
||||
|
||||
const linkType = type === 'opensource' ? 'OpenSource' : type;
|
||||
const isScrimbaLink = url.toLowerCase().includes('scrimba.com');
|
||||
|
||||
return (
|
||||
<a
|
||||
@@ -38,14 +66,14 @@ export function TopicDetailLink(props: TopicDetailLinkProps) {
|
||||
className="group font-medium text-gray-800 underline underline-offset-1 hover:text-black"
|
||||
onClick={onClick}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'mr-2 inline-block rounded-sm px-1.5 py-0.5 text-xs capitalize no-underline',
|
||||
(isPaid ? paidLinkTypes[type] : linkTypes[type]) || 'bg-gray-200',
|
||||
)}
|
||||
>
|
||||
{linkType}
|
||||
</span>
|
||||
<TopicLinkBadge
|
||||
isPaid={isPaid}
|
||||
type={type}
|
||||
discountText={isScrimbaLink ? '20% off' : undefined}
|
||||
className={isScrimbaLink ? 'mr-1' : 'mr-2'}
|
||||
/>
|
||||
{isScrimbaLink && <TopicLinkBadge isPaid={isPaid} type="20% off" />}
|
||||
|
||||
{title}
|
||||
</a>
|
||||
);
|
||||
|
||||
68
src/components/TopicDetail/TopicDetailsTabs.tsx
Normal file
68
src/components/TopicDetail/TopicDetailsTabs.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Earth, WandSparkles, type LucideIcon } from 'lucide-react';
|
||||
|
||||
export type AllowedTopicDetailsTabs = 'content' | 'ai';
|
||||
|
||||
type TopicDetailsTabsProps = {
|
||||
activeTab: AllowedTopicDetailsTabs;
|
||||
setActiveTab: (tab: AllowedTopicDetailsTabs) => void;
|
||||
hasAITutor?: boolean;
|
||||
};
|
||||
|
||||
export function TopicDetailsTabs(props: TopicDetailsTabsProps) {
|
||||
const { activeTab, setActiveTab, hasAITutor = true } = props;
|
||||
|
||||
return (
|
||||
<div className="flex w-max gap-1.5">
|
||||
<TopicDetailsTab
|
||||
isActive={activeTab === 'content'}
|
||||
icon={Earth}
|
||||
label="Resources"
|
||||
onClick={() => setActiveTab('content')}
|
||||
/>
|
||||
<TopicDetailsTab
|
||||
isActive={activeTab === 'ai'}
|
||||
icon={WandSparkles}
|
||||
label="AI Tutor"
|
||||
isNew={true}
|
||||
isDisabled={!hasAITutor}
|
||||
onClick={() => setActiveTab('ai')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type TopicDetailsTabProps = {
|
||||
isActive: boolean;
|
||||
icon: LucideIcon;
|
||||
label: string;
|
||||
isNew?: boolean;
|
||||
isDisabled?: boolean;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
function TopicDetailsTab(props: TopicDetailsTabProps) {
|
||||
const { isActive, icon: Icon, label, isNew, isDisabled, onClick } = props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className="flex select-none disabled:pointer-events-none items-center gap-2 rounded-md border border-gray-300 px-2 py-1 text-sm text-gray-500 hover:border-gray-400 data-[state=active]:border-black data-[state=active]:bg-black data-[state=active]:text-white"
|
||||
data-state={isActive ? 'active' : 'inactive'}
|
||||
onClick={onClick}
|
||||
disabled={isDisabled}
|
||||
type="button"
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
<span className="hidden sm:block">{label}</span>
|
||||
{isNew && !isDisabled && (
|
||||
<span className="hidden rounded-sm bg-yellow-400 px-1 text-xs text-black 2xl:block">
|
||||
New
|
||||
</span>
|
||||
)}
|
||||
{isDisabled && (
|
||||
<span className="hidden rounded-sm bg-gray-400 px-1 text-xs text-white sm:block">
|
||||
Soon
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -14,16 +14,10 @@ import type {
|
||||
} from '../../lib/resource-progress';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { Spinner } from '../ReactIcons/Spinner';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
|
||||
type TopicProgressButtonProps = {
|
||||
topicId: string;
|
||||
resourceId: string;
|
||||
resourceType: ResourceType;
|
||||
|
||||
onClose: () => void;
|
||||
};
|
||||
import { ChevronDown, Loader2 } from 'lucide-react';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { userResourceProgressOptions } from '../../queries/resource-progress';
|
||||
|
||||
const statusColors: Record<ResourceProgressType, string> = {
|
||||
done: 'bg-green-500',
|
||||
@@ -33,8 +27,44 @@ const statusColors: Record<ResourceProgressType, string> = {
|
||||
removed: '',
|
||||
};
|
||||
|
||||
type TopicProgressButtonProps = {
|
||||
topicId: string;
|
||||
resourceId: string;
|
||||
resourceType: ResourceType;
|
||||
dropdownClassName?: string;
|
||||
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
type ProgressDropdownItemProps = {
|
||||
status: ResourceProgressType;
|
||||
shortcutKey: string;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
function ProgressDropdownItem(props: ProgressDropdownItemProps) {
|
||||
const { status, shortcutKey, label, onClick } = props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className="inline-flex justify-between px-3 py-1.5 text-left text-sm text-gray-800 hover:bg-gray-100"
|
||||
onClick={onClick}
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
className={`mr-2 inline-block h-2 w-2 rounded-full ${statusColors[status]}`}
|
||||
></span>
|
||||
{label}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500">{shortcutKey}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
const { topicId, resourceId, resourceType, onClose } = props;
|
||||
const { topicId, resourceId, resourceType, onClose, dropdownClassName } =
|
||||
props;
|
||||
|
||||
const toast = useToast();
|
||||
const [isUpdatingProgress, setIsUpdatingProgress] = useState(true);
|
||||
@@ -66,7 +96,15 @@ export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
// Mark as done
|
||||
useKeydown(
|
||||
'd',
|
||||
() => {
|
||||
(e: KeyboardEvent) => {
|
||||
if (
|
||||
e.target instanceof HTMLTextAreaElement ||
|
||||
e.target instanceof HTMLInputElement ||
|
||||
e.target instanceof HTMLSelectElement
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (progress === 'done') {
|
||||
onClose();
|
||||
return;
|
||||
@@ -80,7 +118,15 @@ export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
// Mark as learning
|
||||
useKeydown(
|
||||
'l',
|
||||
() => {
|
||||
(e: KeyboardEvent) => {
|
||||
if (
|
||||
e.target instanceof HTMLTextAreaElement ||
|
||||
e.target instanceof HTMLInputElement ||
|
||||
e.target instanceof HTMLSelectElement
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (progress === 'learning') {
|
||||
return;
|
||||
}
|
||||
@@ -93,7 +139,15 @@ export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
// Mark as learning
|
||||
useKeydown(
|
||||
's',
|
||||
() => {
|
||||
(e: KeyboardEvent) => {
|
||||
if (
|
||||
e.target instanceof HTMLTextAreaElement ||
|
||||
e.target instanceof HTMLInputElement ||
|
||||
e.target instanceof HTMLSelectElement
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (progress === 'skipped') {
|
||||
onClose();
|
||||
return;
|
||||
@@ -107,9 +161,16 @@ export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
// Mark as pending
|
||||
useKeydown(
|
||||
'r',
|
||||
() => {
|
||||
(e: KeyboardEvent) => {
|
||||
if (
|
||||
e.target instanceof HTMLTextAreaElement ||
|
||||
e.target instanceof HTMLInputElement ||
|
||||
e.target instanceof HTMLSelectElement
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (progress === 'pending') {
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,6 +180,8 @@ export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
);
|
||||
|
||||
const handleUpdateResourceProgress = (progress: ResourceProgressType) => {
|
||||
setShowChangeStatus(false);
|
||||
|
||||
if (isGuest) {
|
||||
onClose();
|
||||
showLoginPopup();
|
||||
@@ -147,7 +210,11 @@ export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
console.error(err);
|
||||
})
|
||||
.finally(() => {
|
||||
setShowChangeStatus(false);
|
||||
setIsUpdatingProgress(false);
|
||||
queryClient.invalidateQueries(
|
||||
userResourceProgressOptions(resourceType, resourceId),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -164,97 +231,79 @@ export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
progress,
|
||||
);
|
||||
|
||||
if (isUpdatingProgress) {
|
||||
return (
|
||||
<button className="inline-flex cursor-default items-center rounded-md border border-gray-300 bg-white p-1 px-2 text-sm text-black">
|
||||
<Spinner className="h-4 w-4" />
|
||||
<span className="ml-2">Updating Status..</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative inline-flex rounded-md border border-gray-300">
|
||||
<span className="inline-flex cursor-default items-center p-1 px-2 text-sm text-black">
|
||||
<span className="flex h-2 w-2">
|
||||
<span
|
||||
className={`relative inline-flex h-2 w-2 rounded-full ${statusColors[progress]}`}
|
||||
></span>
|
||||
</span>
|
||||
<span className="ml-2 capitalize">
|
||||
{progress === 'learning' ? 'In Progress' : progress}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div className="relative inline-flex">
|
||||
<button
|
||||
className="inline-flex cursor-pointer items-center rounded-br-md rounded-tr-md border-l border-l-gray-300 bg-gray-100 p-1 px-2 text-sm text-black hover:bg-gray-200"
|
||||
disabled={isUpdatingProgress}
|
||||
className={cn(
|
||||
'flex 2xl:min-w-[135px] cursor-default cursor-pointer items-center rounded-md border border-gray-300 p-1 px-2 text-sm text-black hover:border-gray-400 disabled:pointer-events-none disabled:opacity-50',
|
||||
)}
|
||||
onClick={() => setShowChangeStatus(true)}
|
||||
>
|
||||
<span className="mr-0.5">Update Status</span>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
{!isUpdatingProgress && (
|
||||
<>
|
||||
<span className="flex h-2 w-2">
|
||||
<span
|
||||
className={cn(
|
||||
'relative inline-flex h-2 w-2 rounded-full',
|
||||
statusColors[progress],
|
||||
isUpdatingProgress && 'animate-pulse',
|
||||
)}
|
||||
></span>
|
||||
</span>
|
||||
<span className="ml-2 mr-2 capitalize">
|
||||
{progress === 'learning' ? 'In Progress' : progress}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{isUpdatingProgress && (
|
||||
<span className="flex items-center">
|
||||
<Loader2 strokeWidth={3} className="size-3 flex-shrink-0 animate-spin" />
|
||||
<span className="ml-2 text-sm">Updating..</span>
|
||||
</span>
|
||||
)}
|
||||
<ChevronDown className="ml-auto h-4 w-4" />
|
||||
</button>
|
||||
|
||||
{showChangeStatus && (
|
||||
<div
|
||||
className="absolute right-0 top-full mt-1 flex min-w-[160px] flex-col divide-y rounded-md border border-gray-200 bg-white shadow-md [&>button:first-child]:rounded-t-md [&>button:last-child]:rounded-b-md"
|
||||
className={cn(
|
||||
'absolute top-full right-0 z-50 mt-1 flex min-w-[160px] flex-col divide-y rounded-md border border-gray-200 bg-white shadow-md [&>button:first-child]:rounded-t-md [&>button:last-child]:rounded-b-md',
|
||||
dropdownClassName,
|
||||
)}
|
||||
ref={changeStatusRef!}
|
||||
>
|
||||
{allowMarkingDone && (
|
||||
<button
|
||||
className="inline-flex justify-between px-3 py-1.5 text-left text-sm text-gray-800 hover:bg-gray-100"
|
||||
<ProgressDropdownItem
|
||||
status="done"
|
||||
shortcutKey="D"
|
||||
label="Done"
|
||||
onClick={() => handleUpdateResourceProgress('done')}
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
className={`mr-2 inline-block h-2 w-2 rounded-full ${statusColors['done']}`}
|
||||
></span>
|
||||
Done
|
||||
</span>
|
||||
<span className="text-xs text-gray-500">D</span>
|
||||
</button>
|
||||
/>
|
||||
)}
|
||||
{allowMarkingLearning && (
|
||||
<button
|
||||
className="inline-flex justify-between px-3 py-1.5 text-left text-sm text-gray-800 hover:bg-gray-100"
|
||||
<ProgressDropdownItem
|
||||
status="learning"
|
||||
shortcutKey="L"
|
||||
label="In Progress"
|
||||
onClick={() => handleUpdateResourceProgress('learning')}
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
className={`mr-2 inline-block h-2 w-2 rounded-full ${statusColors['learning']}`}
|
||||
></span>
|
||||
In Progress
|
||||
</span>
|
||||
|
||||
<span className="text-xs text-gray-500">L</span>
|
||||
</button>
|
||||
/>
|
||||
)}
|
||||
{allowMarkingPending && (
|
||||
<button
|
||||
className="inline-flex justify-between px-3 py-1.5 text-left text-sm text-gray-800 hover:bg-gray-100"
|
||||
<ProgressDropdownItem
|
||||
status="pending"
|
||||
shortcutKey="R"
|
||||
label="Reset"
|
||||
onClick={() => handleUpdateResourceProgress('pending')}
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
className={`mr-2 inline-block h-2 w-2 rounded-full ${statusColors['pending']}`}
|
||||
></span>
|
||||
Reset
|
||||
</span>
|
||||
<span className="text-xs text-gray-500">R</span>
|
||||
</button>
|
||||
/>
|
||||
)}
|
||||
{allowMarkingSkipped && (
|
||||
<button
|
||||
className="inline-flex justify-between px-3 py-1.5 text-left text-sm text-gray-800 hover:bg-gray-100"
|
||||
<ProgressDropdownItem
|
||||
status="skipped"
|
||||
shortcutKey="S"
|
||||
label="Skip"
|
||||
onClick={() => handleUpdateResourceProgress('skipped')}
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
className={`mr-2 inline-block h-2 w-2 rounded-full ${statusColors['skipped']}`}
|
||||
></span>
|
||||
Skip
|
||||
</span>
|
||||
<span className="text-xs text-gray-500">S</span>
|
||||
</button>
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
81
src/components/UserPersona/ChatPersona.tsx
Normal file
81
src/components/UserPersona/ChatPersona.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { UserPersonaForm, type UserPersonaFormData } from './UserPersonaForm';
|
||||
import { roadmapJSONOptions } from '../../queries/roadmap';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { httpPost } from '../../lib/query-http';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { userPersonaOptions } from '../../queries/user-persona';
|
||||
|
||||
type ChatPersonaProps = {
|
||||
roadmapId: string;
|
||||
};
|
||||
|
||||
export function ChatPersona(props: ChatPersonaProps) {
|
||||
const { roadmapId } = props;
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const { data: roadmap } = useQuery(
|
||||
roadmapJSONOptions(roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const { mutate: createUserPersona, isPending: isCreatingUserPersona } =
|
||||
useMutation(
|
||||
{
|
||||
mutationFn: async (data: UserPersonaFormData) => {
|
||||
return httpPost('/v1-set-user-persona', {
|
||||
...data,
|
||||
roadmapId,
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
},
|
||||
onSettled: () => {
|
||||
return queryClient.invalidateQueries(userPersonaOptions(roadmapId));
|
||||
},
|
||||
},
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const roadmapTitle = roadmap?.json.title ?? '';
|
||||
|
||||
return (
|
||||
<div className="relative mx-auto flex h-auto px-4 sm:h-full max-w-[400px] grow flex-col justify-center p-4 sm:p-4 px-2">
|
||||
<div className="mb-4 sm:mb-8 text-left sm:text-center">
|
||||
<img
|
||||
src="/images/gifs/wave.gif"
|
||||
alt="Wave"
|
||||
className="hidden sm:block mx-auto mb-3 sm:mb-5 h-16 sm:h-24 w-16 sm:w-24"
|
||||
/>
|
||||
<h2 className="text-lg sm:text-xl font-semibold">Welcome to the AI Tutor</h2>
|
||||
<p className="mt-1 text-xs sm:text-sm text-balance text-gray-500 pr-8 sm:px-0">
|
||||
Before we start, answer these questions so we can help you better.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<UserPersonaForm
|
||||
roadmapTitle={roadmapTitle}
|
||||
onSubmit={(data) => {
|
||||
const trimmedGoal = data?.goal?.trim();
|
||||
if (!trimmedGoal) {
|
||||
toast.error('Please describe your goal');
|
||||
return;
|
||||
}
|
||||
|
||||
const trimmedCommit = data?.commit?.trim();
|
||||
if (!trimmedCommit) {
|
||||
toast.error(
|
||||
'Please enter how many hours per week you can commit to learning',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
createUserPersona(data);
|
||||
}}
|
||||
isLoading={isCreatingUserPersona}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
92
src/components/UserPersona/UpdatePersonaModal.tsx
Normal file
92
src/components/UserPersona/UpdatePersonaModal.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { userPersonaOptions } from '../../queries/user-persona';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { roadmapJSONOptions } from '../../queries/roadmap';
|
||||
import { Modal } from '../Modal';
|
||||
import { UserPersonaForm, type UserPersonaFormData } from './UserPersonaForm';
|
||||
import { httpPost } from '../../lib/query-http';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
|
||||
type UpdatePersonaModalProps = {
|
||||
roadmapId: string;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export function UpdatePersonaModal(props: UpdatePersonaModalProps) {
|
||||
const { roadmapId, onClose } = props;
|
||||
|
||||
const toast = useToast();
|
||||
const { data: roadmap } = useQuery(
|
||||
roadmapJSONOptions(roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
const { data: userPersona } = useQuery(
|
||||
userPersonaOptions(roadmapId),
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const { mutate: setUserPersona, isPending: isSettingUserPersona } =
|
||||
useMutation(
|
||||
{
|
||||
mutationFn: async (data: UserPersonaFormData) => {
|
||||
return httpPost('/v1-set-user-persona', {
|
||||
...data,
|
||||
roadmapId,
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
},
|
||||
onSuccess: () => {
|
||||
onClose();
|
||||
},
|
||||
onSettled: () => {
|
||||
return queryClient.invalidateQueries(userPersonaOptions(roadmapId));
|
||||
},
|
||||
},
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const roadmapTitle = roadmap?.json.title ?? '';
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onClose={onClose}
|
||||
wrapperClassName="max-w-[450px]"
|
||||
bodyClassName="p-4"
|
||||
>
|
||||
<div className="mb-4 text-left">
|
||||
<h2 className="text-lg font-semibold">Tell us more about yourself</h2>
|
||||
<p className="mt-1 text-sm text-balance text-gray-500">
|
||||
We'll use this information to help you get the best out of the AI
|
||||
Tutor.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<UserPersonaForm
|
||||
className="space-y-4"
|
||||
roadmapTitle={roadmapTitle}
|
||||
defaultValues={userPersona ?? undefined}
|
||||
onSubmit={(data) => {
|
||||
const trimmedGoal = data?.goal?.trim();
|
||||
if (!trimmedGoal) {
|
||||
toast.error('Please describe your goal');
|
||||
return;
|
||||
}
|
||||
|
||||
const trimmedCommit = data?.commit?.trim();
|
||||
if (!trimmedCommit) {
|
||||
toast.error(
|
||||
'Please enter how many hours per week you can commit to learning',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setUserPersona(data);
|
||||
}}
|
||||
isLoading={isSettingUserPersona}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
216
src/components/UserPersona/UserPersonaForm.tsx
Normal file
216
src/components/UserPersona/UserPersonaForm.tsx
Normal file
@@ -0,0 +1,216 @@
|
||||
import { useId, useRef, useState } from 'react';
|
||||
import { SelectNative } from '../SelectNative';
|
||||
import { Loader2Icon, MessageCircle } from 'lucide-react';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
export type UserPersonaFormData = {
|
||||
expertise: string;
|
||||
goal: string;
|
||||
commit: string;
|
||||
about: string;
|
||||
};
|
||||
|
||||
type UserPersonaFormProps = {
|
||||
roadmapTitle: string;
|
||||
defaultValues?: UserPersonaFormData;
|
||||
onSubmit: (data: UserPersonaFormData) => void;
|
||||
|
||||
className?: string;
|
||||
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
export function UserPersonaForm(props: UserPersonaFormProps) {
|
||||
const {
|
||||
roadmapTitle,
|
||||
defaultValues,
|
||||
className = '',
|
||||
onSubmit,
|
||||
isLoading,
|
||||
} = props;
|
||||
const [expertise, setExpertise] = useState(defaultValues?.expertise ?? '');
|
||||
|
||||
const goalOptions = [
|
||||
'Finding a job',
|
||||
'Learning for fun',
|
||||
'Building a side project',
|
||||
'Switching careers',
|
||||
'Getting a promotion',
|
||||
'Filling knowledge gaps',
|
||||
'Other',
|
||||
];
|
||||
|
||||
const getInitialGoalSelection = () => {
|
||||
if (!defaultValues?.goal) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Check if the goal matches any predefined option
|
||||
for (const option of goalOptions.slice(0, -1)) {
|
||||
// Exclude 'Other'
|
||||
if (defaultValues.goal.startsWith(option)) {
|
||||
return option;
|
||||
}
|
||||
}
|
||||
|
||||
return 'Other';
|
||||
};
|
||||
|
||||
const [selectedGoal, setSelectedGoal] = useState(getInitialGoalSelection());
|
||||
const [goal, setGoal] = useState(defaultValues?.goal ?? '');
|
||||
const [commit, setCommit] = useState(defaultValues?.commit ?? '');
|
||||
const [about, setAbout] = useState(defaultValues?.about ?? '');
|
||||
|
||||
const expertiseFieldId = useId();
|
||||
const goalFieldId = useId();
|
||||
const goalSelectId = useId();
|
||||
const commitFieldId = useId();
|
||||
const aboutFieldId = useId();
|
||||
|
||||
const goalRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const handleGoalSelectionChange = (value: string) => {
|
||||
setSelectedGoal(value);
|
||||
|
||||
if (value === 'Other') {
|
||||
setGoal('');
|
||||
setTimeout(() => {
|
||||
goalRef.current?.focus();
|
||||
}, 0);
|
||||
} else {
|
||||
setGoal(value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
onSubmit({ expertise, goal, commit, about });
|
||||
};
|
||||
|
||||
const hasFormCompleted = !!expertise && !!goal && !!commit;
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className={cn('space-y-5', className)}>
|
||||
<div className="flex flex-col gap-3">
|
||||
<label
|
||||
className="text-sm font-medium text-gray-700"
|
||||
htmlFor={expertiseFieldId}
|
||||
>
|
||||
Rate your expertise in {roadmapTitle}:
|
||||
</label>
|
||||
<SelectNative
|
||||
id={expertiseFieldId}
|
||||
value={expertise}
|
||||
defaultValue={expertise}
|
||||
onChange={(e) => setExpertise(e.target.value)}
|
||||
className="h-[40px] border-gray-300 text-sm focus:border-gray-500 focus:ring-1 focus:ring-gray-500"
|
||||
>
|
||||
<option value="">Select your expertise</option>
|
||||
{[
|
||||
'No experience (just starting out)',
|
||||
'Beginner (less than 1 year of experience)',
|
||||
'Intermediate (1-3 years of experience)',
|
||||
'Expert (3-5 years of experience)',
|
||||
'Master (5+ years of experience)',
|
||||
].map((expertise) => (
|
||||
<option key={expertise} value={expertise}>
|
||||
{expertise}
|
||||
</option>
|
||||
))}
|
||||
</SelectNative>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<label
|
||||
className="text-sm font-medium text-gray-700"
|
||||
htmlFor={goalSelectId}
|
||||
>
|
||||
What is your goal?
|
||||
</label>
|
||||
|
||||
<SelectNative
|
||||
id={goalSelectId}
|
||||
value={selectedGoal}
|
||||
onChange={(e) => handleGoalSelectionChange(e.target.value)}
|
||||
className="h-[40px] border-gray-300 text-sm focus:border-gray-500 focus:ring-1 focus:ring-gray-500"
|
||||
>
|
||||
<option value="">Select your goal</option>
|
||||
{goalOptions.map((goalOption) => (
|
||||
<option key={goalOption} value={goalOption}>
|
||||
{goalOption}
|
||||
</option>
|
||||
))}
|
||||
</SelectNative>
|
||||
|
||||
{selectedGoal === 'Other' && (
|
||||
<textarea
|
||||
ref={goalRef}
|
||||
id={goalFieldId}
|
||||
className="block min-h-24 w-full resize-none rounded-lg border border-gray-300 bg-white px-4 py-3 text-sm outline-none placeholder:text-gray-400 focus:border-gray-500 focus:ring-1 focus:ring-gray-500"
|
||||
placeholder="e.g. need to find a job as soon as possible"
|
||||
value={goal}
|
||||
onChange={(e) => setGoal(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<label
|
||||
className="text-sm font-medium text-gray-700"
|
||||
htmlFor={commitFieldId}
|
||||
>
|
||||
How many hours per week can you commit to learning?
|
||||
</label>
|
||||
|
||||
<input
|
||||
id={commitFieldId}
|
||||
className="block h-[40px] w-full resize-none rounded-lg border border-gray-300 bg-white px-4 text-sm outline-none placeholder:text-gray-400 focus:border-gray-500 focus:ring-1 focus:ring-gray-500"
|
||||
placeholder="e.g. 10 hours per week"
|
||||
value={commit}
|
||||
onChange={(e) => setCommit(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<label
|
||||
className="text-sm font-medium text-gray-700"
|
||||
htmlFor={aboutFieldId}
|
||||
>
|
||||
Tell us about yourself (optional but recommended)
|
||||
</label>
|
||||
|
||||
<textarea
|
||||
id={aboutFieldId}
|
||||
className="block min-h-24 w-full resize-none rounded-lg border border-gray-300 bg-white px-4 py-3 text-sm outline-none placeholder:text-gray-400 focus:border-gray-500 focus:ring-1 focus:ring-gray-500"
|
||||
placeholder="e.g. I'm a frontend developer with experience in React, looking to expand my backend skills..."
|
||||
value={about}
|
||||
onChange={(e) => setAbout(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
disabled={isLoading || !hasFormCompleted}
|
||||
type="submit"
|
||||
className="mt-6 flex h-11 w-full items-center justify-center gap-2 rounded-lg bg-black px-6 py-2 text-sm text-white transition-all hover:bg-gray-900 disabled:pointer-events-none disabled:opacity-50"
|
||||
>
|
||||
{isLoading ? (
|
||||
<Loader2Icon className="size-4 animate-spin stroke-[2.5]" />
|
||||
) : (
|
||||
<>
|
||||
{defaultValues ? (
|
||||
<>
|
||||
<MessageCircle className="size-4" />
|
||||
Update Information
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<MessageCircle className="size-4" />
|
||||
Start Chatting
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -11,7 +11,7 @@ export function ModalLoader(props: ModalLoaderProps) {
|
||||
const { isLoading, text, error } = props;
|
||||
|
||||
return (
|
||||
<div className="fixed left-0 right-0 top-0 z-100 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
|
||||
<div className="fixed top-0 right-0 left-0 z-100 h-full items-center justify-center overflow-x-hidden overflow-y-auto overscroll-contain bg-black/50">
|
||||
<div className="relative mx-auto flex h-full w-full items-center justify-center">
|
||||
<div className="popup-body relative rounded-lg bg-white p-5 shadow-sm">
|
||||
<div className="flex items-center">
|
||||
|
||||
@@ -65,8 +65,10 @@ export function UserProgressModal(props: ProgressMapProps) {
|
||||
let resourceJsonUrl = import.meta.env.DEV
|
||||
? 'http://localhost:3000'
|
||||
: 'https://roadmap.sh';
|
||||
if (resourceType === 'roadmap') {
|
||||
if (resourceType === 'roadmap' && renderer === 'balsamiq') {
|
||||
resourceJsonUrl += `/${resourceId}.json`;
|
||||
} else if (resourceType === 'roadmap' && renderer === 'editor') {
|
||||
resourceJsonUrl = `${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${resourceId}`;
|
||||
} else {
|
||||
resourceJsonUrl += `/best-practices/${resourceId}.json`;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export function UserProfileRoadmapRenderer(
|
||||
<div
|
||||
className={cn(
|
||||
'bg-white',
|
||||
isCustomResource ? 'w-full' : 'container relative max-w-[1000px]!',
|
||||
isCustomResource ? 'w-full' : 'relative container max-w-[1000px]!',
|
||||
)}
|
||||
>
|
||||
{isCustomResource ? (
|
||||
@@ -136,7 +136,7 @@ export function UserProfileRoadmapRenderer(
|
||||
<div className="flex w-full justify-center">
|
||||
<Spinner
|
||||
isDualRing={false}
|
||||
className="mb-4 mt-2 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
||||
className="mt-2 mb-4 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user