mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-16 03:41:46 +08:00
Compare commits
1 Commits
feat/analy
...
fix/defaul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21046347da |
@@ -3,6 +3,6 @@
|
||||
"enabled": false
|
||||
},
|
||||
"_variables": {
|
||||
"lastUpdateCheck": 1748277554631
|
||||
"lastUpdateCheck": 1745231680828
|
||||
}
|
||||
}
|
||||
1
.astro/types.d.ts
vendored
1
.astro/types.d.ts
vendored
@@ -1,2 +1 @@
|
||||
/// <reference types="astro/client" />
|
||||
/// <reference path="content.d.ts" />
|
||||
55
package.json
55
package.json
@@ -31,28 +31,20 @@
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^9.2.1",
|
||||
"@astrojs/react": "^4.2.7",
|
||||
"@astrojs/sitemap": "^3.4.0",
|
||||
"@astrojs/node": "^9.2.0",
|
||||
"@astrojs/react": "^4.2.4",
|
||||
"@astrojs/sitemap": "^3.3.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.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",
|
||||
"@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",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"dom-to-image": "^2.6.0",
|
||||
@@ -60,43 +52,42 @@
|
||||
"gray-matter": "^4.0.3",
|
||||
"htm": "^3.1.1",
|
||||
"image-size": "^2.0.2",
|
||||
"jose": "^6.0.11",
|
||||
"jose": "^6.0.10",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.511.0",
|
||||
"lucide-react": "^0.503.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.1",
|
||||
"npm-check-updates": "^18.0.0",
|
||||
"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": "^3.0.2",
|
||||
"react-resizable-panels": "^2.1.8",
|
||||
"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.17.0",
|
||||
"satori": "^0.13.1",
|
||||
"sanitize-html": "^2.16.0",
|
||||
"satori": "^0.12.2",
|
||||
"satori-html": "^0.3.2",
|
||||
"sharp": "^0.34.1",
|
||||
"shiki": "^3.4.2",
|
||||
"shiki": "^3.2.2",
|
||||
"slugify": "^1.6.6",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"tiptap-markdown": "^0.8.10",
|
||||
"turndown": "^7.2.0",
|
||||
"unified": "^11.0.5",
|
||||
"zustand": "^5.0.4"
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ai-sdk/google": "^1.2.18",
|
||||
"@ai-sdk/google": "^1.2.12",
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@types/dom-to-image": "^2.6.7",
|
||||
@@ -106,17 +97,17 @@
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/react-calendar-heatmap": "^1.9.0",
|
||||
"@types/react-slick": "^0.23.13",
|
||||
"@types/sanitize-html": "^2.16.0",
|
||||
"@types/sanitize-html": "^2.15.0",
|
||||
"@types/turndown": "^5.0.5",
|
||||
"ai": "^4.3.16",
|
||||
"ai": "^4.3.9",
|
||||
"csv-parser": "^3.2.0",
|
||||
"gh-pages": "^6.3.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"markdown-it": "^14.1.0",
|
||||
"openai": "^4.100.0",
|
||||
"openai": "^4.95.1",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-astro": "^0.14.1",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"tsx": "^4.19.4"
|
||||
"tsx": "^4.19.3"
|
||||
}
|
||||
}
|
||||
|
||||
1925
pnpm-lock.yaml
generated
1925
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 821 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 370 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 936 KiB |
Binary file not shown.
@@ -906,11 +906,6 @@
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -658,6 +658,11 @@
|
||||
"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": "Basic Authentication",
|
||||
"url": "https://roadmap.sh/guides/basic-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Session Based Authentication",
|
||||
"url": "https://roadmap.sh/guides/session-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Token Based Authentication",
|
||||
"url": "https://roadmap.sh/guides/token-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "JWT Authentication",
|
||||
"url": "https://roadmap.sh/guides/jwt-authentication",
|
||||
"title": "SSO - Single Sign On",
|
||||
"url": "https://roadmap.sh/guides/sso",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -2805,8 +2790,23 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "SSO - Single Sign On",
|
||||
"url": "https://roadmap.sh/guides/sso",
|
||||
"title": "JWT Authentication",
|
||||
"url": "https://roadmap.sh/guides/jwt-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Token Based Authentication",
|
||||
"url": "https://roadmap.sh/guides/token-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Session Based Authentication",
|
||||
"url": "https://roadmap.sh/guides/session-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Basic Authentication",
|
||||
"url": "https://roadmap.sh/guides/basic-authentication",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -328,11 +328,6 @@
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -987,11 +982,6 @@
|
||||
"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",
|
||||
@@ -1248,11 +1238,6 @@
|
||||
"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",
|
||||
@@ -1814,16 +1799,6 @@
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2380,7 +2355,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"
|
||||
},
|
||||
@@ -2597,11 +2572,6 @@
|
||||
"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",
|
||||
|
||||
@@ -3633,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 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 are 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",
|
||||
@@ -3649,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 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 are 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",
|
||||
|
||||
@@ -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 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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Type-Coversion",
|
||||
@@ -281,24 +281,8 @@
|
||||
},
|
||||
"k9c5seRkhgm_yHPpiz2X0": {
|
||||
"title": "unique_ptr",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"uEGEmbxegATIrvGfobJb9": {
|
||||
"title": "Raw Pointers",
|
||||
|
||||
@@ -380,11 +380,6 @@
|
||||
"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",
|
||||
|
||||
@@ -1,973 +0,0 @@
|
||||
{
|
||||
"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,14 +354,8 @@
|
||||
},
|
||||
"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.\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"
|
||||
}
|
||||
]
|
||||
"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": []
|
||||
},
|
||||
"ZuZuzwy-Frsn_PFJZVuAQ": {
|
||||
"title": "Defining and Enforcing Values",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"VlNNwIEDWqQXtqkHWJYzC": {
|
||||
"title": "Internet",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Introduction to Internet",
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"yCnn-NfSxIybUQ2iTuUGq": {
|
||||
"title": "How does the internet work?",
|
||||
"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:",
|
||||
"description": "The internet is a global network that connects computers and devices so they can share information with each other. It’s how you browse websites, send emails, watch videos, and use apps. Think of it like a giant web that links everything together.\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 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:",
|
||||
"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:",
|
||||
"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-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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a Domain Name?",
|
||||
@@ -90,7 +90,7 @@
|
||||
},
|
||||
"aqMaEY8gkKMikiqleV5EP": {
|
||||
"title": "What is hosting?",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is Web Hosting?",
|
||||
@@ -111,7 +111,7 @@
|
||||
},
|
||||
"hkxw9jPGYphmjhTjw8766": {
|
||||
"title": "DNS and how it works?",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is DNS?",
|
||||
@@ -142,7 +142,7 @@
|
||||
},
|
||||
"P82WFaTPgQEPNp5IIuZ1Y": {
|
||||
"title": "Browsers and how they work?",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How Browsers Work",
|
||||
@@ -168,7 +168,7 @@
|
||||
},
|
||||
"yWG2VUkaF5IJVVut6AiSy": {
|
||||
"title": "HTML",
|
||||
"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:",
|
||||
"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:",
|
||||
"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 (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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "W3Schools: Learn HTML",
|
||||
@@ -220,7 +220,7 @@
|
||||
},
|
||||
"z8-556o-PaHXjlytrawaF": {
|
||||
"title": "Writing Semantic HTML",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Guide to Writing Semantic HTML",
|
||||
@@ -246,7 +246,7 @@
|
||||
},
|
||||
"V5zucKEHnIPPjwHqsMPHF": {
|
||||
"title": "Forms and Validations",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "MDN Web Docs: Client-side form validation",
|
||||
@@ -267,7 +267,7 @@
|
||||
},
|
||||
"iJIqi7ngpGHWAqtgdjgxB": {
|
||||
"title": "Accessibility",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Accessibility for Developers by Google",
|
||||
@@ -288,7 +288,7 @@
|
||||
},
|
||||
"mH_qff8R7R6eLQ1tPHLgG": {
|
||||
"title": "SEO Basics",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Complete SEO Course for Beginners",
|
||||
@@ -314,7 +314,7 @@
|
||||
},
|
||||
"ZhJhf1M2OphYbEmduFq-9": {
|
||||
"title": "CSS",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "CSS Complete Course",
|
||||
@@ -345,7 +345,7 @@
|
||||
},
|
||||
"YFjzPKWDwzrgk2HUX952L": {
|
||||
"title": "Learn the basics",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "W3Schools — Learn CSS",
|
||||
@@ -371,7 +371,7 @@
|
||||
},
|
||||
"dXeYVMXv-3MRQ1ovOUuJW": {
|
||||
"title": "Making Layouts",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Learn and Practice Flexbox",
|
||||
@@ -397,7 +397,7 @@
|
||||
},
|
||||
"TKtWmArHn7elXRJdG6lDQ": {
|
||||
"title": "Responsive Design",
|
||||
"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:",
|
||||
"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:",
|
||||
"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 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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated JavaScript Roadmap",
|
||||
@@ -449,7 +449,7 @@
|
||||
},
|
||||
"A4brX0efjZ0FFPTB4r6U0": {
|
||||
"title": "Fetch API / Ajax (XHR)",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Fetch API MDN Docs",
|
||||
@@ -475,7 +475,7 @@
|
||||
},
|
||||
"0MAogsAID9R04R5TTO2Qa": {
|
||||
"title": "Learn DOM Manipulation",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "DOM Tree",
|
||||
@@ -506,7 +506,7 @@
|
||||
},
|
||||
"wQSjQqwKHfn5RGPk34BWI": {
|
||||
"title": "Learn the Basics",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "W3Schools – JavaScript Tutorial",
|
||||
@@ -527,7 +527,7 @@
|
||||
},
|
||||
"MXnFhZlNB1zTsBFDyni9H": {
|
||||
"title": "VCS Hosting",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Git & Github Roadmap",
|
||||
@@ -558,7 +558,7 @@
|
||||
},
|
||||
"NIY7c4TQEEHx0hATu-k5C": {
|
||||
"title": "Version Control Systems",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Git & Github Roadmap",
|
||||
@@ -584,7 +584,7 @@
|
||||
},
|
||||
"R_I4SGYqLk5zze5I1zS_E": {
|
||||
"title": "Git",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Git & Github Roadmap",
|
||||
@@ -615,7 +615,7 @@
|
||||
},
|
||||
"IqvS1V-98cxko3e9sBQgP": {
|
||||
"title": "Package Managers",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PNPM: The Faster, More Performant NPM",
|
||||
@@ -646,7 +646,7 @@
|
||||
},
|
||||
"qmTVMJDsEhNIkiwE_UTYu": {
|
||||
"title": "GitHub",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Git & Github Roadmap",
|
||||
@@ -677,7 +677,7 @@
|
||||
},
|
||||
"zIoSJMX3cuzCgDYHjgbEh": {
|
||||
"title": "GitLab",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "GitLab Website",
|
||||
@@ -703,7 +703,7 @@
|
||||
},
|
||||
"DILBiQp7WWgSZ5hhtDW6A": {
|
||||
"title": "Bitbucket",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How to use BitBucket?",
|
||||
@@ -724,7 +724,7 @@
|
||||
},
|
||||
"yrq3nOwFREzl-9EKnpU-e": {
|
||||
"title": "yarn",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Yarn - Getting Started",
|
||||
@@ -745,7 +745,7 @@
|
||||
},
|
||||
"SLxA5qJFp_28TRzr1BjxZ": {
|
||||
"title": "pnpm",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PNPM Website",
|
||||
@@ -766,7 +766,7 @@
|
||||
},
|
||||
"ib_FHinhrw8VuSet-xMF7": {
|
||||
"title": "npm",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How to NPM",
|
||||
@@ -802,10 +802,10 @@
|
||||
},
|
||||
"eXezX7CVNyC1RuyU_I4yP": {
|
||||
"title": "Pick a Framework",
|
||||
"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:",
|
||||
"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:",
|
||||
"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 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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Angular Roadmap",
|
||||
@@ -844,7 +844,7 @@
|
||||
},
|
||||
"ERAdwL1G9M1bnx-fOm5ZA": {
|
||||
"title": "Vue.js",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Vue Roadmap",
|
||||
@@ -870,7 +870,7 @@
|
||||
},
|
||||
"tG5v3O4lNIFc2uCnacPak": {
|
||||
"title": "React",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Full Stack React Developer Course",
|
||||
@@ -896,7 +896,7 @@
|
||||
},
|
||||
"ZR-qZ2Lcbu3FtqaMd3wM4": {
|
||||
"title": "Svelte",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Svelte Course Playlist for beginners",
|
||||
@@ -927,7 +927,7 @@
|
||||
},
|
||||
"DxOSKnqAjZOPP-dq_U7oP": {
|
||||
"title": "Solid JS",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "SolidJS Website",
|
||||
@@ -953,7 +953,7 @@
|
||||
},
|
||||
"N5DCb6bDfgUnSdHPLYY4g": {
|
||||
"title": "Qwik",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Qwik Website",
|
||||
@@ -969,7 +969,7 @@
|
||||
},
|
||||
"XDTD8el6OwuQ55wC-X4iV": {
|
||||
"title": "Writing CSS",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Modern CSS: A Comprehensive Guide",
|
||||
@@ -990,7 +990,7 @@
|
||||
},
|
||||
"eghnfG4p7i-EDWfp3CQXC": {
|
||||
"title": "Tailwind",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Tailwind CSS Full Course for Beginners",
|
||||
@@ -1026,7 +1026,7 @@
|
||||
},
|
||||
"nPg_YWpMJtlhU2t2UD_6B": {
|
||||
"title": "CSS Architecture",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How to Organize Your CSS with a Modular Architecture",
|
||||
@@ -1047,7 +1047,7 @@
|
||||
},
|
||||
"UTW1pP59dUehuf0zeHXqL": {
|
||||
"title": "CSS Preprocessors",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Sass Website",
|
||||
@@ -1078,7 +1078,7 @@
|
||||
},
|
||||
"dRDmS072xeNLX7p_X565w": {
|
||||
"title": "BEM",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "BEM Official Website",
|
||||
@@ -1099,7 +1099,7 @@
|
||||
},
|
||||
"kukEE5rMSPa4NeNjx21kt": {
|
||||
"title": "Sass",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Sass Website",
|
||||
@@ -1125,7 +1125,7 @@
|
||||
},
|
||||
"9WlPENh9g1xOv-zA64Tfg": {
|
||||
"title": "PostCSS",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PostCSS Website",
|
||||
@@ -1146,7 +1146,7 @@
|
||||
},
|
||||
"i9z0stM4uKu27Cz6NIgNX": {
|
||||
"title": "Build Tools",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Webpack Website",
|
||||
@@ -1172,7 +1172,7 @@
|
||||
},
|
||||
"9VcGfDBBD8YcKatj4VcH1": {
|
||||
"title": "Linters and Formatters",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What Is a Linter?",
|
||||
@@ -1183,7 +1183,7 @@
|
||||
},
|
||||
"hkSc_1x09m7-7BO7WzlDT": {
|
||||
"title": "Module Bundlers",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "The Complete JavaScript Module Bundlers Guide",
|
||||
@@ -1209,7 +1209,7 @@
|
||||
},
|
||||
"NS-hwaWa5ebSmNNRoxFDp": {
|
||||
"title": "Parcel",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Parcel Website",
|
||||
@@ -1230,7 +1230,7 @@
|
||||
},
|
||||
"sCjErk7rfWAUvhl8Kfm3n": {
|
||||
"title": "Rollup",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Rollup Website and Docs",
|
||||
@@ -1251,7 +1251,7 @@
|
||||
},
|
||||
"twufEtHgxcRUWAUQ9bXus": {
|
||||
"title": "Webpack",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Webpack",
|
||||
@@ -1277,7 +1277,7 @@
|
||||
},
|
||||
"4W7UXfdKIUsm1bUrjdTVT": {
|
||||
"title": "esbuild",
|
||||
"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:",
|
||||
"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:",
|
||||
"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 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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Vite - The Build Tool for the Web",
|
||||
@@ -1334,7 +1334,7 @@
|
||||
},
|
||||
"zbkpu_gvQ4mgCiZKzS1xv": {
|
||||
"title": "Prettier",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Prettier Website",
|
||||
@@ -1355,7 +1355,7 @@
|
||||
},
|
||||
"NFjsI712_qP0IOmjuqXar": {
|
||||
"title": "ESLint",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ESLint Website",
|
||||
@@ -1381,7 +1381,7 @@
|
||||
},
|
||||
"igg4_hb3XE3vuvY8ufV-4": {
|
||||
"title": "Testing",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "The Different Types of Software Tests",
|
||||
@@ -1402,7 +1402,7 @@
|
||||
},
|
||||
"hVQ89f6G0LXEgHIOKHDYq": {
|
||||
"title": "Vitest",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Vitest - Next Generation Testing Framework",
|
||||
@@ -1423,7 +1423,7 @@
|
||||
},
|
||||
"g5itUjgRXd9vs9ujHezFl": {
|
||||
"title": "Jest",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Jest Website",
|
||||
@@ -1444,7 +1444,7 @@
|
||||
},
|
||||
"jramLk8FGuaEH4YpHIyZT": {
|
||||
"title": "Playwright",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Playwright Website",
|
||||
@@ -1465,7 +1465,7 @@
|
||||
},
|
||||
"DaynCz5RR26gjT6N6gTDL": {
|
||||
"title": "Cypress",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Cypress Website",
|
||||
@@ -1491,7 +1491,7 @@
|
||||
},
|
||||
"U5mD5FmVx7VWeKxDpQxB5": {
|
||||
"title": "Authentication Strategies",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Basic Authentication",
|
||||
@@ -1527,7 +1527,7 @@
|
||||
},
|
||||
"RDWbG3Iui6IPgp0shvXtg": {
|
||||
"title": "Web Security Basics",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "OWASP Website",
|
||||
@@ -1558,7 +1558,7 @@
|
||||
},
|
||||
"AfH2zCbqzw0Nisg1yyISS": {
|
||||
"title": "CORS",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Cross-Origin Resource Sharing (CORS)",
|
||||
@@ -1579,7 +1579,7 @@
|
||||
},
|
||||
"uum7vOhOUR38vLuGZy8Oa": {
|
||||
"title": "HTTPS",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is HTTPS?",
|
||||
@@ -1610,7 +1610,7 @@
|
||||
},
|
||||
"rmcm0CZbtNVC9LZ14-H6h": {
|
||||
"title": "Content Security Policy",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "MDN Content Security Policy (CSP)",
|
||||
@@ -1631,7 +1631,7 @@
|
||||
},
|
||||
"JanR7I_lNnUCXhCMGLdn-": {
|
||||
"title": "OWASP Security Risks",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "OWASP Web Application Security Testing Checklist",
|
||||
@@ -1662,7 +1662,7 @@
|
||||
},
|
||||
"ruoFa3M4bUE3Dg6GXSiUI": {
|
||||
"title": "Web Components",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Web Components - MDN",
|
||||
@@ -1688,7 +1688,7 @@
|
||||
},
|
||||
"hwPOGT0-duy3KfI8QaEwF": {
|
||||
"title": "Type Checkers",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Flow - Static Type Checker for JavaScript",
|
||||
@@ -1709,7 +1709,7 @@
|
||||
},
|
||||
"VxiQPgcYDFAT6WgSRWpIA": {
|
||||
"title": "Custom Elements",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Using Custom Elements - MDN",
|
||||
@@ -1730,7 +1730,7 @@
|
||||
},
|
||||
"Hk8AVonOd693_y1sykPqd": {
|
||||
"title": "HTML Templates",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Using Templates and Slots - MDN",
|
||||
@@ -1751,7 +1751,7 @@
|
||||
},
|
||||
"-SpsNeOZBkQfDA-rwzgPg": {
|
||||
"title": "Shadow DOM",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Using shadow DOM | MDN web docs",
|
||||
@@ -1772,7 +1772,7 @@
|
||||
},
|
||||
"0asdhvwBH3gn-ercktV7A": {
|
||||
"title": "TypeScript",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated TypeScript Roadmap",
|
||||
@@ -1808,7 +1808,7 @@
|
||||
},
|
||||
"Cxspmb14_0i1tfw-ZLxEu": {
|
||||
"title": "SSR",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Server-Side Rendering (SSR)",
|
||||
@@ -1834,7 +1834,7 @@
|
||||
},
|
||||
"OL8I6nOZ8hGGWmtxg_Mv8": {
|
||||
"title": "Svelte",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Svelte Course Playlist for beginners",
|
||||
@@ -1865,7 +1865,7 @@
|
||||
},
|
||||
"3TE_iYvbklXK0be-5f2M7": {
|
||||
"title": "Vue.js",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Vue.js Website",
|
||||
@@ -1891,7 +1891,7 @@
|
||||
},
|
||||
"k6rp6Ua9qUEW_DA_fOg5u": {
|
||||
"title": "Angular",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Angular Roadmap",
|
||||
@@ -1917,7 +1917,7 @@
|
||||
},
|
||||
"SGDf_rbfmFSHlxI-Czzlz": {
|
||||
"title": "React",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Full Stack React Developer Course",
|
||||
@@ -1943,7 +1943,7 @@
|
||||
},
|
||||
"KJRkrFZIihCUBrOf579EU": {
|
||||
"title": "react-router",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "React Router Website",
|
||||
@@ -1964,7 +1964,7 @@
|
||||
},
|
||||
"zNFYAJaSq0YZXL5Rpx1NX": {
|
||||
"title": "Next.js",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Official Website",
|
||||
@@ -1985,7 +1985,7 @@
|
||||
},
|
||||
"BBsXxkbbEG-gnbM1xXKrj": {
|
||||
"title": "Nuxt.js",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Nuxt.js Fundamentals",
|
||||
@@ -2011,7 +2011,7 @@
|
||||
},
|
||||
"P4st_telfCwKLSAU2WsQP": {
|
||||
"title": "Svelte Kit",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Svelte Kit",
|
||||
@@ -2037,7 +2037,7 @@
|
||||
},
|
||||
"L7AllJfKvClaam3y-u6DP": {
|
||||
"title": "GraphQL",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "visit Dedicated GraphQL Roadmap",
|
||||
@@ -2073,7 +2073,7 @@
|
||||
},
|
||||
"5eUbDdOTOfaOhUlZAmmXW": {
|
||||
"title": "Apollo",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Apollo Website",
|
||||
@@ -2099,7 +2099,7 @@
|
||||
},
|
||||
"0moPO23ol33WsjVXSpTGf": {
|
||||
"title": "Relay Modern",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "facebook/relay",
|
||||
@@ -2120,7 +2120,7 @@
|
||||
},
|
||||
"n0q32YhWEIAUwbGXexoqV": {
|
||||
"title": "Static Site Generators",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a Static Site Generator?",
|
||||
@@ -2146,7 +2146,7 @@
|
||||
},
|
||||
"CMrss8E2W0eA6DVEqtPjT": {
|
||||
"title": "Vuepress",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Vuepress",
|
||||
@@ -2167,7 +2167,7 @@
|
||||
},
|
||||
"XWJxV42Dpu2D3xDK10Pn3": {
|
||||
"title": "Nuxt.js",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Nuxt.js Fundamentals",
|
||||
@@ -2193,7 +2193,7 @@
|
||||
},
|
||||
"iUxXq7beg55y76dkwhM13": {
|
||||
"title": "Astro",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Astro Web Framework Crash Course",
|
||||
@@ -2219,7 +2219,7 @@
|
||||
},
|
||||
"io0RHJWIcVxDhcYkV9d38": {
|
||||
"title": "Eleventy",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Eleventy",
|
||||
@@ -2240,7 +2240,7 @@
|
||||
},
|
||||
"V70884VcuXkfrfHyLGtUg": {
|
||||
"title": "Next.js",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Next.js Website",
|
||||
@@ -2261,7 +2261,7 @@
|
||||
},
|
||||
"PoM77O2OtxPELxfrW1wtl": {
|
||||
"title": "PWAs",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Learn PWA",
|
||||
@@ -2287,7 +2287,7 @@
|
||||
},
|
||||
"VOGKiG2EZVfCBAaa7Df0W": {
|
||||
"title": "Mobile Apps",
|
||||
"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.",
|
||||
"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.",
|
||||
"links": [
|
||||
{
|
||||
"title": "React Native",
|
||||
@@ -2318,7 +2318,7 @@
|
||||
},
|
||||
"dsTegXTyupjS8iU6I7Xiv": {
|
||||
"title": "React Native",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated React Roadmap",
|
||||
@@ -2339,7 +2339,7 @@
|
||||
},
|
||||
"dIQXjFEUAJAGxxfAYceHU": {
|
||||
"title": "Flutter",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Flutter course for beginners",
|
||||
@@ -2375,7 +2375,7 @@
|
||||
},
|
||||
"xmRv6-L45m5MDpHmdHFCL": {
|
||||
"title": "Ionic",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ionic-team/ionic-framework",
|
||||
@@ -2401,7 +2401,7 @@
|
||||
},
|
||||
"KMA7NkxFbPoUDtFnGBFnj": {
|
||||
"title": "Desktop Apps",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Electron Website",
|
||||
@@ -2422,7 +2422,7 @@
|
||||
},
|
||||
"mQHpSyMR4Rra4mqAslgiS": {
|
||||
"title": "Electron",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Electron Website",
|
||||
@@ -2448,7 +2448,7 @@
|
||||
},
|
||||
"GJctl0tVXe4B70s35RkLT": {
|
||||
"title": "Tauri",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Tauri Website",
|
||||
@@ -2469,7 +2469,7 @@
|
||||
},
|
||||
"2MRvAK9G9RGM_auWytcKh": {
|
||||
"title": "Flutter",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Flutter course for beginners",
|
||||
@@ -2505,7 +2505,7 @@
|
||||
},
|
||||
"-DsETM9xLgHyGZthptj1Y": {
|
||||
"title": "PRPL Pattern",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PRPL Pattern - Google Developers",
|
||||
@@ -2521,7 +2521,7 @@
|
||||
},
|
||||
"xD5WfEP7Ez0oi3890UgmH": {
|
||||
"title": "RAIL Model",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "RAIL Model - Google Developers",
|
||||
@@ -2537,7 +2537,7 @@
|
||||
},
|
||||
"X0Y3-IpPiFUCsNDK4RFxw": {
|
||||
"title": "Performance Metrics",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Web Performance Metrics - Google Developers",
|
||||
@@ -2563,7 +2563,7 @@
|
||||
},
|
||||
"RIhHMHLsLLPhNl05Q9aBf": {
|
||||
"title": "Using Lighthouse",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Lighthouse - Google Developers",
|
||||
@@ -2584,7 +2584,7 @@
|
||||
},
|
||||
"3_sJHKTogkDoCjR518-OL": {
|
||||
"title": "Using DevTools",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Chrome DevTools - Google Developers",
|
||||
@@ -2605,7 +2605,7 @@
|
||||
},
|
||||
"raoa-75p_DyBAycvy3yVv": {
|
||||
"title": "Storage",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Web Storage API - MDN",
|
||||
@@ -2626,7 +2626,7 @@
|
||||
},
|
||||
"NDJR8UCoa31v45TBFP7we": {
|
||||
"title": "Web Sockets",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Web Sockets - MDN",
|
||||
@@ -2647,7 +2647,7 @@
|
||||
},
|
||||
"doPe92aUpo-8KWhi45lWK": {
|
||||
"title": "Server Sent Events",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Server-Sent Events - MDN",
|
||||
@@ -2663,7 +2663,7 @@
|
||||
},
|
||||
"TldWoXiqKxM4X3JONKAR7": {
|
||||
"title": "Service Workers",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Service Workers - MDN",
|
||||
@@ -2684,7 +2684,7 @@
|
||||
},
|
||||
"YbGGYoKJEx29PlvopUBiM": {
|
||||
"title": "Location",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Geolocation API - MDN",
|
||||
@@ -2700,7 +2700,7 @@
|
||||
},
|
||||
"6AlcArOiJMhHXguAosDzn": {
|
||||
"title": "Notifications",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Get Started With The Notifications API",
|
||||
@@ -2721,7 +2721,7 @@
|
||||
},
|
||||
"Fd0hQh1DleM0gMzCpGou4": {
|
||||
"title": "Device Orientation",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Device Orientation API - MDN",
|
||||
@@ -2737,7 +2737,7 @@
|
||||
},
|
||||
"MAM1nuVk-h4AvTUk4nvmj": {
|
||||
"title": "Payments",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Payment Request API - MDN",
|
||||
@@ -2753,7 +2753,7 @@
|
||||
},
|
||||
"opu2bAsmdWHqWqtsCscLC": {
|
||||
"title": "Credentials",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Credential Management API - MDN",
|
||||
@@ -2769,7 +2769,7 @@
|
||||
},
|
||||
"h26uS3muFCabe6ekElZcI": {
|
||||
"title": "SWC",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "SWC Website",
|
||||
@@ -2800,7 +2800,7 @@
|
||||
},
|
||||
"wA2fSYsbBYU02VJXAvUz8": {
|
||||
"title": "Astro",
|
||||
"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:",
|
||||
"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:",
|
||||
"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": "video"
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -717,14 +717,8 @@
|
||||
},
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PHP Tutorial CSRF",
|
||||
"url": "https://www.phptutorial.net/php-tutorial/php-csrf/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"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": []
|
||||
},
|
||||
"JbWFfJiCRrXDhnuIx_lqx": {
|
||||
"title": "Password Hashing",
|
||||
@@ -896,7 +890,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Inheritance",
|
||||
"url": "https://www.php.net/manual/en/language.oop5.inheritance.php",
|
||||
"url": "https://www.php.net/manual/en/keyword.extends.php",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
@@ -918,8 +912,8 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Polymorphism",
|
||||
"url": "https://www.phptutorial.net/php-oop/php-polymorphism/",
|
||||
"type": "opensource"
|
||||
"url": "https://www.php.net/manual/en/language.oop5.polymorphism.php",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
{
|
||||
"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,6 +3,11 @@
|
||||
"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 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 are 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": "any",
|
||||
"title": "Arrays",
|
||||
"url": "https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any",
|
||||
"type": "article"
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
},
|
||||
"OpJ2NMKCGXQezpzURE45R": {
|
||||
"title": "API Styles",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "The Difference Between the Composition API and Options API in Vue",
|
||||
@@ -152,7 +152,7 @@
|
||||
},
|
||||
"PPUU3Rb73aCpT4zcyvlJE": {
|
||||
"title": "Options API",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "TypeScript with Options API",
|
||||
@@ -168,7 +168,7 @@
|
||||
},
|
||||
"a0qwdQTunxEjQ9A5wpF-q": {
|
||||
"title": "Composition API",
|
||||
"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:",
|
||||
"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:",
|
||||
"links": [
|
||||
{
|
||||
"title": "TypeScript with Composition API",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 414 KiB |
@@ -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 { htmlToMarkdown } from '../src/lib/html';
|
||||
import { markdownToHtml } from '../src/lib/markdown';
|
||||
import type { Node } from '@roadmapsh/editor';
|
||||
import matter from 'gray-matter';
|
||||
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,23 +21,6 @@ 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);
|
||||
@@ -70,17 +53,13 @@ if (!stats || !stats.isDirectory()) {
|
||||
for (const roadmapId of editorRoadmapIds) {
|
||||
console.log(`🚀 Starting ${roadmapId}`);
|
||||
|
||||
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 {
|
||||
const roadmapDir = path.join(
|
||||
ROADMAP_CONTENT_DIR,
|
||||
roadmapId,
|
||||
`${roadmapId}.json`,
|
||||
);
|
||||
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
|
||||
let { nodes } = JSON.parse(roadmapContent) as {
|
||||
nodes: Node[];
|
||||
};
|
||||
nodes = nodes.filter(
|
||||
@@ -118,11 +97,11 @@ for (const roadmapId of editorRoadmapIds) {
|
||||
> = {};
|
||||
|
||||
for (const node of nodes) {
|
||||
const nodeDirPatternWithoutExt = `${slugify(node?.data?.label as string)}@${node.id}`;
|
||||
const nodeDirPattern = `${nodeDirPatternWithoutExt}.md`;
|
||||
const ndoeDirPatterWithoutExt = `${slugify(node.data.label)}@${node.id}`;
|
||||
const nodeDirPattern = `${ndoeDirPatterWithoutExt}.md`;
|
||||
if (!roadmapContentFiles.includes(nodeDirPattern)) {
|
||||
contentMap[nodeDirPattern] = {
|
||||
title: node?.data?.label as string,
|
||||
title: node.data.label,
|
||||
description: '',
|
||||
links: [],
|
||||
};
|
||||
@@ -190,7 +169,7 @@ for (const roadmapId of editorRoadmapIds) {
|
||||
const description = htmlToMarkdown(htmlStringWithoutLinks);
|
||||
|
||||
contentMap[node.id] = {
|
||||
title: node.data.label as string,
|
||||
title: node.data.label,
|
||||
description,
|
||||
links: listLinks,
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@ 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
|
||||
@@ -51,16 +50,13 @@ if (roadmapFrontmatter.renderer !== 'editor') {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { response: roadmapContent, error } = await httpGet(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${roadmapId}`,
|
||||
const roadmapDir = path.join(
|
||||
ROADMAP_CONTENT_DIR,
|
||||
roadmapId,
|
||||
`${roadmapId}.json`,
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let { nodes, edges } = roadmapContent as {
|
||||
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
|
||||
let { nodes, edges } = JSON.parse(roadmapContent) as {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
};
|
||||
@@ -142,7 +138,7 @@ function writeTopicContent(
|
||||
}
|
||||
|
||||
async function writeNodeContent(node: Node & { parentTitle?: string }) {
|
||||
const nodeDirPattern = `${slugify(node?.data?.label as string)}@${node.id}.md`;
|
||||
const nodeDirPattern = `${slugify(node.data.label)}@${node.id}.md`;
|
||||
if (!roadmapContentFiles.includes(nodeDirPattern)) {
|
||||
console.log(`Missing file for: ${nodeDirPattern}`);
|
||||
return;
|
||||
@@ -156,7 +152,7 @@ async function writeNodeContent(node: Node & { parentTitle?: string }) {
|
||||
return;
|
||||
}
|
||||
|
||||
const topic = node.data.label as string;
|
||||
const topic = node.data.label;
|
||||
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,31 +48,13 @@ if (roadmapFrontmatter.renderer !== 'editor') {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
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 {
|
||||
const roadmapDir = path.join(
|
||||
ROADMAP_CONTENT_DIR,
|
||||
roadmapId,
|
||||
`${roadmapId}.json`,
|
||||
);
|
||||
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
|
||||
let { nodes } = JSON.parse(roadmapContent) as {
|
||||
nodes: Node[];
|
||||
};
|
||||
nodes = nodes.filter(
|
||||
@@ -91,7 +73,7 @@ const roadmapContentFiles = await fs.readdir(roadmapContentDir, {
|
||||
});
|
||||
|
||||
nodes.forEach(async (node, index) => {
|
||||
const nodeDirPattern = `${slugify(node.data.label as string)}@${node.id}.md`;
|
||||
const nodeDirPattern = `${slugify(node.data.label)}@${node.id}.md`;
|
||||
if (roadmapContentFiles.includes(nodeDirPattern)) {
|
||||
console.log(`Skipping ${nodeDirPattern}`);
|
||||
return;
|
||||
|
||||
@@ -2,22 +2,20 @@ 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, wrapperClassName } = props;
|
||||
const { children, activeTab } = props;
|
||||
|
||||
const [isSidebarFloating, setIsSidebarFloating] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<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">
|
||||
<div className="flex flex-row items-center justify-between border-b border-slate-200 px-4 py-3 lg:hidden sticky top-0 bg-white z-10">
|
||||
<a href="/" className="flex flex-row items-center gap-1.5">
|
||||
<RoadmapLogoIcon className="size-6 text-gray-500" color="black" />
|
||||
</a>
|
||||
@@ -29,18 +27,13 @@ export function AITutorLayout(props: AITutorLayoutProps) {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-grow flex-row lg:h-screen">
|
||||
<div className="flex flex-grow lg:h-screen flex-row">
|
||||
<AITutorSidebar
|
||||
onClose={() => setIsSidebarFloating(false)}
|
||||
isFloating={isSidebarFloating}
|
||||
activeTab={activeTab}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-grow flex-col overflow-y-scroll bg-gray-100 p-3 lg:p-4',
|
||||
wrapperClassName,
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-grow flex-col overflow-y-scroll bg-gray-100 p-3 lg:px-4 lg:py-4">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import {
|
||||
BookOpen, Compass,
|
||||
Plus,
|
||||
Star,
|
||||
X,
|
||||
Zap
|
||||
} from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { useIsPaidUser } from '../../queries/billing';
|
||||
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
||||
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';
|
||||
|
||||
type AITutorSidebarProps = {
|
||||
isFloating: boolean;
|
||||
@@ -30,12 +24,6 @@ const sidebarItems = [
|
||||
href: '/ai/courses',
|
||||
icon: BookOpen,
|
||||
},
|
||||
// {
|
||||
// key: 'chat',
|
||||
// label: 'AI Chat',
|
||||
// href: '/ai/chat',
|
||||
// icon: Bot,
|
||||
// },
|
||||
{
|
||||
key: 'staff-picks',
|
||||
label: 'Staff Picks',
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<script async defer src='https://b174.roadmap.sh/script.js'></script>
|
||||
@@ -1,5 +1,3 @@
|
||||
import { httpPost } from '../../lib/query-http';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
gtag: any;
|
||||
@@ -22,18 +20,6 @@ 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,9 +2,8 @@ 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 = {
|
||||
@@ -66,11 +65,7 @@ export function EmailLoginForm(props: EmailLoginFormProps) {
|
||||
const passwordFieldId = `form:${useId()}`;
|
||||
|
||||
return (
|
||||
<form
|
||||
className="w-full"
|
||||
onSubmit={handleFormSubmit}
|
||||
suppressHydrationWarning={true} // Hubspot adds data-* attributes which causes hydration errors
|
||||
>
|
||||
<form className="w-full" onSubmit={handleFormSubmit}>
|
||||
<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 leading-5 font-semibold text-slate-900'>
|
||||
<p class='mb-3 text-2xl font-semibold leading-5 text-slate-900'>
|
||||
Login or Signup
|
||||
</p>
|
||||
<p class='mt-2 text-sm leading-4 text-slate-600'>
|
||||
|
||||
@@ -108,6 +108,8 @@ 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 transition-colors hover:bg-gray-50 disabled:opacity-50"
|
||||
className="rounded-md border border-gray-200 py-2 text-sm font-semibold hover:bg-gray-50 transition-colors 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 transition-colors hover:bg-purple-500 disabled:opacity-50"
|
||||
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"
|
||||
disabled={isPending}
|
||||
onClick={() => {
|
||||
updatePlan({ priceId: selectedPrice.priceId });
|
||||
}}
|
||||
>
|
||||
{isPending && (
|
||||
<Loader2Icon className="mr-2 size-4 animate-spin stroke-[2.5]" />
|
||||
<Loader2Icon className="size-4 animate-spin stroke-[2.5] mr-2" />
|
||||
)}
|
||||
{!isPending && 'Confirm'}
|
||||
</button>
|
||||
|
||||
@@ -136,14 +136,6 @@ 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;
|
||||
}
|
||||
@@ -270,35 +262,13 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
|
||||
}
|
||||
onClick={() => {
|
||||
setSelectedPlan(plan.interval);
|
||||
|
||||
if (!currentPlanPriceId) {
|
||||
const currentUrlPath = window.location.pathname;
|
||||
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 || `${currentUrlPath}?s=1`,
|
||||
cancel: cancel || `${currentUrlPath}?s=0`,
|
||||
});
|
||||
|
||||
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,40 +30,11 @@ 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
|
||||
@@ -76,11 +47,11 @@ export function VerifyUpgrade(props: VerifyUpgradeProps) {
|
||||
<h3 className="text-xl font-bold text-black">Subscription Activated</h3>
|
||||
</div>
|
||||
|
||||
<p className="mt-2 text-center text-balance text-gray-600">
|
||||
<p className="mt-2 text-balance text-center text-gray-600">
|
||||
Your subscription has been activated successfully.
|
||||
</p>
|
||||
|
||||
<p className="mt-4 text-center text-balance text-gray-600">
|
||||
<p className="mt-4 text-balance text-center 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>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
.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;
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
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,
|
||||
}),
|
||||
];
|
||||
},
|
||||
});
|
||||
@@ -1,175 +0,0 @@
|
||||
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();
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -36,9 +36,7 @@ export function EditorRoadmap(props: EditorRoadmapProps) {
|
||||
|
||||
const { response, error } = await httpGet<
|
||||
Omit<RoadmapRendererProps, 'resourceId'>
|
||||
>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${switchRoadmapId || resourceId}`,
|
||||
);
|
||||
>(`/${switchRoadmapId || resourceId}.json`);
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { GuideFileType, GuideFrontmatter } from '../../lib/guide';
|
||||
import { type QuestionGroupType } from '../../lib/question-group';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export interface GuideListItemProps {
|
||||
guide: GuideFileType | QuestionGroupType;
|
||||
@@ -28,11 +27,6 @@ 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"
|
||||
@@ -41,17 +35,19 @@ export function GuideListItem(props: GuideListItemProps) {
|
||||
<span className="text-sm transition-transform group-hover:translate-x-2 md:text-base">
|
||||
{frontmatter.title}
|
||||
|
||||
{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">
|
||||
{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">
|
||||
New
|
||||
<span className="hidden sm:inline">
|
||||
·
|
||||
{frontmatter.date ? dayjs(frontmatter.date).format('MMMM') : ''}
|
||||
{new Date(frontmatter.date || '').toLocaleString('default', {
|
||||
month: 'long',
|
||||
})}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="hidden text-xs text-gray-500 capitalize sm:block">
|
||||
<span className="hidden text-xs capitalize text-gray-500 sm:block">
|
||||
{guideType}
|
||||
</span>
|
||||
|
||||
|
||||
@@ -67,13 +67,14 @@ 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='Follow on LinkedIn'
|
||||
href='https://www.linkedin.com/company/roadmapsh'
|
||||
aria-label='Write us an email'
|
||||
href='mailto:info@roadmap.sh'
|
||||
class='hover:text-white'
|
||||
target='_blank'
|
||||
>
|
||||
<AstroIcon icon='linkedin-2' class='inline-block h-5 w-5 fill-current' />
|
||||
<AstroIcon icon='letter' class='inline-block h-5 w-5' />
|
||||
</a>
|
||||
<a
|
||||
aria-label='Subscribe to YouTube channel'
|
||||
@@ -94,17 +95,6 @@ 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,14 +51,6 @@ 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();
|
||||
|
||||
@@ -56,7 +56,7 @@ export function FineTuneCourse(props: FineTuneCourseProps) {
|
||||
return (
|
||||
<div className="mt-0 flex flex-col">
|
||||
<Question
|
||||
label="Tell us about yourself"
|
||||
label="Tell us about your self"
|
||||
placeholder="e.g. I am a frontend developer and have good knowledge of HTML, CSS, and JavaScript."
|
||||
value={about}
|
||||
onChange={setAbout}
|
||||
|
||||
@@ -5,7 +5,6 @@ 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={'+2.1M'} secondaryValue="+90k" />
|
||||
<OpenSourceStat text='Registered Users' value={'+1.5M'} secondaryValue="+90k" />
|
||||
<OpenSourceStat
|
||||
text='Discord Members'
|
||||
value={discordInfo.totalFormatted}
|
||||
|
||||
@@ -1,420 +0,0 @@
|
||||
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,11 +1,7 @@
|
||||
---
|
||||
import { getGuideTableOfContent, type HeadingGroupType } from '../../lib/guide';
|
||||
import { markdownToHtml } from '../../lib/markdown';
|
||||
import {
|
||||
type QuestionGroupType,
|
||||
type QuestionType,
|
||||
} from '../../lib/question-group';
|
||||
import { slugify } from '../../lib/slugger';
|
||||
import { type QuestionGroupType } from '../../lib/question-group';
|
||||
import { RelatedGuides } from '../Guide/RelatedGuides';
|
||||
import MarkdownFile from '../MarkdownFile.astro';
|
||||
import { TableOfContent } from '../TableOfContent/TableOfContent';
|
||||
@@ -17,27 +13,8 @@ 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();
|
||||
let tableOfContent: HeadingGroupType[] = [
|
||||
const tableOfContent: HeadingGroupType[] = [
|
||||
...getGuideTableOfContent(allHeadings),
|
||||
{
|
||||
depth: 2,
|
||||
@@ -47,33 +24,36 @@ let tableOfContent: HeadingGroupType[] = [
|
||||
},
|
||||
{
|
||||
depth: 2,
|
||||
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 {
|
||||
children: [
|
||||
{
|
||||
depth: 2,
|
||||
title: 'Beginner Level',
|
||||
children: [],
|
||||
slug: topicSlug,
|
||||
text: topicText,
|
||||
};
|
||||
}),
|
||||
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: '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]'>
|
||||
@@ -99,7 +79,7 @@ const showTableOfContent = tableOfContent.length > 0;
|
||||
]}
|
||||
>
|
||||
<MarkdownFile>
|
||||
<h1 class='mb-3 text-4xl font-bold text-balance'>
|
||||
<h1 class='mb-3 text-balance text-4xl font-bold'>
|
||||
{guideFrontmatter.title}
|
||||
</h1>
|
||||
{
|
||||
@@ -112,7 +92,7 @@ const showTableOfContent = tableOfContent.length > 0;
|
||||
<img
|
||||
alt={author.frontmatter.name}
|
||||
src={author.frontmatter.imageUrl}
|
||||
class='mr-2 mb-0 inline h-5 w-5 rounded-full'
|
||||
class='mb-0 mr-2 inline h-5 w-5 rounded-full'
|
||||
/>
|
||||
{author.frontmatter.name}
|
||||
</a>
|
||||
@@ -149,30 +129,31 @@ const showTableOfContent = tableOfContent.length > 0;
|
||||
</p>
|
||||
|
||||
{
|
||||
Object.keys(questionsGroupedByTopics).map((questionLevel) => (
|
||||
['beginner', 'intermediate', 'advanced'].map((questionLevel) => (
|
||||
<div class='mb-5'>
|
||||
<h3 id={slugify(questionLevel)} class='mb-0 capitalize'>
|
||||
{questionLevel}{' '}
|
||||
{['Beginner', 'Intermediate', 'Advanced'].includes(questionLevel)
|
||||
? 'Level'
|
||||
: ''}
|
||||
<h3 id={`${questionLevel}-level`} class='mb-0 capitalize'>
|
||||
{questionLevel} Level
|
||||
</h3>
|
||||
{questionsGroupedByTopics[questionLevel].map((q) => (
|
||||
<div class='mb-5'>
|
||||
<h4>{q.question}</h4>
|
||||
<div set:html={markdownToHtml(q.answer, false)} />
|
||||
</div>
|
||||
))}
|
||||
{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>
|
||||
))}
|
||||
</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>
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
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');
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
.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;
|
||||
}
|
||||
@@ -1,731 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
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,20 +2,19 @@
|
||||
import {
|
||||
ArrowLeftIcon,
|
||||
BookOpenIcon,
|
||||
Bot,
|
||||
FolderKanbanIcon,
|
||||
MapIcon,
|
||||
MessageCircle,
|
||||
} from 'lucide-react';
|
||||
import { type RoadmapFrontmatter } from '../lib/roadmap';
|
||||
import LoginPopup from './AuthenticationFlow/LoginPopup.astro';
|
||||
import { DownloadRoadmapButton } from './DownloadRoadmapButton';
|
||||
import { MarkFavorite } from './FeaturedItems/MarkFavorite';
|
||||
import ProgressHelpPopup from './ProgressHelpPopup.astro';
|
||||
import { ScheduleButton } from './Schedule/ScheduleButton';
|
||||
import { ShareRoadmapButton } from './ShareRoadmapButton';
|
||||
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 { DownloadRoadmapButton } from './DownloadRoadmapButton';
|
||||
import { CourseAnnouncement } from './SQLCourse/CourseAnnouncement';
|
||||
export interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
@@ -30,7 +29,6 @@ export interface Props {
|
||||
hasSearch?: boolean;
|
||||
projectCount?: number;
|
||||
coursesCount?: number;
|
||||
hasAIChat?: boolean;
|
||||
question?: RoadmapFrontmatter['question'];
|
||||
hasTopics?: boolean;
|
||||
isForkable?: boolean;
|
||||
@@ -45,7 +43,6 @@ const {
|
||||
isUpcoming = false,
|
||||
note,
|
||||
hasTopics = false,
|
||||
hasAIChat = false,
|
||||
projectCount = 0,
|
||||
question,
|
||||
activeTab = 'roadmap',
|
||||
@@ -128,7 +125,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-2'>
|
||||
<div class='relative top-px flex gap-1 sm:gap-3'>
|
||||
<TabLink
|
||||
url={`/${roadmapId}`}
|
||||
icon={MapIcon}
|
||||
@@ -150,16 +147,6 @@ 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'
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -161,12 +161,9 @@ 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: successUrl,
|
||||
success: `/courses/${SQL_COURSE_SLUG}?${COURSE_PURCHASE_SUCCESS_PARAM}=1`,
|
||||
cancel: `/courses/${SQL_COURSE_SLUG}?${COURSE_PURCHASE_SUCCESS_PARAM}=0`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@ 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__';
|
||||
const COURSE_ANNOUNCEMENT_STORAGE_KEY = '__course_announcement_closed_at__';
|
||||
|
||||
export function CourseAnnouncement() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const hiddenEndAt = Number(
|
||||
localStorage.getItem(HIDE_ANNOUNCEMENT_END_KEY) || '0',
|
||||
const closedAt = Number(
|
||||
localStorage.getItem(COURSE_ANNOUNCEMENT_STORAGE_KEY) || '0',
|
||||
);
|
||||
|
||||
// only show if the closed at passed 14 days ago
|
||||
const shouldShow = hiddenEndAt < Date.now();
|
||||
// only show if the closed at passed 3 days ago
|
||||
const shouldShow = closedAt < Date.now();
|
||||
if (!shouldShow) {
|
||||
return;
|
||||
}
|
||||
@@ -56,11 +56,10 @@ export function CourseAnnouncement() {
|
||||
onClick={(e) => {
|
||||
setIsVisible(false);
|
||||
|
||||
// 14 days from now
|
||||
const fourteenDaysFromNow = Date.now() + 1000 * 60 * 60 * 24 * 14;
|
||||
const threeDaysFromNow = Date.now() + 1000 * 60 * 60 * 24 * 3;
|
||||
localStorage.setItem(
|
||||
HIDE_ANNOUNCEMENT_END_KEY,
|
||||
String(fourteenDaysFromNow),
|
||||
COURSE_ANNOUNCEMENT_STORAGE_KEY,
|
||||
String(threeDaysFromNow),
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
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,7 +4,6 @@ import { cn } from '../lib/classname.ts';
|
||||
type TabLinkProps = {
|
||||
icon: LucideIcon;
|
||||
text: string;
|
||||
mobileText?: string;
|
||||
isActive: boolean;
|
||||
isExternal?: boolean;
|
||||
badgeText?: string;
|
||||
@@ -20,7 +19,6 @@ export function TabLink(props: TabLinkProps) {
|
||||
isExternal = false,
|
||||
url,
|
||||
text,
|
||||
mobileText,
|
||||
isActive,
|
||||
hideTextOnMobile = false,
|
||||
className: additionalClassName = '',
|
||||
@@ -77,8 +75,7 @@ export function TabLink(props: TabLinkProps) {
|
||||
className={className}
|
||||
>
|
||||
<Icon className="h-4 w-4 shrink-0" />
|
||||
<span className={cn(textClass, 'hidden sm:inline')}>{text}</span>
|
||||
<span className={cn(textClass, 'inline sm:hidden')}>{mobileText || text}</span>
|
||||
<span className={textClass}>{text}</span>
|
||||
{badgeNode}
|
||||
</a>
|
||||
);
|
||||
|
||||
@@ -62,6 +62,15 @@ 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,
|
||||
@@ -83,7 +92,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
return response;
|
||||
}
|
||||
|
||||
async function renderResource() {
|
||||
async function renderResource(jsonUrl: string) {
|
||||
const page = await getResourceMeta(resourceType, resourceId);
|
||||
if (!page) {
|
||||
toast.error('Resource not found');
|
||||
@@ -93,22 +102,11 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
const renderer = page.renderer || 'balsamiq';
|
||||
setRenderer(renderer);
|
||||
|
||||
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 res = await fetch(jsonUrl, {});
|
||||
const json = await res.json();
|
||||
const svg =
|
||||
renderer === 'editor'
|
||||
? await renderFlowJSON(json)
|
||||
? await renderFlowJSON(json as any)
|
||||
: await wireframeJSONToSVG(json, {
|
||||
fontURL: '/fonts/balsamiq.woff2',
|
||||
});
|
||||
@@ -131,13 +129,19 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerEl.current || !resourceId || !resourceType || !teamId) {
|
||||
if (
|
||||
!containerEl.current ||
|
||||
!resourceJsonUrl ||
|
||||
!resourceId ||
|
||||
!resourceType ||
|
||||
!teamId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
Promise.all([
|
||||
renderResource(),
|
||||
renderResource(resourceJsonUrl),
|
||||
getMemberProgress(teamId, member._id, resourceType, resourceId),
|
||||
])
|
||||
.then(([_, memberProgress = {}]) => {
|
||||
@@ -272,7 +276,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
}, [member]);
|
||||
|
||||
return (
|
||||
<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="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
|
||||
id={renderer === 'editor' ? undefined : 'customized-roadmap'}
|
||||
className="relative mx-auto h-full w-full max-w-4xl p-4 md:h-auto"
|
||||
@@ -300,14 +304,14 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
||||
<div className="flex w-full justify-center">
|
||||
<Spinner
|
||||
isDualRing={false}
|
||||
className="mt-2 mb-4 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
||||
className="mb-4 mt-2 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
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 ${
|
||||
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 ${
|
||||
isCurrentUser ? 'hover:bg-gray-800' : 'hover:bg-gray-100'
|
||||
}`}
|
||||
onClick={onClose}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -42,7 +42,16 @@ 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 TopicDetailProps = {
|
||||
resourceId?: string;
|
||||
resourceTitle?: string;
|
||||
resourceType?: ResourceType;
|
||||
renderer?: AllowedRoadmapRenderer;
|
||||
|
||||
isEmbed?: boolean;
|
||||
canSubmitContribution: boolean;
|
||||
};
|
||||
|
||||
type PaidResourceType = {
|
||||
_id?: string;
|
||||
@@ -83,41 +92,13 @@ 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,
|
||||
renderer = 'balsamiq',
|
||||
wrapperClassName,
|
||||
bodyClassName,
|
||||
overlayClassName,
|
||||
closeButtonClassName,
|
||||
onClose,
|
||||
shouldCloseOnBackdropClick = true,
|
||||
shouldCloseOnEscape = true,
|
||||
defaultActiveTab = 'content',
|
||||
resourceTitle,
|
||||
} = props;
|
||||
|
||||
const [hasEnoughLinks, setHasEnoughLinks] = useState(false);
|
||||
@@ -132,14 +113,12 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
const [topicHtmlTitle, setTopicHtmlTitle] = useState('');
|
||||
const [links, setLinks] = useState<RoadmapContentDocument['links']>([]);
|
||||
const [activeTab, setActiveTab] =
|
||||
useState<AllowedTopicDetailsTabs>(defaultActiveTab);
|
||||
useState<AllowedTopicDetailsTabs>('content');
|
||||
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] =
|
||||
@@ -156,20 +135,15 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
const [paidResources, setPaidResources] = useState<PaidResourceType[]>([]);
|
||||
|
||||
const handleClose = () => {
|
||||
onClose?.();
|
||||
setIsActive(false);
|
||||
setShowUpgradeModal(false);
|
||||
setAiChatHistory(defaultChatHistory);
|
||||
setActiveTab('content');
|
||||
setShowSubjectSearchModal(false);
|
||||
};
|
||||
|
||||
// Close the topic detail when user clicks outside the topic detail
|
||||
useOutsideClick(
|
||||
topicRef,
|
||||
shouldCloseOnBackdropClick ? handleClose : undefined,
|
||||
);
|
||||
useKeydown('Escape', shouldCloseOnEscape ? handleClose : undefined);
|
||||
useOutsideClick(topicRef, handleClose);
|
||||
useKeydown('Escape', handleClose);
|
||||
|
||||
useEffect(() => {
|
||||
if (resourceType !== 'roadmap' || !defaultResourceId) {
|
||||
@@ -338,17 +312,12 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
}
|
||||
|
||||
topicHtml = topicDom.body.innerHTML;
|
||||
const topicHasContent = otherElems.length > 0;
|
||||
|
||||
setLinks(listLinks);
|
||||
setHasContent(topicHasContent);
|
||||
setHasContent(otherElems.length > 0);
|
||||
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 || '');
|
||||
@@ -371,9 +340,7 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isActive) {
|
||||
topicRef?.current?.focus();
|
||||
}
|
||||
if (isActive) topicRef?.current?.focus();
|
||||
|
||||
lockBodyScroll(isActive);
|
||||
}, [isActive]);
|
||||
@@ -382,6 +349,10 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const resourceTitleForSearch = resourceTitle
|
||||
?.toLowerCase()
|
||||
?.replace(/\s+?roadmap/gi, '');
|
||||
|
||||
const tnsLink =
|
||||
'https://thenewstack.io/devops/?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Topic';
|
||||
|
||||
@@ -391,26 +362,23 @@ 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={cn('relative z-92', wrapperClassName)}>
|
||||
<div className={'relative z-92'}>
|
||||
<div
|
||||
ref={topicRef}
|
||||
tabIndex={0}
|
||||
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,
|
||||
)}
|
||||
className="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"
|
||||
>
|
||||
{showUpgradeModal && (
|
||||
<UpgradeAccountModal onClose={() => setShowUpgradeModal(false)} />
|
||||
)}
|
||||
|
||||
{showSubjectSearchModal && (
|
||||
<CreateCourseModal onClose={() => setShowSubjectSearchModal(false)} />
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<div className="flex h-full w-full justify-center">
|
||||
<Spinner
|
||||
@@ -454,16 +422,13 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
}
|
||||
resourceId={resourceId}
|
||||
resourceType={resourceType}
|
||||
onClose={() => null}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
)}
|
||||
<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,
|
||||
)}
|
||||
className="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"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<X className="size-4" />
|
||||
@@ -478,20 +443,11 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
topicId={topicId}
|
||||
aiChatHistory={aiChatHistory}
|
||||
setAiChatHistory={setAiChatHistory}
|
||||
hasUpgradeButtons={hasUpgradeButtons}
|
||||
onUpgrade={() => setShowUpgradeModal(true)}
|
||||
onLogin={() => {
|
||||
handleClose();
|
||||
showLoginPopup();
|
||||
}}
|
||||
onShowSubjectSearchModal={() => {
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
setShowSubjectSearchModal(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -624,7 +580,7 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
<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"
|
||||
className="hidden items-center justify-center rounded-md border bg-gray-200 px-2 py-2 text-sm hover:bg-gray-300 sm:flex"
|
||||
>
|
||||
<GitHubIcon className="mr-2 inline-block h-4 w-4 text-current" />
|
||||
Help us Improve this Content
|
||||
@@ -673,9 +629,7 @@ export function TopicDetail(props: TopicDetailProps) {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={cn('fixed inset-0 z-30 bg-gray-900/50', overlayClassName)}
|
||||
></div>
|
||||
<div className="fixed inset-0 z-30 bg-gray-900/50"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,9 @@ import {
|
||||
ChevronRightIcon,
|
||||
Gift,
|
||||
Loader2Icon,
|
||||
LockIcon, SendIcon, Trash2,
|
||||
WandSparkles
|
||||
LockIcon,
|
||||
SendIcon,
|
||||
Trash2,
|
||||
} from 'lucide-react';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { cn } from '../../lib/classname';
|
||||
@@ -36,15 +37,11 @@ type TopicDetailAIProps = {
|
||||
resourceType: ResourceType;
|
||||
topicId: string;
|
||||
|
||||
hasUpgradeButtons?: boolean;
|
||||
|
||||
aiChatHistory: AIChatHistoryType[];
|
||||
setAiChatHistory: (history: AIChatHistoryType[]) => void;
|
||||
|
||||
onUpgrade: () => void;
|
||||
onLogin: () => void;
|
||||
|
||||
onShowSubjectSearchModal: () => void;
|
||||
};
|
||||
|
||||
export function TopicDetailAI(props: TopicDetailAIProps) {
|
||||
@@ -54,10 +51,8 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
|
||||
resourceId,
|
||||
resourceType,
|
||||
topicId,
|
||||
hasUpgradeButtons = true,
|
||||
onUpgrade,
|
||||
onLogin,
|
||||
onShowSubjectSearchModal,
|
||||
} = props;
|
||||
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
@@ -263,19 +258,6 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
|
||||
<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"
|
||||
>
|
||||
@@ -303,14 +285,6 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
|
||||
))}
|
||||
</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>
|
||||
)}
|
||||
@@ -354,7 +328,7 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!isPaidUser && hasUpgradeButtons && (
|
||||
{!isPaidUser && (
|
||||
<>
|
||||
<button
|
||||
className="hidden rounded-md bg-gray-200 px-2 py-1 text-sm hover:bg-gray-300 sm:block"
|
||||
@@ -474,7 +448,6 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
|
||||
)}
|
||||
</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} />
|
||||
|
||||
@@ -54,7 +54,7 @@ function TopicDetailsTab(props: TopicDetailsTabProps) {
|
||||
<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">
|
||||
<span className="hidden rounded-sm bg-yellow-400 px-1 text-xs text-black sm:block">
|
||||
New
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -14,10 +14,9 @@ import type {
|
||||
} from '../../lib/resource-progress';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { ChevronDown, Loader2 } from 'lucide-react';
|
||||
import { Spinner } from '../ReactIcons/Spinner';
|
||||
import { ChevronDown } 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',
|
||||
@@ -180,8 +179,6 @@ export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
);
|
||||
|
||||
const handleUpdateResourceProgress = (progress: ResourceProgressType) => {
|
||||
setShowChangeStatus(false);
|
||||
|
||||
if (isGuest) {
|
||||
onClose();
|
||||
showLoginPopup();
|
||||
@@ -212,9 +209,6 @@ export function TopicProgressButton(props: TopicProgressButtonProps) {
|
||||
.finally(() => {
|
||||
setShowChangeStatus(false);
|
||||
setIsUpdatingProgress(false);
|
||||
queryClient.invalidateQueries(
|
||||
userResourceProgressOptions(resourceType, resourceId),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -231,38 +225,32 @@ 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 isDualRing={false} className="h-4 w-4" />
|
||||
<span className="ml-2">Please wait..</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative inline-flex">
|
||||
<button
|
||||
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',
|
||||
'flex cursor-default cursor-pointer items-center rounded-md border border-gray-300 p-1 px-2 text-sm text-black hover:border-gray-400',
|
||||
)}
|
||||
onClick={() => setShowChangeStatus(true)}
|
||||
>
|
||||
{!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" />
|
||||
<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>
|
||||
<ChevronDown className="ml-2 h-4 w-4" />
|
||||
</button>
|
||||
|
||||
{showChangeStatus && (
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
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 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="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="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,10 +65,8 @@ export function UserProgressModal(props: ProgressMapProps) {
|
||||
let resourceJsonUrl = import.meta.env.DEV
|
||||
? 'http://localhost:3000'
|
||||
: 'https://roadmap.sh';
|
||||
if (resourceType === 'roadmap' && renderer === 'balsamiq') {
|
||||
if (resourceType === 'roadmap') {
|
||||
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' : 'relative container max-w-[1000px]!',
|
||||
isCustomResource ? 'w-full' : 'container relative max-w-[1000px]!',
|
||||
)}
|
||||
>
|
||||
{isCustomResource ? (
|
||||
@@ -136,7 +136,7 @@ export function UserProfileRoadmapRenderer(
|
||||
<div className="flex w-full justify-center">
|
||||
<Spinner
|
||||
isDualRing={false}
|
||||
className="mt-2 mb-4 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
||||
className="mb-4 mt-2 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: 'AI Agents, Red Teaming Roadmaps and Community Courses'
|
||||
description: 'New roadmaps for AI Agents and Red Teaming plus access to community-generated AI courses'
|
||||
images:
|
||||
'AI Agents': 'https://assets.roadmap.sh/guest/ai-agents-roadmap-min-poii3.png'
|
||||
'Red Teaming': 'https://assets.roadmap.sh/guest/ai-red-teaming-omyvx.png'
|
||||
'AI Community Courses': 'https://assets.roadmap.sh/guest/ai-community-courses.png'
|
||||
seo:
|
||||
title: 'AI Agents, Red Teaming Roadmaps and Community Courses'
|
||||
description: ''
|
||||
date: 2025-05-12
|
||||
---
|
||||
|
||||
We added new AI roadmaps for AI Agents and Red Teaming and made our AI Tutor better with courses created by the community.
|
||||
|
||||
- We just released a new [AI Agents Roadmap](https://roadmap.sh/ai-agents) that covers how to build, design, and run smart autonomous systems.
|
||||
- There's also a new [Red Teaming Roadmap](https://roadmap.sh/ai-red-teaming) for people who want to learn about testing AI systems for weaknesses and security flaws.
|
||||
- Our [AI Tutor](https://roadmap.sh/ai) now lets you use courses made by other users. You can learn from their content or share your own learning plans.
|
||||
@@ -15,4 +15,6 @@ We have revised the C++ and Java roadmaps and introduced an AI tutor to help you
|
||||
|
||||
- We just launched an [AI Tutor](https://roadmap.sh/ai), just give it a topic, pick a difficulty level and it will generate a personalized study plan for you. There is a map view, quizzes an embedded chat to help you along the way.
|
||||
- [C++ roadmap](https://roadmap.sh/cpp) has been revised with improved content
|
||||
- We have also redrawn the [Java roadmap](https://roadmap.sh/java) from scratch, replacing the deprecated items, adding new content and improving the overall structure.
|
||||
- We have also redrawn the [Java roadmap](https://roadmap.sh/java) from scratch, replacing the deprecated items, adding new content and improving the overall structure.
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ seo:
|
||||
title: "Data Science vs Statistics: How do they compare?"
|
||||
description: "Not sure whether to pursue data science or statistics? This guide breaks down the key differences, career paths, and skills you need to make an informed choice."
|
||||
ogImageUrl: 'https://assets.roadmap.sh/guest/data-science-vs-statistics-e3rtw.jpg'
|
||||
isNew: false
|
||||
isNew: true
|
||||
type: 'textual'
|
||||
date: 2025-03-24
|
||||
sitemap:
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
---
|
||||
title: 'Data Analyst Career Path: My Pro Advice'
|
||||
description: 'Wondering where a data analyst role can take you? Learn what your data analyst career path could look like.'
|
||||
authorId: fernando
|
||||
excludedBySlug: '/data-analyst/career-path'
|
||||
seo:
|
||||
title: 'Data Analyst Career Path: My Pro Advice'
|
||||
description: 'Wondering where a data analyst role can take you? Learn what your data analyst career path could look like.'
|
||||
ogImageUrl: 'https://assets.roadmap.sh/guest/data-analyst-career-path-heu4b.jpg'
|
||||
isNew: false
|
||||
type: 'textual'
|
||||
date: 2025-05-15
|
||||
sitemap:
|
||||
priority: 0.7
|
||||
changefreq: 'weekly'
|
||||
tags:
|
||||
- 'guide'
|
||||
- 'textual-guide'
|
||||
- 'guide-sitemap'
|
||||
---
|
||||
|
||||

|
||||
|
||||
# Data Analyst Career Path: My Pro Advice
|
||||
|
||||
Data analysts sit at the heart of decision‑making in virtually every industry today. From uncovering customer behavior patterns in retail to optimizing operations in healthcare, the ability to collect, clean, and interpret data has become a critical superpower.
|
||||
|
||||
To help you map out your future data analyst career path, in this guide I'll mix in two cornerstone resources from roadmap.sh: the very detailed [Data Analyst Roadmap](https://roadmap.sh/data-analyst), which lays out the skills and milestones you'll need from beginner to pro, and the hands‑on [SQL Course](https://roadmap.sh/courses/sql), designed to build your foundation in one of the most common languages when it comes to data operations. Together, these tools will serve as your compass and toolkit, ensuring you have a clear path forward and the practical know‑how to tackle real‑world challenges.
|
||||
|
||||
## Options for a Data Analyst Career Path
|
||||
|
||||

|
||||
|
||||
Knowing where to go and how to grow in data analysis is not trivial, simply because there are too many very valid and interesting options for data analysts.
|
||||
|
||||
To narrow the list of options, we can think of three core trajectories, each with its own set of responsibilities, key skills, and growth opportunities:
|
||||
|
||||
### Junior Data Analyst → Senior Data Analyst → Analytics Manager
|
||||
|
||||
| Level | Focus | Key skills | Goal |
|
||||
| :---- | :---- | :---- | :---- |
|
||||
| **Entry-level (i.e junior analyst)** | Clean & transform data using SQL or MS Excel | Basic data modeling, reporting, req. gathering. | Provide actionable insights. |
|
||||
| **Mid-level** | Complex analysis, advanced statistical analysis, and project ownership. | Python/R, creating ETLs, mentoring | Shaping data strategy, collaborating with business or operations, and influencing decision-making |
|
||||
| **Leadership** | Defining implementation roadmaps | Leadership, stakeholder management, expectation management with clients. | Lead key meetings with clients, become VP of analytics, or similar role. |
|
||||
|
||||
### Data Analytics Consultant / BI Analyst
|
||||
|
||||
| Level | Focus | Key Skills | Goal |
|
||||
| :---- | :---- | :---- | :---- |
|
||||
| **Data Analytics Consultant** | Acting as a strategic advisor, you help organizations define their data strategy and translate business requirements. | Data Strategy & Governance, Client Engagement, deep SQL. | Delivering a scalable analytics roadmap, implementing dashboards, and earning trust as a go‑to advisor. |
|
||||
| **BI Analyst** | Embedding within a single organization or business unit to build and maintain self‑service reporting environments. | ETL, dashboard development. | Influence data strategy, mentor JR Data Scientists. |
|
||||
|
||||
### Specialized Data Scientist Tracks → Chief Data Officer
|
||||
|
||||
| Level | Focus | Key Skills | Goal |
|
||||
| :---- | :---- | :---- | :---- |
|
||||
| **Data science option** | Go from descriptive analytics to machine learning algorithms. | Advanced Python, Statistical Analysis, Data modeling. | Deliver a working predictive solution |
|
||||
| **Advanced Statistics** | Tackle large‑scale analytical problems | Expertise in advanced statistical programming, big Data, and a bit of storytelling | Influence data strategy, mentor JR Data Scientists |
|
||||
| **CDO** | Oversee data governance, compliance, and ensure that analytics and machine learning initiatives align with strategic objectives. | Strategic leadership, understanding of data privacy, data governance. | Implement robust data governance & privacy frameworks, deliver analytics roadmap. |
|
||||
|
||||
### What should you pick?
|
||||
|
||||
In the end, either through any of these variations of the data analyst career path, there isn't a single option that is clearly better than the others.
|
||||
|
||||
* If you love turning raw numbers into charts and dashboards, the **junior→senior analyst** route offers steady, skill‑based progression.
|
||||
|
||||
* If you thrive on variety and advising multiple teams, consider the **analytics consultant/BI analyst** track.
|
||||
|
||||
* If you're drawn to algorithms and predictive work, the **data science** trajectory can propel you toward senior data scientist roles and, ultimately, a chief data officer position.
|
||||
|
||||
## Is Data Analysis Right for You?
|
||||
|
||||
Figuring out if the data analyst career path is the right place for you is not an easy task; in fact, many will need to go through the process of working in the field to retroactively answer the question.
|
||||
|
||||
But to give you a basic guide and help you understand whether you'd enjoy the position or not, you have to consider that pursuing a data analytics career begins with an honest curiosity about how raw data translates into actionable insights. Data analysis isn't just number crunching; it's about asking the right questions, designing robust statistical tests, and building data models that answer real business problems.
|
||||
|
||||
## Learning Path & Essential Skills
|
||||
|
||||
Charting your learning path starts with a clear learning roadmap, and there's no better place to begin than the [Data Analyst Roadmap](https://roadmap.sh/data-analyst).
|
||||
|
||||

|
||||
|
||||
Following its structured progression ensures you're building the right technical skill set in the right order.
|
||||
|
||||
As part of the roadmap, you'll have to tackle different languages such as SQL, R, Python, and others. To learn more about it, you can try this hands-on [SQL Course](https://roadmap.sh/courses/sql) that walks you through writing efficient queries, designing relational schemas, and performing complex joins and aggregations.
|
||||
|
||||
You'll also need **data visualization tools** and the storytelling mindset that makes your analyses resonate.
|
||||
|
||||
Finally, you'll start noticing that soft skills are particularly needed as a data analyst. For example, clear communication, problem solving, and a collaborative spirit are non‑negotiable when gathering requirements, iterating on dashboards, or presenting to senior management.
|
||||
|
||||
## 3 Portfolio Project Ideas
|
||||
|
||||
Below are three end‑to‑end projects designed to showcase the abilities that hiring managers look for in data analyst candidates. Each idea maps to stages on the [Data Analyst Roadmap](https://roadmap.sh/data-analyst) and gives you a chance to apply SQL, Python/R, and visualization tools to real‑world questions.
|
||||
|
||||
### Interactive Sales Dashboard
|
||||
|
||||
**Objective:** In this project, you can build a live dashboard that empowers marketing and senior management to spot seasonal patterns, best‑selling products, and under‑performing regions.
|
||||
|
||||
**Data & tools:** For this project, you can source a public retail or e-commerce dataset (such as Kaggle "Online Retail II"). You can use Python and SQL, the rest is up to you to decide how to show the results.
|
||||
|
||||
**Key skills demonstrated:** In this project, you're covering a bit of Data Modeling, ETL pipelines, and mostly Data Visualization tools.
|
||||
|
||||
### Customer Churn Prediction Model
|
||||
|
||||
**Objective:** For this one, you'll show how statistical analysis and basic machine learning can predict which customers are most likely to churn, enabling proactive retention strategies.
|
||||
|
||||
**Data & Tools:** For this one, you can find some sort of telecom dataset (like IBM Telco Customer Churn), use Python and SQL again to do some exploratory analysis, and finally train a classification model using scikit-learn.
|
||||
|
||||
**Key skills demonstrated:** During this project, you'll work on statistical analysis, data mining, and, as usual, some actionable insights turned into storytelling.
|
||||
|
||||
### A/B Testing Analysis for Website Redesign
|
||||
|
||||
**Objective**: Conduct and interpret an A/B test to determine which landing‑page design maximizes conversion, showcasing your ability to drive business analytics projects from hypothesis to recommendation.
|
||||
|
||||
**Data & Tools**: You can get some synthetic data for this one using something like ChatGPT, as long as it simulates A/B test data. Then, using either SQL or even MS Excel, you can do some aggregations and finally do the last calculations with Python or R. Try to plot the results on something like PowerBI at the end.
|
||||
|
||||
**Key skills demonstrated**: For this project, you'll be doing some experimental design, some business intelligence, and of course, decision making by translating statistical outcomes into a go/no‑go recommendation, acting as a market research analyst.
|
||||
|
||||

|
||||
|
||||
## My tips from personal experience
|
||||
|
||||
With all of this out of the way, let me quickly run you through some of my personal tips when it comes to growing and moving forward as a data analyst.
|
||||
|
||||
1. **Build a strong network and find mentors:** Connect with other data analysts, data scientists, and analytics managers through LinkedIn groups, local meetups, or virtual conferences. Ask others who have gone through the same about their journey, about the problems they found along the way. Learn from them.
|
||||
2. **Showcase your work with purpose:** Your first data analyst job will depend on having a solid portfolio (since you don't have any actual experience). Try to host your projects on GitHub or a personal blog, and include clear READMEs that explain your data strategy, the tech stack you used, and the business impact (showing you understand the value of your work), whether it's "increased conversion rate by X%" or "optimized inventory planning".
|
||||
3. **Stay ahead with the latest tools and techniques:** Data visualization tools and programming languages are constantly evolving. One key language you'll be using quite regularly is SQL, and if you ignore it, your progress will slow down. Find yourself a [SQL Course](https://roadmap.sh/courses/sql) that works for you and ensure you master it as soon as possible.
|
||||
4. **Embrace feedback and cultivate a growth mindset:** Whether you're presenting to marketing teams or senior management, feedback is your friend. After each project or presentation, or even on a regular basis try to get constructive feedback on your data modeling, storytelling, and communication style. Use this input to refine your processes, improving both your essential skills and your ability to communicate insights.
|
||||
5. **Plan for credentials that matter:** Getting credentials that validate your expertise with a certain tool or a type of analysis is going to help you stand out in the sea of analysts fighting for the same position. So, consider pursuing data analytics certifications (e.g., Google Data Analytics or Microsoft Power BI). They will not ensure you get the job, but they'll help you demonstrate a certain level of expertise at first glance.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Congrats, you now have a clear playbook for launching and advancing your data analyst career:
|
||||
|
||||
1. **Choose your path.** Understand exactly what you enjoy the most, and find the best career path for you.
|
||||
|
||||
2. **Assess your fit**. Understand the role you want, and make sure you'll enjoy the day-to-day of it.
|
||||
|
||||
3. **Build your skills**. Follow the [Data Analyst Roadmap](https://roadmap.sh/data-analyst) to structure your learning, and dive into the [SQL Course](https://roadmap.sh/courses/sql) to master the foundation of every data role.
|
||||
|
||||
4. **Practice with real projects**. Even if it's with fake, test or even raw data, tackle real-world problems to show you're able to transmit insights in the right way.
|
||||
|
||||
5. **Finally,** remember to network with other analysts, seek feedback, stay current on tools and techniques, and earn targeted certifications when you're ready to stand out.
|
||||
|
||||
Your journey into becoming a successful data analyst begins today: pick one section of the roadmap, schedule time to complete the SQL course module, and start your first portfolio project.
|
||||
|
||||
Go!
|
||||
@@ -1,218 +0,0 @@
|
||||
---
|
||||
title: "How to Become a Data Analyst with No Experience: My Advice"
|
||||
description: "Learn how to become a data analyst with no experience through smart steps, skill-building tips, and my real-world guidance."
|
||||
authorId: fernando
|
||||
excludedBySlug: '/data-analyst/how-to-become'
|
||||
seo:
|
||||
title: "How to Become a Data Analyst with No Experience: My Advice"
|
||||
description: "Learn how to become a data analyst with no experience through smart steps, skill-building tips, and my real-world guidance."
|
||||
ogImageUrl: 'https://assets.roadmap.sh/guest/become-a-data-analyst-with-no-experience-khk03.jpg'
|
||||
isNew: true
|
||||
relatedTitle: "Other Guides"
|
||||
relatedGuidesId: data-analyst
|
||||
type: 'textual'
|
||||
date: 2025-05-14
|
||||
sitemap:
|
||||
priority: 0.7
|
||||
changefreq: 'weekly'
|
||||
tags:
|
||||
- 'guide'
|
||||
- 'textual-guide'
|
||||
- 'guide-sitemap'
|
||||
---
|
||||
|
||||

|
||||
|
||||
Breaking into the data analytics industry can be scary, especially when you have no prior background in data analysis.
|
||||
|
||||
Yet the need for entry-level data analysts has never been higher, data is at the heart of everything, and organizations across the globe depend on data-driven insights to inform strategic decisions in every area of the enterprise.
|
||||
|
||||
Many aspiring data professionals incorrectly believe that a degree in computer science or a long resume in data science is a prerequisite for anything. The truth is that mastering the **data analysis process**, building a solid **analytics portfolio**, and showcasing your **data skills** can open doors to your first **junior data analyst** role, just like a long resume or a fancy degree.
|
||||
|
||||
In this practical guide on how to become a data analyst with no experience, I'm going to show you the key skills you should focus your learning path on (like SQL and statistical analysis).
|
||||
|
||||
I'll also prepare you on how to hone critical soft skills and leverage expert resources such as the [Data Analyst Roadmap](https://roadmap.sh/data-analyst) and this [SQL course](https://roadmap.sh/courses/sql) to help you improve your skills in that language.
|
||||
|
||||
## Step 1: Master the Essential Data Analyst Skills
|
||||
|
||||
In the analysis life cycle (from data collection and unprocessed data wrangling to statistical modeling and presentation), some **techniques** and **skills (both technical and non-technical)** are non-negotiable. The first thing you have to do is to focus on these core areas to build a solid foundation for your first **entry-level data analyst** or **junior data analyst** role:
|
||||
|
||||
### Technical Skills:
|
||||
|
||||
A data analyst's toolkit revolves around five foundational technical domains, each critical to unlocking insights from raw data and driving informed decisions. Begin by mastering **Structured Query Language (SQL)**, the backbone of relational data interrogation. With our [**SQL course on roadmap.sh**](https://roadmap.sh/courses/sql), you will learn to write complex queries—filtering, joining, aggregations, and advanced window functions—that enable you to extract and **manipulate data** at scale.
|
||||
|
||||
Next, tackle Data cleansing and Manipulation, the art of transforming messy inputs into reliable analytical datasets. Utilizing libraries such as pandas in Python or dplyr in R, you will develop workflows for handling missing values, normalizing formats, and removing duplicates, thereby ensuring high **data quality** before any modeling or visualization begins.
|
||||
|
||||
With a pristine dataset in hand, dive into different Analysis techniques to derive meaningful patterns. Key concepts such as descriptive statistics, hypothesis testing, correlations, and regression form the core of **these analysis techniques** used to validate assumptions and generate **actionable insights**. Proficiency in these methods equips you to support business cases with evidence-backed conclusions.
|
||||
|
||||
Translating numerical results into compelling narratives requires strong **Data Visualization** skills. Whether you choose Tableau, Power BI, or Python's matplotlib and seaborn libraries, you will create interactive dashboards and visual stories that highlight trends, outliers, and opportunities. Effective visualization not only conveys findings clearly but also drives stakeholder engagement.
|
||||
|
||||
Finally, solid programming skills in Python or R tie your analytical process together. From automating repetitive tasks and integrating APIs to building end-to-end **data analytics** pipelines, a programming mindset enables scalability, reproducibility, and integration of advanced tools, positioning you for roles across the industry.
|
||||
|
||||
### Soft Skills:
|
||||
|
||||
In the realm of data analytics, technical prowess must be complemented by strong interpersonal abilities (i.e, soft skills). Analytical thinking and structured problem-solving are at the heart of your daily workflow. When you encounter complex datasets, you must decide which analysis techniques best uncover the story hidden in the numbers.
|
||||
|
||||
This thoughtful approach ensures you derive accurate, actionable insights from unprocessed data.
|
||||
|
||||
Equally vital is the capacity to interpret data and translate it into meaningful recommendations. Effective communication means crafting a narrative around your findings that resonates with diverse audiences, from C-suite executives to frontline team members.
|
||||
|
||||
By designing clear visualizations and presenting data insights through compelling dashboards or concise slide decks, you empower stakeholders to make informed decisions.
|
||||
|
||||
Documenting every phase of your analysis process (data collection, cleaning, transformation, modeling, and reporting) creates a transparent audit trail. This meticulous record-keeping not only bolsters confidence in your results but also sets the foundation for reproducibility and continuous improvement.
|
||||
|
||||
Collaboration and adaptability define success in dynamic business environments. As a business intelligence analyst, sales data analyst, or healthcare data analyst, you'll partner with professionals across marketing, finance, operations, and IT.
|
||||
|
||||
Learning to navigate different communication styles, incorporating feedback, and swiftly adopting new tools or programming skills are essential to deliver timely, value-driven analyses.
|
||||
|
||||
Finally, incorporate a growth mindset by viewing every project as an opportunity to refine your soft skills. Seek constructive feedback from mentors and peers, participate in cross-functional workshops, and mentor aspiring professionals. Cultivating empathy, resilience, and lifelong learning habits ensures you evolve into a well-rounded data analyst capable of driving organizational success.
|
||||
|
||||
Starting with these essential skills will give you the confidence to tackle **data-projects**, close the **data skills gap**, and stand out among **aspiring analysts** in a competitive **job market.** After the foundation is done, you're free to specialize and focus in the areas that you feel are more appealing to you.
|
||||
|
||||
## Step 2: Follow a Structured Learning Path
|
||||
|
||||
Getting started on your journey as a new data analyst without formal experience can feel a bit overwhelming, so it's very important to follow a **structured learning path**. This way, you'll ensure you acquire the right **data analysis skills** in a way that makes sense according to your progression during your journey.
|
||||
|
||||
Here's how to follow a proven roadmap:
|
||||
|
||||
1. **Explore the [Data Analyst Roadmap](https://roadmap.sh/data-analyst)**: Start with the roadmap to grasp the full analysis life cycle.
|
||||
2. **Master SQL Early**: As a key technology for the field, the sooner you start tackling SQL, the better it will be. So, enroll in a [SQL course](https://roadmap.sh/courses/sql) to build a strong foundation in SQL (Structured Query Language). Practice querying, filtering, aggregations, and manipulation on realistic datasets.
|
||||
3. **Earn Recognized Certifications**: Boost your resume with entry-level certifications that validate your expertise. Consider doing any of the following:
|
||||
* **Google Data Analytics Professional Certificate** (Coursera)
|
||||
* **IBM Data Analyst Professional Certificate**
|
||||
* **Microsoft Certified: Data Analyst Associate**. These programs cover essential **data science** concepts, **data cleansing**, **visualization**, and real-world **data projects**.
|
||||
4. **Take Complementary Courses**: Figure out what your **data analysis skills gaps** are and fill them up by learning:
|
||||
* **Programming languages** (Python, R)
|
||||
* **Statistical analysis** and hypothesis testing
|
||||
* **Business intelligence** tools (Tableau, Power BI). Platforms like DataCamp, Udacity, and LinkedIn Learning offer targeted modules.
|
||||
5. **Apply Knowledge Through Projects**: Especially at the beginning, when you don't have any experience, try to reinforce your learning by tackling guided analysis projects on platforms like [Kaggle](https://www.kaggle.com/) or GitHub. Focus on end-to-end workflows (data collection, data cleaning, analyzing data, visualizing data, and presenting insights).
|
||||
|
||||
This structured learning path will help you in several ways:
|
||||
|
||||
* You'll be able to systematically build basic data analysis skills.
|
||||
* You'll end up developing a compelling and very complete portfolio showcasing your skills.
|
||||
* And you'll also be able to demonstrate to potential hiring managers your commitment to becoming a great junior data analyst.
|
||||
|
||||
## Step 3: Build a Data Analytics Portfolio
|
||||
|
||||
Building a robust data analyst portfolio will help you demonstrate your ability to tackle end-to-end **projects**, making you a standout candidate in a competitive **job market**. Here's why it matters and how to craft compelling portfolio pieces:
|
||||
|
||||
### Why a Strong Portfolio Matters
|
||||
|
||||
Building a strong portfolio helps you validate your own **analysis process**, from **data collection** and cleaning to visualization and interpretation, you'll be working on the entire thing.
|
||||
|
||||
It also helps you showcase your proficiency in **data manipulation & cleaning**, and extracting **actionable insights** from unprocessed data.
|
||||
|
||||
As part of the analysis life cycle, your **data visualization** and storytelling skills will have to shine in your portfolio. They're critical for roles like **BI analyst** or **data analyst**, so make sure you use them on every project in your portfolio.
|
||||
|
||||
Overall, the portfolio will help you highlight every single skill that is needed for the job, and if you build one with varied projects, different types of visualizations, and business goals, by the end, you'll be broadcasting to every hiring manager that you know what you're doing.
|
||||
|
||||
### Portfolio Project Ideas
|
||||
|
||||
But what project can you put into your portfolio? Let's go over some ideas that might highlight just the right set of skills:
|
||||
|
||||
* **Public Dataset Analysis**: Pick datasets (e.g., healthcare metrics, sales transactions, fraud detection) and perform a full workflow—ingestion, cleaning, exploratory analysis, statistical modeling, and reporting.
|
||||
* **Data Cleaning & QA Showcase**: Use a messy real-world dataset to demonstrate handling missing values, outliers, normalization, and quality checks—include before/after snapshots and code snippets.
|
||||
* **Interactive Dashboards**: Build dashboards with Tableau, Power BI, or Plotly to **visualize data** trends and present insights; add filters, annotations, and user controls.
|
||||
* **Domain-Specific Projects**: Create analyses for niche roles—e.g., evaluate patient outcomes as a **healthcare data analyst**.
|
||||
* **Data Storytelling**: Craft a narrative-driven project (e.g., COVID-19 trends, climate data) combining charts, maps, and written insights to tell a compelling story.
|
||||
|
||||
And if you don't really know how to begin working on them, you can simply ask one of the many LLMs available online to give you a detailed project plan, and start working on it. You'll be closing projects right and left in no time.
|
||||
|
||||
### Best Practices
|
||||
|
||||
Where should you create your portfolio? What tech stack should you use? How often should you update it? When should you publish it?
|
||||
These are all valid questions that don't have a single answer, however, I can show you some of the best practices I've seen around throughout my career when it comes to building data analyst portfolios:
|
||||
|
||||
* Host projects on GitHub with clear READMEs, organized code, and visual previews.
|
||||
* Document your **analysis life cycle** in Jupyter notebooks or blog posts, explaining each step and decision.
|
||||
* Use authentic tools and workflows: query public APIs with **SQL**, automate tasks with Python or R, and integrate BI tools.
|
||||
* Continuously update your portfolio as you learn new **data analysis techniques** and **programming skills**.
|
||||
|
||||
## Step 4: Gain Practical Experience
|
||||
|
||||
Securing practical experience allows you to apply theoretical knowledge, bridge the skills gap, and prove to hiring managers that you can deliver on real-world data analysis projects. Here are several pathways to get your foot in the door:
|
||||
|
||||
* **Internships & Volunteer Projects:**
|
||||
Look for data internships (even unpaid if you can) or volunteer to help non‑profits, student organizations, or local businesses with their **data collection**, **data cleaning**, and **data visualization** needs. These roles not only strengthen your **technical skills** but also give you concrete examples for your **data analytics portfolio**.
|
||||
* **Freelance & Gig Work:**
|
||||
Platforms like Upwork, Fiverr, or even LinkedIn can connect you with short‑term gigs, everything from database queries to dashboard creation. Freelance tasks force you to **manipulate data**, produce **actionable insights**, and **present data-driven insights** under real-world deadlines, building both technical chops and business communication skills.
|
||||
* **Entry‑Level & Support Roles:**
|
||||
Apply for roles labeled **data technician**, **data entry**, or **junior data analyst**. These positions often focus on **data quality**, routine reports, and simple analyses; they're perfect stepping stones that let you collaborate with senior analysts and refine your **analysis** and **manipulation** techniques.
|
||||
* **Kaggle Competitions & Public Challenges:**
|
||||
I've mentioned this platform before, but consider participating in Kaggle or similar platforms to demonstrate your ability to tackle problems like a **fraud detection** challenge or sales forecasting. Even if you don't win, documenting your approach, code, and learnings shows resilience and a methodical **analysis process**.
|
||||
* **Capstone & Guided Projects:**
|
||||
Many certification programs include capstone projects. Treat these as mini‑portfolio pieces: choose datasets relevant to your target roles, and clearly outline your **data analysis life cycle**, from **raw data** ingestion through **data visualization** and storytelling.
|
||||
|
||||
Build experience in as many diverse **practical experiences as you can**. That way, you'll not only close any **data skills gap** you might have, but also amass a collection of tangible accomplishments, making it far easier to articulate your value as an **aspiring data analyst** during interviews.
|
||||
|
||||
## Step 5: Network and Engage with the Data Community
|
||||
|
||||
Spending time in the right communities accelerates your growth as an **aspiring data analyst** by helping you stay current with industry trends, learn from seasoned **professionals**, and uncover hidden **data analytics job** opportunities. After all, a big part of growing in any technology role is about networking (shocking, I know\!) with other professionals who've gone through what you're going through now and can provide advice, help, or even a boost when least expected.
|
||||
|
||||
Networking is not easy, and there are many ways to do it. Here's just one example of how to build and leverage your own network:
|
||||
|
||||
* **Leverage Professional Platforms:** Connect with **qualified data analysts** and **data scientists** on LinkedIn, GitHub, and specialized forums like Stack Overflow or Reddit's r/datascience. Engage by asking thoughtful questions, sharing your **data analysis projects**, and commenting on discussions.
|
||||
* **Attend Industry Events:** Participate in local meetups, virtual conferences, webinars, and workshops hosted by organizations such as Data Science Saudi, Meetup groups, or platform-specific events (e.g., Tableau User Groups). These gatherings expose you to emerging **analysis techniques**, **data visualization** best practices, and evolving industry standards.
|
||||
* **Join Online Communities:** Become active in Slack channels, Discord servers, or professional associations where **data analysts** share resources, job leads, and **actionable insights**. Regularly review community channels for project feedback, collaboration opportunities, and announcements about **entry-level data analyst** roles.
|
||||
* **Find a Mentor or Buddy:** Seek out mentorship programs through bootcamps or university-alumni networks. A mentor, someone like a **senior data analyst** or BI (**business intelligence) analyst**, can provide personalized advice on your **data analysis life cycle**, resume reviews, and mock interviews.
|
||||
* **Share Your Knowledge:** Contribute to blogs, record tutorials, or present lightning talks at virtual events. While you might just be starting, chances are someone else is going through the same process, and learning how you solved it can help them. Also, creating content like this will force you to truly understand the topic you're covering before being able to explain it. So even while you're teaching others, you're also cementing that understanding within you.
|
||||
|
||||
Networking is not easy, and when you're just getting started, it might seem even harder than getting an actual data analyst job. The above list is just a set of options, you don't have to go through all of them (or any, to be honest), just find a way that works for you and follow that path.
|
||||
|
||||
## Step 6: Continuous Learning and Career Development
|
||||
|
||||
The analytics industry is constantly evolving, and your professional growth must keep up accordingly. Continuous learning is probably one of the only ways to ensure you stay ahead of emerging trends, refine your existing skills, and prepare for more advanced roles.
|
||||
|
||||
### Lifelong Learning and Advanced Skills
|
||||
|
||||
You should embrace a mindset of lifelong learning by deepening your technical expertise over time. Accept that you'll never be done learning and revisit fundamental tools like SQL and master advanced features such as window functions and recursive queries.
|
||||
|
||||
Expand your proficiency in Python by exploring libraries like pandas for data manipulation and scikit-learn for machine learning workflows. If you prefer R, dive into packages like tidyverse for efficient data processing and ggplot2 for sophisticated visualizations.
|
||||
|
||||
Additionally, enroll in specialized courses on machine learning algorithms, cloud-based analytics platforms (e.g., AWS Redshift, Google BigQuery), and emerging open-source tools. Staying informed through industry blogs, podcasts, and research publications will help you hone your skills in both data science and data mining.
|
||||
|
||||
### Charting Your Career Path
|
||||
|
||||
Once you have a solid foundation in data analysis techniques, the next step is to start mapping out your career trajectory. In other words: where do you want to go?
|
||||
|
||||
Initially, you might specialize as a BI analyst, crafting interactive dashboards and KPI reports that inform strategic decisions.
|
||||
|
||||
Next, consider roles such as sales data analyst, where you'll focus on revenue analytics and customer behavior modeling, or healthcare analyst, tasked with evaluating patient outcomes and ensuring regulatory compliance.
|
||||
|
||||
As you gain experience, you might want to pivot toward senior data analyst or data scientist positions, taking on end-to-end projects that incorporate predictive modeling, anomaly detection, and advanced statistical analysis.
|
||||
|
||||
### Setting Goals and Measuring Progress
|
||||
|
||||
You can't know if you're moving in the right direction if you don't measure your progress. It's a very common mistake many new analysts make, and one you should try to avoid.
|
||||
|
||||
Define clear, measurable objectives to guide your learning journey. Set milestones—such as completing a certification, publishing a portfolio project, or mastering a new visualization tool (and celebrate each achievement once you get it).
|
||||
|
||||
Keep a learning journal or log within your portfolio to document projects, record the techniques applied, and reflect on lessons learned.
|
||||
|
||||
Regularly try to get feedback from peers and mentors through code reviews, presentations, or mock interviews to identify areas for improvement and validate your progress.
|
||||
|
||||
### Mentorship and Community Involvement
|
||||
|
||||
Long-term success in data analysis relies on strong professional relationships and community engagement. Continue participating in forums, meetups, and virtual conferences to exchange insights with both aspiring analysts and seasoned professionals.
|
||||
|
||||
Seek opportunities to mentor newcomers, contribute to open-source or community-driven data projects, and share your expertise through blog posts or lightning talks.
|
||||
|
||||
Teaching concepts such as data cleansing, analysis workflows, and visualization best practices not only reinforces your own knowledge but also establishes your reputation as a qualified data professional in the broader data analyst job market.
|
||||
|
||||
Try to incorporate these strategies into your routine; that way, you'll enhance your data analyst skills dynamically, showcase your commitment to growth, and position yourself for roles with greater responsibility and impact.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Embarking on the path of how to become a data analyst with no experience may seem challenging, yes, but by leveraging structured learning pathways like the [Data Analyst Roadmap](https://roadmap.sh/data-analyst) and our [Mastering SQL course](https://roadmap.sh/courses/sql), you'll build the core technical foundation.
|
||||
|
||||
Complementing these tools with thoughtfully chosen data analysis projects in your portfolio will validate your practical skills and demonstrate your ability to extract actionable insights from complex datasets (which is the end goal of a good data analyst).
|
||||
|
||||
Beyond technical proficiency, remember that confidence and communication are equally critical. Whether you're preparing for your first entry-level data analyst interview or collaborating as a junior data analyst on real-world projects, articulating your data analysis process will set you apart.
|
||||
|
||||
Practical experiences, such as internships or community-driven challenges, will further reinforce your résumé and show your commitment to continuous growth in the analytics industry.
|
||||
|
||||
Every day, countless developers and analysts successfully transition into data roles by following these strategies.
|
||||
|
||||
Now it's your turn: take the first step, mapping out your learning journey, and launching your own data analytics projects. Your journey from aspiring analyst to qualified data professional starts today!
|
||||
|
||||
@@ -27,7 +27,7 @@ In fact, companies and individuals seeking to deliver applications and services
|
||||
|
||||
Despite their shared goal of improving the software delivery process, some nuances set DevOps and SRE apart.
|
||||
|
||||
This guide provides an in-depth discussion of the key differences between each approach, what they entail, similarities, and the [tools](https://roadmap.sh/devops/tools) and technologies involved. Finally, it offers roadmaps for your DevOps or SRE journey.
|
||||
This guide provides an in-depth discussion of the key differences between each approach, what they entail, similarities, and the tools and technologies involved.Finally, it offers roadmaps for your DevOps or SRE journey.
|
||||
|
||||
## Differences between DevOps and SRE
|
||||
|
||||
@@ -153,9 +153,9 @@ DevOps and SRE principles have become popular and widely adopted by organization
|
||||
- Provision resources, deploy, and manage applications on cloud platforms like [AWS](https://roadmap.sh/aws), Azure, Google Cloud, etc.
|
||||
- Creates standards and manages configuration to enforce and maintain system integrity across multiple environments.
|
||||
- Creates a plan to optimize system performance and resource utilization.
|
||||
- Promotes knowledge sharing by carefully documenting processes, procedures, and [best practices](https://roadmap.sh/devops/best-practices).
|
||||
- Promotes knowledge sharing by carefully documenting processes, procedures, and best practices.
|
||||
|
||||
To perform these responsibilities, the DevOps team uses many tools to automate and improve their workflow. Here are some of the [DevOps automation tools](https://roadmap.sh/devops/automation-tools) commonly used:
|
||||
To perform these responsibilities, the DevOps team uses many tools to automate and improve their workflow. Here are some of the DevOps tools commonly used:
|
||||
|
||||
- **Docker**: [Docker](https://roadmap.sh/docker) is an open-source platform that enables developers to build, deploy, and run containerized applications.
|
||||
- **Kubernetes**: [Kubernetes](https://roadmap.sh/kubernetes) is an open-source orchestration platform for automating the deployment processes, scaling, and managing containerized applications.
|
||||
|
||||
195
src/data/guides/frontend-vs-backend-ai.md
Normal file
195
src/data/guides/frontend-vs-backend-ai.md
Normal file
@@ -0,0 +1,195 @@
|
||||
---
|
||||
title: 'Frontend vs. Backend in AI Development'
|
||||
description: 'Learn the key differences between frontend and backend in AI development, from roles to tools, and how they impact project success.'
|
||||
authorId: william
|
||||
excludedBySlug: '/frontend/vs-backend-ai'
|
||||
seo:
|
||||
title: 'Frontend vs. Backend in AI Development'
|
||||
description: 'Learn the key differences between frontend and backend in AI development, from roles to tools, and how they impact project success.'
|
||||
ogImageUrl: 'https://assets.roadmap.sh/guest/frontend-vs-backend-in-ai-43wtm.jpg'
|
||||
relatedTitle: "Other Guides"
|
||||
relatedGuidesId: frontend
|
||||
isNew: false
|
||||
type: 'textual'
|
||||
date: 2024-10-17
|
||||
sitemap:
|
||||
priority: 0.7
|
||||
changefreq: 'weekly'
|
||||
tags:
|
||||
- 'guide'
|
||||
- 'textual-guide'
|
||||
- 'guide-sitemap'
|
||||
---
|
||||
|
||||

|
||||
|
||||
Many software developers begin their careers by choosing an area of focus: backend or front end development. If you're an aspiring software developer, understanding the differences between frontend and backend can help you choose a focus for your career path. This guide focuses on the [front-end](https://roadmap.sh/frontend) and [back-end](https://roadmap.sh/backend) development for AI.
|
||||
|
||||
Frontend vs Backend is a common topic in software engineering and understanding both frontend and backend development is crucial for creating effective and efficient websites. Both are essential for a well-rounded web development process. Both career paths are in high demand.
|
||||
|
||||
Front-end development refers to the visual elements that users can directly interact with. It is the user facing side of an application also known as the client side of an application. Back-end development includes everything the user cannot see. It focuses on the application’s overall functionality and business logic.
|
||||
|
||||
Despite frontend and backend developers in AI having specific roles in the overall software development life cycle, they work together to design, program, test, and deploy AI applications. They collaborate to ensure AI applications meet quality and security standards. In addition to front-end and back-end developers, there are also full stack developers. Full stack developers work and specialize in both frontend and backend of web development.
|
||||
|
||||

|
||||
|
||||
The table below presents a comparison of frontend vs backend development AI specializations.
|
||||
|
||||
| Frontend AI development | Backend AI development |
|
||||
| ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Focuses on the visual aspects of an AI application which is the UI and users directly interact with. | Focuses on the server-side development of an application which has data storage and the user does not directly interact with. |
|
||||
| Specializes in the client side of the application. | Not concerned with the client side of web applications. |
|
||||
| The front end is about user interface (UI) and user experience (UX). | Focuses on the application’s functionality and business logic. |
|
||||
| Uses HTML, CSS and JavaScript as part of the toolbox. | Uses back-end programming languages like Java, C#, Python and so on. |
|
||||
|
||||
Let’s look at frontend vs backend in detail.
|
||||
|
||||
## What is frontend development for AI?
|
||||
|
||||
Frontend development for AI involves the design and implementation of the visual elements of an AI application. Several AI applications are being used on a daily basis, such as Chatbots, Virtual assistants, face recognition systems, etc. A user interface (UI) enables you to interact with these applications.
|
||||
|
||||
An AI frontend developer designs and builds the parts of an AI application that users can directly interact with. For larger projects, front-end developers will work with a digital and web designer who is responsible for creating a graphic design for the web page of the application. Frontend developers are also referred to as web developers.
|
||||
|
||||
Frontends are also built on other platforms asides the web, such as mobile apps for Android and iOS, or desktop apps.
|
||||
|
||||
The next section presents who a front-end developer is and the tools they use for building applications for the web.
|
||||
|
||||
## Who is a frontend developer?
|
||||
|
||||
A [frontend developer](https://roadmap.sh/frontend) builds the visual part of an application, which includes the parts users see and directly interact with, such as the graphical user interface (GUI) and the command line, including the design, navigation menus, texts, images, videos, etc. A page or screen a user sees with several UI components is called a document object model (DOM).
|
||||
|
||||

|
||||
|
||||
Frontend developers build these visual parts using front end programming languages such as:
|
||||
|
||||
- HTML (Hypertext Markup Language)
|
||||
- CSS
|
||||
- JavaScript
|
||||
|
||||
### HTML (Hypertext Markup Language):
|
||||
|
||||
The basic building block of an application. It defines the markup of the language.
|
||||
|
||||
### CSS (Cascading Style Sheets)
|
||||
|
||||
Builds upon HTML and defines the layout and style of an application.
|
||||
|
||||
### JavaScript
|
||||
|
||||
The front-end programming language that adds logic to an application. It can be used for both the frontend and backend (NodeJs, ExpressJs, NestJS).
|
||||
|
||||
HTML, CSS, and [JavaScript](https://roadmap.sh/javascript) are fundamental tools in a frontend developer’s toolkit and are used to determine the look and functionality of the client side of an application.
|
||||
|
||||
In addition to these languages, there are frontend frameworks, libraries, and CSS preprocessors that help to create websites and applications efficiently. Some of the popular ones are [React](https://roadmap.sh/react), [Vue](https://roadmap.sh/vue), [Angular](https://roadmap.sh/angular), and SASS.
|
||||
|
||||
## Front end developers responsibilities for AI
|
||||
|
||||
The core responsibilities of AI front end developers include:
|
||||
|
||||
- Designing and developing dashboards
|
||||
- Developing graphs and charts for data visualization
|
||||
- Integrating with AI services
|
||||
- Testing and validating the integrated AI services
|
||||
- Optimizing the user experience of AI applications
|
||||
|
||||
### Designing and developing dashboards
|
||||
|
||||
A dashboard conveys different but related information in an understandable format. Frontend developers are responsible for designing and developing dashboards that convey different AI data and metrics. They use design programs to lay out a dashboard prototype and ensure that the dashboards are implemented according to specifications.
|
||||
|
||||
### Developing graphs and charts for data visualization
|
||||
|
||||
Data visualization is an important process in AI that presents data in visual formats such as graphs and charts. Frontend AI developers use libraries such as Chart.js, Plotly, and D3.js to create graphs and charts to visualize and interpret data.
|
||||
|
||||
### Integrating with AI services
|
||||
|
||||
AI front end developers connect frontend applications to AI services via Application Programming Interfaces (APIs) to fetch data and predict or perform certain actions. For example, a weather application’s frontend connects to weather prediction services through API endpoints or other means of communication and displays the information users interact with.
|
||||
|
||||
### Testing and validating integrated AI services
|
||||
|
||||
After integrating AI services into an application, frontend AI developers also test and validate that these services function properly and provide accurate and efficient data. Testing and validation are important for identifying and resolving technical issues that might come up and for addressing optimization requirements.
|
||||
|
||||
### Optimizing the user experience of AI applications
|
||||
|
||||
Frontend AI developers focus on improving the user experience of software and other mobile applications by optimizing UI elements and adding features like hovering effects or tooltips, navigation flows, and application interactions. They also iterate on designs and features with UI/UX experts based on user feedback to enhance user satisfaction and produce a responsive web design.
|
||||
|
||||
## Frontend developer skills for AI
|
||||
|
||||
To be a frontend AI developer, you need a combination of soft and technical skills. Some of the skills you require to be a frontend AI developer include:
|
||||
|
||||
- Deep understanding of HTML, CSS, and JavaScript/TypeScript.
|
||||
- Knowledge of at least one web application framework or library, e.g., React, Vue, and Angular.
|
||||
- Knowledge of data visualization libraries. e.g., Chart.js, Plotly.
|
||||
- Basic understanding of machine learning and machine learning models. e.g., linear regression, random forest, etc.
|
||||
- Collaboration and communication skills.
|
||||
- Problem-solving skills.
|
||||
|
||||
## What is back end development for AI?
|
||||
|
||||
Back end development for AI is the design and implementation of the server side of an AI application. As opposed to frontend development, which involves the visual and interactive elements of an application, backend development involves the part of an application a user cannot directly interact with. The next section goes into detail about who a back-end developer is and their role in the software development process and lifecycle.
|
||||
|
||||
## Who is a back-end developer?
|
||||
|
||||
A [back-end developer](https://roadmap.sh/backend) specializes in the server-side development of an AI application that users cannot see and directly interact with. A back-end developer manages the behind-the-scenes part of an application, such as the servers, databases, and machine learning models that power AI applications.
|
||||
|
||||

|
||||
|
||||
AI back end developers use server-side programming languages such as C#, [Java](https://roadmap.sh/java), [Python](https://roadmap.sh/python), and [Rust](https://roadmap.sh/rust) and frameworks such as [Spring Boot](https://roadmap.sh/spring-boot), [ASP.NET core](https://roadmap.sh/aspnet-core), Django, and Ruby on Rails to develop the backend of AI applications.
|
||||
|
||||
## Back end developers responsibilities for AI
|
||||
|
||||
The responsibilities of AI back end developers include:
|
||||
|
||||
- Database design and management
|
||||
- AI model development
|
||||
- Application Programming Interface design and development
|
||||
- Performance optimization
|
||||
|
||||
### Database design and management
|
||||
|
||||
Data is stored and retrieved from databases. AI deals with a large amount of data, which can be structured or unstructured. Back end developers are responsible for setting up these databases to save AI data. Two common types of databases are:
|
||||
|
||||
- [Relational databases](https://roadmap.sh/sql) /Structured Query Language (SQL) e.g., PostgreSQL, Microsoft SQL Server
|
||||
- NoSQL databases, e.g., [MongoDB](https://roadmap.sh/mongodb).
|
||||
|
||||
### AI model development
|
||||
|
||||
AI models are computer programs that recognize patterns in data and make predictions. They rely heavily on trained and untrained data, and each model is suitable for different cases. Examples of AI models include:
|
||||
|
||||
- Classification models, e.g., random forest, K-nearest neighbor, naive bayes
|
||||
- Regression models, e.g., linear regression, decision trees
|
||||
|
||||
Backend AI developers use tools and frameworks such as Pandas, Numpy, Scikit-Learn, PyTorch, and so on to develop AI these AI models.
|
||||
|
||||
### API design and development
|
||||
|
||||
A backend AI developer designs and develops APIs that are consumed by the frontend of an AI application or other services. API development involves creating endpoints that provide data that users can visualize and interact with. Backend AI developers use different tools to design and document APIs; a common one is [Swagger](https://swagger.io/).
|
||||
|
||||
### Performance optimization
|
||||
|
||||
Backend AI developers are constantly optimizing the performance of AI applications. They do this by scaling applications to ensure the backend can handle large volumes of requests. The performance optimization involves code refactoring, optimizing database queries, adding caching, and load balancing.
|
||||
|
||||
## Backend developer skills for AI
|
||||
|
||||
Some of the job-ready skills needed to excel as a backend AI developer include:
|
||||
|
||||
- In-depth knowledge of at least one back-end programming language.
|
||||
- Knowledge of database systems.
|
||||
- Basic knowledge of web servers.
|
||||
- Basic knowledge of how to deploy applications on cloud services or on-premise.
|
||||
- Knowledge of machine learning models.
|
||||
- Knowledge of data structures and algorithm
|
||||
- Knowledge of web application frameworks
|
||||
- Problem-solving and logical reasoning skills.
|
||||
- Collaboration and communication skills.
|
||||
|
||||
## Which should I go for - frontend vs backend?
|
||||
|
||||
As you’ve seen in the frontend vs backend comparison, frontend and backend developers who specialize in AI perform different responsibilities and require various skill sets.
|
||||
|
||||

|
||||
|
||||
If you like user interfaces, keen on sound design, and like the visual aspects of creating apps, then perhaps you would be most interested in becoming a front end software developer. If you are more interested in servers, databases, and how systems work behind the scenes, then you should consider backend development.
|
||||
|
||||
You can begin your frontend or backend engineering career by obtaining a bachelor’s degree in computer science degree from a college.
|
||||
|
||||
roadmap.sh provides you with step-by-step guidance on how to become a [frontend developer](https://roadmap.sh/frontend) and [backend developer](https://roadmap.sh/backend). You can also explore the [full stack developer roadmap](https://roadmap.sh/full-stack) if you are interested in learning full stack development, which is a combination of frontend and backend development. Signing up on roadmap.sh makes it easy to track your progress and also share it on your profile. You can also draw up your personalized roadmap or work with AI to [generate new roadmaps](https://roadmap.sh/ai).
|
||||
@@ -1,283 +0,0 @@
|
||||
---
|
||||
title: 'TypeScript vs JavaScript: Which to Choose For Your Project'
|
||||
description: 'TypeScript vs JavaScript: Learn the pros, cons, and best use cases to choose the right language for your next project.'
|
||||
authorId: william
|
||||
excludedBySlug: '/javascript/vs-typescript'
|
||||
seo:
|
||||
title: 'TypeScript vs JavaScript: Which to Choose For Your Project'
|
||||
description: 'TypeScript vs JavaScript: Learn the pros, cons, and best use cases to choose the right language for your next project.'
|
||||
ogImageUrl: 'https://assets.roadmap.sh/guest/typescript-or-javascript-99fzl.jpg'
|
||||
isNew: false
|
||||
type: 'textual'
|
||||
date: 2025-05-15
|
||||
sitemap:
|
||||
priority: 0.7
|
||||
changefreq: 'weekly'
|
||||
tags:
|
||||
- 'guide'
|
||||
- 'textual-guide'
|
||||
- 'guide-sitemap'
|
||||
---
|
||||
|
||||

|
||||
|
||||
Choosing between [TypeScript](https://roadmap.sh/typescript) and [JavaScript](https://roadmap.sh/javascript) can feel like picking between a Swiss Army knife and a scalpel. Both are useful, but their strengths depend on the task.
|
||||
|
||||
If you're working on a large-scale project or need better code maintainability, TypeScript is a great choice. On the other hand, if you're working on a project that requires a quick development cycle or working within a small team, JavaScript might be enough.
|
||||
|
||||
At the start of my tech career, I used JavaScript to learn basic programming concepts and build small projects to add to my portfolio. It was very common to learn JavaScript if you wanted to become a web developer. When I got my first full time role as a web developer, I realized my team was using TypeScript and that was when my journey with TypeScript began.
|
||||
|
||||
While both languages have their strengths, selecting the right one depends on several factors, such as project requirements, developer needs, learning curve, etc.
|
||||
|
||||
In this guide, I'll compare the features of TypeScript and JavaScript to help you determine which language best suits your project. Drawing from personal experience and real-world examples, I'll provide insights to support your decision-making.
|
||||
|
||||
## Differences between TypeScript and JavaScript
|
||||
|
||||
The table below summarizes the differences between TypeScript and JavaScript.
|
||||
|
||||
| **Criteria** | **TypeScript** | **JavaScript** |
|
||||
| ---------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| Typing | Static typing. | Dynamic typing. |
|
||||
| Error checking | Compile time error checking. | Runtime error checking. |
|
||||
| Use case | For large scale projects where type safety is crucial. | For smaller projects and rapid prototyping. |
|
||||
| Backward compatibility | Can work with JavaScript code by including type definitions. | Can run natively on any JavaScript engine. |
|
||||
| Compilation | It is compiled to JavaScript before execution. | Interpreted directly by browsers and Node.js without compilation. |
|
||||
| Debugging | Easier to debug because of the type system. | Requires more effort and time to debug. |
|
||||
| IDE support | Support for code refactoring, type checking, and auto completion. | Limited IDE support, mostly auto completion, and syntax highlighting. |
|
||||
| File extension | .ts | .js |
|
||||
| Learning curve | Has a steeper learning curve because you have to learn additional features. | Easier to start with due to dynamic typing and less strict rules. |
|
||||
|
||||
## What are JavaScript and TypeScript?
|
||||
|
||||
JavaScript is a simple and versatile programming language, and probably the most widely used in web development. It supports dynamic typing and works on both the frontend and backend. If you're aiming to become a web developer, you should be comfortable with using JavaScript.
|
||||
|
||||
TypeScript, on the other hand, is a superset of JavaScript that adds static typing and some features that JavaScript doesn't offer out of the box. It was designed to solve specific problems that JavaScript developers face and make JavaScript development more efficient. Since valid JavaScript is also valid TypeScript, it's easy to start using. TypeScript helps make codebases more robust and maintainable, especially as projects grow. Its type system is great for catching bugs early and making the development process smoother overall.
|
||||
|
||||
## Features of JavaScript
|
||||
|
||||
Some of the key features of JavaScript include:
|
||||
|
||||
1. **Dynamic typing**: In JavaScript, you do not have to declare variables with any type. The types can also change during runtime.
|
||||
|
||||
```javascript
|
||||
let programmingLanguage = "JavaScript";
|
||||
console.log(typeof programmingLanguage); // "string"
|
||||
|
||||
programmingLanguage = 12;
|
||||
console.log(typeof programmingLanguage); // "number"
|
||||
```
|
||||
|
||||
From the code snippet above, you see that the variable `programmingLanguage` was initially assigned a string value and later assigned to a number.
|
||||
|
||||
2. **Interaction with the Document Object Model (DOM)**: With JavaScript, you can manipulate the DOM of any webpage by changing its content, structure, and style.
|
||||
|
||||
```javascript
|
||||
const element = document.getElementById("demo");
|
||||
element.style.color = "red";
|
||||
```
|
||||
|
||||
The code snippet selects an HTML element by its Id `demo` and then changes its color.
|
||||
|
||||
3. **Event handling**: In JavaScript, event listeners allow you to respond to user actions such as clicks, keyboard input, and mouse events.
|
||||
|
||||
```javascript
|
||||
document.getElementById("demo").addEventListener("click", () => {
|
||||
console.log("I was clicked");
|
||||
});
|
||||
```
|
||||
|
||||
The code adds a click event listener to the element, and when the element is clicked, a message is logged into the console.
|
||||
|
||||
4. **Asynchronous programming**: JavaScript supports asynchronous programming using callbacks, promises, and async/await for non-blocking operations like reading and writing data to a database, fetching data from a server, etc.
|
||||
|
||||
```javascript
|
||||
const data = async() => {
|
||||
try{
|
||||
const response = await fetch("http://api.com/data");
|
||||
if(!response.ok){
|
||||
throw new Error("Error while fetching data");
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
}
|
||||
catch(error){
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The code uses async/await to fetch data from an endpoint. If successful, the data returned will be logged into the console.
|
||||
|
||||

|
||||
|
||||
## Features of TypeScript
|
||||
|
||||
Some of the key features of TypeScript include:
|
||||
|
||||
1. **Static typing**: Unlike JavaScript, TypeScript supports static typing. When defining a variable or object, you can specify the type you want it to have. This helps you catch errors at compile time rather than run time.
|
||||
|
||||
```typescript
|
||||
let programmingLanguage: string = "TypeScript";
|
||||
programmingLanguage = 23 // this will show a compile error
|
||||
```
|
||||
|
||||
The code snippet demonstrates how to define a variable in TypeScript. You can also do this without explicitly specifying the type string; TypeScript will infer the type implicitly. You can notice the effect of the static type on the following line, where a number is assigned to the variable. This will show a compile error because the variable was initialized as a string.
|
||||
|
||||
2. **Interfaces**: Interfaces allow you to define an object's structure and ensure that the structure is respected wherever you use that object. The code below defines an interface and creates an object based on the interface structure.
|
||||
|
||||
```typescript
|
||||
interface IStudent{
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
age: number;
|
||||
}
|
||||
|
||||
const student: IStudent = {firstName: "John", lastName: "Doe", age: 30};
|
||||
```
|
||||
|
||||
3. **Generics**: TypeScript allows you to create reusable and flexible components that can work with multiple data types instead of one.
|
||||
|
||||
```typescript
|
||||
const returnValue<T> = (arg: T): T => console.log(arg);
|
||||
|
||||
// usage
|
||||
returnValue("JavaScript"); // "JavaScript"
|
||||
returnValue(43); // 43
|
||||
returnValue(true); // true
|
||||
```
|
||||
|
||||
The code above demonstrates how to use generics in TypeScript. A function `returnValue` is declared to accept arguments of any data type and logs them to the console.
|
||||
|
||||
4. **Enums**: This is a feature added in TypeScript and is not native to JavaScript. It allows you to define a set of constants and helps to improve code readability and maintainability.
|
||||
|
||||
```typescript
|
||||
enum Level{
|
||||
Low,
|
||||
Medium,
|
||||
High
|
||||
}
|
||||
|
||||
const level: Level = Level.Low;
|
||||
```
|
||||
|
||||
TypeScript also supports modern JavaScript features like classes and inheritance, with additional benefits like access modifiers (public, private, protected) and abstract classes.
|
||||
|
||||

|
||||
|
||||
## When should you use TypeScript or JavaScript?
|
||||
|
||||
Choosing between TypeScript and JavaScript depends on several factors such as the kind of project, if you are working in a team or alone, and so on. Knowing which to choose for your project will enhance your productivity. I will provide you with a guide on when to choose TypeScript over JavaScript and vice versa.
|
||||
|
||||
**When JavaScript is enough**
|
||||
|
||||
1. **Small projects or quick prototypes**: JavaScript is suitable when you want to quickly test an idea or build small projects where static typing is not needed. It removes the compilation step that TypeScript has, leading to a faster development process.
|
||||
|
||||
I once worked on a project to build a static website for a drug store using TypeScript, thinking it would give me an extra layer of safety and structure. But as I got into the development process, I realized I was spending so much time fixing type errors, tweaking interfaces, and trying to make the compiler happy, rather than focusing on the core logic of the app.
|
||||
|
||||
In a project where speed and simplicity were the goals, TypeScript started to feel like a roadblock rather than a benefit. The experience taught me that for smaller projects, TypeScript can be a bit of an overkill. JavaScript does the job faster.
|
||||
|
||||
2. **Learning purposes**: If you are new to programming or want to learn new concepts, JavaScript is enough. It helps you grasp the fundamentals without the extra layer of static typing.
|
||||
|
||||
3. **Working solo**: Whenever I work alone, I use JavaScript because it is effective for small projects and I'm familiar with its data interactions. JavaScript should be enough if you are working alone.
|
||||
|
||||

|
||||
|
||||
**When TypeScript is a great fit**
|
||||
|
||||
1. **Large projects with multiple developers**: TypeScript is a great fit for large projects with multiple developers. Its typing system helps you write code that is easier to read, manage, and debug.
|
||||
|
||||
I worked on a project where built a fairly large and growing codebase entirely in JavaScript. At first, everything seemed to move quickly, and we could iterate fast. But as the project scaled, we started having problems.
|
||||
|
||||
One of the most frustrating issues we ran into was type-related bugs. Debugging those bugs was painful. We'd waste a lot of time trying to find the error, only to find out that it was a function receiving the wrong data shape, something TypeScript could have caught during development. The experience proved how valuable static typing can be for large-scale applications.
|
||||
|
||||
2. **When code maintainability is a priority**: Applications where code maintainability is crucial benefit from TypeScript type checking. You can detect errors during compile time instead of during run time. Many IDEs like Visual Studio Code support type checking and will notify you of any type mismatch while coding.
|
||||
|
||||
3. **When dealing with strict API contracts**: If you are making requests to an API endpoint with a strict structure, TypeScript's type system helps to model the return of the endpoint using an Interface or Type and avoids throwing exceptions.
|
||||
|
||||
In my early career days, I had the task to implement a feature to fetch the price of some vehicles using the vehicleIds. It involved calling the price REST endpoint. I used JavaScript's fetch to do this. Everything looked right - the request went out, the response came back, and I tried working with the data. But then, it threw an exception at runtime.
|
||||
|
||||
I was confused because I was sure I'd handled everything correctly. After spending some time reading the API documentation, I realized the issue was in how I was handling the response. The API returned something slightly different from what I expected, and JavaScript didn't give any warning, it just failed when I tried to use the data.
|
||||
|
||||
Later, when I was faced with the same kind of problem using TypeScript, it was a better experience. With TypeScript, I was forced to define the expected response type up front. If I tried to access a field that didn't exist, the compiler will immediately flag it.
|
||||
|
||||
4. **When working with frameworks like Angular**: [Angular](https://roadmap.sh/angular) is a popular web development framework and is built on TypeScript. So if you want to work with Angular, then using TypeScript is a must.
|
||||
|
||||

|
||||
|
||||
## Is TypeScript hard to learn if you know JavaScript?
|
||||
|
||||
If you already know JavaScript, it's not hard to learn TypeScript. As explained in the previous section, TypeScript is JavaScript with static typing, so most of your JavaScript knowledge still applies. It was relatively easy for me to pick up TypeScript because I had been using JavaScript for a long time.
|
||||
|
||||
The typical learning curve challenges include:
|
||||
|
||||
- **Getting used to explicit typing**: Explicitly defining types for variables or objects might be challenging at first, but over time, you see the benefit and get used to it. The common data types in TypeScript are boolean, string, and number.
|
||||
|
||||
- **Handling type errors**: One of the advantages of TypeScript is that you can see type errors while coding. It can be tricky at first when you see a type error message, but read the error message carefully, and you will be able to make sense of it.
|
||||
|
||||
- **Generics**: This was one of the most confusing aspects of TypeScript for me. Generics help create reusable and flexible types and make your code more generic.
|
||||
|
||||
```typescript
|
||||
const generic = <T>(arg: T): T => console.log(arg);
|
||||
|
||||
generic("string") // "string"
|
||||
generic(34) // 34
|
||||
```
|
||||
|
||||
The code snippet shows a function that takes arguments of different types and logs the argument to the console. This is the power of generics.
|
||||
|
||||
- **Setting up configurations**: You will also have to get used to setting up a tsconfig file. A tsconfig file is a configuration file that tells the compiler how to compile TypeScript into JavaScript. It's usually confusing at first, but you'll get used to it over time by reading the documentation.
|
||||
|
||||
- **Interfaces and Types**: With Interfaces and Types, you can define the shape of an object. At first, I was confused about when to use an Interface or a Type because they can be used interchangeably. Yes, while they are alike, they have a slight difference. An Interface can be extended with new properties, but a Type cannot. I recommend taking a look at the TypeScript [documentation](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#interfaces) for this.
|
||||
|
||||
## TypeScript vs. JavaScript for frontend and backend
|
||||
|
||||
Are you caught in between choosing either TypeScript or JavaScript for either backend or frontend development? You can use TypeScript and JavaScript for both frontend and backend development. However, as you have seen in the previous sections, TypeScript is suitable for large-scale applications, while JavaScript is suitable for small projects.
|
||||
|
||||
Let's have a look at frontend and backend development using JavaScript and TypeScript.
|
||||
|
||||
**Frontend development: TypeScript vs. JavaScript**
|
||||
|
||||
The [frontend](https://roadmap.sh/frontend) is the visual part of an application that users interact with. It is built using HTML, CSS, JavaScript, or TypeScript. Popular JavaScript libraries and frameworks such as [React](https://roadmap.sh/react), [Vue](https://roadmap.sh/vue), and Angular also make frontend development easier.
|
||||
|
||||
JavaScript is the default language of the web. It is compatible with every browser and is used for web development. However, due to its lack of static typing, it is used for smaller frontend projects and small React applications. Whenever I want to build a small project with React, I opt for JavaScript because I develop faster and don't have to worry about types.
|
||||
|
||||
On the other hand, TypeScript is used for large-scale frontend applications. It is easier to scale and maintain a frontend application written in TypeScript than with JavaScript. The popular frameworks support TypeScript, but it is required for Angular, which does not support JavaScript.
|
||||
|
||||
I worked on a large-scale React project that was initially built with JavaScript. We ran into a log of bugs, and debugging was a nightmare. It was also quite difficult to debug state management using MobX. We switched to TypeScript, and debugging became easier. The code was also maintainable, thereby increasing developer productivity.
|
||||
|
||||

|
||||
|
||||
**Backend development: TypeScript vs. JavaScript**
|
||||
|
||||
The backend of an application handles the non-visual parts, such as the business logic, database, authentication logic, and APIs. [Node.js](https://roadmap.sh/nodejs) is the common runtime environment that helps developers run JavaScript and TypeScript in the backend.
|
||||
|
||||
JavaScript is more common in quick Minimum Viable Products (MVP) or prototypes using frameworks like Express.js, where a fast development cycle is a priority. You don't need extra setups to get started, and you don't need to worry about typing.
|
||||
|
||||
On the other hand, TypeScript brings better scalability, maintainability, and reliability to backend development. It supports Object-Oriented Programming and is best suited for building enterprise applications. It also supports static typing and avoids runtime errors, making backend services more reliable.
|
||||
|
||||

|
||||
|
||||
## Should you learn TypeScript or JavaScript first?
|
||||
|
||||
I work with TypeScript on a daily basis, expanding my knowledge of its features and looking for better ways to use the language. My experience has been enjoyable so far. However, before I got comfortable with TypeScript, I had worked with JavaScript for many years.
|
||||
|
||||
If you are new to web development, I recommend you start with JavaScript. Having a strong foundation of JavaScript will make it easier to grasp TypeScript. Then, after getting comfortable with JavaScript, you can pick up TypeScript and practice it by building small projects.
|
||||
|
||||
If you already know the basics of TypeScript and you are working in a team or a large code base, you should use TypeScript because of its advantages, such as static typing and maintainability.
|
||||
|
||||
## Will TypeScript replace JavaScript?
|
||||
|
||||
Many people, especially newbies, always ask if TypeScript will replace JavaScript. Well, the simple answer is no. JavaScript is not going anywhere. Although TypeScript shares many of the properties of JavaScript and adds some benefits like static typing, it still compiles to plain JavaScript because web browsers cannot execute TypeScript code. So, JavaScript is still very much needed.
|
||||
|
||||
However, many teams are migrating to TypeScript because of better tooling, scalability, and maintainability. Also, more frameworks, such as Vue and Next.js, are providing support for TypeScript.
|
||||
|
||||
Despite the popularity of TypeScript, JavaScript will still be the foundation and relevant in the future.
|
||||
|
||||
## Wrapping up
|
||||
|
||||
In this guide, we've seen the difference between JavaScript and TypeScript. For many developers, JavaScript is their go-to language because of its simplicity and low entry barrier. It is ideal for building small and responsive web and mobile apps, building prototypes, or experimenting with new programming concepts. JavaScript has a large ecosystem. With so many libraties and frameworks, it's easy to find tools that suit your needs.
|
||||
|
||||
On the other hand, TypeScript is an excellent choice if you are working on a large-scale project with multiple developers. As your project grows, having a strong type system provides huge benefits such as catching errors early, enforcing consistent data structures, and reduce runtime bugs. TypeScript provides developers with great tooling such as code completion, navigation and refactoring suggestions.
|
||||
|
||||
However, I recommend you learn both JavaScript and TypeScript and use them based on the project you are working on. roadmap.sh offers structured [JavaScript](https://roadmap.sh/javascript) and [TypeScript](https://roadmap.sh/typescript) roadmaps where you can track and share your progress on your profile. You can also customize your roadmap based on your learning needs.
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
```python
|
||||
accuracy = (TP + TN) / (TP + TN + FP + FN)
|
||||
precision = TP / (TP + FP) # How many selected items are relevant
|
||||
recall = TP / (TP + FN) # How many relevant items are selected
|
||||
f1_score = 2 * (precision * recall) / (precision + recall) # Harmonic mean
|
||||
|
||||
# For imbalanced datasets, consider:
|
||||
specificity = TN / (TN + FP)
|
||||
balanced_accuracy = (recall + specificity) / 2
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
- .map(): Works only on Series, applies a function element-wise
|
||||
- .apply(): More versatile, works on both Series and DataFrames
|
||||
- .applymap(): Applies a function to every element in a DataFrame
|
||||
|
||||
```python
|
||||
# map - simple transformation of Series values
|
||||
df['category'] = df['category_id'].map({1: 'Electronics', 2: 'Clothing'})
|
||||
|
||||
# apply on Series - more complex operations
|
||||
df['name'] = df['name'].apply(lambda x: x.title())
|
||||
|
||||
# apply on DataFrame - process entire rows or columns
|
||||
df.apply(lambda x: x.max() - x.min())
|
||||
|
||||
# applymap - element-wise operation on entire DataFrame
|
||||
df.applymap(lambda x: f"{x:.2f}" if isinstance(x, float) else x)
|
||||
```
|
||||
@@ -1,8 +0,0 @@
|
||||
Ensemble techniques in machine learning combine multiple weak models into a strong, more accurate predictive model, using the collective intelligence of diverse models to improve performance. Bagging and boosting are different ensemble techniques that use multiple models to reduce error and optimize the model.
|
||||
|
||||

|
||||
|
||||
The bagging technique uses multiple models trained on different subsets of data. It decreases the variance and helps to avoid overfitting. It is usually applied to decision tree methods and is a special case of the model averaging approach. Boosting is an ensemble modeling technique designed to create a strong classifier by combining multiple weak classifiers. The process involves building models sequentially, where each new model aims to correct the errors made by the previous ones.
|
||||
|
||||
- **Bagging:** Builds multiple models in parallel using bootstrapped datasets to reduce variance (e.g., Random Forest).
|
||||
- **Boosting:** Builds models sequentially, each trying to correct errors from the previous, reducing bias (e.g., XGBoost).
|
||||
@@ -1,7 +0,0 @@
|
||||
Batch learning is a term in artificial intelligence that refers to the process of training a machine learning model on a large set of data all at once, instead of continuously updating the model as new data comes in. This method allows for greater consistency and efficiency in the training process, as the model can learn from a fixed set of data before being deployed for use.
|
||||
|
||||
In batch learning, the model sees the entire dataset multiple times (known as epochs), refining its understanding with each pass. By processing data in large chunks, it converges more slowly but generally achieves higher accuracy.
|
||||
|
||||
Online learning takes a continuous, incremental approach. Instead of waiting for all the data to be available, you feed it to the model bit by bit, just like learning something new every day instead of cramming for a final exam. The model updates with each new data point, so it's constantly learning and evolving.
|
||||
|
||||
For example, imagine you're monitoring customer behavior on a website. Every time a user clicks or makes a purchase, your model gets smarter, learning from that single interaction and refining its predictions for the next.
|
||||
@@ -1,3 +0,0 @@
|
||||
The bias-variance tradeoff refers to the balance between a model's ability to learn patterns (low bias) and its ability to generalize to new data (low variance). Bias is the error made when the model makes strong assumptions about the data: high bias could lead to underfitting. Variance is the model's sensitivity to small fluctuations in the data, and high variance could lead to overfitting.
|
||||
|
||||

|
||||
@@ -1,5 +0,0 @@
|
||||
A categorical variable is a column with fixed options based on qualities or labels like gender, age group, or education level. You can't do math on them directly, so during preprocessing, I usually apply one-hot encoding to turn those categories into binary columns the model can understand.
|
||||
|
||||

|
||||
|
||||
A continuous variable, on the other hand, can take on any value within a range like height, temperature, or speed. These are numeric, so you can run calculations on them. But before feeding them into a model, I scale them using normalization or standardization to keep all features on a similar range. This prevents one feature from overpowering the rest just because it has larger numbers.
|
||||
@@ -1,3 +0,0 @@
|
||||
The Central Limit Theorem (CLT) states that if you take enough random samples from any dataset, even if the data is skewed or messy, the average of those samples will start to form a bell-shaped or normal distribution. This only holds if the samples are random, independent, and large enough, usually 30 or more.
|
||||
|
||||
Why does this matter? Because once those averages form a normal shape, you can use all the tools from the normal distribution. You can calculate standard errors, build confidence intervals, run hypothesis tests, make estimates, and use z-scores, even if the original data wasn’t normal to begin with.
|
||||
@@ -1,9 +0,0 @@
|
||||
When I combine data from different sources with inconsistent formats, the first thing I do is standardize everything: dates, column names, booleans, numbers, etc., so they all speak the same language. After doing that, I'll:
|
||||
|
||||
- **Align schemas:** match columns across datasets. If one has extras, I drop or keep them depending on relevance.
|
||||
- **Unify categories:** I clean up inconsistencies like "Y" vs. "Yes" to avoid downstream issues.
|
||||
- **Tag the source:** I add a source column so I know where each row came from. This is super useful for tracking or debugging later.
|
||||
- **Merge or stack:** If the structure is the same, I **concat()**. If I'm matching on something like customer ID, I go with a merge or join.
|
||||
- **Final clean-up:** I look for duplicates, mismatched types, or broken values post-merge and fix them.
|
||||
|
||||
I avoid merging before checking data types or keys. That’s a fast track to lost or duplicated rows.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user