Compare commits
1 Commits
fix/bcrypt
...
fix/count
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3143cf68b |
@@ -3,6 +3,6 @@
|
||||
"enabled": false
|
||||
},
|
||||
"_variables": {
|
||||
"lastUpdateCheck": 1727095669945
|
||||
"lastUpdateCheck": 1724925726721
|
||||
}
|
||||
}
|
||||
@@ -2,67 +2,12 @@
|
||||
|
||||
First of all, thank you for considering to contribute. Please look at the details below:
|
||||
|
||||
- [Hacktoberfest Contributions](#hacktoberfest-contributions)
|
||||
- [New Roadmaps](#new-roadmaps)
|
||||
- [Existing Roadmaps](#existing-roadmaps)
|
||||
- [Adding Projects](#adding-projects)
|
||||
- [Adding Content](#adding-content)
|
||||
- [Guidelines](#guidelines)
|
||||
|
||||
## Hacktoberfest Contributions
|
||||
|
||||
We are taking part in [Hacktoberfest 11](https://hacktoberfest.com/)!
|
||||
|
||||
Before you start to contribute to our project in order to satisfy [Hacktoberfest requirements](https://hacktoberfest.com/participation/#contributors), please bare in mind the following:
|
||||
|
||||
* There is not a Hacktoberfest t-shirt this year [(see their FAQ)](https://hacktoberfest.com/participation/#faq).
|
||||
* There is not an infinite opportunity to contribute to the roadmap.sh project.
|
||||
|
||||
### Hacktoberfest Specific Contribution rules
|
||||
|
||||
As Hacktoberfest attracts a lot of contributors (which is awesome), it does require a more rigid and strictly enforced set of guidelines than the average contribution.
|
||||
|
||||
These are as follows:
|
||||
|
||||
1. No single file contributions, please contribute to a minimum of two.
|
||||
|
||||
Whilst single file contributions, such as adding one link to a single topic, is perfectly fine outside of hacktoberfest, this can (and probably will) result it an easy 4 pull requests for everyone and we will just become a Hacktoberfest farming project.
|
||||
|
||||
***Note: If you contribute the entire contents of a topic i.e. the topic has 0 copy and 0 links, this will count.***
|
||||
|
||||
2. Typo fixes will not count (by themselves).
|
||||
|
||||
Whilst fixing typos is a great thing to do, lets bundle them in with actual contributions if we see them!
|
||||
|
||||
3. The same basic rules apply.
|
||||
|
||||
- Content must be in English.
|
||||
- Maximum of 8 links per topic.
|
||||
- Follow the below style guide for content.
|
||||
|
||||
Here is an example of a **fully complete** topic:
|
||||
|
||||
```markdown
|
||||
# Redis
|
||||
|
||||
Redis is an open-source, in-memory data structure store known for its speed and versatility. It supports various data types, including strings, lists, sets, hashes, and sorted sets, and provides functionalities such as caching, session management, real-time analytics, and message brokering. Redis operates as a key-value store, allowing for rapid read and write operations, and is often used to enhance performance and scalability in applications. It supports persistence options to save data to disk, replication for high availability, and clustering for horizontal scaling. Redis is widely used for scenarios requiring low-latency access to data and high-throughput performance.
|
||||
|
||||
Learn more from the following resources:
|
||||
|
||||
[@official@Link 1](https:/google.com)
|
||||
[@article@Link 2](https:/google.com)
|
||||
[@article@Link 3](https:/google.com)
|
||||
[@course@Link 4](https:/google.com)
|
||||
[@course@Link 5](https:/google.com)
|
||||
[@video@Link 6](https:/google.com)
|
||||
[@video@Link 7](https:/google.com)
|
||||
[@video@Link 8](https:/google.com)
|
||||
```
|
||||
|
||||
Contributions to the project that meet these requirements will be given the label `hacktoberfest-accepted` and merged, contributions that do not meet the requirements will simply be closed.
|
||||
|
||||
Any attempts at spam PRs will be given the `spam` tag. If you recieve 2 `spam` tags against you, you will be [disqualified from Hacktoberfest](https://hacktoberfest.com/participation/#spam).
|
||||
|
||||
## New Roadmaps
|
||||
|
||||
For new roadmaps, you can either:
|
||||
|
||||
352
package-lock.json
generated
@@ -18,7 +18,7 @@
|
||||
"@resvg/resvg-js": "^2.6.2",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"astro": "^4.15.4",
|
||||
"astro": "^4.14.6",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.12",
|
||||
"dom-to-image": "^2.6.0",
|
||||
@@ -29,18 +29,16 @@
|
||||
"jose": "^5.6.3",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.419.0",
|
||||
"luxon": "^3.5.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"nanostores": "^0.10.3",
|
||||
"node-html-parser": "^6.1.13",
|
||||
"npm-check-updates": "^17.0.0",
|
||||
"playwright": "^1.47.1",
|
||||
"playwright": "^1.45.3",
|
||||
"prismjs": "^1.29.0",
|
||||
"react": "^18.3.1",
|
||||
"react-calendar-heatmap": "^1.9.0",
|
||||
"react-confetti": "^6.1.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-slick": "^0.30.2",
|
||||
"react-tooltip": "^5.27.1",
|
||||
"reactflow": "^11.11.4",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
@@ -49,7 +47,6 @@
|
||||
"satori": "^0.10.14",
|
||||
"satori-html": "^0.3.2",
|
||||
"sharp": "^0.33.4",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"slugify": "^1.6.6",
|
||||
"tailwind-merge": "^2.4.0",
|
||||
"tailwindcss": "^3.4.7",
|
||||
@@ -62,10 +59,8 @@
|
||||
"@tailwindcss/typography": "^0.5.13",
|
||||
"@types/dom-to-image": "^2.6.7",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/prismjs": "^1.26.4",
|
||||
"@types/react-calendar-heatmap": "^1.6.7",
|
||||
"@types/react-slick": "^0.23.13",
|
||||
"@types/turndown": "^5.0.5",
|
||||
"csv-parser": "^3.0.0",
|
||||
"gh-pages": "^6.1.1",
|
||||
@@ -511,9 +506,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz",
|
||||
"integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==",
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz",
|
||||
"integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.24.8",
|
||||
"@babel/helper-validator-identifier": "^7.24.7",
|
||||
@@ -1754,50 +1749,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test/node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test/node_modules/playwright": {
|
||||
"version": "1.46.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz",
|
||||
"integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright-core": "1.46.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test/node_modules/playwright-core": {
|
||||
"version": "1.46.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz",
|
||||
"integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/background": {
|
||||
"version": "11.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz",
|
||||
@@ -2315,21 +2266,13 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@shikijs/core": {
|
||||
"version": "1.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.16.3.tgz",
|
||||
"integrity": "sha512-yETIvrETCeC39gSPIiSADmjri9FwKmxz0QvONMtTIUYlKZe90CJkvcjPksayC2VQOtzOJonEiULUa8v8crUQvA==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.14.1.tgz",
|
||||
"integrity": "sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw==",
|
||||
"dependencies": {
|
||||
"@shikijs/vscode-textmate": "^9.2.0",
|
||||
"@types/hast": "^3.0.4",
|
||||
"oniguruma-to-js": "0.3.3",
|
||||
"regex": "4.3.2"
|
||||
"@types/hast": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/vscode-textmate": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.2.2.tgz",
|
||||
"integrity": "sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg=="
|
||||
},
|
||||
"node_modules/@shuding/opentype.js": {
|
||||
"version": "1.4.0-beta.0",
|
||||
"resolved": "https://registry.npmjs.org/@shuding/opentype.js/-/opentype.js-1.4.0-beta.0.tgz",
|
||||
@@ -2671,12 +2614,6 @@
|
||||
"integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/luxon": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz",
|
||||
"integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mdast": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
|
||||
@@ -2753,15 +2690,6 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-slick": {
|
||||
"version": "0.23.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.13.tgz",
|
||||
"integrity": "sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/sax": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
|
||||
@@ -2972,17 +2900,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro": {
|
||||
"version": "4.15.4",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-4.15.4.tgz",
|
||||
"integrity": "sha512-wqy+m3qygt9DmCSqMsckxyK4ccCUFtti2d/WlLkEpAlqHgyDIg20zRTLHO2v/H4YeSlJ8sAcN0RW2FhOeYbINg==",
|
||||
"version": "4.14.6",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-4.14.6.tgz",
|
||||
"integrity": "sha512-MIDyNhtu3L4uakHvlTprh21eQPehYOtZSuSLtd+r6xZcl3lB+mlBz/hs1W3iHEQAORyJnKArWSY/aVOBKUyflA==",
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^2.10.3",
|
||||
"@astrojs/internal-helpers": "0.4.1",
|
||||
"@astrojs/markdown-remark": "5.2.0",
|
||||
"@astrojs/telemetry": "3.1.0",
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/generator": "^7.25.5",
|
||||
"@babel/parser": "^7.25.4",
|
||||
"@babel/plugin-transform-react-jsx": "^7.25.2",
|
||||
"@babel/types": "^7.25.6",
|
||||
"@babel/traverse": "^7.25.4",
|
||||
"@babel/types": "^7.25.4",
|
||||
"@oslojs/encoding": "^0.4.1",
|
||||
"@rollup/pluginutils": "^5.1.0",
|
||||
"@types/babel__core": "^7.20.5",
|
||||
@@ -3005,8 +2936,8 @@
|
||||
"es-module-lexer": "^1.5.4",
|
||||
"esbuild": "^0.21.5",
|
||||
"estree-walker": "^3.0.3",
|
||||
"execa": "^8.0.1",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fastq": "^1.17.1",
|
||||
"flattie": "^1.1.1",
|
||||
"github-slugger": "^2.0.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
@@ -3015,7 +2946,6 @@
|
||||
"js-yaml": "^4.1.0",
|
||||
"kleur": "^4.1.5",
|
||||
"magic-string": "^0.30.11",
|
||||
"magicast": "^0.3.5",
|
||||
"micromatch": "^4.0.8",
|
||||
"mrmime": "^2.0.0",
|
||||
"neotraverse": "^0.6.18",
|
||||
@@ -3027,15 +2957,14 @@
|
||||
"prompts": "^2.4.2",
|
||||
"rehype": "^13.0.1",
|
||||
"semver": "^7.6.3",
|
||||
"shiki": "^1.16.1",
|
||||
"shiki": "^1.14.1",
|
||||
"string-width": "^7.2.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"tinyexec": "^0.3.0",
|
||||
"tsconfck": "^3.1.3",
|
||||
"tsconfck": "^3.1.1",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vfile": "^6.0.3",
|
||||
"vite": "^5.4.2",
|
||||
"vitefu": "^1.0.2",
|
||||
"vitefu": "^0.2.5",
|
||||
"which-pm": "^3.0.0",
|
||||
"xxhash-wasm": "^1.0.2",
|
||||
"yargs-parser": "^21.1.1",
|
||||
@@ -3995,11 +3924,6 @@
|
||||
"iconv-lite": "^0.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/enquire.js": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
|
||||
"integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw=="
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
@@ -4116,6 +4040,28 @@
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
|
||||
},
|
||||
"node_modules/execa": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
|
||||
"integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"get-stream": "^8.0.1",
|
||||
"human-signals": "^5.0.0",
|
||||
"is-stream": "^3.0.0",
|
||||
"merge-stream": "^2.0.0",
|
||||
"npm-run-path": "^5.1.0",
|
||||
"onetime": "^6.0.0",
|
||||
"signal-exit": "^4.1.0",
|
||||
"strip-final-newline": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
@@ -4379,6 +4325,17 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stream": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
|
||||
"integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.7.6",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.6.tgz",
|
||||
@@ -4763,6 +4720,14 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/human-signals": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
|
||||
"integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
|
||||
"engines": {
|
||||
"node": ">=16.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/humanize-ms": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
|
||||
@@ -4962,6 +4927,17 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
|
||||
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-unicode-supported": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz",
|
||||
@@ -5022,12 +4998,6 @@
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/jquery": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
|
||||
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/js-cookie": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
|
||||
@@ -5063,14 +5033,6 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/json2mq": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
|
||||
"integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
|
||||
"dependencies": {
|
||||
"string-convert": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
@@ -5197,11 +5159,6 @@
|
||||
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
@@ -5287,14 +5244,6 @@
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz",
|
||||
"integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.11",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
|
||||
@@ -5303,16 +5252,6 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/magicast": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz",
|
||||
"integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.25.4",
|
||||
"@babel/types": "^7.25.4",
|
||||
"source-map-js": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||
@@ -5587,6 +5526,11 @@
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
@@ -6174,6 +6118,17 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-fn": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
|
||||
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-function": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
|
||||
@@ -6370,6 +6325,31 @@
|
||||
"npm": ">=8.12.1"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-path": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
|
||||
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
|
||||
"dependencies": {
|
||||
"path-key": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-path/node_modules/path-key": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
|
||||
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/nth-check": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||
@@ -6417,12 +6397,18 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/oniguruma-to-js": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.3.3.tgz",
|
||||
"integrity": "sha512-m90/WEhgs8g4BxG37+Nu3YrMfJDs2YXtYtIllhsEPR+wP3+K4EZk6dDUvy2v2K4MNFDDOYKL4/yqYPXDqyozTQ==",
|
||||
"node_modules/onetime": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
|
||||
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
|
||||
"dependencies": {
|
||||
"mimic-fn": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/openai": {
|
||||
@@ -6752,11 +6738,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.47.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz",
|
||||
"integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==",
|
||||
"version": "1.46.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz",
|
||||
"integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.47.2"
|
||||
"playwright-core": "1.46.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -6769,9 +6755,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.47.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz",
|
||||
"integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==",
|
||||
"version": "1.46.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz",
|
||||
"integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
@@ -7221,22 +7207,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-slick": {
|
||||
"version": "0.30.2",
|
||||
"resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.30.2.tgz",
|
||||
"integrity": "sha512-XvQJi7mRHuiU3b9irsqS9SGIgftIfdV5/tNcURTb5LdIokRA5kIIx3l4rlq2XYHfxcSntXapoRg/GxaVOM1yfg==",
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.5",
|
||||
"enquire.js": "^2.1.6",
|
||||
"json2mq": "^0.2.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"resize-observer-polyfill": "^1.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-tooltip": {
|
||||
"version": "5.27.1",
|
||||
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.27.1.tgz",
|
||||
@@ -7286,11 +7256,6 @@
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/regex": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/regex/-/regex-4.3.2.tgz",
|
||||
"integrity": "sha512-kK/AA3A9K6q2js89+VMymcboLOlF5lZRCYJv3gzszXFHBr6kO6qLGzbm+UIugBEV8SMMKCTR59txoY6ctRHYVw=="
|
||||
},
|
||||
"node_modules/rehype": {
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.1.tgz",
|
||||
@@ -7441,11 +7406,6 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/resize-observer-polyfill": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
|
||||
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
@@ -7832,12 +7792,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/shiki": {
|
||||
"version": "1.16.3",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-1.16.3.tgz",
|
||||
"integrity": "sha512-GypUE+fEd06FqDs63LSAVlmq7WsahhPQU62cgZxGF+TJT5LjD2k7HTxXj4/CKOVuMM3+wWQ1t4Y5oooeJFRRBQ==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-1.14.1.tgz",
|
||||
"integrity": "sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==",
|
||||
"dependencies": {
|
||||
"@shikijs/core": "1.16.3",
|
||||
"@shikijs/vscode-textmate": "^9.2.0",
|
||||
"@shikijs/core": "1.14.1",
|
||||
"@types/hast": "^3.0.4"
|
||||
}
|
||||
},
|
||||
@@ -7888,14 +7847,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
|
||||
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
|
||||
},
|
||||
"node_modules/slick-carousel": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz",
|
||||
"integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==",
|
||||
"peerDependencies": {
|
||||
"jquery": ">=1.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/slugify": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
|
||||
@@ -7945,11 +7896,6 @@
|
||||
"resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz",
|
||||
"integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w=="
|
||||
},
|
||||
"node_modules/string-convert": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
|
||||
"integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A=="
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
@@ -8072,6 +8018,17 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-final-newline": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
|
||||
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-outer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
|
||||
@@ -8293,11 +8250,6 @@
|
||||
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz",
|
||||
"integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg=="
|
||||
},
|
||||
"node_modules/to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
@@ -8367,9 +8319,9 @@
|
||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
|
||||
},
|
||||
"node_modules/tsconfck": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.3.tgz",
|
||||
"integrity": "sha512-ulNZP1SVpRDesxeMLON/LtWM8HIgAJEIVpVVhBM6gsmvQ8+Rh+ZG7FWGvHh7Ah3pRABwVJWklWCr/BTZSv0xnQ==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.1.tgz",
|
||||
"integrity": "sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==",
|
||||
"bin": {
|
||||
"tsconfck": "bin/tsconfck.js"
|
||||
},
|
||||
@@ -9159,9 +9111,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitefu": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.2.tgz",
|
||||
"integrity": "sha512-0/iAvbXyM3RiPPJ4lyD4w6Mjgtf4ejTK6TPvTNG3H32PLwuT0N/ZjJLiXug7ETE/LWtTeHw9WRv7uX/tIKYyKg==",
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz",
|
||||
"integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==",
|
||||
"peerDependencies": {
|
||||
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0"
|
||||
},
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@resvg/resvg-js": "^2.6.2",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"astro": "^4.15.4",
|
||||
"astro": "^4.14.6",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.12",
|
||||
"dom-to-image": "^2.6.0",
|
||||
@@ -50,12 +50,11 @@
|
||||
"jose": "^5.6.3",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.419.0",
|
||||
"luxon": "^3.5.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"nanostores": "^0.10.3",
|
||||
"node-html-parser": "^6.1.13",
|
||||
"npm-check-updates": "^17.0.0",
|
||||
"playwright": "^1.47.1",
|
||||
"playwright": "^1.45.3",
|
||||
"prismjs": "^1.29.0",
|
||||
"react": "^18.3.1",
|
||||
"react-calendar-heatmap": "^1.9.0",
|
||||
@@ -81,10 +80,8 @@
|
||||
"@tailwindcss/typography": "^0.5.13",
|
||||
"@types/dom-to-image": "^2.6.7",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/prismjs": "^1.26.4",
|
||||
"@types/react-calendar-heatmap": "^1.6.7",
|
||||
"@types/react-slick": "^0.23.13",
|
||||
"@types/turndown": "^5.0.5",
|
||||
"csv-parser": "^3.0.0",
|
||||
"gh-pages": "^6.1.1",
|
||||
|
||||
1724
pnpm-lock.yaml
generated
|
Before Width: | Height: | Size: 386 KiB |
|
Before Width: | Height: | Size: 256 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1013 KiB |
@@ -1,582 +0,0 @@
|
||||
{
|
||||
"_hYN0gEi9BL24nptEtXWU": {
|
||||
"title": "Introduction",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"GN6SnI7RXIeW8JeD-qORW": {
|
||||
"title": "What is an AI Engineer?",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"jSZ1LhPdhlkW-9QJhIvFs": {
|
||||
"title": "AI Engineer vs ML Engineer",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"wf2BSyUekr1S1q6l8kyq6": {
|
||||
"title": "LLMs",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"KWjD4xEPhOOYS51dvRLd2": {
|
||||
"title": "Inference",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"xostGgoaYkqMO28iN2gx8": {
|
||||
"title": "Training",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"XyEp6jnBSpCxMGwALnYfT": {
|
||||
"title": "Embeddings",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"LnQ2AatMWpExUHcZhDIPd": {
|
||||
"title": "Vector Databases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"9JwWIK0Z2MK8-6EQQJsCO": {
|
||||
"title": "RAG",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Dc15ayFlzqMF24RqIF_-X": {
|
||||
"title": "Prompt Engineering",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"9XCxilAQ7FRet7lHQr1gE": {
|
||||
"title": "AI Agents",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"5QdihE1lLpMc3DFrGy46M": {
|
||||
"title": "AI vs AGI",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"qJVgKe9uBvXc-YPfvX_Y7": {
|
||||
"title": "Impact on Product Development",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"K9EiuFgPBFgeRxY4wxAmb": {
|
||||
"title": "Roles and Responsiblities",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"d7fzv_ft12EopsQdmEsel": {
|
||||
"title": "Pre-trained Models",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"1Ga6DbOPc6Crz7ilsZMYy": {
|
||||
"title": "Benefits of Pre-trained Models",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"MXqbQGhNM3xpXlMC2ib_6": {
|
||||
"title": "Limitations and Considerations",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"2WbVpRLqwi3Oeqk1JPui4": {
|
||||
"title": "Open AI Models",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"vvpYkmycH0_W030E-L12f": {
|
||||
"title": "Capabilities / Context Length",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"LbB2PeytxRSuU07Bk0KlJ": {
|
||||
"title": "Cut-off Dates / Knowledge",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"hy6EyKiNxk1x84J63dhez": {
|
||||
"title": "Anthropic's Claude",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"oe8E6ZIQWuYvHVbYJHUc1": {
|
||||
"title": "Google's Gemini",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"3PQVZbcr4neNMRr6CuNzS": {
|
||||
"title": "Azure AI",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"OkYO-aSPiuVYuLXHswBCn": {
|
||||
"title": "AWS Sagemaker",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"8XjkRqHOdyH-DbXHYiBEt": {
|
||||
"title": "Hugging Face Models",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"n-Ud2dXkqIzK37jlKItN4": {
|
||||
"title": "Mistral AI",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"a7qsvoauFe5u953I699ps": {
|
||||
"title": "Cohere",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"5ShWZl1QUqPwO-NRGN85V": {
|
||||
"title": "OpenAI Models",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"zdeuA4GbdBl2DwKgiOA4G": {
|
||||
"title": "OpenAI API",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"_bPTciEA1GT1JwfXim19z": {
|
||||
"title": "Chat Completions API",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"9-5DYeOnKJq9XvEMWP45A": {
|
||||
"title": "Writing Prompts",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"nyBgEHvUhwF-NANMwkRJW": {
|
||||
"title": "Open AI Playground",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"15XOFdVp0IC-kLYPXUJWh": {
|
||||
"title": "Fine-tuning",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"qzvp6YxWDiGakA2mtspfh": {
|
||||
"title": "Maximum Tokens",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"FjV3oD7G2Ocq5HhUC17iH": {
|
||||
"title": "Token Counting",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"DZPM9zjCbYYWBPLmQImxQ": {
|
||||
"title": "Pricing Considerations",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"8ndKHDJgL_gYwaXC7XMer": {
|
||||
"title": "AI Safety and Ethics",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"cUyLT6ctYQ1pgmodCKREq": {
|
||||
"title": "Prompt Injection Attacks",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"lhIU0ulpvDAn1Xc3ooYz_": {
|
||||
"title": "Bias and Fareness",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"sWBT-j2cRuFqRFYtV_5TK": {
|
||||
"title": "Security and Privacy Concerns",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Pt-AJmSJrOxKvolb5_HEv": {
|
||||
"title": "Conducting adversarial testing",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"ljZLa3yjQpegiZWwtnn_q": {
|
||||
"title": "OpenAI Moderation API",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"4Q5x2VCXedAWISBXUIyin": {
|
||||
"title": "Adding end-user IDs in prompts",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"qmx6OHqx4_0JXVIv8dASp": {
|
||||
"title": "Robust prompt engineering",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"t1SObMWkDZ1cKqNNlcd9L": {
|
||||
"title": "Know your Customers / Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"ONLDyczNacGVZGojYyJrU": {
|
||||
"title": "Constraining outputs and inputs",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"a_3SabylVqzzOyw3tZN5f": {
|
||||
"title": "OpenSource AI",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"RBwGsq9DngUsl8PrrCbqx": {
|
||||
"title": "Open vs Closed Source Models",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"97eu-XxYUH9pYbD_KjAtA": {
|
||||
"title": "Popular Open Source Models",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"v99C5Bml2a6148LCJ9gy9": {
|
||||
"title": "Hugging Face",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"YLOdOvLXa5Fa7_mmuvKEi": {
|
||||
"title": "Hugging Face Hub",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"YKIPOiSj_FNtg0h8uaSMq": {
|
||||
"title": "Hugging Face Tasks",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"3kRTzlLNBnXdTsAEXVu_M": {
|
||||
"title": "Inference SDK",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"bGLrbpxKgENe2xS1eQtdh": {
|
||||
"title": "Transformers.js",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"rTT2UnvqFO3GH6ThPLEjO": {
|
||||
"title": "Ollama",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"ro3vY_sp6xMQ-hfzO-rc1": {
|
||||
"title": "Ollama Models",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"TsG_I7FL-cOCSw8gvZH3r": {
|
||||
"title": "Ollama SDK",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"--ig0Ume_BnXb9K2U7HJN": {
|
||||
"title": "What are Embeddings",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"eMfcyBxnMY_l_5-8eg6sD": {
|
||||
"title": "Semantic Search",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"HQe9GKy3p0kTUPxojIfSF": {
|
||||
"title": "Recommendation Systems",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"AglWJ7gb9rTT2rMkstxtk": {
|
||||
"title": "Anomaly Detection",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"06Xta-OqSci05nV2QMFdF": {
|
||||
"title": "Data Classification",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"l6priWeJhbdUD5tJ7uHyG": {
|
||||
"title": "Open AI Embeddings API",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"y0qD5Kb4Pf-ymIwW-tvhX": {
|
||||
"title": "Open AI Embedding Models",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"4GArjDYipit4SLqKZAWDf": {
|
||||
"title": "Pricing Considerations",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"apVYIV4EyejPft25oAvdI": {
|
||||
"title": "Open-Source Embeddings",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"ZV_V6sqOnRodgaw4mzokC": {
|
||||
"title": "Sentence Transformers",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"dLEg4IA3F5jgc44Bst9if": {
|
||||
"title": "Models on Hugging Face",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"tt9u3oFlsjEMfPyojuqpc": {
|
||||
"title": "Vector Databases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"WcjX6p-V-Rdd77EL8Ega9": {
|
||||
"title": "Purpose and Functionality",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"dSd2C9lNl-ymmCRT9_ZC3": {
|
||||
"title": "Chroma",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"_Cf7S1DCvX7p1_3-tP3C3": {
|
||||
"title": "Pinecone",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"VgUnrZGKVjAAO4n_llq5-": {
|
||||
"title": "Weaviate",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"JurLbOO1Z8r6C3yUqRNwf": {
|
||||
"title": "FAISS",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"rjaCNT3Li45kwu2gXckke": {
|
||||
"title": "LanceDB",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"DwOAL5mOBgBiw-EQpAzQl": {
|
||||
"title": "Qdrant",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"9kT7EEQsbeD2WDdN9ADx7": {
|
||||
"title": "Supabase",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"j6bkm0VUgLkHdMDDJFiMC": {
|
||||
"title": "MongoDB Atlas",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"5TQnO9B4_LTHwqjI7iHB1": {
|
||||
"title": "Indexing Embeddings",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"ZcbRPtgaptqKqWBgRrEBU": {
|
||||
"title": "Performing Similarity Search",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"lVhWhZGR558O-ljHobxIi": {
|
||||
"title": "RAG & Implementation",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"GCn4LGNEtPI0NWYAZCRE-": {
|
||||
"title": "RAG Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"qlBEXrbV88e_wAGRwO9hW": {
|
||||
"title": "RAG vs Fine-tuning",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"mX987wiZF7p3V_gExrPeX": {
|
||||
"title": "Chunking",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"grTcbzT7jKk_sIUwOTZTD": {
|
||||
"title": "Embedding",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"zZA1FBhf1y4kCoUZ-hM4H": {
|
||||
"title": "Vector Database",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"OCGCzHQM2LQyUWmiqe6E0": {
|
||||
"title": "Retrieval Process",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"2jJnS9vRYhaS69d6OxrMh": {
|
||||
"title": "Generation",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"WZVW8FQu6LyspSKm1C_sl": {
|
||||
"title": "Using SDKs Directly",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"ebXXEhNRROjbbof-Gym4p": {
|
||||
"title": "Langchain",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"d0ontCII8KI8wfP-8Y45R": {
|
||||
"title": "Llama Index",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"eOqCBgBTKM8CmY3nsWjre": {
|
||||
"title": "Open AI Assistant API",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"c0RPhpD00VIUgF4HJgN2T": {
|
||||
"title": "Replicate",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"AeHkNU-uJ_gBdo5-xdpEu": {
|
||||
"title": "AI Agents",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"778HsQzTuJ_3c9OSn5DmH": {
|
||||
"title": "Agents Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"voDKcKvXtyLzeZdx2g3Qn": {
|
||||
"title": "ReAct Prompting",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"6xaRB34_g0HGt-y1dGYXR": {
|
||||
"title": "Manual Implementation",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Sm0Ne5Nx72hcZCdAcC0C2": {
|
||||
"title": "OpenAI Functions / Tools",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"mbp2NoL-VZ5hZIIblNBXt": {
|
||||
"title": "OpenAI Assistant API",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"W7cKPt_UxcUgwp8J6hS4p": {
|
||||
"title": "Multimodal AI",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"sGR9qcro68KrzM8qWxcH8": {
|
||||
"title": "Multimodal AI Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"fzVq4hGoa2gdbIzoyY1Zp": {
|
||||
"title": "Image Understanding",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"49BWxYVFpIgZCCqsikH7l": {
|
||||
"title": "Image Generation",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"TxaZCtTCTUfwCxAJ2pmND": {
|
||||
"title": "Video Understanding",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"mxQYB820447DC6kogyZIL": {
|
||||
"title": "Audio Processing",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"GCERpLz5BcRtWPpv-asUz": {
|
||||
"title": "Text-to-Speech",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"jQX10XKd_QM5wdQweEkVJ": {
|
||||
"title": "Speech-to-Text",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"CRrqa-dBw1LlOwVbrZhjK": {
|
||||
"title": "OpenAI Vision API",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"LKFwwjtcawJ4Z12X102Cb": {
|
||||
"title": "DALL-E API",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"OTBd6cPUayKaAM-fLWdSt": {
|
||||
"title": "Whisper API",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"EIDbwbdolR_qsNKVDla6V": {
|
||||
"title": "Hugging Face Models",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"j9zD3pHysB1CBhLfLjhpD": {
|
||||
"title": "LangChain for Multimodal Apps",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"akQTCKuPRRelj2GORqvsh": {
|
||||
"title": "LlamaIndex for Multimodal Apps",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"NYge7PNtfI-y6QWefXJ4d": {
|
||||
"title": "Development Tools",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"XcKeQfpTA5ITgdX51I4y-": {
|
||||
"title": "AI Code Editors",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"TifVhqFm1zXNssA8QR3SM": {
|
||||
"title": "Code Completion Tools",
|
||||
"description": "",
|
||||
"links": []
|
||||
}
|
||||
}
|
||||
@@ -12,42 +12,12 @@
|
||||
},
|
||||
"DE3cMpeRYuUPw2ADtfS-3": {
|
||||
"title": "Angular Architecture",
|
||||
"description": "Angular follows a modular architecture pattern, dividing the application into distinct modules, components, services, and other elements, which enhances code organization and maintainability. The key building blocks include modules, which are containers grouping related components, services, directives, and other elements to ensure proper encapsulation and reusability. Components are the building blocks of Angular applications, representing parts of the user interface with associated logic, consisting of templates, styles, and a class defining behavior. Services encapsulate reusable business logic, data manipulation, and API communication, enabling data and functionality sharing across components. Directives are HTML attributes or elements that extend HTML functionality, allowing reusable behaviors across the application. Lastly, pipes transform data before displaying it in templates, providing convenient ways to format, filter, and sort data.\n\nVisit the following resources to learn more:",
|
||||
"description": "Visit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Angular coding style guide",
|
||||
"url": "https://angular.dev/style-guide",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "The Ultimate Guide to Angular Architecture: Best Practices for efficient coding with Angular Framework",
|
||||
"url": "https://angulardive.com/blog/the-ultimate-guide-to-angular-architecture-best-practices-for-efficient-coding-with-angular-framework/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Modern Architectures with Angular Part 1: Strategic design with Sheriff and Standalone Components",
|
||||
"url": "https://www.angulararchitects.io/en/blog/modern-architectures-with-angular-part-1-strategic-design-with-sheriff-and-standalone-components/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Optimizing the architecture of large web applications with Angular",
|
||||
"url": "https://albertobasalo.medium.com/optimizing-the-architecture-of-large-web-applications-with-angular-79d03b01a92b",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Angular Architecture Concepts and Patterns",
|
||||
"url": "https://www.bigscal.com/blogs/frontend/angular-architecture-concepts-and-patterns/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Top 10 Angular Architecture Mistakes",
|
||||
"url": "https://angularexperts.io/blog/top-10-angular-architecture-mistakes",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Architecting Angular: A Guide to effective project structure",
|
||||
"url": "https://medium.com/@nile.bits/architecting-angular-a-guide-to-effective-project-structure-9756bae92262",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -886,7 +856,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Understanding Pipes",
|
||||
"url": "https://angular.dev/tutorials/learn-angular/22-pipes",
|
||||
"url": "https://angular.dev/guide/pipes",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1953,7 +1953,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Python Roadmap",
|
||||
"url": "https://roadmap.sh/python",
|
||||
"url": "/python",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -1994,7 +1994,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Go Roadmap",
|
||||
"url": "https://roadmap.sh/golang",
|
||||
"url": "/golang",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -2056,7 +2056,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated React Roadmap",
|
||||
"url": "https://roadmap.sh/react",
|
||||
"url": "/react",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -2097,7 +2097,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Angular Roadmap",
|
||||
"url": "https://roadmap.sh/angular",
|
||||
"url": "/angular",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -2118,7 +2118,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Vue Roadmap",
|
||||
"url": "https://roadmap.sh/vue",
|
||||
"url": "/vue",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Python Roadmap",
|
||||
"url": "https://roadmap.sh/python",
|
||||
"url": "/python",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -107,7 +107,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Go Roadmap",
|
||||
"url": "https://roadmap.sh/golang",
|
||||
"url": "/golang",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -292,7 +292,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Java Roadmap",
|
||||
"url": "https://roadmap.sh/java",
|
||||
"url": "/java",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -372,6 +372,16 @@
|
||||
"url": "https://www.coursera.org/lecture/data-structures/doubly-linked-lists-jpGKD",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "CS 61B Lecture 7: Linked Lists I",
|
||||
"url": "https://archive.org/details/ucberkeley_webcast_htzJdKoEmO0",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "CS 61B Lecture 7: Linked Lists II",
|
||||
"url": "https://archive.org/details/ucberkeley_webcast_-c4I3gFYe3w",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Linked List Data Structure | Illustrated Data Structures",
|
||||
"url": "https://www.youtube.com/watch?v=odW9FU8jPRQ",
|
||||
@@ -382,16 +392,6 @@
|
||||
"url": "https://www.youtube.com/watch?v=F8AbOfQwl1c",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CS 61B Lecture 7: Linked Lists I",
|
||||
"url": "https://archive.org/details/ucberkeley_webcast_htzJdKoEmO0",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "CS 61B Lecture 7: Linked Lists II",
|
||||
"url": "https://archive.org/details/ucberkeley_webcast_-c4I3gFYe3w",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Why you should avoid Linked Lists?",
|
||||
"url": "https://www.youtube.com/watch?v=YQs6IC-vgmo",
|
||||
@@ -511,14 +511,14 @@
|
||||
"url": "https://www.coursera.org/lecture/data-structures/dynamic-arrays-EwbnV",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Array Data Structure | Illustrated Data Structures",
|
||||
"url": "https://www.youtube.com/watch?v=QJNwK2uJyGs",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "UC Berkeley CS61B - Linear and Multi-Dim Arrays (Start watching from 15m 32s)",
|
||||
"url": "https://archive.org/details/ucberkeley_webcast_Wp8oiO_CZZE",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Array Data Structure | Illustrated Data Structures",
|
||||
"url": "https://www.youtube.com/watch?v=QJNwK2uJyGs",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
@@ -3786,7 +3786,7 @@
|
||||
},
|
||||
"7r7o8pYhFHVAJIv0wNT6X": {
|
||||
"title": "Hashing / Encryption / Encoding",
|
||||
"description": "Hashing is a one-way function that takes an input and produces a fixed-length output. The output is called a hash. The hash is a unique representation of the input. The hash is deterministic, meaning that the same input will always produce the same hash. The hash is irreversible, meaning that it is impossible to go from the hash back to the original input. The hash is not collision-resistant, meaning that it is possible to find two different inputs that produce the same hash.\n\nEncryption is a two-way function that takes an input and produces an output. The output is called ciphertext. The ciphertext is a unique representation of the input. The ciphertext is deterministic, meaning that the same input will always produce the same ciphertext. The ciphertext is reversible, meaning that it is possible to go from the ciphertext back to the original input. The ciphertext is collision-resistant, meaning that it is impossible to find two different inputs that produce the same ciphertext.\n\nEncoding is a two-way function that takes an input and produces an output. The output is called encoded text. The encoded text is a unique representation of the input. The encoded text is deterministic, meaning that the same input will always produce the same encoded text. The encoded text is reversible, meaning that it is possible to go from the encoded text back to the original input. The encoded text is collision-resistant, meaning that it is impossible to find two different inputs that produce the same encoded text.\n\nVisit the following resources to learn more:",
|
||||
"description": "Hashing is a one-way function that takes an input and produces a fixed-length output. The output is called a hash. The hash is a unique representation of the input. The hash is deterministic, meaning that the same input will always produce the same hash. The hash is irreversible, meaning that it is impossible to go from the hash back to the original input. The hash is collision-resistant, meaning that it is impossible to find two different inputs that produce the same hash.\n\nEncryption is a two-way function that takes an input and produces an output. The output is called ciphertext. The ciphertext is a unique representation of the input. The ciphertext is deterministic, meaning that the same input will always produce the same ciphertext. The ciphertext is reversible, meaning that it is possible to go from the ciphertext back to the original input. The ciphertext is collision-resistant, meaning that it is impossible to find two different inputs that produce the same ciphertext.\n\nEncoding is a two-way function that takes an input and produces an output. The output is called encoded text. The encoded text is a unique representation of the input. The encoded text is deterministic, meaning that the same input will always produce the same encoded text. The encoded text is reversible, meaning that it is possible to go from the encoded text back to the original input. The encoded text is not collision-resistant, meaning that it is possible to find two different inputs that produce the same encoded text.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Explore top posts about Encryption",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
]
|
||||
},
|
||||
"DFMR-0MbmVCCrJu0I9JWG": {
|
||||
"title": "Prescriptive Analytics",
|
||||
"title": "Prespective Analytics",
|
||||
"description": "Prescriptive analytics, a crucial type of data analytics, is essential for making data-driven decisions in business and organizational contexts. As a data analyst, the goal of prescriptive analytics is to recommend various actions using predictions on the basis of known parameters to help decision makers understand likely outcomes. Prescriptive analytics employs a blend of techniques and tools such as algorithms, machine learning, computational modelling procedures, and decision-tree structures to enable automated decision making. Therefore, prescriptive analytics not only anticipates what will happen and when it will happen, but also explains why it will happen, contributing to the significance of a data analyst’s role in an organization.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
@@ -1355,12 +1355,12 @@
|
||||
"description": "Parallel processing is an efficient form of data processing that allows Data Analysts to deal with larger volumes of data at a faster pace. It is a computational method that allows multiple tasks to be performed concurrently, instead of sequentially, thus, speeding up data processing. Parallel processing proves to be invaluable for Data Analysts, as they are often tasked with analyzing huge data sets and compiling reports in real-time. As the demand for rapid data processing and quick analytics is on the rise, the technique of parallel processing forms a critical element in the versatile toolkit of a Data Analyst.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What Is Parallel Processing?",
|
||||
"title": "What is parallel processing?",
|
||||
"url": "https://www.spiceworks.com/tech/iot/articles/what-is-parallel-processing/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "How Parallel Computing Works",
|
||||
"title": "How parallel computing works?",
|
||||
"url": "https://computer.howstuffworks.com/parallel-processing.htm",
|
||||
"type": "article"
|
||||
}
|
||||
|
||||
@@ -232,24 +232,8 @@
|
||||
},
|
||||
"X0xUzEP0S6SyspvqyoDDk": {
|
||||
"title": "Technical Documentation",
|
||||
"description": "Technical documentation refers to the process of creating and maintaining written descriptions of software, hardware, and other technical products or systems. It involves communicating complex information in a clear, concise, and organized manner to help users understand, operate, and maintain the product or system. Effective technical documentation can take many forms, including user manuals, guides, tutorials, release notes, and API documentation, and is typically created by technical writers, developers, and subject matter experts. The goal of technical documentation is to provide accurate, up-to-date, and easily accessible information that enables users to get the most out of a product or system, while also reducing support queries and improving overall user experience.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Technical Writer Roadmap",
|
||||
"url": "https://roadmap.sh/technical-writer",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Technical Writing - MDN Docs",
|
||||
"url": "https://developer.mozilla.org/en-US/blog/technical-writing/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Technical Documentation - Wikipedia",
|
||||
"url": "https://en.wikipedia.org/wiki/Technical_documentation",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"urbtoZtuJryK-6TJ3lmRN": {
|
||||
"title": "Social Media",
|
||||
@@ -369,76 +353,18 @@
|
||||
},
|
||||
"aSYXa25_0O2qQl1O-N3xl": {
|
||||
"title": "Basic Programming Skills",
|
||||
"description": "Basic programming skills are essential for Developer Relations professionals to effectively communicate with developers, understand their needs, and provide technical support. These skills include proficiency in at least one programming language, understanding of data structures and algorithms, and familiarity with development tools and technologies. Having basic programming skills enables Developer Relations professionals to create educational content, provide technical guidance, and participate in code reviews.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "TheNewStack.io - Devrel",
|
||||
"url": "https://thenewstack.io/devrel-for-beginners-how-to-get-started/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Getting Started With Devrel- Dev.to",
|
||||
"url": "https://dev.to/rejoice/getting-started-with-devrel-22n3",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "How to become a DevRel",
|
||||
"url": "https://www.pluralsight.com/resources/blog/tech-operations/developer-relations-getting-a-devrel-job",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"KdFYmj36M2jrGfsYkukpo": {
|
||||
"title": "IDEs",
|
||||
"description": "The concept of Integrated Development Environments (IDEs) traces its roots back to the 1960s and 1970s when basic text editors and simple programming tools were used in early computing. The first notable IDEs emerged with the rise of Unix systems in the 1970s, such as the EMACS text editor, which included features like code editing and debugging. Today, IDEs have become essential for developers, supporting multiple programming languages and integrating cloud-based tools, continuous integration, and real-time collaboration. IDEs like Visual Studio Code, IntelliJ IDEA, and Eclipse are widely adopted by developers across industries.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "History of IDEs",
|
||||
"url": "https://multiqos.com/blogs/guide-to-integrated-development-environment/#:~:text=While%20TurboPascal%20may%20have%20popularized,significant%20popularity%20in%20the%201980s.",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Visual Studio Code",
|
||||
"url": "https://code.visualstudio.com/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "JetBrains IDEs",
|
||||
"url": "https://www.jetbrains.com/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"j5nNSYI8s-cH8EA6G1EWY": {
|
||||
"title": "VS Code",
|
||||
"description": "Visual Studio Code (VSCode) was first announced by Microsoft in 2015 and quickly became one of the most popular and widely used Integrated Development Environments (IDEs). Built on Electron, a framework that allows web technologies like JavaScript, HTML, and CSS to create desktop applications, VSCode offers a lightweight and highly extensible platform for developers. VSCode focuses on being a streamlined code editor with the ability to install extensions that add features such as debugging, version control, and language-specific tooling. Microsoft's vision was to create a flexible environment that could cater to all types of developers, from beginners to seasoned professionals.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visual Studio Code",
|
||||
"url": "https://code.visualstudio.com/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Docs - Visual Studio Code",
|
||||
"url": "https://code.visualstudio.com/docs",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "FreeCodeCamp Crash Course",
|
||||
"url": "https://www.youtube.com/watch?v=WPqXP_kLzpo",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "VSCode Basics",
|
||||
"url": "https://www.youtube.com/watch?v=B-s71n0dHUk",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "VSCode in 100 Seconds",
|
||||
"url": "https://www.youtube.com/watch?v=KMxo3T_MTvY",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"NCnKS435DCl-8vilr1_XE": {
|
||||
"title": "JetBrains IDEs",
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated JavaScript Roadmap",
|
||||
"url": "https://roadmap.sh/javascript",
|
||||
"url": "/javascript",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -257,17 +257,17 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated React Roadmap",
|
||||
"url": "https://roadmap.sh/react",
|
||||
"url": "/react",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "React Website",
|
||||
"url": "https://react.dev/",
|
||||
"url": "https://reactjs.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Official Getting Started",
|
||||
"url": "https://react.dev/learn/tutorial-tic-tac-toe",
|
||||
"url": "https://reactjs.org/tutorial/tutorial.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -650,7 +650,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated PostgreSQL DBA Roadmap",
|
||||
"url": "https://roadmap.sh/postgresql-dba",
|
||||
"url": "/postgresql-dba",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -378,17 +378,12 @@
|
||||
"links": []
|
||||
},
|
||||
"AaRZiItRcn8fYb5R62vfT": {
|
||||
"title": "GDScript",
|
||||
"description": "GDScript is a high-level, dynamically-typed programming language designed specifically for the Godot Engine, an open-source game development platform. It is tailored for ease of use and rapid development of game logic and functionality. GDScript features a syntax similar to Python, which simplifies learning and coding for developers familiar with Python, while providing direct access to Godot's rich set of built-in functions and game-specific APIs. The language integrates closely with Godot's scene system and scripting environment, enabling developers to create and manipulate game objects, handle input, and control game behavior efficiently.\n\nLearn more from the following resources:",
|
||||
"title": "Assembly",
|
||||
"description": "**Assembly** is a low-level programming language, often used for direct hardware manipulation, real-time systems, and to write performance-critical code. It provides a strong correspondence between its instructions and the architecture's machine-code instructions, since it directly represents the specific commands of the computer's CPU structure. However, it's closer to machine language (binary code) than to human language, which makes it difficult to read and understand. The syntax varies greatly, which depends upon the CPU architecture for which it's designed, thus Assembly language written for one type of processor can't be used on another. Despite its complexity, time-intensive coding process and machine-specific nature, Assembly language is still utilized for speed optimization and hardware manipulation where high-level languages may not be sufficient.",
|
||||
"links": [
|
||||
{
|
||||
"title": "GDScript Website",
|
||||
"url": "https://gdscript.com/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "How to program in Godot - GDScript Tutorial",
|
||||
"url": "https://www.youtube.com/watch?v=e1zJS31tr88",
|
||||
"title": "Code walkthrough of a game written in x64 assembly",
|
||||
"url": "https://www.youtube.com/watch?v=WUoqlp30M78",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
@@ -462,7 +457,7 @@
|
||||
},
|
||||
"odfZWKtPbb-lC35oeTCNV": {
|
||||
"title": "Specular",
|
||||
"description": "`Specular` reflections are mirror-like reflections. In these cases, the rays of light are reflected, more than they are absorbed. The angle of incidence is equal to the angle of reflection, that is to say that the angle at which the light enters the medium and then bounces off, the angle of the beam that bounced off would be the same.\n\nLearn more from the following resources:\n\n\\-[@video@Specular reflection](https://www.youtube.com/watch?v=2cFvJkc4pQk)",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"THMmnx8p_P0X-dSPoHvst": {
|
||||
@@ -472,35 +467,13 @@
|
||||
},
|
||||
"iBZ1JsEWI0xuLgUvfWfl-": {
|
||||
"title": "Texture",
|
||||
"description": "`Texture` is the visual quality of an object. Where the `mesh` determines the shape or `topology` of an object, the texture describes the quality of said object. For instance, if there is a spherical mesh, is it supposed to be shiny? is it supposed to be rough? is it supposed to be of rock or of wood? questions of this ilk are often resolved using textures. Textures are often just 2D images that are wrapped onto 3D meshes. The 3D mesh is first divided into segments and unfurled; the 3D meshes are converted into 2D chunks, this process is known as `UV Unwrapping`. Once a mesh has been unwrapped, the textures in the form of an image are applied to the 2D chunks of the 3D mesh, this way the texture knows how to properly wrap around the mesh and avoid any conflicts. Textures determine the visual feel and aesthetics of the game.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "How Nintendo textures work",
|
||||
"url": "https://www.youtube.com/watch?v=WrCMzHngLxI",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "How Pixar textures work",
|
||||
"url": "https://www.youtube.com/watch?v=o_I6jxlN-Ck",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"r4UkMd5QURbvJ3Jlr_H9H": {
|
||||
"title": "Bump",
|
||||
"description": "`Bump` is very similar to texture. It is, as a matter of fact, a type of texture itself. If you take the texture of a bricked wall, it will becoming increasingly obvious that the amount of detail present inside the wall, if geometrically processed would be incredibly demanding and wasteful. In order to combat this ineffeciency, the `bump` maps were created. Traditionally, a flat texture would just be an image of something called a `color map`, that is to say, where does each individual color of the pixel should be to represent a texture. When you take the picture of your floor, wall, or any object, that image in essence is the color map. The bump map is different as it informs the texture about it's `normal` values. So, if you take a flat 2D mesh and apply a bump map on it, it will render the same 2D mesh with all the normal values baked into the flat 2D mesh, creating a graphically effect mimicking 3-dimensionality.\n\nThere is also something known as a normal map, and displacement maps.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Normals, Normal maps and Bump maps",
|
||||
"url": "https://www.youtube.com/watch?v=l5PYyzsZED8",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Bump, normal and displacement",
|
||||
"url": "https://www.youtube.com/watch?v=43Ilra6fNGc",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"YGeGleEN203nokiZIYJN8": {
|
||||
"title": "Parallax",
|
||||
|
||||
@@ -13,11 +13,6 @@
|
||||
"url": "https://www.datacamp.com/blog/all-about-git",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Version Control (Git) - The Missing Semester of Your CS Education",
|
||||
"url": "https://missing.csail.mit.edu/2020/version-control/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "GUI Clients",
|
||||
"url": "https://git-scm.com/downloads/guis",
|
||||
@@ -220,11 +215,6 @@
|
||||
"title": ".gitignore",
|
||||
"description": "Ignored files are tracked in a special file named `.gitignore` that is checked in at the root of your repository. There is no explicit git ignore command: instead the `.gitignore` file must be edited and committed by hand when you have new files that you wish to ignore. `.gitignore` files contain patterns that are matched against file names in your repository to determine whether or not they should be ignored.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "gitignore - A collection of useful .gitignore templates",
|
||||
"url": "https://github.com/github/gitignore",
|
||||
"type": "opensource"
|
||||
},
|
||||
{
|
||||
"title": "gitignore Documentation",
|
||||
"url": "https://git-scm.com/docs/gitignore/en",
|
||||
@@ -267,11 +257,6 @@
|
||||
"url": "https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Learn Git Branching",
|
||||
"url": "https://learngitbranching.js.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Git Branches Tutorial",
|
||||
"url": "https://www.youtube.com/watch?v=e2IbNHi4uCI",
|
||||
@@ -300,7 +285,7 @@
|
||||
"description": "Renaming a branch in Git means changing the name of a branch to something different while preserving its history and the commits it contains. The branch itself remains the same in terms of the code and history it tracks, but the reference (the name by which you refer to it) is updated\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Renaming a Branch - GitHub Docs",
|
||||
"title": "Renaming a branch - GitHub Docs",
|
||||
"url": "https://docs.github.com/github/administering-a-repository/renaming-a-branch",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -366,26 +351,15 @@
|
||||
},
|
||||
"NeSvgS2yA5x8qbcJHrV__": {
|
||||
"title": "GitHub Essentials",
|
||||
"description": "GitHub Essentials refers to the core features and functionalities that form the foundation of GitHub's version control and collaboration platform. These essentials include repositories for storing and managing code, branches for parallel development, pull requests for code review and merging, issues for tracking tasks and bugs, and collaborative tools like project boards and wikis. Understanding and mastering these fundamental components allows developers to effectively manage their projects, collaborate with team members, and contribute to open-source initiatives, making GitHub an indispensable tool in modern software development workflows.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Github Essentials - Microsoft",
|
||||
"url": "https://learn.microsoft.com/en-us/contribute/content/git-github-fundamentals",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Hello World",
|
||||
"url": "https://docs.github.com/en/get-started/start-your-journey/hello-world",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "GitHub Essentials refers to the core features and functionalities that form the foundation of GitHub's version control and collaboration platform. These essentials include repositories for storing and managing code, branches for parallel development, pull requests for code review and merging, issues for tracking tasks and bugs, and collaborative tools like project boards and wikis. Understanding and mastering these fundamental components allows developers to effectively manage their projects, collaborate with team members, and contribute to open-source initiatives, making GitHub an indispensable tool in modern software development workflows.",
|
||||
"links": []
|
||||
},
|
||||
"i7fIIHcBEk473te8bniJ5": {
|
||||
"title": "Creating Account",
|
||||
"description": "To get started with GitHub, you'll need to create a free personal account on [GitHub.com](http://GitHub.com) and verify your email address. Every person who uses [GitHub.com](http://GitHub.com) signs in to a personal account. Your personal account is your identity on [GitHub.com](http://GitHub.com) and has a username and profile.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Creating an Account on GitHub",
|
||||
"title": "Creating an account on GitHub",
|
||||
"url": "https://docs.github.com/en/get-started/start-your-journey/creating-an-account-on-github",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -439,7 +413,7 @@
|
||||
"description": "A GitHub Profile README is a special repository that allows users to showcase their skills, projects, and personality directly on their GitHub profile. To create one, you need to make a new repository with the same name as your GitHub username. This repository should contain a [README.md](http://README.md) file, which GitHub will automatically display on your profile page. The README can be customized with Markdown formatting, allowing you to add text, images, links, and even dynamic content like GitHub stats or recent blog posts. This feature provides a unique opportunity to make your GitHub profile more engaging and informative for visitors, effectively serving as a personalized landing page for your GitHub presence.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Managing your Profile README",
|
||||
"title": "Managing your profile README",
|
||||
"url": "https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -466,7 +440,7 @@
|
||||
"description": "In Git, a remote is a reference to a repository that exists on another server or system. Remotes allow you to access and interact with a copy of your repository that is stored elsewhere, making it possible to collaborate with others, share your work, and maintain multiple copies of your repository for backup and disaster recovery purposes. When you add a remote to your local repository, Git creates a reference to the remote repository, enabling you to push changes from your local repository to the remote one, pull changes from the remote to your local one, or fetch changes from the remote without updating your local copy. This enables distributed development and helps maintain a centralized version of your project's history, making it easier to track changes, manage conflicts, and ensure that everyone has access to the most up-to-date code.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "About Remote Repositories",
|
||||
"title": "About remote repositories",
|
||||
"url": "https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -503,7 +477,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Git Push and Pull Tutorial",
|
||||
"title": "GIT Push and Pull Tutorial",
|
||||
"url": "https://www.datacamp.com/tutorial/git-push-pull",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -598,22 +572,22 @@
|
||||
"description": "Cloning a repository in Git and GitHub involves creating a local copy of a remote repository on your computer. This allows you to work on the project locally, commit changes, and later push those changes back to the remote repository.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "git clone",
|
||||
"title": "Git clone",
|
||||
"url": "https://git-scm.com/docs/git-clone/en",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Cloning a Repository",
|
||||
"title": "Cloning a repository",
|
||||
"url": "https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Clone a Git Repository",
|
||||
"title": "Clone a git respository",
|
||||
"url": "https://www.atlassian.com/git/tutorials/setting-up-a-repository/git-clone",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Cloning Remote Repository into local machine",
|
||||
"title": "Cloning remote respository into local machine",
|
||||
"url": "https://youtu.be/xeQih8LVtZM?si=djlyTDpLNS0oyqQH",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -716,14 +690,8 @@
|
||||
},
|
||||
"oWMaOWU06juoIuzXNe-wt": {
|
||||
"title": "Mentions",
|
||||
"description": "Mentions on GitHub allow you to notify specific users or teams about comments, issues, pull requests, or other activities. This feature improves collaboration by encouraging participation and discussion among team members, increasing visibility of important topics, and streamlining communication within your repository. To use mentions, simply type `@username` or `@teamname` in a comment, and GitHub will auto-complete the mention as you type, automatically linking their username to the comment and notifying them about the discussion.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Mention Somebody",
|
||||
"url": "https://github.blog/news-insights/mention-somebody-they-re-notified/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "Mentions on GitHub allow you to notify specific users or teams about comments, issues, pull requests, or other activities. This feature improves collaboration by encouraging participation and discussion among team members, increasing visibility of important topics, and streamlining communication within your repository. To use mentions, simply type `@username` or `@teamname` in a comment, and GitHub will auto-complete the mention as you type, automatically linking their username to the comment and notifying them about the discussion.",
|
||||
"links": []
|
||||
},
|
||||
"XstmIysIi_lWo6RzszLBt": {
|
||||
"title": "Reactions",
|
||||
@@ -738,19 +706,8 @@
|
||||
},
|
||||
"0rHDUbAdXqH9zQW2VfJ8v": {
|
||||
"title": "Merge Strategies",
|
||||
"description": "When combining changes from one branch into another, Git provides various merge strategies to choose from. These methods allow for flexibility and customization in integrating code updates into your main branch. The available options include:\n\n* Fast Forward (FF)\n* Non-Fast Forward\n* Rebase\n* Squash\n* Cherry Picking\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Git Merge Strategies",
|
||||
"url": "https://git-scm.com/docs/merge-strategies",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Git Merge Options",
|
||||
"url": "https://www.atlassian.com/git/tutorials/using-branches/merge-strategy",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "When combining changes from one branch into another, Git provides various merge strategies to choose from. These methods allow for flexibility and customization in integrating code updates into your main branch. The available options include:\n\n* Fast Forward (FF)\n* Non-Fast Forward\n* Rebase\n* Squash\n* Cherry Picking",
|
||||
"links": []
|
||||
},
|
||||
"agtPWS8j6i6wQPk10cy8E": {
|
||||
"title": "Fast-Forward vs Non-FF",
|
||||
@@ -841,17 +798,17 @@
|
||||
"description": "Cherry-picking in Git allows you to apply a specific commit from one branch to another, without merging the entire branch. This is useful when you want to bring in a specific feature or fix from one branch to another without incorporating all the changes from the source branch.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "git-cherry-pick docs",
|
||||
"title": "git-cherry-pick-docs",
|
||||
"url": "https://git-scm.com/docs/git-cherry-pick",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Git Cherry Pick",
|
||||
"title": "Git cherry pick",
|
||||
"url": "https://www.atlassian.com/git/tutorials/cherry-pick",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Git Cherry Pick - Tutorial",
|
||||
"title": "Git-CHERRY-PICK-Tutorial",
|
||||
"url": "https://youtu.be/i657Bg_HAWI?si=3jjn2X8Hi1na--F4",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -909,7 +866,7 @@
|
||||
"description": "Pull Request (PR) guidelines are essential for maintaining a smooth and efficient code review process in collaborative development environments. These guidelines typically outline best practices for creating, formatting, and submitting PRs, ensuring that changes are well-documented, easy to review, and align with the project's standards. They may cover aspects such as PR size, commit message formatting, documentation requirements, and testing expectations. By establishing clear PR guidelines, teams can streamline their workflow, improve code quality, and facilitate effective communication among contributors.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Best Practices for Pull Requests",
|
||||
"title": "Best practices for pull requests",
|
||||
"url": "https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/best-practices-for-pull-requests",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -941,7 +898,7 @@
|
||||
"description": "Contribution guidelines are essential for collaborative projects on GitHub as they help streamline collaboration, set expectations for contributions, and maintain the project's quality and consistency.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Setting Guidelines for Repository Contributors",
|
||||
"title": "Setting guidelines for respository contributors",
|
||||
"url": "https://docs.github.com/articles/setting-guidelines-for-repository-contributors",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1005,21 +962,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"lONqOqD-4slxa9B5i9ADX": {
|
||||
"github-wikis@lONqOqD-4slxa9B5i9ADX.md": {
|
||||
"title": "GitHub Wikis",
|
||||
"description": "GitHub Wikis are collaborative documentation spaces integrated directly into GitHub repositories. They provide a platform for teams to create, edit, and organize project-related information, such as documentation, guidelines, and FAQs. Wikis support Markdown formatting, making it easy to structure content and include images or links. With version control and the ability to clone wiki repositories, teams can collaboratively maintain up-to-date documentation alongside their code, enhancing project understanding and facilitating knowledge sharing among contributors and users.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "About Wikis",
|
||||
"url": "https://docs.github.com/en/communities/documenting-your-project-with-wikis/about-wikis",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Documenting your project with Wikis",
|
||||
"url": "https://docs.github.com/en/communities/documenting-your-project-with-wikis",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"i3AbARgzQtxtlB-1AS8zv": {
|
||||
"title": "Clean Git History",
|
||||
@@ -1031,7 +977,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Git Best Practice Tip: Clean Commit History",
|
||||
"title": "Git best practice tip: clean commit history",
|
||||
"url": "https://youtu.be/bZpiVijzd2g?si=8lJTlR3LfY9ZUd77",
|
||||
"type": "video"
|
||||
}
|
||||
@@ -1039,19 +985,8 @@
|
||||
},
|
||||
"yMx3LdadPz4g25CL3N8da": {
|
||||
"title": "Working in a Team",
|
||||
"description": "Working in a team on GitHub involves collaborative development using Git's distributed version control system. Team members can work on separate branches, create pull requests for code reviews, and merge changes into the main codebase. GitHub's features like issues, projects, and discussions facilitate communication and project management. Effective teamwork on GitHub requires clear communication, adherence to agreed-upon workflows, and proper use of Git commands to manage code changes and resolve conflicts. This collaborative approach enables teams to work efficiently on complex projects, maintain code quality, and track progress effectively.\n\nGitHub also offers an organization and team management interface, allowing teams to manage projects, members, and collaboration settings.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Getting Started with Teams",
|
||||
"url": "https://docs.github.com/en/get-started/onboarding/getting-started-with-github-team",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Github Team Docs",
|
||||
"url": "https://docs.github.com/organizations/organizing-members-into-teams/about-teams",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "Working in a team on GitHub involves collaborative development using Git's distributed version control system. Team members can work on separate branches, create pull requests for code reviews, and merge changes into the main codebase. GitHub's features like issues, projects, and discussions facilitate communication and project management. Effective teamwork on GitHub requires clear communication, adherence to agreed-upon workflows, and proper use of Git commands to manage code changes and resolve conflicts. This collaborative approach enables teams to work efficiently on complex projects, maintain code quality, and track progress effectively.\n\nGitHub also offers an organization and team management interface, allowing teams to manage projects, members, and collaboration settings.",
|
||||
"links": []
|
||||
},
|
||||
"RMrxQLhrINO5g4Mhxh5qS": {
|
||||
"title": "GitHub Organizations",
|
||||
@@ -1090,12 +1025,12 @@
|
||||
"description": "GitHub Organizations allow you to create teams within your organization, which helps in organizing members based on their roles and responsibilities.\n\n* Grouping: Team members can be grouped together according to the company or group's structure.\n* Access permissions: Access permissions can be cascaded from one team member to another.\n* Mentions: Team mentions allow for easy referencing of specific teams in repository discussions.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Organizing Members into Teams",
|
||||
"title": "Organizing members into teams",
|
||||
"url": "https://docs.github.com/en/organizations/organizing-members-into-teams",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Best Practices for Organizations and Teams using GitHub Enterprise Cloud",
|
||||
"title": "Best practices for organizations and teams using GitHub Enterprise Cloud",
|
||||
"url": "https://github.blog/enterprise-software/devops/best-practices-for-organizations-and-teams-using-github-enterprise-cloud/",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -1151,10 +1086,10 @@
|
||||
},
|
||||
"sxvT2hGko2PDRBoBrCGWD": {
|
||||
"title": "Roadmaps",
|
||||
"description": "GitHub Roadmaps are a feature that helps you visualize and organize plans for your projects, allowing you to create a high-level view of milestones and goals, and collaborate on planning and tracking progress with team members.\n\nVisit the following resources to learn more:",
|
||||
"description": "GitHub roadmaps are a feature that helps you visualize and organize plans for your projects, allowing you to create a high-level view of milestones and goals, and collaborate on planning and tracking progress with team members.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Customizing the Roadmap Layout",
|
||||
"title": "Customizing the roadmap layout",
|
||||
"url": "https://docs.github.com/en/issues/planning-and-tracking-with-projects/customizing-views-in-your-project/customizing-the-roadmap-layout",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1186,7 +1121,7 @@
|
||||
"description": "GitHub Discussions is a collaborative communication feature within GitHub repositories that provides a dedicated space for community conversations, questions, and knowledge sharing. It allows team members, contributors, and users to engage in threaded discussions, share ideas, ask for help, and make announcements outside of specific code changes or issues. This feature enhances project collaboration by centralizing important conversations, reducing noise in the issue tracker, and fostering a sense of community around open-source projects or team initiatives.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "GitHub Discussions Docs",
|
||||
"title": "official GitHub Discussions Docs",
|
||||
"url": "https://docs.github.com/en/discussions",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1274,7 +1209,7 @@
|
||||
},
|
||||
"qFEonbCMLri8iA0yONwuf": {
|
||||
"title": "git log options",
|
||||
"description": "`git log` is a command in Git that shows the commit history of your repository. It provides a detailed view of all commits, including their hashes, authors, dates, and messages.\n\nHere are some common git log options:\n\n* `-2`: Only show the last two commits.\n* `-- <file-name>`: Show the commits that modified a specific file.\n* `--all`: Show all branches in the repository.\n* `--graph`: Display the commit history as a graph.\n* `--pretty`: Enable clean colorized output.\n* `--no-color`: Disable colorized output.\n* `--stat`: Show a statistical summary of changes.\n* `**-S`: Only show commits with modified files.\n\nYou can combine these options to tailor your log output to suit your needs.\n\nFor example, `git log -2 --graph` will display the last two commits in graph form.\n\nVisit the following resources to learn more:",
|
||||
"description": "`git log` is a command in Git that shows the commit history of your repository. It provides a detailed view of all commits, including their hashes, authors, dates, and messages.\n\nHere are some common git log options:\n\n* `-2`: Only show the last two commits.\n* `--all`: Show all branches in the repository.\n* `--graph`: Display the commit history as a graph.\n* `--pretty`: Enable clean colorized output.\n* `--no-color`: Disable colorized output.\n* `--stat`: Show a statistical summary of changes.\n* `**-S`: Only show commits with modified files.\n\nYou can combine these options to tailor your log output to suit your needs.\n\nFor example, `git log -2 --graph` will display the last two commits in graph form.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Git Log",
|
||||
@@ -1290,24 +1225,8 @@
|
||||
},
|
||||
"0Yi4cryT2v2SGBjouOas3": {
|
||||
"title": "Undoing Changes",
|
||||
"description": "If mistakes or unwanted changes have been committed to your Git repository, there are ways to correct them. Two common methods for reverting changes include:\n\n* Git Reset: Resets the branch to a previous commit.\n* Git Revert: Creates a new commit that reverts specified changes.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Undoing Changes",
|
||||
"url": "https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Undo Anything in Git",
|
||||
"url": "https://github.blog/open-source/git/how-to-undo-almost-anything-with-git/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Undoing Changes in Git",
|
||||
"url": "https://www.atlassian.com/git/tutorials/undoing-changes",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "If mistakes or unwanted changes have been committed to your Git repository, there are ways to correct them. Two common methods for reverting changes include:\n\n* Git Reset: Resets the branch to a previous commit.\n* Git Revert: Creates a new commit that reverts specified changes.",
|
||||
"links": []
|
||||
},
|
||||
"dLr55Om7IOvI53c1DgTKc": {
|
||||
"title": "git revert",
|
||||
@@ -1330,7 +1249,7 @@
|
||||
"description": "Git reset is a command that allows you to \"undo\" or reset your current branch to a previous state by moving its HEAD pointer, effectively discarding changes made since then. When using git reset, it's essential to specify one of the three modes: soft, hard, or mixed. The mode you choose will determine how Git interacts with files in your working directory and staging area.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "git reset",
|
||||
"title": "Git reset",
|
||||
"url": "https://www.atlassian.com/git/tutorials/undoing-changes/git-reset",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1376,19 +1295,8 @@
|
||||
},
|
||||
"-0zQvCHG8jS_ghSjmTeIx": {
|
||||
"title": "Viewing Diffs",
|
||||
"description": "Viewing diffs in Git is crucial for understanding the changes made to your code. This is especially important when collaborating with others or reviewing your own work over time. Diffs show you exactly what lines have been added, modified, or removed between different versions of your files. This feature helps in code review processes, troubleshooting issues, and maintaining a clear history of your project's evolution. Git provides various commands and tools to view these differences, making it easier to track and manage changes effectively.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Git Diff Documentation",
|
||||
"url": "https://git-scm.com/docs/git-diff",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Git Diff",
|
||||
"url": "https://www.atlassian.com/git/tutorials/saving-changes/git-diff",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "Viewing diffs in Git is crucial for understanding the changes made to your code. This is especially important when collaborating with others or reviewing your own work over time. Diffs show you exactly what lines have been added, modified, or removed between different versions of your files. This feature helps in code review processes, troubleshooting issues, and maintaining a clear history of your project's evolution. Git provides various commands and tools to view these differences, making it easier to track and manage changes effectively.",
|
||||
"links": []
|
||||
},
|
||||
"Rwpeltygwzcf6hnuZNURE": {
|
||||
"title": "Between Commits",
|
||||
@@ -1456,19 +1364,8 @@
|
||||
},
|
||||
"sOoC-XxEoIvwKct00oKlX": {
|
||||
"title": "Rewriting History",
|
||||
"description": "In certain situations, you might need to modify or remove commits from your Git repository's history. This can be achieved using various methods:\n\n* `git commit --amend`: Allows you to edit the most recent commit.\n* `git rebase`: Replaces one branch with another, preserving the commit history.\n* `git filter-branch`: Removes specific commits from a branch without altering the original branch.\n* `git push --force`: Updates the remote repository while respecting existing pull requests.\n\nRewriting history in Git is typically necessary when:\n\n* Fixing mistakes: Correcting errors or typos in commit messages.\n* Removing sensitive data: Deleting confidential information from commits, like API keys or database credentials.\n* Simplifying complex histories: Reorganizing branches to improve clarity and reduce complexity.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Git Tools - Rewriting History",
|
||||
"url": "https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Methods of Rewriting History in Git",
|
||||
"url": "https://www.atlassian.com/git/tutorials/rewriting-history",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "In certain situations, you might need to modify or remove commits from your Git repository's history. This can be achieved using various methods:\n\n* `git commit --amend`: Allows you to edit the most recent commit.\n* `git rebase`: Replaces one branch with another, preserving the commit history.\n* `git filter-branch`: Removes specific commits from a branch without altering the original branch.\n* `git push --force`: Updates the remote repository while respecting existing pull requests.\n\nRewriting history in Git is typically necessary when:\n\n* Fixing mistakes: Correcting errors or typos in commit messages.\n* Removing sensitive data: Deleting confidential information from commits, like API keys or database credentials.\n* Simplifying complex histories: Reorganizing branches to improve clarity and reduce complexity.",
|
||||
"links": []
|
||||
},
|
||||
"NjPnEXLf1Lt9qzgxccogv": {
|
||||
"title": "git commit --amend",
|
||||
@@ -1501,7 +1398,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "git rebase",
|
||||
"title": "git rebase ",
|
||||
"url": "https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1519,22 +1416,12 @@
|
||||
},
|
||||
"BKVA6Q7DXemAYjyQOA0nh": {
|
||||
"title": "git filter-branch",
|
||||
"description": "You can use `git filter-branch` to rewrite Git revision history by applying custom filters on each revision.\n\n* Filter types: You can modify trees (e.g., removing a file or running a Perl script) or information about each commit.\n* Preserving original data: The command preserves all original commit times, merge information, and other details unless specified otherwise.\n* Rewriting specific branches: Only the positive refs mentioned in the command line are rewritten; if no filters are specified, commits are recommitted without changes.\n\nNotably, there exists a simpler, safer, and more powerful alternative: `git filter-repo`. This tool is actively promoted by Git and offers a streamlined approach to filtering revisions, making it a preferred choice for rewriting your Git history, especially when managing large repositories.\n\nVisit the following resources to learn more:",
|
||||
"description": "You can use `git filter-branch` to rewrite Git revision history by applying custom filters on each revision.\n\n* Filter types: You can modify trees (e.g., removing a file or running a Perl script) or information about each commit.\n* Preserving original data: The command preserves all original commit times, merge information, and other details unless specified otherwise.\n* Rewriting specific branches: Only the positive refs mentioned in the command line are rewritten; if no filters are specified, commits are recommitted without changes.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "git filter-branch",
|
||||
"url": "https://git-scm.com/docs/git-filter-branch",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "git filter-repo",
|
||||
"url": "https://github.com/newren/git-filter-repo",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Removing sensitive data from a repository",
|
||||
"url": "https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1769,19 +1656,8 @@
|
||||
},
|
||||
"fjAFNjNNbPOzme9Uk_fDV": {
|
||||
"title": "Submodules",
|
||||
"description": "In Git, submodules allow you to include another repository within a project. This feature enables the management of external dependencies as part of the main project.\n\n* Including external repositories: Submodules can be used to include other Git repositories within your project.\n* Managing dependencies: They provide a way to manage and track changes in external dependencies.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Git Submodules",
|
||||
"url": "https://git-scm.com/book/en/v2/Git-Tools-Submodules",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Git Submodules Tutorial",
|
||||
"url": "https://www.atlassian.com/git/tutorials/git-submodule",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "In Git, submodules allow you to include another repository within a project. This feature enables the management of external dependencies as part of the main project.\n\n* Including external repositories: Submodules can be used to include other Git repositories within your project.\n* Managing dependencies: They provide a way to manage and track changes in external dependencies.",
|
||||
"links": []
|
||||
},
|
||||
"x4bnsPVTiX2xOCSyrgWpF": {
|
||||
"title": "Adding / Updating",
|
||||
@@ -1801,7 +1677,7 @@
|
||||
},
|
||||
"d0-u_-_vtPK8tnUpnj_NB": {
|
||||
"title": "What and Why use?",
|
||||
"description": "Git submodules are a feature that allows you to include one Git repository within another. They are useful for managing external dependencies or shared components across projects.\n\nKey points\n----------\n\n1. Separate repositories with independent histories\n2. Parent repository tracks specific submodule commits\n3. Enables code reuse and modular project structure\n4. Helps manage dependencies and keep main repository focused\n5. Facilitates collaboration on complex projects\n\nBenefits\n--------\n\n* Including third-party libraries\n* Sharing common code\n* Managing multi-component projects\n* Keeping main repository lightweight\n\nNote: While powerful, submodules can add complexity to your workflow, so careful consideration is needed before implementation.\n\nLearn more from the following resources:",
|
||||
"description": "Git submodules are a feature that allows you to include one Git repository within another. They are useful for managing external dependencies or shared components across projects.\n\nKey points:\n\n1. Separate repositories with independent histories\n2. Parent repository tracks specific submodule commits\n3. Enables code reuse and modular project structure\n4. Helps manage dependencies and keep main repository focused\n5. Facilitates collaboration on complex projects\n\nBenefits:\n\n* Including third-party libraries\n* Sharing common code\n* Managing multi-component projects\n* Keeping main repository lightweight\n\nNote: While powerful, submodules can add complexity to your workflow, so careful consideration is needed before implementation.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Git Submodules: Core Concept, Workflows, and Tips",
|
||||
@@ -1820,7 +1696,7 @@
|
||||
"description": "GitHub CLI is a command-line interface tool that brings GitHub functionality to your terminal. It allows developers to interact with GitHub directly from the command line, enabling them to manage repositories, create issues, pull requests, and perform various GitHub operations without leaving their terminal environment. This powerful tool streamlines workflows, enhances productivity, and provides a seamless integration between local development and GitHub's collaborative features, making it easier for developers to incorporate GitHub into their daily coding routines.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "GitHub CLI Docs",
|
||||
"title": "official GitHub CLI Docs",
|
||||
"url": "https://cli.github.com/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -1846,7 +1722,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "GitHub CLI Quickstart",
|
||||
"title": "GitHub CLI quickstart",
|
||||
"url": "https://docs.github.com/en/github-cli/github-cli/quickstart",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -1918,16 +1794,6 @@
|
||||
"title": "YouTube: GitHub Actions Playlist",
|
||||
"url": "https://www.youtube.com/watch?v=-hVG9z0fCac&list=PLArH6NjfKsUhvGHrpag7SuPumMzQRhUKY&pp=iAQB",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Github Actions",
|
||||
"url": "https://docs.github.com/en/actions",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What are Github Actions",
|
||||
"url": "https://www.youtube.com/watch?v=URmeTqglS58",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1935,16 +1801,6 @@
|
||||
"title": "YAML Syntax",
|
||||
"description": "YAML (YAML Ain't Markup Language) is a human-readable data serialization standard for all programming languages. It is designed to be easily readable by humans while also being machine-parsable. Key features of YAML include:\n\n1. Simplicity: YAML uses a minimalist syntax with significant whitespace and indentation.\n \n2. Versatility: It can represent various data types, including scalars, lists, and associative arrays.\n \n3. Readability: Its clear, concise format makes it easy for both humans and machines to understand.\n \n4. Language-independent: YAML can be used with any programming language that has a YAML parser.\n \n\nYAML is commonly used for:\n\n* Configuration files: Many applications and tools use YAML for their configuration settings.\n* Data exchange: It serves as a lightweight alternative to XML or JSON for data transfer between systems.\n* Data storage: YAML can be used to store structured data in a human-readable format.\n* DevOps and CI/CD: It's widely used in tools like Docker, Kubernetes, and various CI/CD platforms for defining workflows and configurations.\n\nUnderstanding YAML syntax is crucial for working with modern development tools, especially in the realms of DevOps, cloud computing, and containerization.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "YAML",
|
||||
"url": "https://yaml.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "YAML Cheatsheet",
|
||||
"url": "https://cheatsheets.zip/yaml",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is YAML?",
|
||||
"url": "https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/",
|
||||
@@ -2093,7 +1949,7 @@
|
||||
},
|
||||
"PUnYNBrAZWoZiopjtNgnA": {
|
||||
"title": "Usecases",
|
||||
"description": "GitHub Actions offer a wide range of automation possibilities for your development workflow. Here are some common use cases:\n\n1. Continuous Integration (CI): Automatically build and test your code on every push or pull request.\n2. Continuous Deployment (CD): Automatically deploy your application to various environments after successful builds.\n3. Code Quality Checks: Run linters, formatters, and other code quality tools automatically.\n4. Dependency Updates: Automatically create pull requests for outdated dependencies.\n5. Issue and PR Management: Automatically label, assign, or close issues and pull requests based on certain conditions.\n6. Scheduled Tasks: Run periodic maintenance tasks, backups, or data processing jobs.\n7. Security Scanning: Perform automated security checks on your codebase and dependencies.\n8. Documentation Generation: Automatically generate and publish documentation for your project.\n9. Cross-platform Testing: Test your code on multiple operating systems and environments simultaneously.\n10. Release Management: Automate the creation of release notes and asset uploads for new versions.\n\nLearn more from the following resources:",
|
||||
"description": "GitHub Actions offer a wide range of automation possibilities for your development workflow. Here are some common use cases:\n\n1. Continuous Integration (CI): Automatically build and test your code on every push or pull request.\n2. Continuous Deployment (CD): Automatically deploy your application to various environments after successful builds.\n3. Code Quality Checks: Run linters, formatters, and other code quality tools automatically.\n4. Dependency Updates: Automatically create pull requests for outdated dependencies.\n5. Issue and PR Management: Automatically label, assign, or close issues and pull requests based on certain conditions.\n6. Scheduled Tasks: Run periodic maintenance tasks, backups, or data processing jobs.\n7. Security Scanning: Perform automated security checks on your codebase and dependencies.\n8. Documentation Generation: Automatically generate and publish documentation for your project.\n9. Cross-platform Testing: Test your code on multiple operating systems and environments simultaneously.\n10. Release Management: Automate the creation of release notes and asset uploads for new versions.\n\nThese use cases demonstrate how GitHub Actions can streamline your development process, improve code quality, and increase productivity.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "GitHub Actions Documentation",
|
||||
@@ -2260,12 +2116,12 @@
|
||||
"description": "The GitHub API is a powerful tool that allows developers to interact with the GitHub platform programmatically. It provides access to various GitHub features, such as user data, repository information, and commit history, through both REST and GraphQL interfaces. The API supports authentication, implements rate limiting, and offers webhooks for real-time notifications, enabling developers to automate tasks, create custom integrations, and build applications that leverage GitHub's functionality.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Github API Docs",
|
||||
"title": "official API Docs",
|
||||
"url": "https://docs.github.com/en/rest?apiVersion=2022-11-28",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Getting Started",
|
||||
"title": "Getting started",
|
||||
"url": "https://docs.github.com/en/rest/using-the-rest-api/getting-started-with-the-rest-api?apiVersion=2022-11-28",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -2332,7 +2188,7 @@
|
||||
},
|
||||
"qrdOARfqGPF9xhF6snbAn": {
|
||||
"title": "OAuth Apps",
|
||||
"description": "GitHub OAuth Apps allow developers to integrate with GitHub using OAuth 2.0 authentication. They enable secure, token-based access to specific GitHub resources like repositories, issues, and pull requests. OAuth Apps can automate tasks, personalize interactions, and provide real-time notifications through webhooks, all while allowing users to approve only the necessary permissions without sharing their credentials.\n\nVisit the following resources to learn more:",
|
||||
"description": "GitHub OAuth Apps are a way to integrate with the GitHub platform using OAuth authentication. They allow developers to create custom integrations that can automate tasks, provide real-time notifications, and build custom workflows.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Creating an OAuth app",
|
||||
@@ -2393,7 +2249,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Creating a GitHub Pages site",
|
||||
"title": "Ceating a GitHub Pages site",
|
||||
"url": "https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-github-pages-site",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2409,12 +2265,12 @@
|
||||
"description": "A GitHub Gist is a small code or text snippet that can be shared with others. It is a simple way to share code, configuration files, or other snippets of text without creating a full-fledged repository. Gists are useful for sharing examples, demos, or tutorials, and they can also serve as a starting point for larger projects. Each gist has a unique URL that can be shared with others, allowing them to view and edit the content. Gists support various file types, including code files, text files, and even images. They also provide features like syntax highlighting, line numbers, and commit history.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Creating Gists",
|
||||
"title": "Creating gists",
|
||||
"url": "https://docs.github.com/en/get-started/writing-on-github/editing-and-sharing-content-with-gists/creating-gists",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "REST API endpoints for Gists",
|
||||
"title": "REST API endpoints for gists",
|
||||
"url": "https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -2457,7 +2313,7 @@
|
||||
"description": "On GitHub Pages, users can customize their site's URL by connecting a custom domain to their repository. This feature allows users to use their own domain name instead of the default [GitHub.io](http://GitHub.io) subdomain, giving their site a more professional and personalized look.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Configuring a Custom Domain for Your GitHub Pages Site",
|
||||
"title": "Configuring a custom domain for your GitHub Pages site",
|
||||
"url": "https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -2580,7 +2436,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Dependabot Quick-start Guide",
|
||||
"title": "Dependabot quickstart guide",
|
||||
"url": "https://docs.github.com/en/code-security/getting-started/dependabot-quickstart-guide",
|
||||
"type": "article"
|
||||
},
|
||||
|
||||
@@ -112,29 +112,8 @@
|
||||
},
|
||||
"IduGSdUa2Fi7VFMLKgmsS": {
|
||||
"title": "iOS Architecture",
|
||||
"description": "iOS architecture refers to the design principles and patterns used to build iOS applications. It focuses on how to structure code, manage data, and ensure a smooth user experience. These architectural patterns help developers create maintainable, scalable, and testable applications while following best practices specific to iOS development. Use cases of these architectures may vary according to the requirements of the application. For example, MVC is used for simple apps, while MVVM is considered when the app is large and complex.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Model-View-Controller Pattern in swift (MVC) for Beginners",
|
||||
"url": "https://ahmedaminhassanismail.medium.com/model-view-controller-pattern-in-swift-mvc-for-beginners-35db8d479832",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "MVVM in iOS Swift",
|
||||
"url": "https://medium.com/@zebayasmeen76/mvvm-in-ios-swift-6afb150458fd",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "MVC Design Pattern Explained with Example",
|
||||
"url": "https://youtu.be/sbYaWJEAYIY?t=2",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "MVVM Design Pattern Explained with Example",
|
||||
"url": "https://www.youtube.com/watch?v=sLHVxnRS75w",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"IdGdLNgJI3WmONEFsMq-d": {
|
||||
"title": "Core OS",
|
||||
@@ -986,11 +965,6 @@
|
||||
"title": "SwiftUI",
|
||||
"description": "SwiftUI is Apple's modern declarative framework for building user interfaces across all Apple platforms. Introduced in 2019, it allows developers to create UIs using Swift code, describing the desired layout and behavior rather than implementing them imperatively. SwiftUI offers a more concise and intuitive approach to UI development, with features like automatic support for Dark Mode, dynamic type, and localization. It uses a state-driven approach, automatically updating the UI when underlying data changes. While newer than UIKit, SwiftUI is rapidly evolving and gaining adoption, offering seamless integration with UIKit when needed.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "HackingWithSwift - 100 Days of SwiftUI",
|
||||
"url": "https://www.hackingwithswift.com/100/swiftui",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "SwiftUI Documentation",
|
||||
"url": "https://developer.apple.com/xcode/swiftui/",
|
||||
|
||||
@@ -429,11 +429,6 @@
|
||||
"title": "String",
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "JavaScript Strings",
|
||||
"url": "https://javascript.info/string",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -503,7 +498,7 @@
|
||||
},
|
||||
"R6ICrk6vjoBxx5nRGo4Jg": {
|
||||
"title": "Symbol",
|
||||
"description": "Symbols are a unique and immutable primitive data type in JavaScript, introduced in ECMAScript 6 (ES6). They are often used to create unique property keys for objects, ensuring no property key collisions occur. Each Symbol value is distinct, even when multiple are created with the same description. Symbols can be created using the Symbol() function, and their primary use case is to add hidden or special properties to objects that won’t interfere with other properties or methods.\n\nLearn more from the following resources:",
|
||||
"description": "Symbols are a unique and immutable primitive data type in JavaScript, introduced in ECMAScript 6 (ES6). They are often used to create unique property keys for objects, ensuring that no property key collisions occur. Each Symbol value is unique, even if created with the same description. Symbols can be created using the Symbol() function, and their primary use case is to add hidden or special properties to objects that won’t interfere with other properties or methods.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Symbol data type in JavaScript",
|
||||
@@ -514,16 +509,6 @@
|
||||
"title": "Symbol type",
|
||||
"url": "https://javascript.info/symbol",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Symbol",
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Symbols in Javascript",
|
||||
"url": "https://www.youtube.com/watch?v=E5Bblr-SFbA",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2087,11 +2072,6 @@
|
||||
"title": "Network request - Fetch",
|
||||
"url": "https://javascript.info/fetch",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Abort a fetch request manually in JavaScript",
|
||||
"url": "https://www.amitmerchant.com/abort-fetch-request-manually-in-javascript/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
{
|
||||
"_7uvOebQUI4xaSwtMjpEd": {
|
||||
"title": "Programming Fundamentals",
|
||||
"description": "ML programming fundamentals encompass the essential skills and concepts needed to develop machine learning models effectively. Key aspects include understanding data structures and algorithms, as well as proficiency in programming languages commonly used in ML, such as Python and R. Familiarity with libraries and frameworks like TensorFlow, PyTorch, and scikit-learn is crucial for implementing machine learning algorithms and building models. Additionally, concepts such as data preprocessing, feature engineering, model evaluation, and hyperparameter tuning are vital for optimizing performance. A solid grasp of statistics and linear algebra is also important, as these mathematical foundations underpin many ML techniques, enabling practitioners to analyze data and interpret model results accurately.",
|
||||
"description": "Programming is the key requirement for MLOps. You need to be proficient in atleast one programming language. Python is the most popular language for MLOps.",
|
||||
"links": []
|
||||
},
|
||||
"Vh81GnOUOZvDOlOyI5PwT": {
|
||||
"title": "Python",
|
||||
"description": "Python is an interpreted high-level general-purpose programming language. Its design philosophy emphasizes code readability with its significant use of indentation. Its language constructs as well as its object-oriented approach aim to help programmers write clear, logical code for small and large-scale projects. Python is dynamically-typed and garbage-collected. It supports multiple programming paradigms, including structured (particularly, procedural), object-oriented and functional programming. Python is often described as a \"batteries included\" language due to its comprehensive standard library.\n\nLearn more from the following resources:",
|
||||
"description": "Python is an interpreted high-level general-purpose programming language. Its design philosophy emphasizes code readability with its significant use of indentation. Its language constructs as well as its object-oriented approach aim to help programmers write clear, logical code for small and large-scale projects. Python is dynamically-typed and garbage-collected. It supports multiple programming paradigms, including structured (particularly, procedural), object-oriented and functional programming. Python is often described as a \"batteries included\" language due to its comprehensive standard library.\n\nTo start learning Python, here are some useful resources:\n\nRemember, practice is key, and the more you work with Python, the more you'll appreciate its utility in the world of cyber security.",
|
||||
"links": [
|
||||
{
|
||||
"title": "Python Roadmap",
|
||||
"url": "https://roadmap.sh/python",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Python.org",
|
||||
"url": "https://www.python.org/",
|
||||
@@ -37,11 +32,11 @@
|
||||
},
|
||||
"vdVq3RQvQF3mF8PQc6DMg": {
|
||||
"title": "Go",
|
||||
"description": "Go, also known as Golang, is an open-source programming language developed by Google that emphasizes simplicity, efficiency, and strong concurrency support. Designed for modern software development, Go features a clean syntax, garbage collection, and built-in support for concurrent programming through goroutines and channels, making it well-suited for building scalable, high-performance applications, especially in cloud computing and microservices architectures. Go's robust standard library and tooling ecosystem, including a powerful package manager and testing framework, further streamline development processes, promoting rapid application development and deployment. Visit the following resources to learn more:",
|
||||
"description": "Go is an open source programming language supported by Google. Go can be used to write cloud services, CLI tools, used for API development, and much more.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Go Roadmap",
|
||||
"url": "https://roadmap.sh/golang",
|
||||
"url": "/golang",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -54,6 +49,16 @@
|
||||
"url": "https://go.dev/doc/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Go by Example - annotated example programs",
|
||||
"url": "https://gobyexample.com/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "W3Schools Go Tutorial ",
|
||||
"url": "https://www.w3schools.com/go/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Making a RESTful JSON API in Go",
|
||||
"url": "https://thenewstack.io/make-a-restful-json-api-go/",
|
||||
@@ -70,27 +75,16 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Go Programming Course",
|
||||
"url": "https://www.youtube.com/watch?v=un6ZyFkqFKo",
|
||||
"title": "Go Class by Matt",
|
||||
"url": "https://www.youtube.com/playlist?list=PLoILbKo9rG3skRCj37Kn5Zj803hhiuRK6",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mMzqJF2KQ49TDEk5F3VAI": {
|
||||
"title": "Bash",
|
||||
"description": "Bash (Bourne Again Shell) is a Unix shell and command language used for interacting with the operating system through a terminal. It allows users to execute commands, automate tasks via scripting, and manage system operations. As the default shell for many Linux distributions, it supports command-line utilities, file manipulation, process control, and text processing. Bash scripts can include loops, conditionals, and functions, making it a powerful tool for system administration, automation, and task scheduling.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "bash-guide",
|
||||
"url": "https://github.com/Idnan/bash-guide",
|
||||
"type": "opensource"
|
||||
},
|
||||
{
|
||||
"title": "Bash Scripting Course",
|
||||
"url": "https://www.youtube.com/watch?v=tK9Oc6AEnR4",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "Understanding bash is essential for MLOps tasks.\n\n* **Book Suggestion:** _The Linux Command Line, 2nd Edition_ by William E. Shotts",
|
||||
"links": []
|
||||
},
|
||||
"oUhlUoWQQ1txx_sepD5ev": {
|
||||
"title": "Version Control Systems",
|
||||
@@ -110,13 +104,8 @@
|
||||
},
|
||||
"06T5CbZAGJU6fJhCmqCC8": {
|
||||
"title": "Git",
|
||||
"description": "Git is a distributed version control system used to track changes in source code during software development. It enables multiple developers to collaborate on a project by managing versions of code, allowing for branching, merging, and tracking of revisions. Git ensures that changes are recorded with a complete history, enabling rollback to previous versions if necessary. It supports distributed workflows, meaning each developer has a complete local copy of the project’s history, facilitating seamless collaboration, conflict resolution, and efficient management of code across different teams or environments.\n\nVisit the following resources to learn more:",
|
||||
"description": "[Git](https://git-scm.com/) is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Learn Git & GitHub",
|
||||
"url": "https://roadmap.sh/git-github",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Learn Git with Tutorials, News and Tips - Atlassian",
|
||||
"url": "https://www.atlassian.com/git",
|
||||
@@ -141,23 +130,28 @@
|
||||
},
|
||||
"7t7jSb3YgyWlhgCe8Se1I": {
|
||||
"title": "GitHub",
|
||||
"description": "GitHub is a web-based platform built on top of Git that provides version control, collaboration tools, and project management features for software development. It enables developers to host Git repositories, collaborate on code through pull requests, and review and track changes. GitHub also offers additional features like issue tracking, continuous integration, automated workflows, and documentation hosting. With its social coding environment, GitHub fosters open-source contributions and team collaboration, making it a central hub for many software development projects.\n\nVisit the following resources to learn more:",
|
||||
"description": "GitHub is a provider of Internet hosting for software development and version control using Git. It offers the distributed version control and source code management functionality of Git, plus its own features.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Learn Git & GitHub",
|
||||
"url": "https://roadmap.sh/git-github",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "GitHub Website",
|
||||
"url": "https://github.com",
|
||||
"type": "article"
|
||||
"type": "opensource"
|
||||
},
|
||||
{
|
||||
"title": "GitHub Documentation",
|
||||
"url": "https://docs.github.com/en/get-started/quickstart",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "How to Use Git in a Professional Dev Team",
|
||||
"url": "https://ooloo.io/project/github-flow",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Learn Git Branching",
|
||||
"url": "https://learngitbranching.js.org/?locale=en_us",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about GitHub",
|
||||
"url": "https://app.daily.dev/tags/github?ref=roadmapsh",
|
||||
@@ -167,24 +161,28 @@
|
||||
"title": "What is GitHub?",
|
||||
"url": "https://www.youtube.com/watch?v=w3jLJU7DT5E",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Git vs. GitHub: Whats the difference?",
|
||||
"url": "https://www.youtube.com/watch?v=wpISo9TNjfU",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Git and GitHub for Beginners",
|
||||
"url": "https://www.youtube.com/watch?v=RGOj5yH7evk",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Git and GitHub - CS50 Beyond 2019",
|
||||
"url": "https://www.youtube.com/watch?v=eulnSXkhE7I",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"00GZcwe25QYi7rDzaOoMt": {
|
||||
"title": "Cloud Computing",
|
||||
"description": "**Cloud Computing** refers to the delivery of computing services over the internet rather than using local servers or personal devices. These services include servers, storage, databases, networking, software, analytics, and intelligence. Cloud Computing enables faster innovation, flexible resources, and economies of scale. There are various types of cloud computing such as public clouds, private clouds, and hybrids clouds. Furthermore, it's divided into different services like Infrastructure as a Service (IaaS), Platform as a Service (PaaS), and Software as a Service (SaaS). These services differ mainly in the level of control an organization has over their data and infrastructures.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is cloud computing?",
|
||||
"url": "https://azure.microsoft.com/en-gb/resources/cloud-computing-dictionary/what-is-cloud-computing",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is Cloud Computing? | Amazon Web Services",
|
||||
"url": "https://www.youtube.com/watch?v=mxT233EdY5c",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "**Cloud Computing** refers to the delivery of computing services over the internet rather than using local servers or personal devices. These services include servers, storage, databases, networking, software, analytics, and intelligence. Cloud Computing enables faster innovation, flexible resources, and economies of scale. There are various types of cloud computing such as public clouds, private clouds, and hybrids clouds. Furthermore, it's divided into different services like Infrastructure as a Service (IaaS), Platform as a Service (PaaS), and Software as a Service (SaaS). These services differ mainly in the level of control an organization has over their data and infrastructures.",
|
||||
"links": []
|
||||
},
|
||||
"u3E7FGW4Iwdsu61KYFxCX": {
|
||||
"title": "AWS / Azure / GCP",
|
||||
@@ -214,28 +212,12 @@
|
||||
},
|
||||
"kbfucfIO5KCsuv3jKbHTa": {
|
||||
"title": "Cloud-native ML Services",
|
||||
"description": "Most of the cloud providers offer managed services for machine learning. These services are designed to help data scientists and machine learning engineers to build, train, and deploy machine learning models at scale. These services are designed to be cloud-native, meaning they are designed to work with other cloud services and are optimized for the cloud environment.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "AWS Sage Maker",
|
||||
"url": "https://aws.amazon.com/sagemaker/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Azure ML",
|
||||
"url": "https://azure.microsoft.com/en-gb/products/machine-learning",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is Cloud Native?",
|
||||
"url": "https://www.youtube.com/watch?v=fp9_ubiKqFU",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "Most of the cloud providers offer managed services for machine learning. These services are designed to help data scientists and machine learning engineers to build, train, and deploy machine learning models at scale. These services are designed to be cloud-native, meaning they are designed to work with other cloud services and are optimized for the cloud environment.\n\nHere are the services offered by the major cloud providers:\n\n* **Amazon Web Services (AWS)**: SageMaker\n* **Google Cloud Platform (GCP)**: AI Platform\n* **Microsoft Azure**: Azure Machine Learning",
|
||||
"links": []
|
||||
},
|
||||
"tKeejLv8Q7QX40UtOjpav": {
|
||||
"title": "Containerization",
|
||||
"description": "Containers are a construct in which cgroups, namespaces, and chroot are used to fully encapsulate and isolate a process. This encapsulated process, called a container image, shares the kernel of the host with other containers, allowing containers to be significantly smaller and faster than virtual machines.\n\nThese images are designed for portability, allowing for full local testing of a static image, and easy deployment to a container management platform.\n\nVisit the following resources to learn more:",
|
||||
"description": "Containers are a construct in which [cgroups](https://en.wikipedia.org/wiki/Cgroups), [namespaces](https://en.wikipedia.org/wiki/Linux_namespaces), and [chroot](https://en.wikipedia.org/wiki/Chroot) are used to fully encapsulate and isolate a process. This encapsulated process, called a container image, shares the kernel of the host with other containers, allowing containers to be significantly smaller and faster than virtual machines.\n\nThese images are designed for portability, allowing for full local testing of a static image, and easy deployment to a container management platform.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What are Containers?",
|
||||
@@ -292,13 +274,8 @@
|
||||
},
|
||||
"XQoK9l-xtN2J8ZV8dw53X": {
|
||||
"title": "Kubernetes",
|
||||
"description": "Kubernetes is an open source container management platform, and the dominant product in this space. Using Kubernetes, teams can deploy images across multiple underlying hosts, defining their desired availability, deployment logic, and scaling logic in YAML. Kubernetes evolved from Borg, an internal Google platform used to provision and allocate compute resources (similar to the Autopilot and Aquaman systems of Microsoft Azure). The popularity of Kubernetes has made it an increasingly important skill for the DevOps Engineer and has triggered the creation of Platform teams across the industry. These Platform engineering teams often exist with the sole purpose of making Kubernetes approachable and usable for their product development colleagues.\n\nVisit the following resources to learn more:",
|
||||
"description": "Kubernetes is an [open source](https://github.com/kubernetes/kubernetes) container management platform, and the dominant product in this space. Using Kubernetes, teams can deploy images across multiple underlying hosts, defining their desired availability, deployment logic, and scaling logic in YAML. Kubernetes evolved from Borg, an internal Google platform used to provision and allocate compute resources (similar to the Autopilot and Aquaman systems of Microsoft Azure).\n\nThe popularity of Kubernetes has made it an increasingly important skill for the DevOps Engineer and has triggered the creation of Platform teams across the industry. These Platform engineering teams often exist with the sole purpose of making Kubernetes approachable and usable for their product development colleagues.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Kubernetes Roadmap",
|
||||
"url": "https://roadmap.sh/kubernetes",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Kubernetes Website",
|
||||
"url": "https://kubernetes.io/",
|
||||
@@ -309,6 +286,11 @@
|
||||
"url": "https://kubernetes.io/docs/home/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Primer: How Kubernetes Came to Be, What It Is, and Why You Should Care",
|
||||
"url": "https://thenewstack.io/primer-how-kubernetes-came-to-be-what-it-is-and-why-you-should-care/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Kubernetes: An Overview",
|
||||
"url": "https://thenewstack.io/kubernetes-an-overview/",
|
||||
@@ -328,93 +310,28 @@
|
||||
},
|
||||
"ulka7VEVjz6ls5SnI6a6z": {
|
||||
"title": "Machine Learning Fundamentals",
|
||||
"description": "Machine learning fundamentals encompass the key concepts and techniques that enable systems to learn from data and make predictions or decisions without being explicitly programmed. At its core, machine learning involves algorithms that can identify patterns in data and improve over time with experience. Key areas include supervised learning (where models are trained on labeled data), unsupervised learning (where models identify patterns in unlabeled data), and reinforcement learning (where agents learn to make decisions based on feedback from their actions). Essential components also include data preprocessing, feature selection, model training, evaluation metrics, and the importance of avoiding overfitting. Understanding these fundamentals is crucial for developing effective machine learning applications across various domains. Learn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Fundamentals of Machine Learning - Microsoft",
|
||||
"url": "https://learn.microsoft.com/en-us/training/modules/fundamentals-machine-learning/",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "MLCourse.ai",
|
||||
"url": "https://mlcourse.ai/",
|
||||
"type": "course"
|
||||
},
|
||||
{
|
||||
"title": "Fast.ai",
|
||||
"url": "https://course.fast.ai",
|
||||
"type": "course"
|
||||
}
|
||||
]
|
||||
"description": "An MLOps engineer should have a basic understanding of machine learning models.\n\n* **Courses:** [MLCourse.ai](https://mlcourse.ai/), [Fast.ai](https://course.fast.ai)\n* **Book Suggestion:** _Applied Machine Learning and AI for Engineers_ by Jeff Prosise",
|
||||
"links": []
|
||||
},
|
||||
"VykbCu7LWIx8fQpqKzoA7": {
|
||||
"title": "Data Engineering Fundamentals",
|
||||
"description": "Data Engineering is essentially dealing with the collection, validation, storage, transformation, and processing of data. The objective is to provide reliable, efficient, and scalable data pipelines and infrastructure that allow data scientists to convert data into actionable insights. It involves steps like data ingestion, data storage, data processing, and data provisioning. Important concepts include designing, building, and maintaining data architecture, databases, processing systems, and large-scale processing systems. It is crucial to have extensive technical knowledge in various tools and programming languages like SQL, Python, Hadoop, and more.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Data Engineering 101",
|
||||
"url": "https://www.redpanda.com/guides/fundamentals-of-data-engineering",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Fundamentals of Data Engineering",
|
||||
"url": "https://www.youtube.com/watch?v=mPSzL8Lurs0",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "Data Engineering is essentially dealing with the collection, validation, storage, transformation, and processing of data. The objective is to provide reliable, efficient, and scalable data pipelines and infrastructure that allow data scientists to convert data into actionable insights. It involves steps like data ingestion, data storage, data processing, and data provisioning. Important concepts include designing, building, and maintaining data architecture, databases, processing systems, and large-scale processing systems. It is crucial to have extensive technical knowledge in various tools and programming languages like SQL, Python, Hadoop, and more.",
|
||||
"links": []
|
||||
},
|
||||
"cOg3ejZRYE-u-M0c89IjM": {
|
||||
"title": "Data Pipelines",
|
||||
"description": "Data pipelines are a series of automated processes that transport and transform data from various sources to a destination for analysis or storage. They typically involve steps like data extraction, cleaning, transformation, and loading (ETL) into databases, data lakes, or warehouses. Pipelines can handle batch or real-time data, ensuring that large-scale datasets are processed efficiently and consistently. They play a crucial role in ensuring data integrity and enabling businesses to derive insights from raw data for reporting, analytics, or machine learning.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a data pipeline?",
|
||||
"url": "https://www.ibm.com/topics/data-pipeline",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What are data pipelines?",
|
||||
"url": "https://www.youtube.com/watch?v=oKixNpz6jNo",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "Data pipelines refer to a set of processes that involve moving data from one system to another, for purposes such as data integration, data migration, data transformation, or data synchronization. These processes can involve a variety of data sources and destinations, and may often require data to be cleaned, enriched, or otherwise transformed along the way. It's a key concept in data engineering to ensure that data is appropriately processed from its source to the location where it will be used, typically a data warehouse, data mart, or a data lake. As such, data pipelines play a crucial part in building an effective and efficient data analytics setup, enabling the flow of data to be processed for insights.\n\nIt is important to understand the difference between ELT and ETL pipelines. ELT stands for Extract, Load, Transform, and refers to a process where data is first extracted from source systems, then loaded into a target system, and finally transformed within the target system. ETL, on the other hand, stands for Extract, Transform, Load, and refers to a process where data is first extracted from source systems, then transformed, and finally loaded into a target system. The choice between ELT and ETL pipelines depends on the specific requirements of the data processing tasks at hand, and the capabilities of the systems involved.",
|
||||
"links": []
|
||||
},
|
||||
"wOogVDV4FIDLXVPwFqJ8C": {
|
||||
"title": "Data Lakes & Warehouses",
|
||||
"description": "\"**Data Lakes** are large-scale data repository systems that store raw, untransformed data, in various formats, from multiple sources. They're often used for big data and real-time analytics requirements. Data lakes preserve the original data format and schema which can be modified as necessary. On the other hand, **Data Warehouses** are data storage systems which are designed for analyzing, reporting and integrating with transactional systems. The data in a warehouse is clean, consistent, and often transformed to meet wide-range of business requirements. Hence, data warehouses provide structured data but require more processing and management compared to data lakes.\"\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Data lake definition",
|
||||
"url": "https://azure.microsoft.com/en-gb/resources/cloud-computing-dictionary/what-is-a-data-lake",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is a data lake?",
|
||||
"url": "https://www.youtube.com/watch?v=LxcH6z8TFpI",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "@hat is a data warehouse?",
|
||||
"url": "https://www.youtube.com/watch?v=k4tK2ttdSDg",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "\"**Data Lakes** are large-scale data repository systems that store raw, untransformed data, in various formats, from multiple sources. They're often used for big data and real-time analytics requirements. Data lakes preserve the original data format and schema which can be modified as necessary. On the other hand, **Data Warehouses** are data storage systems which are designed for analyzing, reporting and integrating with transactional systems. The data in a warehouse is clean, consistent, and often transformed to meet wide-range of business requirements. Hence, data warehouses provide structured data but require more processing and management compared to data lakes.\"",
|
||||
"links": []
|
||||
},
|
||||
"Berd78HvnulNEGOsHCf8n": {
|
||||
"title": "Data Ingestion Architecture",
|
||||
"description": "Data ingestion is the process of collecting, transferring, and loading data from various sources to a destination where it can be stored and analyzed. There are several data ingestion architectures that can be used to collect data from different sources and load it into a data warehouse, data lake, or other storage systems. These architectures can be broadly classified into two categories: batch processing and real-time processing. How you choose to ingest data will depend on the volume, velocity, and variety of data you are working with, as well as the latency requirements of your use case.\n\nLambda and Kappa architectures are two popular data ingestion architectures that combine batch and real-time processing to handle large volumes of data efficiently.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Data Ingestion Patterns",
|
||||
"url": "https://docs.aws.amazon.com/whitepapers/latest/aws-cloud-data-ingestion-patterns-practices/data-ingestion-patterns.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is a data pipeline?",
|
||||
"url": "https://www.youtube.com/watch?v=kGT4PcTEPP8",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "Data ingestion is the process of collecting, transferring, and loading data from various sources to a destination where it can be stored and analyzed. There are several data ingestion architectures that can be used to collect data from different sources and load it into a data warehouse, data lake, or other storage systems. These architectures can be broadly classified into two categories: batch processing and real-time processing. How you choose to ingest data will depend on the volume, velocity, and variety of data you are working with, as well as the latency requirements of your use case.\n\nLambda and Kappa architectures are two popular data ingestion architectures that combine batch and real-time processing to handle large volumes of data efficiently.",
|
||||
"links": []
|
||||
},
|
||||
"pVSlVHXIap0unFxLGM-lQ": {
|
||||
"title": "Airflow",
|
||||
@@ -434,13 +351,8 @@
|
||||
},
|
||||
"UljuqA89_SlCSDWWMD_C_": {
|
||||
"title": "Spark",
|
||||
"description": "Apache Spark is an open-source distributed computing system designed for big data processing and analytics. It offers a unified interface for programming entire clusters, enabling efficient handling of large-scale data with built-in support for data parallelism and fault tolerance. Spark excels in processing tasks like batch processing, real-time data streaming, machine learning, and graph processing. It’s known for its speed, ease of use, and ability to process data in-memory, significantly outperforming traditional MapReduce systems. Spark is widely used in big data ecosystems for its scalability and versatility across various data processing tasks.\n\nVisit the following resources to learn more:",
|
||||
"description": "Apache Spark is an open-source distributed computing system used for big data processing and analytics. It provides an interface for programming entire clusters with implicit data parallelism and fault tolerance.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ApacheSpark",
|
||||
"url": "https://spark.apache.org/documentation.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Spark By Examples",
|
||||
"url": "https://sparkbyexamples.com",
|
||||
@@ -476,7 +388,7 @@
|
||||
},
|
||||
"o6GQ3-8DgDtHzdX6yeg1w": {
|
||||
"title": "Flink",
|
||||
"description": "Apache Flink is an open-source stream processing framework designed for real-time and batch data processing with low latency and high throughput. It supports event time processing, fault tolerance, and stateful operations, making it ideal for applications like real-time analytics, fraud detection, and event-driven systems. Flink is highly scalable, integrates with various data systems, and is widely used in industries for large-scale, real-time data processing tasks.\n\nVisit the following resources to learn more:",
|
||||
"description": "Apache Flink is a distributed stream processing framework that is used to process large amounts of data in real-time. It is designed to be highly scalable and fault-tolerant. Flink is built on top of the Apache Kafka messaging system and is used to process data streams in real-time.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Apache Flink Documentation",
|
||||
@@ -487,17 +399,12 @@
|
||||
"title": "Explore top posts about Apache Flink",
|
||||
"url": "https://app.daily.dev/tags/apache-flink?ref=roadmapsh",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Apache Flink Tutorialpoint",
|
||||
"url": "https://www.tutorialspoint.com/apache_flink/apache_flink_introduction.htm",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iTsEHVCo6KGq7H2HMgy5S": {
|
||||
"title": "MLOps Principles",
|
||||
"description": "MLOps (Machine Learning Operations) principles focus on streamlining the deployment, monitoring, and management of machine learning models in production environments. Key principles include:\n\n1. **Collaboration**: Foster collaboration between data scientists, developers, and operations teams to ensure alignment on model goals, performance, and lifecycle management.\n \n2. **Automation**: Automate workflows for model training, testing, deployment, and monitoring to enhance efficiency, reduce errors, and speed up the development lifecycle.\n \n3. **Version Control**: Implement version control for both code and data to track changes, reproduce experiments, and maintain model lineage.\n \n4. **Continuous Integration and Deployment (CI/CD)**: Establish CI/CD pipelines tailored for machine learning to facilitate rapid model iteration and deployment.\n \n5. **Monitoring and Governance**: Continuously monitor model performance and data drift in production to ensure models remain effective and compliant with regulatory requirements.\n \n6. **Scalability**: Design systems that can scale to handle varying workloads and accommodate changes in data volume and complexity.\n \n7. **Reproducibility**: Ensure that experiments can be reliably reproduced by standardizing environments and workflows, making it easier to validate and iterate on models.\n \n\nThese principles help organizations efficiently manage the lifecycle of machine learning models, from development to deployment and beyond.",
|
||||
"description": "Awareness of MLOps principles and maturity factors is required.\n\n* **Books:**\n * _Designing Machine Learning Systems_ by Chip Huyen\n * _Introducing MLOps_ by Mark Treveil and Dataiku\n* **Assessment:** [MLOps maturity assessment](https://marvelousmlops.substack.com/p/mlops-maturity-assessment)\n* **Great resource on MLOps:** [ml-ops.org](https://ml-ops.org)",
|
||||
"links": []
|
||||
},
|
||||
"l1xasxQy2vAY34NWaqKEe": {
|
||||
@@ -528,62 +435,23 @@
|
||||
},
|
||||
"a6vawajw7BpL6plH_nuAz": {
|
||||
"title": "CI/CD",
|
||||
"description": "CI/CD (Continuous Integration and Continuous Deployment/Delivery) is a software development practice that automates the process of integrating code changes, running tests, and deploying updates. Continuous Integration focuses on regularly merging code changes into a shared repository, followed by automated testing to ensure code quality. Continuous Deployment extends this by automatically releasing every validated change to production, while Continuous Delivery ensures code is always in a deployable state, but requires manual approval for production releases. CI/CD pipelines improve code reliability, reduce integration risks, and speed up the development lifecycle.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is CI/CD?",
|
||||
"url": "https://www.redhat.com/en/topics/devops/what-is-ci-cd",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "CI/CD In 5 Minutes",
|
||||
"url": "https://www.youtube.com/watch?v=42UP1fxi2SY",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "Critical for traceable and reproducible ML model deployments.\n\n* **Books:**\n * _Learning GitHub Actions_ by Brent Laster\n * _Learning Git_ by Anna Skoulikari\n* **Tutorials & Courses:** [Git & GitHub for beginners](https://www.youtube.com/watch?v=RGOj5yH7evk), [Python to Production guide](https://www.udemy.com/course/setting-up-the-linux-terminal-for-software-development/), [Version Control Missing Semester](https://missing.csail.mit.edu/2020/version-control/), [https://learngitbranching.js.org/](https://learngitbranching.js.org/)\n* **Tool:** [Pre-commit hooks](https://marvelousmlops.substack.com/p/welcome-to-pre-commit-heaven)",
|
||||
"links": []
|
||||
},
|
||||
"fes7M--Y8i08_zeP98tVV": {
|
||||
"title": "Orchestration",
|
||||
"description": "ML orchestration refers to the process of managing and coordinating the various tasks and workflows involved in the machine learning lifecycle, from data preparation and model training to deployment and monitoring. It involves integrating multiple tools and platforms to streamline operations, automate repetitive tasks, and ensure seamless collaboration among data scientists, engineers, and operations teams. By using orchestration frameworks, organizations can enhance reproducibility, scalability, and efficiency, enabling them to manage complex machine learning pipelines and improve the overall quality of models in production. This ensures that models are consistently updated and maintained, facilitating rapid iteration and adaptation to changing data and business needs.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "ML Observability: what, why, how",
|
||||
"url": "https://ubuntu.com/blog/ml-observability",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "Systems like Airflow and Mage are important in ML engineering.\n\n* **Course:** [Introduction to Airflow in Python](https://app.datacamp.com/learn/courses/introduction-to-airflow-in-python)\n* **Note:** Airflow is also featured in the _ML Engineering with Python_ book and [_The Full Stack 7-Steps MLOps Framework_](https://www.pauliusztin.me/courses/the-full-stack-7-steps-mlops-framework).",
|
||||
"links": []
|
||||
},
|
||||
"fGGWKmAJ50Ke6wWJBEgby": {
|
||||
"title": "Experiment Tracking & Model Registry",
|
||||
"description": "**Experiment Tracking** is an essential part of MLOps, providing a system to monitor and record the different experiments conducted during the machine learning model development process. This involves capturing, organizing and visualizing the metadata associated with each experiment, such as hyperparameters used, models produced, metrics like accuracy or loss, and other information about the computational environment. This tracking allows for reproducibility of experiments, comparison across different experiment runs, and helps in identifying the best models.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Experiment Tracking",
|
||||
"url": "https://madewithml.com/courses/mlops/experiment-tracking/#dashboard",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "ML Flow Model Registry",
|
||||
"url": "https://mlflow.org/docs/latest/model-registry.html",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "**Experiment Tracking** is an essential part of MLOps, providing a system to monitor and record the different experiments conducted during the machine learning model development process. This involves capturing, organizing and visualizing the metadata associated with each experiment, such as hyperparameters used, models produced, metrics like accuracy or loss, and other information about the computational environment. This tracking allows for reproducibility of experiments, comparison across different experiment runs, and helps in identifying the best models.\n\nLogging metadata, parameters, and artifacts of training runs.\n\n* **Tool:** MLflow\n* **Courses:** [MLflow Udemy course](https://www.udemy.com/course/mlflow-course/), [End-to-end machine learning (MLflow piece)](https://www.udemy.com/course/sustainable-and-scalable-machine-learning-project-development/)",
|
||||
"links": []
|
||||
},
|
||||
"6XgP_2NLuiw654zvTyueT": {
|
||||
"title": "Data Lineage & Feature Stores",
|
||||
"description": "**Data Lineage** refers to the life-cycle of data, including its origins, movements, characteristics and quality. It's a critical component in MLOps for tracking the journey of data through every process in a pipeline, from raw input to model output. Data lineage helps in maintaining transparency, ensuring compliance, and facilitating data debugging or tracing data related bugs. It provides a clear representation of data sources, transformations, and dependencies thereby aiding in audits, governance, or reproduction of machine learning models.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is Data Lineage?",
|
||||
"url": "https://www.ibm.com/topics/data-lineage",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is a feature store",
|
||||
"url": "https://www.snowflake.com/guides/what-feature-store-machine-learning/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "**Data Lineage** refers to the life-cycle of data, including its origins, movements, characteristics and quality. It's a critical component in MLOps for tracking the journey of data through every process in a pipeline, from raw input to model output. Data lineage helps in maintaining transparency, ensuring compliance, and facilitating data debugging or tracing data related bugs. It provides a clear representation of data sources, transformations, and dependencies thereby aiding in audits, governance, or reproduction of machine learning models.\n\nFeature stores are a crucial component of MLOps infrastructure.\n\n* **Tutorial:** Creating a feature store with Feast [Part 1](https://kedion.medium.com/creating-a-feature-store-with-feast-part-1-37c380223e2f) [Part 2](https://kedion.medium.com/feature-storage-for-ml-with-feast-part-2-34df1971a8d3) [Part 3](https://kedion.medium.com/feature-storage-for-ml-with-feast-a061899fc4a2)\n* **Tool:** DVC for data tracking\n* **Course:** [End-to-end machine learning (DVC piece)](https://www.udemy.com/course/sustainable-and-scalable-machine-learning-project-development/)",
|
||||
"links": []
|
||||
},
|
||||
"zsW1NRb0dMgS-KzWsI0QU": {
|
||||
"title": "Model Training & Serving",
|
||||
@@ -592,39 +460,33 @@
|
||||
},
|
||||
"r4fbUwD83uYumEO1X8f09": {
|
||||
"title": "Monitoring & Observability",
|
||||
"description": "**Monitoring** in MLOps primarily involves tracking the performance of machine learning (ML) models in production to ensure that they continually deliver accurate and reliable results. Such monitoring is necessary because the real-world data that these models handle may change over time, a scenario known as data drift. These changes can adversely affect model performance. Monitoring helps to detect any anomalies in the model’s behaviour or performance and such alerts can trigger the retraining of models with new data. From a broader perspective, monitoring also involves tracking resources and workflows to detect and rectify any operational issues in the MLOps pipeline.\n\nLearn more from the following resources:",
|
||||
"description": "**Monitoring** in MLOps primarily involves tracking the performance of machine learning (ML) models in production to ensure that they continually deliver accurate and reliable results. Such monitoring is necessary because the real-world data that these models handle may change over time, a scenario known as data drift. These changes can adversely affect model performance. Monitoring helps to detect any anomalies in the model’s behaviour or performance and such alerts can trigger the retraining of models with new data. From a broader perspective, monitoring also involves tracking resources and workflows to detect and rectify any operational issues in the MLOps pipeline.",
|
||||
"links": [
|
||||
{
|
||||
"title": "ML Monitoring vs ML Observability",
|
||||
"url": "https://medium.com/marvelous-mlops/ml-monitoring-vs-ml-observability-understanding-the-differences-fff574a8974f",
|
||||
"title": "ML Monitoring vs Observability article",
|
||||
"url": "https://marvelousmlops.substack.com/p/ml-monitoring-vs-ml-observability",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "ML Observability vs ML Monitoring: What's the difference?",
|
||||
"url": "https://www.youtube.com/watch?v=k1Reed3QIYE",
|
||||
"type": "video"
|
||||
"title": "Machine learning monitoring concepts",
|
||||
"url": "https://app.datacamp.com/learn/courses/machine-learning-monitoring-concepts",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Monitoring ML in Python",
|
||||
"url": "https://app.datacamp.com/learn/courses/monitoring-machine-learning-in-python",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Prometheus, Grafana",
|
||||
"url": "https://www.udemy.com/course/mastering-prometheus-and-grafana/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sf67bSL7HAx6iN7S6MYKs": {
|
||||
"title": "Infrastructure as Code",
|
||||
"description": "Infrastructure as Code (IaC) is a modern approach to managing and provisioning IT infrastructure through machine-readable configuration files, rather than manual processes. It enables developers and operations teams to define and manage infrastructure resources—such as servers, networks, and databases—using code, which can be versioned, tested, and deployed like application code. IaC tools, like Terraform and AWS CloudFormation, allow for automated, repeatable deployments, reducing human error and increasing consistency across environments. This practice facilitates agile development, enhances collaboration between teams, and supports scalable and efficient infrastructure management.",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is Infrastructure as Code?",
|
||||
"url": "https://www.redhat.com/en/topics/automation/what-is-infrastructure-as-code-iac",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Terraform course for beginners",
|
||||
"url": "https://www.youtube.com/watch?v=SLB_c_ayRMo",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "8 Terraform best practices",
|
||||
"url": "https://www.youtube.com/watch?v=gxPykhPxRW0",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "Essential for a reproducible MLOps framework.\n\n* **Course:** [Terraform course for beginners](https://www.youtube.com/watch?v=SLB_c_ayRMo)\n* **Video:** [8 Terraform best practices by Techworld by Nana](https://www.youtube.com/watch?v=gxPykhPxRW0)\n* **Book Suggestion:** _Terraform: Up and Running, 3rd Edition_ by Yevgeniy Brikman",
|
||||
"links": []
|
||||
}
|
||||
}
|
||||
@@ -1244,8 +1244,8 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about NestJS",
|
||||
"url": "https://app.daily.dev/tags/nestjs?ref=roadmapsh",
|
||||
"title": "Explore top posts about JavaScript",
|
||||
"url": "https://app.daily.dev/tags/javascript?ref=roadmapsh",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -2085,58 +2085,7 @@
|
||||
},
|
||||
"M62lAWBOrTe99TfpFOQ-Y": {
|
||||
"title": "Common Built-in Modules",
|
||||
"description": "These are the core modules that come with `Node.js` out of the box. This module provides tools or APIs for performing out certain standard `Node.js` operations. like interacting with the file system, url parsing, or logging information to the console.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Nodejs fs module",
|
||||
"url": "https://nodejs.org/api/fs.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nodejs url module",
|
||||
"url": "https://nodejs.org/api/url.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nodejs console module",
|
||||
"url": "https://nodejs.org/api/console.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nodejs util module",
|
||||
"url": "https://nodejs.org/api/util.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nodejs events module",
|
||||
"url": "https://nodejs.org/api/events.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nodejs os module",
|
||||
"url": "https://nodejs.org/api/os.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nodejs worker threads module",
|
||||
"url": "https://nodejs.org/api/worker_threads.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nodejs child process module",
|
||||
"url": "https://nodejs.org/api/child_process.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nodejs process object",
|
||||
"url": "https://nodejs.org/api/process.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nodejs crypto module",
|
||||
"url": "https://nodejs.org/api/crypto.html",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "These are the common modules that come with `Node.js` out of the box. This module provides tools or APIs for performing out certain standard `Node.js` operations. like interacting with the file system, url parsing, or logging information to the console.",
|
||||
"links": []
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,7 @@
|
||||
"lDIy56RyC1XM7IfORsSLD": {
|
||||
"title": "Introduction",
|
||||
"description": "PostgreSQL is a powerful, open-source Object-Relational Database Management System (ORDBMS) that is known for its robustness, extensibility, and SQL compliance. It was initially developed at the University of California, Berkeley, in the 1980s and has since become one of the most popular open-source databases in the world.",
|
||||
"links": [
|
||||
{
|
||||
"title": "History of POSTGRES to PostgreSQL",
|
||||
"url": "https://www.postgresql.org/docs/current/history.html",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"links": []
|
||||
},
|
||||
"soar-NBWCr4xVKj7ttfnc": {
|
||||
"title": "What are Relational Databases?",
|
||||
@@ -312,12 +306,7 @@
|
||||
"description": "Multi-Version Concurrency Control (MVCC) is a technique used by PostgreSQL to allow multiple transactions to access the same data concurrently without conflicts or delays. It ensures that each transaction has a consistent snapshot of the database and can operate on its own version of the data.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Intro to MVCC",
|
||||
"url": "https://www.postgresql.org/docs/current/mvcc-intro.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Multiversion concurrency control - Wikipedia",
|
||||
"title": "",
|
||||
"url": "https://en.wikipedia.org/wiki/Multiversion_concurrency_control",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -550,7 +539,7 @@
|
||||
"description": "PostgreSQL offers a comprehensive set of data types to cater to diverse data needs, including numeric types like `INTEGER`, `FLOAT`, and `SERIAL` for auto-incrementing fields; character types such as `VARCHAR` and `TEXT` for variable-length text; and temporal types like `DATE`, `TIME`, and `TIMESTAMP` for handling date and time data. Additionally, PostgreSQL supports `BOOLEAN` for true/false values, `ENUM` for enumerated lists, and composite types for complex structures. It also excels with `JSON` and `JSONB` for storing and querying semi-structured data, arrays for storing multiple values in a single field, and geometric types for spatial data. These data types ensure flexibility and robust data management for various applications.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "PostgreSQL® Data Types: Mappings to SQL, JDBC, and Java Data Types",
|
||||
"title": "",
|
||||
"url": "https://www.instaclustr.com/blog/postgresql-data-types-mappings-to-sql-jdbc-and-java-data-types/",
|
||||
"type": "article"
|
||||
},
|
||||
@@ -769,11 +758,6 @@
|
||||
"title": "PostgreSQL INTERSECT Operator",
|
||||
"url": "https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-intersect/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "PostgreSQL EXCEPT Operator",
|
||||
"url": "https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-except/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -866,7 +850,7 @@
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Query Planning",
|
||||
"title": "Query Planning@",
|
||||
"url": "https://www.postgresql.org/docs/current/runtime-config-query.html",
|
||||
"type": "article"
|
||||
}
|
||||
@@ -2091,16 +2075,6 @@
|
||||
"url": "https://www.postgresql.org/docs/8.1/triggers.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "PostgreSQL Triggers",
|
||||
"url": "https://www.postgresqltutorial.com/postgresql-triggers/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Understanding PostgreSQL Triggers",
|
||||
"url": "https://hevodata.com/learn/postgresql-triggers/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Using PostgreSQL triggers to automate processes with Supabase",
|
||||
"url": "https://www.youtube.com/watch?v=0N6M5BBe9AE",
|
||||
|
||||
@@ -58,11 +58,6 @@
|
||||
"title": "Python for Beginners: Data Types",
|
||||
"url": "https://thenewstack.io/python-for-beginners-data-types/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Python Variables and Data Types",
|
||||
"url": "https://www.youtube.com/playlist?list=PLBlnK6fEyqRhN-sfWgCU1z_Qhakc1AGOn",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -242,11 +237,6 @@
|
||||
"title": "Python \"for\" Loops (Definite Iteration)",
|
||||
"url": "https://realpython.com/python-for-loop/#the-guts-of-the-python-for-loop",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Python For Loops",
|
||||
"url": "https://www.youtube.com/watch?v=KWgYha0clzw",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -346,6 +336,11 @@
|
||||
"url": "https://www.programiz.com/dsa",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "DSA Course by Google",
|
||||
"url": "https://www.udacity.com/course/data-structures-and-algorithms-in-python--ud513",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Explore top posts about Algorithms",
|
||||
"url": "https://app.daily.dev/tags/algorithms?ref=roadmapsh",
|
||||
@@ -628,11 +623,6 @@
|
||||
"title": "OOP in Python One Shot",
|
||||
"url": "https://www.youtube.com/watch?v=Ej_02ICOIgs",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Python OOP Tutorial",
|
||||
"url": "https://www.youtube.com/watch?v=IbMDCwVm63M",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1134,10 +1124,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"virtualenv@_IXXTSwQOgYzYIUuKVWNE.md": {
|
||||
"title": "virtualenv",
|
||||
"description": "",
|
||||
"links": []
|
||||
"_IXXTSwQOgYzYIUuKVWNE": {
|
||||
"title": "virutalenv",
|
||||
"description": "`virtualenv` is a tool to create isolated Python environments. It creates a folder which contains all the necessary executables to use the packages that a Python project would need.\n\nLearn more about `virtualenv` by visiting the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Virtual Environments",
|
||||
"url": "https://virtualenv.pypa.io/en/latest/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"N5VaKMbgQ0V_BC5tadV65": {
|
||||
"title": "pyenv",
|
||||
|
||||
@@ -222,19 +222,14 @@
|
||||
"description": "React follows a declarative approach to rendering components, which means that developers specify what a component should look like, and React takes care of rendering the component to the screen. This is in contrast to an imperative approach, where developers would write code to manually manipulate the DOM (Document Object Model) to update the UI.\n\nThe virtual DOM (VDOM) is an important aspect of how React works. It is a lightweight in-memory representation of the DOM (Document Object Model), and it is used to optimize the rendering of components in a React application.\n\n* Components are written as JavaScript classes or functions that define a render method. The render method returns a description of what the component should look like, using JSX syntax.\n* When a component is rendered, React creates a virtual DOM (VDOM) representation of the component. The VDOM is a lightweight in-memory representation of the DOM, and it is used to optimize the rendering of components.\n* React compares the VDOM representation of the component with the previous VDOM representation (if it exists). If there are differences between the two VDOMs, React calculates the minimum number of DOM updates needed to bring the actual DOM into line with the new VDOM.\n* React updates the actual DOM with the minimum number of DOM updates needed to reflect the changes in the VDOM.\n\nThis process is known as reconciliation, and it is an important aspect of how React works. By using a declarative approach and a VDOM, React is able to optimize the rendering of components and improve the performance of web applications.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Render and Commit - Official Docs",
|
||||
"url": "https://react.dev/learn/render-and-commit",
|
||||
"title": "Rendering - Official Docs",
|
||||
"url": "https://legacy.reactjs.org/docs/rendering-elements.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Rendering in React - ui.dev",
|
||||
"url": "https://ui.dev/why-react-renders",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Fix the slow render before you fix the re-render",
|
||||
"url": "https://kentcdodds.com/blog/fix-the-slow-render-before-you-fix-the-re-render",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -658,7 +653,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "React Documentation on useCallback",
|
||||
"url": "https://react.dev/reference/react/useCallback",
|
||||
"url": "https://reactjs.org/docs/hooks-reference.html#usecallback",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,929 +0,0 @@
|
||||
{
|
||||
"-3pADOHMDQ0H6ZKNjURyn": {
|
||||
"title": "What is Redis?",
|
||||
"description": "Redis is an open-source, in-memory data structure store, primarily used as a database, cache, and message broker. It supports various data structures like strings, hashes, lists, sets, and sorted sets, making it highly versatile. Redis operates with extremely low latency due to its in-memory nature, enabling fast access to data. It is often used in real-time applications such as session management, leaderboards, or caching mechanisms, where quick data retrieval is critical. Additionally, Redis supports data persistence by periodically writing the dataset to disk, balancing memory speed with data reliability.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is redis?",
|
||||
"url": "https://redis.io/docs/latest/get-started/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"M-EXrTDeAEMz_IkEi-ab4": {
|
||||
"title": "In-memory Data Structure Store",
|
||||
"description": "An in-memory database is a purpose-built database that relies primarily on internal memory for data storage. It enables minimal response times by eliminating the need to access standard disk drives (SSDs). In-memory databases are ideal for applications that require microsecond response times or have large spikes in traffic, such as gaming leaderboards, session stores, and real-time data analytics. The terms main memory database (MMDB), in-memory database system (IMDS), and real-time database system (RTDB) also refer to in-memory databases.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Amazon MemoryDB",
|
||||
"url": "https://aws.amazon.com/memorydb/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"l2aXyO3STnhbFjvUXPpm2": {
|
||||
"title": "Key-value Database",
|
||||
"description": "Key-value Database is a non-relational (NoSQL) database that stores data as a collection of key-value pairs. In this model, each piece of data is associated with a unique identifier (key) that is used to retrieve the corresponding value. This simple structure allows for high performance and scalability, making key-value databases ideal for certain use cases.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a Key-Value Database?",
|
||||
"url": "https://redis.io/nosql/key-value-databases/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Key Value Store - System Design Interview Basics",
|
||||
"url": "https://www.youtube.com/watch?v=ozJHmm05EXM",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"eHuBz_zSZK3rubn7nkd7g": {
|
||||
"title": "Cache",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"mgGJTBU8ofvOzl9gYWhnG": {
|
||||
"title": "Message Broker",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"-TjnSOY8txYrhhxRV1OIl": {
|
||||
"title": "Caching",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"bVJASI7bfSYqYnNhX83ng": {
|
||||
"title": "Real-time Analytics",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"URxGmhZHr0Y8nyrYj0gJl": {
|
||||
"title": "Session Management",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"ZCyId3aIoLv3duxoJdk2P": {
|
||||
"title": "Pub/Sub Messaging",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Fv1iGX22sApIEifM2IpJz": {
|
||||
"title": "Leaderboards and Counters",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"8uRpPJ0iD4XnQPKruQc8P": {
|
||||
"title": "Data Persistence Options",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"uVewcyaFi1Pt2Gs0KrkfA": {
|
||||
"title": "Rich Data Structures",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"5-3pd4rLDqRzMzSRVLdXh": {
|
||||
"title": "High Performance and Scalability",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"EvWiEx_AoxAht6sKxzW2l": {
|
||||
"title": "Redis vs SQL/NoSQL DBs",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"1Af5H0BgdAsRdBCNdHz5v": {
|
||||
"title": "When to choose Redis?",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Bf_kLfmy7_uflqC9N0-jt": {
|
||||
"title": "Using Package Managers",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"yBZ79s6mzGdj5AnX2H_Hy": {
|
||||
"title": "Pre-compiled Binaries",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"TDxv0q7jlZ26uZYYlneem": {
|
||||
"title": "Using Docker",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"43LvShQhmoWQ8Nye7fLkz": {
|
||||
"title": "Starting the Server",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"BOGXTjmCLo6WI6mYDsqRu": {
|
||||
"title": "Connecting using Redis CLI",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"NhcZM4nUQoSBBf_1qXi6l": {
|
||||
"title": "Basic Commands / SET, GET",
|
||||
"description": "In Redis, the SET and GET commands are fundamental operations used to store and retrieve key-value pairs. Redis is an in-memory key-value store, and these commands form the basis for working with data in Redis.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "SET Docs",
|
||||
"url": "https://redis.io/docs/latest/commands/set/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "GET Docs",
|
||||
"url": "https://redis.io/docs/latest/commands/get/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Redis command cheat sheet",
|
||||
"url": "https://redis.io/learn/howtos/quick-start/cheat-sheet",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"DOdNkTY1yIMipWA2CD9xH": {
|
||||
"title": "Settings and Getting Keys",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"lV_MnUNTB2h925idX0YWk": {
|
||||
"title": "DEL",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"U84XgBFPyIbY0W5afH4cx": {
|
||||
"title": "Overview of Data Types",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"ltF4vCT9ZA2XuUuHnuGnN": {
|
||||
"title": "SET",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"mQc4H2ZMMSVjh33LJY8mK": {
|
||||
"title": "GET",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"5K9qyC4mrhXYWOC8WSq8C": {
|
||||
"title": "INCR",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"t4BXPofF8OCqH5KHwdYVh": {
|
||||
"title": "DECR",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"cPWd53BO6tm-uy4gqLdtZ": {
|
||||
"title": "APPEND",
|
||||
"description": "Redis APPEND command is used to add some value in a key. If the key already exists and is a string, this command appends the value at the end of the string. If key does not exist it is created and set as an empty string,\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "APPEND Docs",
|
||||
"url": "https://redis.io/docs/latest/commands/append/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Redis - String Append Command",
|
||||
"url": "https://www.tutorialspoint.com/redis/strings_append.htm",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"eJQW986HM4Wf1o1i2FnXs": {
|
||||
"title": "STRLEN",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"0v8uLWRCbAqEmKKdYaRQW": {
|
||||
"title": "More Commands",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"2_E2VwbjTgk4xxTFWfHuV": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"nS0DHhfy4wxHItgOFhulA": {
|
||||
"title": "EXPR",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Vll7VMmGWSI5XGZ9JpHyl": {
|
||||
"title": "TTL",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Kq7G89KaZZMFkrH-9WZoS": {
|
||||
"title": "LPUSH",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"jC8G1o7yFj7D_PGmOIgcD": {
|
||||
"title": "RPUSH",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"voa61RTWMJD3Sk8DNJoVQ": {
|
||||
"title": "LPOP",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"brUGqWZ287EWtvl9uUbNt": {
|
||||
"title": "RPOP",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"8JTtBy6oD2wFYDizVkcVa": {
|
||||
"title": "LRANGE",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"hBFEUXtuzUTzWZKp2qWaZ": {
|
||||
"title": "LINDEX",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"4oCcP9FxDJSDMHCEVBCNa": {
|
||||
"title": "LLEN",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"9KvHcS5F4Jj5ZXgIAdOQY": {
|
||||
"title": "LMOVE",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"eBeEUYY-IL_CMkcm31lUL": {
|
||||
"title": "More Commands",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"XTwNCCtzXvZMdaex4gZEh": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Qgkpr9vf9d6-vUg1o8XFj": {
|
||||
"title": "Sets",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"xUKoQps69FFQrJeuhD1pz": {
|
||||
"title": "SADD",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"mQ0ILns53n1By0Tq6xSZI": {
|
||||
"title": "SMEMBERS",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"WQWVL5GT_scHdgfCtI7WT": {
|
||||
"title": "SREM",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Ji5ghlcGJtlmErHFqVf3d": {
|
||||
"title": "SISMEMBER",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"5aLfNBewK4Dx017qVNO3T": {
|
||||
"title": "SINTER",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"2gZL4a9aWGKWLa89iyHTc": {
|
||||
"title": "SCARD",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"6QoYa-N2BKNBaRtvNeVNm": {
|
||||
"title": "SUNION",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"JX5ajmcUmkshTO-mLF8lH": {
|
||||
"title": "SDIFF",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"2SG4Hr9Tuv6cxmGkrKjYZ": {
|
||||
"title": "More Commands",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"3hayYoSZepw7pppBubotg": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"b48EUyFGUeSjtT5fOa_m6": {
|
||||
"title": "More Commands",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Wl23Jh-ASJOQ850yjaTIU": {
|
||||
"title": "Strings",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"4-C4XqACUp4nvcMIj6djF": {
|
||||
"title": "Lists",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"wY46Qj5Inw_ClBNI9PB_2": {
|
||||
"title": "Hashes",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"BOJzn9SWad9oRRdY_ub01": {
|
||||
"title": "HSET",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"MsKg9m5jFwHM2Bzjf-vdu": {
|
||||
"title": "HGET",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"TpR33sJ-tAjeG3jpGTvYR": {
|
||||
"title": "HGETALL",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"E7xFZkqqbzokD5KGTn9zJ": {
|
||||
"title": "HDEL",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"adhLMuSmfYMRyWTwIgnyE": {
|
||||
"title": "HEXISTS",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"jtVnUD-na-WffMaS8qYfu": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"QTbkWZ7BpqYmBhUivccPu": {
|
||||
"title": "Sorted Sets",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"0swsBD0sOY-o5lzibT999": {
|
||||
"title": "ZADD",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"3pFChX6YIItrBz9lxu4XM": {
|
||||
"title": "ZRANGE",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"OlbixGa5RmdqEt7snY04j": {
|
||||
"title": "ZRANGEBYSCORE",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"m0FZDPwNE71zcwM_gUwz0": {
|
||||
"title": "ZREM",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"W4v7FIQr2k-Vbm-HdfKog": {
|
||||
"title": "ZINCRBY",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"AF_kWM4V8n5Ux06IgEVTl": {
|
||||
"title": "ZRANK",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"O-fZM_U-tW0pYtNzN_8Ax": {
|
||||
"title": "ZCOUNT",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"P6TDUCroLlEI7qePBFHIH": {
|
||||
"title": "More Commands",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"lxevY15ZyP43s_JrEqMX7": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"o6e_CwxfPoU6qkfWkwKwj": {
|
||||
"title": "More Commands",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"jCaVx5wvsvuyqtwh6m8si": {
|
||||
"title": "Naming Conventions",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"UlQHqw1dbxZnAKbsWsOgU": {
|
||||
"title": "Retrieval by Pattern",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"OSIYDYPGz8Vgo9SU9GGH9": {
|
||||
"title": "Expiration",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"jrgaoDnt_RxTu79hk4hCD": {
|
||||
"title": "Atomicity in Redis",
|
||||
"description": "Atomicity in Redis refers to the property that ensures a set of operations is executed as a single, indivisible unit. This means that either all the operations are executed successfully or none of them are. Atomicity is crucial in Redis to maintain consistency, especially when multiple operations need to be performed together.\n\nLearn more from the following resources:\n\n* [@official@Atomicity with Lua](https://redis.io/learn/develop/java/spring/rate-limiting/fixed-window/reactive-lua) -[@article@Atomicity in Redis operations](https://lucaspin.medium.com/atomicity-in-redis-operations-a1d7bc9f4a90)",
|
||||
"links": []
|
||||
},
|
||||
"LHlwjN3WHYUBUafzzwsWQ": {
|
||||
"title": "Pipelining",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"7JzeyTrkZ_1_yxMVrqvZU": {
|
||||
"title": "Batch Operations",
|
||||
"description": "Batch operations in Redis allow you to execute multiple commands efficiently in a single network round-trip. While Redis does not have true batching like some databases (where a set of operations are sent together and processed atomically), it provides ways to send multiple commands together to reduce the overhead of individual network requests. These include Pipelining, Transactions (MULTI/EXEC), and Lua Scripting.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Using pipelining to batch issue commands",
|
||||
"url": "https://www.alibabacloud.com/help/en/redis/use-cases/use-pipelining-to-batch-issue-commands#:~:text=You%20can%20use%20the%20Redis,network%20latency%20and%20improving%20performance.",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"0Q3AkE8leWAyYsww3-BHX": {
|
||||
"title": "Bitmaps",
|
||||
"description": "In Redis, Bitmaps are a data structure that allows you to manipulate individual bits within a string value. While Redis doesn't have a native \"bitmap\" data type, it uses strings to represent bitmaps. The power of bitmaps comes from their ability to perform operations on binary data at the bit level, making them extremely memory-efficient for certain types of applications, like tracking the presence/absence of elements (such as daily active users, features, etc.).\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Redis Bitmap docs",
|
||||
"url": "https://redis.io/docs/latest/develop/data-types/bitmaps/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Redis bitmap explained",
|
||||
"url": "https://youtu.be/oj8LdJQjhJo?si=jem54LfPbZtrpnEP",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"B-YUFhPQNdr1KZNupmR5N": {
|
||||
"title": "SETBIT",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"0HFLJfcrcSnAVTecG3P8W": {
|
||||
"title": "GETBIT",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"jpcyXSSib7q4WBPmpgnXA": {
|
||||
"title": "BITCOUNT",
|
||||
"description": "The BITCOUNT command in Redis is used to count the number of bits set to 1 (i.e., the number of binary 1s) in the value stored at a specific key. Since Redis allows string values to be stored as binary data, the BITCOUNT command becomes useful for operations involving bits, like efficiently tracking and counting bits in binary-encoded data.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "BITCOUNT - Docs",
|
||||
"url": "https://redis.io/docs/latest/commands/bitcount/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "BITCOUNT",
|
||||
"url": "https://upstash.com/docs/redis/sdks/ts/commands/bitmap/bitcount",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tkrxArg_oYH0aQfM8NkD2": {
|
||||
"title": "BITOP",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Df1Eu7CuA-ARYii9JVvnm": {
|
||||
"title": "BITPOS",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"s7PEr-5TAm5EGJm0RSjPJ": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"cszjT3YK8oyhGpqLTQzwX": {
|
||||
"title": "HyperLogLog",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"8a4DmPZrX2xGZ7zdWxS63": {
|
||||
"title": "PFADD",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"JWT30KIJQHVw0MXI5sGR6": {
|
||||
"title": "PFCOUNT",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"s50jr_XOUcxh65-tGCKf5": {
|
||||
"title": "PFMERGE",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"XPeCvikPuu6EJ8UcOLGPh": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"zXs_9n2yEb_eVi0WuOQKH": {
|
||||
"title": "Streams",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"7isWhgrUA6M5IGM2U2tm4": {
|
||||
"title": "XADD",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"4sKiAtX5aIL4NDsQkilNC": {
|
||||
"title": "XREAD",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"CiYFuYE8XudZkR6AW2NQ7": {
|
||||
"title": "XRANGE",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"DQJCMEw13lELcw_AwLfrT": {
|
||||
"title": "XLEN",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"zXlSBfa-Gi9_GhSYEzre3": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"4-z4hDKm86qQatYnmE21R": {
|
||||
"title": "More Commands",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"_NiUdVQ85qnvryI38k_vQ": {
|
||||
"title": "Geospatial Indexes",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"U3N1EgHFs1-YUaB_VrJfw": {
|
||||
"title": "GEOADD",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"OWWDLuGTbdNwME7v2jxVP": {
|
||||
"title": "GEOSEARCH",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"GNMjrLPkpTphneoQ0GoZF": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"FCbdKnkI1ZHGekT6yiGua": {
|
||||
"title": "More Commands",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"9W_jaK1DSEZHRKdPcUM7h": {
|
||||
"title": "Pub/Sub",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"55BCntuWlaQiLPqNtb-2i": {
|
||||
"title": "SUBSCRIBE",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"5gkZzm2F4vu6IxUoJLYbK": {
|
||||
"title": "UNSUBSCRIBE",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"gIPo-2CNqE1BsOaDzmkCU": {
|
||||
"title": "PUBLISH",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"V-d6q-3Sf0dl5v8xiCQwl": {
|
||||
"title": "More Commands",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"MvyE_JUJej0UB9xe8Anfj": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"YHbWlKrQqptUDbaQVy0_A": {
|
||||
"title": "Transactions",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"c-y5Eck8VtSyIf8RAW9p7": {
|
||||
"title": "WATCH",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Ljy-Mc0EBBX4_vXfYZ5-4": {
|
||||
"title": "EXEC",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"U6hST1MkS16T2CHV3-Ise": {
|
||||
"title": "MULTI",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"msW0Wd2H-6FFNDnjC64t-": {
|
||||
"title": "Optimistic Locking",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Veb30QrPYNjUn13dtGbUr": {
|
||||
"title": "Lua Scripting",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"rjeq3i9oX8IGyQzo--L3c": {
|
||||
"title": "EVAL",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"3X0x_PcJGWBVPL-LSVAln": {
|
||||
"title": "EVALSHA",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"kF_nGo845XDwLkwcQt008": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"bQaek7f3dAaZfSUhwovm1": {
|
||||
"title": "Persistence Options",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"D3pZdAjwPFMRxX1-iyu5-": {
|
||||
"title": "How RDB Works?",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"_pb2DPrFUUZabKxWsuFUo": {
|
||||
"title": "Configuring Save Interval",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Z6yJwUkcDX08HoMyf1LwX": {
|
||||
"title": "Usecases / Best Practices",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"PTj6oxvpw8vP295WvAI80": {
|
||||
"title": "How AOF Works?",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"ibaZ34-laQtUyxAsERi7o": {
|
||||
"title": "AOF rewrite & compaction",
|
||||
"description": "Persistence refers to the writing of data to durable storage, such as a solid-state disk (SSD). Redis provides a range of persistence options of which AOF (Append Only File) is one of the options. AOF persistence logs every write operation received by the server. These operations can then be replayed again at server startup, reconstructing the original dataset.The rewrite will create a small optimized version of the current Append Only File.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Persistence in redis",
|
||||
"url": "https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Enabling redis persistence",
|
||||
"url": "https://youtu.be/qBKnUeR0p10?si=TPvcFtpFMcTZB-Be",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"9ToKTUqbi-NV5Wcwb21PT": {
|
||||
"title": "Truncation / Corruption",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"VvOQUO22ZF8VvDSqHENNU": {
|
||||
"title": "Usecases",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"vzp7DUpjklzIA0E9WxJQA": {
|
||||
"title": "Usecases / Best Practices",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"80035BzcB-fKCvD_3N8zE": {
|
||||
"title": "No Persistence Option",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"3S-qqOlfr60HR4VvDr4He": {
|
||||
"title": "RDB vs AOF Tradeoffs",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"S5Y26m1oHCQpB-oLCdtac": {
|
||||
"title": "Hybrid Persistence",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"rSD8nJ-uNpHJVe5Hn66h7": {
|
||||
"title": "Replication Basics",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"nci3OB1NE1zJHUPfZCOpT": {
|
||||
"title": "Redis Sentinel",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"AQiCcHS6dBAAAPloxiXub": {
|
||||
"title": "Clustering",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Qy42paiTUsO8HIwbWTMui": {
|
||||
"title": "Authentication",
|
||||
"description": "Authentication in Redis is a security feature used to restrict access to the server by requiring clients to authenticate themselves with a password before performing any commands. This helps prevent unauthorized users from connecting to your Redis instance and performing operations.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "AUTH",
|
||||
"url": "https://redis.io/docs/latest/commands/auth/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
},
|
||||
"wsuKH7YwGDV6GYQbdhA4o": {
|
||||
"title": "Network Security",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"gdiWwTQg6A-BFHdQBmgmH": {
|
||||
"title": "SSL/TLS Encryption",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"q2Jw49QUWCUGIfcEC1bZI": {
|
||||
"title": "INFO",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"jBtEiylcedtaE6E20Uk4V": {
|
||||
"title": "MONITOR",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"XBPwHgIsXupMsyoOFkJZ0": {
|
||||
"title": "RedisInsight",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"y5FPSAi6T-5X9SUfR58_-": {
|
||||
"title": "RedisCommander",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"xF0wQYmtwXYkrzvWg5gOO": {
|
||||
"title": "Memory Management",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Sd1ENOXSFCz1YqccXjr2A": {
|
||||
"title": "Max Memory Policy",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"yaCWw2KjX58SaPajUAb0d": {
|
||||
"title": "Slow Log Analysis",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"kgHwK4N-sfh6dHjd_D_me": {
|
||||
"title": "redis-benchmark",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"2p5RF4lVYfRvYTo1Ofm-a": {
|
||||
"title": "Monitoring",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"hLIT00Iz7rV56ZBIUhWYn": {
|
||||
"title": "Redis Modules",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"jicsfYw56VrbRUt7M8c85": {
|
||||
"title": "RedisJSON",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"_GdTXcJO8uJlhPdfrmeXG": {
|
||||
"title": "Search",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"RBr8opWSh2TKXC8Fmdg0j": {
|
||||
"title": "RedisTimeSeries",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"GwVL5CvbnHsiWb1hVh7lK": {
|
||||
"title": "RedisBloom",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"giyKPtQ-pziA064P8OQD-": {
|
||||
"title": "redis.conf",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"wXRDsNGFckXV_CSiit5sN": {
|
||||
"title": "Backup and Recovery",
|
||||
"description": "Backing up and recovering Redis data is crucial for ensuring data persistence and reliability. Redis, by default, stores its data in memory for fast access, but it provides mechanisms to persist data to disk to allow for recovery in case of failure or system restarts. The primary methods for backup and recovery in Redis are RDB snapshots and AOF (Append-Only File). These methods can be used individually or in combination, depending on the specific use case.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Backup and recovery",
|
||||
"url": "https://redis.io/redis-enterprise/technology/backup-disaster-recovery/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Backup & Restore Redis Cluster with Stash",
|
||||
"url": "https://youtu.be/Py_Ivv-2dcQ?si=z8gAAmhlpUBce4jY",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Cb-KazR4PuR86VX5oT0zi": {
|
||||
"title": "Upgrading Redis",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"nUIfTkgm3PlSiqgun1BS7": {
|
||||
"title": "Disaster Recovery",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"8lyXDuZJ-KHl4v2_8Ew1h": {
|
||||
"title": "Redis Enterprise",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"cybF72wlJyJbHLUjitLvn": {
|
||||
"title": "Active-Active geo Distribution",
|
||||
"description": "An Active-Active architecture is a data resiliency architecture that distributes the database information over multiple data centers via independent and geographically distributed clusters and nodes. It is a network of separate processing nodes, each having access to a common replicated database such that all nodes can participate in a common application ensuring local low latency with each region being able to run in isolation.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Active-Active geo-distribution",
|
||||
"url": "https://redis.io/active-active/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is active active geo-distribution",
|
||||
"url": "https://youtu.be/x5iHPPZIlQg?si=ZZCwU-tDCIVDboXc",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sWOFnbh2EyaHRzquz1UeF": {
|
||||
"title": "Redis on Flash",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"ujs77bV8g8-FOm5hBtZFd": {
|
||||
"title": "Security and Compliance",
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"JlLwy69eQ1bPHAOOJNqjo": {
|
||||
"title": "When to consider enterprise?",
|
||||
"description": "",
|
||||
"links": []
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "What is Software Architecture in Software Engineering?",
|
||||
"url": "https://www.future-processing.com/blog/what-is-software-architecture-in-software-engineering/",
|
||||
"url": "https://webcache.googleusercontent.com/search?q=cache:ya4xvYaEckQJ:https://www.future-processing.com/blog/what-is-software-architecture-in-software-engineering/&cd=1&hl=es-419&ct=clnk&gl=ar",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -148,7 +148,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Java Roadmap",
|
||||
"url": "https://roadmap.sh/java",
|
||||
"url": "/java",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -184,7 +184,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Python Roadmap",
|
||||
"url": "https://roadmap.sh/python",
|
||||
"url": "/python",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -246,7 +246,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated Go Roadmap",
|
||||
"url": "https://roadmap.sh/golang",
|
||||
"url": "/golang",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -282,7 +282,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated JavaScript Roadmap",
|
||||
"url": "https://roadmap.sh/javascript",
|
||||
"url": "/javascript",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -1155,21 +1155,10 @@
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"6FDGecsHbqY-cm32yTZJa": {
|
||||
"functional-programming@6FDGecsHbqY-cm32yTZJa.md": {
|
||||
"title": "Functional Programming",
|
||||
"description": "Functional programming is a programming paradigm designed to handle pure mathematical functions. This paradigm is totally focused on writing more compounded and pure functions.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Functional Programming with JavaScript",
|
||||
"url": "https://www.telerik.com/blogs/functional-programming-javascript",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Learning Functional Programming",
|
||||
"url": "https://youtube.com/watch?v=e-5obm1G_FY",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"mCiYCbKIOVU34qil_q7Hg": {
|
||||
"title": "React, Vue, Angular",
|
||||
@@ -1177,17 +1166,22 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Visit Dedicated React Roadmap",
|
||||
"url": "https://roadmap.sh/react",
|
||||
"url": "/react",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "React Website",
|
||||
"url": "https://react.dev/",
|
||||
"url": "https://reactjs.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Official Getting Started",
|
||||
"url": "https://react.dev/learn/tutorial-tic-tac-toe",
|
||||
"url": "https://reactjs.org/tutorial/tutorial.html",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Beta React Docs",
|
||||
"url": "https://reactjs.org/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
@@ -1397,19 +1391,8 @@
|
||||
},
|
||||
"Rq1Wi-cHjS54SYo-Btp-e": {
|
||||
"title": "ITIL",
|
||||
"description": "**ITIL (Information Technology Infrastructure Library)** is a set of best practices for IT service management, designed to align IT services with business needs. Its primary focus is the efficient and effective delivery of value through managing the IT service lifecycle, which includes five phases: `Strategy`, `Design`, `Transition`, `Operation`, and `Continual Service Improvement`. ITIL includes key processes such as incident management, problem management, change management, configuration management, and service level agreements (SLAs). These practices aim to optimize performance, service quality, and customer satisfaction. Adopting ITIL helps organizations improve operational efficiency, reduce risks, and maintain clear control over IT services over time.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Documentation",
|
||||
"url": "https://www.axelos.com/certifications/itil-service-management/itil-4-foundation",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is ITIL?",
|
||||
"url": "https://www.youtube.com/watch?v=wgnpfMK8vDk",
|
||||
"type": "video"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"SJ5lrlvyXgtAwOx4wvT2W": {
|
||||
"title": "Prince2",
|
||||
@@ -1454,29 +1437,8 @@
|
||||
},
|
||||
"PKqwKvoffm0unwcFwpojk": {
|
||||
"title": "Scrum",
|
||||
"description": "`Scrum` is a popular agile framework used for project management, particularly in software development. It emphasizes iterative development, collaboration, and flexibility to deliver high-quality products.\n\nKey elements of Scrum:\n\n* **Sprints**: Time-boxed iterations (usually 2-4 weeks) where teams work on specific goals.\n* **Product Backlog**: Prioritized list of features or requirements for the product.\n* **Sprint Backlog**: Selected items from the Product Backlog to be completed during a Sprint.\n* **Daily Scrum (Stand-up)**: Brief daily meeting where team members share progress, challenges, and plans for the day.\n* **Sprint Review**: Meeting at the end of a Sprint to demonstrate completed work and gather feedback.\n* **Sprint Retrospective**: Meeting to reflect on the Sprint, identify improvements, and adjust processes for the next Sprint.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is scrum and how to get started",
|
||||
"url": "https://www.atlassian.com/agile/scrum.",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Scrum Methodology: The Complete Guide & Best Practices",
|
||||
"url": "https://thedigitalprojectmanager.com/projects/pm-methodology/scrum-methodology-complete-guide/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Essential Topics for the Scrum Product Owner",
|
||||
"url": "https://www.scrum.org/resources/blog/essential-topics-scrum-product-owner",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Scrum • Topics - Thriving Technologist",
|
||||
"url": "https://thrivingtechnologist.com/topics/scrum/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"7fL9lSu4BD1wRjnZy9tM9": {
|
||||
"title": "XP",
|
||||
@@ -1517,24 +1479,8 @@
|
||||
},
|
||||
"UCCT7-E_QUKPg3jAsjobx": {
|
||||
"title": "TCP/IP Model",
|
||||
"description": "The `TCP/IP model` defines how devices should transmit data between them and enables communication over networks and large distances. The model represents how data is exchanged and organized over networks. It is split into four layers, which set the standards for data exchange and represent how data is handled and packaged when being delivered between applications, devices, and servers.\n\n* **Network Access Layer**: The network access layer is a group of applications requiring network communications. This layer is responsible for generating the data and requesting connections.\n \n* **Internet Layer**: The internet layer is responsible for sending packets from a network and controlling their movement across a network to ensure they reach their destination.\n \n* **Transport Layer**: The transport layer is responsible for providing a solid and reliable data connection between the original application or device and its intended destination.\n \n* **Application Layer**: The application layer refers to programs that need TCP/IP to help them communicate with each other.\n \n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is Transmission Control Protocol TCP/IP? - Fortinet",
|
||||
"url": "https://www.fortinet.com/resources/cyberglossary/tcp-ip#:~:text=The%20TCP%2FIP%20model%20defines,exchanged%20and%20organized%20over%20networks.",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "TCP/IP Model",
|
||||
"url": "https://www.geeksforgeeks.org/tcp-ip-model/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "What is TCP/IP and How Does it Work?",
|
||||
"url": "https://www.techtarget.com/searchnetworking/definition/TCP-IP",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"Nq6o6Ty8VyNRsvg-UWp7D": {
|
||||
"title": "HTTP, HTTPS",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"R9DQNc0AyAQ2HLpP4HOk6": {
|
||||
"title": "What are Relational Databases?",
|
||||
"description": "Relational databases are a type of database management system (DBMS) that stores and provides access to data points that are related to one another. Based on the relational model introduced by E.F. Codd in 1970, they use a structure that allows data to be organized into tables with rows and columns. Key features include:\n\n* Use of SQL (Structured Query Language) for querying and managing data\n* Support for ACID transactions (Atomicity, Consistency, Isolation, Durability)\n* Enforcement of data integrity through constraints (e.g., primary keys, foreign keys)\n* Ability to establish relationships between tables, enabling complex queries and data retrieval\n* Scalability and support for multi-user environments\n\nExamples of popular relational database systems include MySQL, PostgreSQL, Oracle, and Microsoft SQL Server. They are widely used in various applications, from small-scale projects to large enterprise systems, due to their reliability, consistency, and powerful querying capabilities.\n\nLearn more from the following resources:",
|
||||
"description": "Relational databases are a type of database management system (DBMS) that stores and provides access to data points that are related to one another. Based on the relational model introduced by E.F. Codd in 1970, they use a structure that allows data to be organized into tables with rows and columns. Key features include:\n\n* Use of SQL (Structured Query Language) for querying and managing data\n* Support for ACID transactions (Atomicity, Consistency, Isolation, Durability)\n* Enforcement of data integrity through constraints (e.g., primary keys, foreign keys)\n* bility to establish relationships between tables, enabling complex queries and data retrieval\n* Scalability and support for multi-user environments\n\nExamples of popular relational database systems include MySQL, PostgreSQL, Oracle, and Microsoft SQL Server. They are widely used in various applications, from small-scale projects to large enterprise systems, due to their reliability, consistency, and powerful querying capabilities.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "What is a relational database - AWS",
|
||||
@@ -1258,7 +1258,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "SQL Server Indexes",
|
||||
"url": "https://www.sqlservercentral.com/articles/introduction-to-indexes",
|
||||
"url": "https://www.sqlservercentral.com/articles/sql-server-indexes",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -449,14 +449,8 @@
|
||||
},
|
||||
"fm8oUyNvfdGWTgLsYANUr": {
|
||||
"title": "Environment Variables",
|
||||
"description": "Environment variables can be used to customize various aspects of Terraform. You can set these variables to change the default behaviour of terraform such as increase verbosity, update log file path, set workspace, etc. Envrionment variables are optional and terraform does not need them by default.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Environment Variables",
|
||||
"url": "https://developer.hashicorp.com/terraform/cli/config/environment-variables",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"rdphcVd-Vq972y4H8CxIj": {
|
||||
"title": "Variable Definition File",
|
||||
@@ -476,30 +470,13 @@
|
||||
},
|
||||
"U2n2BtyUrOFLnw9SZYV_w": {
|
||||
"title": "Validation Rules",
|
||||
"description": "Validation rules can be used to specify custom validations to a variable. The motive of adding validation rules is to make the variable comply with the rules. The validation rules can be added using a `validation` block.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Custom Validation Rules",
|
||||
"url": "https://developer.hashicorp.com/terraform/language/values/variables#custom-validation-rules",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"1mFih8uFs3Lc-1PLgwiAU": {
|
||||
"title": "Local Values",
|
||||
"description": "Local values can be understood as a name assigned to any expression to use it multiple times directly by the name in your terraform module. Local values are referred to as locals and can be declared using the `locals` block. Local values can be a literal constants, resource attributes, variables, or other local values. Local values are helpful to define expressions or values that you need to use multiple times in the module as it allows the value to be updated easily just by updating the local value. A local value can be accessed using the `local` argument like `local.<value_name>`.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Local Values",
|
||||
"url": "https://developer.hashicorp.com/terraform/language/values/locals",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "@Article@Terraform Locals",
|
||||
"url": "https://spacelift.io/blog/terraform-locals",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"7GK4fQf1FRKrZgZkxNahj": {
|
||||
"title": "Outputs",
|
||||
|
||||
@@ -401,7 +401,7 @@
|
||||
},
|
||||
"HD1UGOidp7JGKdW6CEdQ_": {
|
||||
"title": "satisfies keyword",
|
||||
"description": "The `satisfies` operator lets us validate that the type of an expression matches some type, without changing the resulting type of that expression.\n\nLearn more from the following resources:",
|
||||
"description": "TypeScript developers are often faced with a dilemma: we want to ensure that some expression matches some type, but also want to keep the most specific type of that expression for inference purposes.\n\nFor example:\n\n // Each property can be a string or an RGB tuple.\n const palette = {\n red: [255, 0, 0],\n green: '#00ff00',\n bleu: [0, 0, 255],\n // ^^^^ sacrebleu - we've made a typo!\n };\n \n // We want to be able to use array methods on 'red'...\n const redComponent = palette.red.at(0);\n \n // or string methods on 'green'...\n const greenNormalized = palette.green.toUpperCase();\n \n\nNotice that we’ve written `bleu`, whereas we probably should have written `blue`. We could try to catch that `bleu` typo by using a type annotation on palette, but we’d lose the information about each property.\n\n type Colors = 'red' | 'green' | 'blue';\n type RGB = [red: number, green: number, blue: number];\n \n const palette: Record<Colors, string | RGB> = {\n red: [255, 0, 0],\n green: '#00ff00',\n bleu: [0, 0, 255],\n // ~~~~ The typo is now correctly detected\n };\n // But we now have an undesirable error here - 'palette.red' \"could\" be a string.\n const redComponent = palette.red.at(0);\n \n\nThe `satisfies` operator lets us validate that the type of an expression matches some type, without changing the resulting type of that expression. As an example, we could use `satisfies` to validate that all the properties of palette are compatible with `string | number[]`:\n\n type Colors = 'red' | 'green' | 'blue';\n type RGB = [red: number, green: number, blue: number];\n \n const palette = {\n red: [255, 0, 0],\n green: '#00ff00',\n bleu: [0, 0, 255],\n // ~~~~ The typo is now caught!\n } satisfies Record<Colors, string | RGB>;\n \n // Both of these methods are still accessible!\n const redComponent = palette.red.at(0);\n const greenNormalized = palette.green.toUpperCase();\n \n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "satisfies Keyword",
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
"url": "https://www.businessballs.com/improving-workplace-performance/nudge-theory/",
|
||||
"type": "article"
|
||||
},
|
||||
{
|
||||
"title": "Nudge Theory Explained with Examples (on YouTube)",
|
||||
"url": "https://www.youtube.com/watch?v=u3yxxteiyya&ab_channel=epm",
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"title": "Nudge Theory Explained in less than 10 minutes",
|
||||
"url": "https://youtu.be/fA5eGIMZTRQ",
|
||||
@@ -53,7 +58,7 @@
|
||||
},
|
||||
"2NlgbLeLBYwZX2u2rKkIO": {
|
||||
"title": "BJ Fogg's Behavior Model",
|
||||
"description": "B.J. Fogg, a renowned psychologist, and researcher at Stanford University, proposed the [Fogg Behavior Model (FBM)](https://www.behaviormodel.org/). This insightful model helps UX designers understand and influence user behavior by focusing on three core elements. These key factors are motivation, ability, and prompts.\n\n* **Motivation**: This element emphasizes the user's desire to perform a certain action or attain specific outcomes. Motivation can be linked to three core elements specified as sensation (pleasure/pain), anticipation (hope/fear), and social cohesion (belonging/rejection).\n \n* **Ability**: Ability refers to the user's capacity, both physical and mental, to perform desired actions. To enhance the ability of users, UX designers should follow the principle of simplicity. The easier it is to perform an action, the more likely users will engage with the product. Some factors to consider are time, financial resources, physical efforts, and cognitive load.\n \n* **Prompts**: Prompts are the cues, notifications, or triggers that signal users to take an action. For an action to occur, prompts should be presented at the right time when the user has adequate motivation and ability.\n \n\nUX designers should strive to find the balance between these three factors to facilitate the desired user behavior. By understanding your audience and their needs, implementing clear and concise prompts, and minimizing the effort required for action, the FBM can be an effective tool for designing user-centered products.",
|
||||
"description": "B.J. Fogg, a renowned psychologist, and researcher at Stanford University, proposed the [Fogg Behavior Model (FBM)](https://www.behaviormodel.org/). This insightful model helps UX designers understand and influence user behavior by focusing on three core elements. These key factors are motivation, ability, and triggers.\n\n* **Motivation**: This element emphasizes the user's desire to perform a certain action or attain specific outcomes. Motivation can be linked to three core elements specified as sensation (pleasure/pain), anticipation (hope/fear), and social cohesion (belonging/rejection).\n \n* **Ability**: Ability refers to the user's capacity, both physical and mental, to perform desired actions. To enhance the ability of users, UX designers should follow the principle of simplicity. The easier it is to perform an action, the more likely users will engage with the product. Some factors to consider are time, financial resources, physical efforts, and cognitive load.\n \n* **Triggers**: Triggers are the cues, notifications, or prompts that signal users to take an action. For an action to occur, triggers should be presented at the right time when the user has adequate motivation and ability.\n \n\nUX designers should strive to find the balance between these three factors to facilitate the desired user behavior. By understanding your audience and their needs, implementing clear and concise triggers, and minimizing the effort required for action, the FBM can be an effective tool for designing user-centered products.",
|
||||
"links": [
|
||||
{
|
||||
"title": "meaning of BJ fogg's behavior model",
|
||||
@@ -64,7 +69,7 @@
|
||||
},
|
||||
"kcG4IpneJzA6di0uqTiwb": {
|
||||
"title": "CREATE Action Funnel",
|
||||
"description": "Stephen Wendel's CREATE Action Funnel is a behavioral design framework aimed at helping individuals or organizations encourage specific behaviors in others, especially in the context of product design. It breaks down the process of motivating action into six key stages. Each stage helps identify where users might drop off or face barriers, allowing designers or strategists to address these pain points effectively. These stages are:\n\n* **CUE:** The user must notice a cue or prompt that tells them to act. This could be a notification, a visual element, or any kind of reminder.\n \n* **REACTION:** The user must react positively to the cue. This stage involves emotional and cognitive processing, where the individual decides if the action is relevant or attractive.\n \n* **EVALUATION:** The user evaluates whether the action is worth their time, energy, or resources. They assess the benefits versus the effort required.\n \n* **ABILITY:** The user must feel capable of taking the action. This involves ensuring that the action is easy enough to do and aligns with their skills and resources.\n \n* **TIMING:** The action needs to happen at the right time. Users need to have the opportunity and be in the right context to act.\n \n* **EXECUTION:** Finally, the action must be carried out successfully. This is the stage where the behavior is completed.\n \n\nThe CREATE Action Funnel is helpful for product designers, marketers, or behavior change professionals, as it provides a structured way to understand user actions and design interventions to improve completion rates. It identifies and solves the gaps that occur between intention and action.",
|
||||
"description": "Stephen Wendell's Create Action Funnel is a UX design framework focused on converting website visitors into active customers through a systematic and engaging process. The approach emphasizes on understanding user behavior, catering to their needs, and directing them towards specific actions. The Action Funnel consists of four major steps:\n\n* **Establish the Objectives:** Before diving into the design, clearly define the goals you want to achieve through the website or app. Determine what actions you want the users to take (e.g., sign up, make a purchase, share content) and what constitutes a successful conversion.\n \n* **Understand User Mindsets:** Identify your target audience and recognize their needs, preferences, emotions, and pain points. Accomplishing this requires user research, creating personas, storyboarding, and empathy mapping, among other methods.\n \n* **Design the Optimal User Flow:** Craft a seamless and intuitive user journey by designing a clear path from the landing page to the desired action. Prioritize simplicity, usability, and efficiency. Make sure to include meaningful touchpoints and interactions to engage the users and make it easy for them to complete the intended action.\n \n* **Refine and Test the Experience:** Use wireframes and prototypes to test and iteratively refine the user experience. Employ user testing, A/B testing, and analytics to gather insight on user behavior, preferences, and engagement. Continuously use feedback to make improvements, ensuring that the design effectively leads visitors down the action funnel.\n \n\nBy implementing Stephen Wendell's `Create Action Funnel`, you can effectively guide users through an engaging journey that motivates them to become active customers, ultimately increasing conversion rates and overall satisfaction.",
|
||||
"links": [
|
||||
{
|
||||
"title": "Behavioral Science Crash Course: Steve Wendel's CREATE Action Funnel",
|
||||
@@ -75,7 +80,7 @@
|
||||
},
|
||||
"0Df110GZcDw5wbAe1eKoA": {
|
||||
"title": "Spectrum of Thinking Interventions",
|
||||
"description": "The _Spectrum of Thinking Interventions_ provides a structure for understanding the different types of decision-making processes by illustrating how our minds would respond in a _default, lowest energy way_, if we didn't consciously do something different. This spectrum ranges from situations requiring minimal thought to those demanding intensive thinking, and includes the mechanisms (\"interventions\") that our minds will likely use.\n\n* **Habits:** Triggering a learned routine based on familiar cues\n* **Other intuitive responses:** Used in familiar or semi-familiar situations, with responses based on past experiences\n* **Active mindset or self-concept:** Used in ambiguous scenarios with multiple possible interpretations\n* **Heuristics:** Used in situations requiring conscious attention, but where decisions can be made more easily\n* **Focused, conscious calculation:** Used in unfamiliar scenarios or crucial decisions where deliberate focus is needed\n\nWith this spectrum in mind, it is essential as a UX designer to leverage on the mind's decision-making process, analyze which mechanisms are most applicable to your target users and design the most accessible and effective solutions.",
|
||||
"description": "The _Spectrum of Thinking Interventions_ provides a structure to guide your UX design process, helping you identify the types and range of thinking interventions that the user may require. This spectrum encompasses four primary categories: guidance, explanation, exploration, and creation.\n\nGuidance\n--------\n\nGuidance-based interventions are designed to help users navigate through a digital product or service with minimal effort. They may be aimed at full-fledged beginners, casual users, or experts in their respective domains. Such interventions may include signposts, tooltips, and clearly articulated labels.\n\n_Examples:_\n\n* Visual cues (e.g., icons, colors)\n* Signposting (e.g., breadcrumbs)\n* In-context information (e.g., tool tips, hints)\n\nExplanation\n-----------\n\nExplanation-based interventions provide users with detailed narratives, overviews, or background information that helps them make informed decisions. This may include tutorials, articles, videos, or any other mediums that help explain complex concepts or instructions.\n\n_Examples:_\n\n* Multimedia tutorials\n* Articles or blog posts\n* Infographics or diagrams\n\nExploration\n-----------\n\nExploration-based interventions encourage users to understand and interact with the product by investigating, asking questions, or searching for solutions on their own. This can be done by providing interactive elements, multiple pathways, and opportunities for discovery.\n\n_Examples:_\n\n* Interactive simulations or models\n* Advanced search capabilities\n* Multiple UI paths for task completion\n\nCreation\n--------\n\nCreation-based interventions engage users by offering them the tools and resources to co-create or customize their experience. This type of intervention often involves a more extensive level of input and involvement from the user as they become active participants in the design process.\n\n_Examples:_\n\n* Customizable user interfaces\n* Allowing users to create their content\n* Enabling users to manage their preferences, settings, and configurations\n\nWith this spectrum in mind, it is essential as a UX designer to analyze which types of thinking interventions are most relevant to your target users and design the most accessible and effective solutions. Always consider how these interventions will influence users' decision-making processes and their overall satisfaction with your digital product or service.",
|
||||
"links": []
|
||||
},
|
||||
"kWA8CvocP1pkom2N7O4gb": {
|
||||
|
||||
@@ -242,25 +242,13 @@
|
||||
},
|
||||
"NCIzs3jbQTv1xXhAaGfZN": {
|
||||
"title": "v-text",
|
||||
"description": "The `v-text` directive is used to set the textContent property of an element. It's important to note that when using this directive it will overwrite the HTML content inside the element. The expected input is a string, so it's important to wrap any text in single quotes.\n\nExample:\n\n <template>\n <p v-text=\"'I am some text'\"></p>\n </template>\n \n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "v-text documentation",
|
||||
"url": "https://vuejs.org/api/built-in-directives.html#v-text",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"bZxtIBeIfeUcR32LZWrPW": {
|
||||
"title": "v-html",
|
||||
"description": "The `v-thml` directive is similar to the `v-text` directive, but the difference is that `v-html` renders its content as HTML. This means that if you pass an HTML element it will be rendered as an element and not plain text. Since the content is render as HTMl, it can pose a security risk if the content contains malicius JavaScript code. For this reason you should never use this directive in combination with user input, unless the input is first properly sanitized.\n\nExample:\n\n <template>\n <p v-html=\"'<h1>Text</h1>'\"></p>\n </template>\n \n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "v-html documentation",
|
||||
"url": "https://vuejs.org/api/built-in-directives.html#v-html",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"_TlbGTKFCMO0wdLbC6xHX": {
|
||||
"title": "v-show",
|
||||
@@ -297,25 +285,13 @@
|
||||
},
|
||||
"a9caVhderJaVo0v14w8WB": {
|
||||
"title": "v-else-if",
|
||||
"description": "This directive is used to add additional conditions to a v-if and v-else block.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "v-else-if Documentation",
|
||||
"url": "https://vuejs.org/api/built-in-directives.html#v-else-if",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"3ftwRjQ9e1-qDT9BV53zr": {
|
||||
"title": "v-for",
|
||||
"description": "The `v-for` directive is used to render an HTML element, a block of elements, or even a component based on an array, an object, or a set number of times. When using this directive it is important to assign a unique key to each item to avoid issues and improve perfomance. This directive follows the `item in items` syntax.\n\nExample:\n\n <script setup>\n import { ref } from 'vue';\n const foods = ref([\n {id: 1, name: \"apple\"},\n {id: 2, name: \"pear\"},\n {id: 3, name: \"pizza\"}\n ]);\n </script>\n \n <template>\n <p v-for=\"food in foods\" :key=\"food.id\">{{ food.name }}</p>\n </template>\n \n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "v-for documentation",
|
||||
"url": "https://vuejs.org/guide/essentials/list#v-for",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"hVuRmhXVP65IPtuHTORjJ": {
|
||||
"title": "v-on",
|
||||
@@ -324,18 +300,12 @@
|
||||
},
|
||||
"cuM9q9vYy8JpZPGeBffd1": {
|
||||
"title": "v-bind",
|
||||
"description": "The `v-bind` directive dynamically binds an HTML attribute to data.\n\nThe shorthand for this directive is `:`\n\nExample:\n\n <script setup>\n import { ref } from 'vue';\n const image_url = ref(\"path/to/image.png\")\n </script>\n \n <template>\n <img :src=\"image_url\" />\n </template>\n \n\nVisit the following resources for more information:",
|
||||
"links": [
|
||||
{
|
||||
"title": "v-bind documentation",
|
||||
"url": "https://vuejs.org/api/built-in-directives.html#v-bind",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"cxu2Wbt306SxM4JKQQqnL": {
|
||||
"title": "v-model",
|
||||
"description": "The v-model directive in Vue.js is used for creating two-way data bindings on form input elements, such as `<input>`, `<textarea>`, and `<select>`. This means that the data can be updated in the component when the user inputs something, and the UI will update if the data in the component changes.",
|
||||
"description": "The v-model directive in Vue.js is used for creating two-way data bindings on form input elements, such as , , and . This means that the data can be updated in the component when the user inputs something, and the UI will update if the data in the component changes.",
|
||||
"links": [
|
||||
{
|
||||
"title": "Form Input Bindings",
|
||||
@@ -346,58 +316,28 @@
|
||||
},
|
||||
"m9pQ3daR3KiwRATcQysHA": {
|
||||
"title": "v-slot",
|
||||
"description": "The v-slot directive to define slots in components, allowing you to pass and render content dynamically inside a component.\n\nFor named slots, you use v-slot with a specific slot name. This lets you pass different content to different parts of a component:\n\n <template>\n <custom-component>\n <template v-slot:header>\n <h1>Header Content</h1>\n </template>\n <template v-slot:footer>\n <p>Footer Content</p>\n </template>\n </custom-component>\n </template>\n \n\nThe shorthand for `v-slot` is `#`, for example `v-slot:header` becomes `#header`.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "v-slot documentation",
|
||||
"url": "https://vuejs.org/api/built-in-directives.html#v-slot",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"5k9CrbzhNy9iiS6ez2UE6": {
|
||||
"title": "v-once",
|
||||
"description": "The `v-once` directive makes an HTML element render only once, skipping every future update.\n\nExample:\n\n <script setup>\n import { ref } from 'vue';\n const input = ref(\"Some Text\");\n </script>\n \n <template>\n <input v-model=\"input\">\n <p v-once>{{ input }}</p>\n </template>\n \n\nIn this example the **p** element will not change its text even if the input variable is changed through the **input** element.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "v-once documentation",
|
||||
"url": "https://vuejs.org/api/built-in-directives.html#v-once",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"mlsrhioiEkqnRIL6O3hNa": {
|
||||
"title": "v-pre",
|
||||
"description": "The `v-pre` directive makes an element render its content as-is, skipping its compilation. The most common use case is when displaying raw mustache syntax.\n\nExample:\n\n <script setup>\n import { ref } from 'vue';\n const text = ref(\"Some Text\")\n </script>\n \n <template>\n <p v-pre >{{ text }}</p>\n </template>\n \n\nThe **p** element will display: `{{ text }}` and not `Some Text` because the compilation is skipped.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "v-pre Documentation",
|
||||
"url": "https://vuejs.org/api/built-in-directives.html#v-pre",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"RrSekP8Ub01coegMwLP6a": {
|
||||
"title": "v-cloak",
|
||||
"description": "The v-cloak directive is used to prevent the uncompiled Vue template from being visible while the Vue instance is still loading. It temporarily hides the content until Vue has finished compiling the template\n\nThe v-cloak directive remains until the component instance is mounted.\n\n <div v-cloak>\n {{ message }}\n </div>\n \n\nCombined with CSS, you can hide elements with v-cloak until they are ready.\n\n [v-cloak] {\n display: none;\n }\n \n\nThe `<div>` will not be visible until the compilation is done.\n\nVisit the following resources to learn more:",
|
||||
"links": [
|
||||
{
|
||||
"title": "v-cloak documentation",
|
||||
"url": "https://vuejs.org/api/built-in-directives.html#v-cloak",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"RRPhAxIqvAcjZIcLe_N8-": {
|
||||
"title": "Optimizing Renders",
|
||||
"description": "Optimizing rendering is crucial for ensuring a smooth and efficient user experience across all your frontend projects. Sluggish webpages can lead to frustration for users, and potentially cause them to entirely abandon your web application. This issue comes up most often in single-page applications (SPAs), where the entirety of your application is loaded within a single webpage, and updates to it are handled dynamically without needing a full reload of the webpage.\n\nLearn more from the following resources:",
|
||||
"links": [
|
||||
{
|
||||
"title": "Optimizing rendering in Vue",
|
||||
"url": "https://blog.logrocket.com/optimizing-rendering-vue/",
|
||||
"type": "article"
|
||||
}
|
||||
]
|
||||
"description": "",
|
||||
"links": []
|
||||
},
|
||||
"dxwKfBxd5KYVkfEPMdHp-": {
|
||||
"title": "Debugging",
|
||||
|
||||
|
Before Width: | Height: | Size: 478 KiB |
|
Before Width: | Height: | Size: 494 KiB |
@@ -71,7 +71,6 @@ Here is the list of available roadmaps with more being actively worked upon.
|
||||
- [Design System Roadmap](https://roadmap.sh/design-system)
|
||||
- [PostgreSQL Roadmap](https://roadmap.sh/postgresql-dba)
|
||||
- [SQL Roadmap](https://roadmap.sh/sql)
|
||||
- [Redis Roadmap](https://roadmap.sh/redis)
|
||||
- [Blockchain Roadmap](https://roadmap.sh/blockchain)
|
||||
- [ASP.NET Core Roadmap](https://roadmap.sh/aspnet-core)
|
||||
- [System Design Roadmap](https://roadmap.sh/system-design)
|
||||
|
||||
@@ -90,6 +90,9 @@ const COMPRESS_CONFIG = {
|
||||
case '.avif':
|
||||
imageBuffer = await sharp(image).avif(COMPRESS_CONFIG.avif).toBuffer();
|
||||
break;
|
||||
case '.gif':
|
||||
imageBuffer = await sharp(image).gif(COMPRESS_CONFIG.gif).toBuffer();
|
||||
break;
|
||||
case '.heif':
|
||||
imageBuffer = await sharp(image).heif().toBuffer();
|
||||
break;
|
||||
@@ -108,8 +111,6 @@ const COMPRESS_CONFIG = {
|
||||
case '.webp':
|
||||
imageBuffer = await sharp(image).webp(COMPRESS_CONFIG.webp).toBuffer();
|
||||
break;
|
||||
case '.gif':
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!imageBuffer) {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { type APIContext } from 'astro';
|
||||
import { api } from './api.ts';
|
||||
|
||||
export type LeaderboardUserDetails = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar?: string;
|
||||
count: number;
|
||||
};
|
||||
|
||||
export type ListLeaderboardStatsResponse = {
|
||||
streaks: {
|
||||
active: LeaderboardUserDetails[];
|
||||
lifetime: LeaderboardUserDetails[];
|
||||
};
|
||||
projectSubmissions: {
|
||||
currentMonth: LeaderboardUserDetails[];
|
||||
lifetime: LeaderboardUserDetails[];
|
||||
};
|
||||
githubContributors: {
|
||||
currentMonth: LeaderboardUserDetails[];
|
||||
};
|
||||
referrals: {
|
||||
currentMonth: LeaderboardUserDetails[];
|
||||
lifetime: LeaderboardUserDetails[];
|
||||
};
|
||||
};
|
||||
|
||||
export function leaderboardApi(context: APIContext) {
|
||||
return {
|
||||
listLeaderboardStats: async function () {
|
||||
return api(context).get<ListLeaderboardStatsResponse>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-list-leaderboard-stats`,
|
||||
{},
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { type APIContext } from 'astro';
|
||||
import { api } from './api.ts';
|
||||
import type { RoadmapDocument } from '../components/CustomRoadmap/CreateRoadmap/CreateRoadmapModal.tsx';
|
||||
import type { PageType } from '../components/CommandMenu/CommandMenu.tsx';
|
||||
|
||||
export type ListShowcaseRoadmapResponse = {
|
||||
data: Pick<
|
||||
@@ -38,30 +37,3 @@ export function roadmapApi(context: APIContext) {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export type ProjectPageType = {
|
||||
id: string;
|
||||
title: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export async function getProjectList() {
|
||||
const baseUrl = import.meta.env.DEV
|
||||
? 'http://localhost:3000'
|
||||
: 'https://roadmap.sh';
|
||||
const pages = await fetch(`${baseUrl}/pages.json`).catch((err) => {
|
||||
console.error(err);
|
||||
return [];
|
||||
});
|
||||
|
||||
const pagesJson = await (pages as any).json();
|
||||
const projects: ProjectPageType[] = pagesJson
|
||||
.filter((page: any) => page?.group?.toLowerCase() === 'projects')
|
||||
.map((page: any) => ({
|
||||
id: page.id,
|
||||
title: page.title,
|
||||
url: page.url,
|
||||
}));
|
||||
|
||||
return projects;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { type APIContext } from 'astro';
|
||||
import { api } from './api.ts';
|
||||
import type { ResourceType } from '../lib/resource-progress.ts';
|
||||
import type { ProjectStatusDocument } from '../components/Projects/ListProjectSolutions.tsx';
|
||||
|
||||
export const allowedRoadmapVisibility = ['all', 'none', 'selected'] as const;
|
||||
export type AllowedRoadmapVisibility =
|
||||
@@ -100,7 +99,6 @@ export type GetPublicProfileResponse = Omit<
|
||||
> & {
|
||||
activity: UserActivityCount;
|
||||
roadmaps: ProgressResponse[];
|
||||
projects: ProjectStatusDocument[];
|
||||
isOwnProfile: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { Zap, ZapOff } from 'lucide-react';
|
||||
import {Flame, X, Zap, ZapOff} from 'lucide-react';
|
||||
import { useOutsideClick } from '../../hooks/use-outside-click';
|
||||
import { StreakDay } from './StreakDay';
|
||||
import {
|
||||
@@ -11,8 +11,14 @@ import {
|
||||
} from '../../stores/page.ts';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
import { $accountStreak, type StreakResponse } from '../../stores/streak.ts';
|
||||
import { InviteFriends } from './InviteFriends.tsx';
|
||||
|
||||
type StreakResponse = {
|
||||
count: number;
|
||||
longestCount: number;
|
||||
previousCount?: number | null;
|
||||
firstVisitAt: Date;
|
||||
lastVisitAt: Date;
|
||||
};
|
||||
|
||||
type AccountStreakProps = {};
|
||||
|
||||
@@ -21,7 +27,12 @@ export function AccountStreak(props: AccountStreakProps) {
|
||||
const dropdownRef = useRef(null);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const accountStreak = useStore($accountStreak);
|
||||
const [accountStreak, setAccountStreak] = useState<StreakResponse>({
|
||||
count: 0,
|
||||
longestCount: 0,
|
||||
firstVisitAt: new Date(),
|
||||
lastVisitAt: new Date(),
|
||||
});
|
||||
const [showDropdown, setShowDropdown] = useState(false);
|
||||
|
||||
const $roadmapsDropdownOpen = useStore(roadmapsDropdownOpen);
|
||||
@@ -38,11 +49,6 @@ export function AccountStreak(props: AccountStreakProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (accountStreak) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
const { response, error } = await httpGet<StreakResponse>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-streak`,
|
||||
@@ -54,7 +60,7 @@ export function AccountStreak(props: AccountStreakProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
$accountStreak.set(response);
|
||||
setAccountStreak(response);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
@@ -70,7 +76,7 @@ export function AccountStreak(props: AccountStreakProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let { count: currentCount = 0 } = accountStreak || {};
|
||||
let { count: currentCount } = accountStreak;
|
||||
const previousCount =
|
||||
accountStreak?.previousCount || accountStreak?.count || 0;
|
||||
|
||||
@@ -104,7 +110,7 @@ export function AccountStreak(props: AccountStreakProps) {
|
||||
ref={dropdownRef}
|
||||
className="absolute right-0 top-full z-50 w-[335px] translate-y-1 rounded-lg bg-slate-800 shadow-xl"
|
||||
>
|
||||
<div className="py-5 pl-4 pr-5">
|
||||
<div className="pl-4 pr-5 py-3">
|
||||
<div className="flex items-center justify-between gap-2 text-sm text-slate-500">
|
||||
<p>
|
||||
Current Streak
|
||||
@@ -120,7 +126,7 @@ export function AccountStreak(props: AccountStreakProps) {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-6 mt-9">
|
||||
<div className="mb-5 mt-8">
|
||||
<div className="grid grid-cols-10 gap-1">
|
||||
{Array.from({ length: totalCircles }).map((_, index) => {
|
||||
let dayCount,
|
||||
@@ -174,13 +180,9 @@ export function AccountStreak(props: AccountStreakProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="-mt-[0px] mb-[1.5px] text-center text-xs tracking-wide text-slate-500">
|
||||
Visit every day to keep your streak going!
|
||||
<p className="text-center text-xs text-slate-600 tracking-wide mb-[1.75px] -mt-[0px]">
|
||||
Visit every day to keep your streak alive!
|
||||
</p>
|
||||
|
||||
<InviteFriends
|
||||
refByUserCount={accountStreak?.refByUserCount || 0}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import { Copy, Heart } from 'lucide-react';
|
||||
import { useAuth } from '../../hooks/use-auth';
|
||||
import { useCopyText } from '../../hooks/use-copy-text';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { CheckIcon } from '../ReactIcons/CheckIcon';
|
||||
import { TrophyEmoji } from '../ReactIcons/TrophyEmoji.tsx';
|
||||
|
||||
type InviteFriendsProps = {
|
||||
refByUserCount: number;
|
||||
};
|
||||
|
||||
export function InviteFriends(props: InviteFriendsProps) {
|
||||
const { refByUserCount } = props;
|
||||
|
||||
const user = useAuth();
|
||||
const { copyText, isCopied } = useCopyText();
|
||||
|
||||
const referralLink = new URL(
|
||||
`/signup?rc=${user?.id}`,
|
||||
import.meta.env.DEV ? 'http://localhost:3000' : 'https://roadmap.sh',
|
||||
).toString();
|
||||
|
||||
return (
|
||||
<div className="-mx-4 mt-6 flex flex-col border-t border-dashed border-t-slate-600 px-4 pt-5 text-center text-sm">
|
||||
<p className="text-slate-500">Invite people to join roadmap.sh</p>
|
||||
|
||||
<div className="flex flex-col items-center rounded-lg bg-slate-900/40 pb-4 pt-5 my-4">
|
||||
<p className="text-xs text-slate-500">
|
||||
{refByUserCount === 0 && <>You haven't invited anyone yet.</>}
|
||||
{refByUserCount > 0 && refByUserCount < 10 && (
|
||||
<>{refByUserCount} of 10 users joined</>
|
||||
)}
|
||||
</p>
|
||||
|
||||
{refByUserCount >= 10 && <>🎉 You've invited {refByUserCount} users</>}
|
||||
|
||||
<div className="my-3 flex flex-row items-center justify-center gap-1">
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Heart
|
||||
key={index}
|
||||
className={cn(
|
||||
'size-[23px] fill-current',
|
||||
index < refByUserCount ? 'text-yellow-300' : 'text-slate-700',
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<p className={'mb-3 text-xs text-slate-500'}>
|
||||
Share the link below with anyone you think would benefit from using
|
||||
roadmap.sh
|
||||
</p>
|
||||
<p className="text-slate-500">
|
||||
<button
|
||||
onClick={() => {
|
||||
copyText(referralLink);
|
||||
}}
|
||||
className={cn(
|
||||
'rounded-md hover:bg-slate-500/80 hover:text-slate-100 px-3 py-1 text-xs text-slate-300 bg-slate-600',
|
||||
{
|
||||
'bg-green-500 text-black hover:text-black hover:bg-green-500': isCopied,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{!isCopied ? 'Copy Invite Link' : 'Invite Link Copied'}{' '}
|
||||
{!isCopied && (
|
||||
<Copy
|
||||
className="relative -top-[1.25px] ml-1.5 inline-block size-3"
|
||||
strokeWidth={3}
|
||||
/>
|
||||
)}
|
||||
{isCopied && (
|
||||
<CheckIcon additionalClasses="relative ml-1.5 -top-[1.25px] inline-block size-3" />
|
||||
)}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="text-center text-xs">
|
||||
<a
|
||||
href="/leaderboard"
|
||||
className="text-purple-400 underline-offset-2 hover:underline"
|
||||
>
|
||||
See how you rank on the leaderboard
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -5,10 +5,6 @@ import { ResourceProgress } from './ResourceProgress';
|
||||
import { pageProgressMessage } from '../../stores/page';
|
||||
import { EmptyActivity } from './EmptyActivity';
|
||||
import { ActivityStream, type UserStreamActivity } from './ActivityStream';
|
||||
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
|
||||
import type { PageType } from '../CommandMenu/CommandMenu';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { ProjectProgress } from './ProjectProgress';
|
||||
|
||||
type ProgressResponse = {
|
||||
updatedAt: string;
|
||||
@@ -51,14 +47,11 @@ export type ActivityResponse = {
|
||||
};
|
||||
}[];
|
||||
activities: UserStreamActivity[];
|
||||
projects: ProjectStatusDocument[];
|
||||
};
|
||||
|
||||
export function ActivityPage() {
|
||||
const toast = useToast();
|
||||
const [activity, setActivity] = useState<ActivityResponse>();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [projectDetails, setProjectDetails] = useState<PageType[]>([]);
|
||||
|
||||
async function loadActivity() {
|
||||
const { error, response } = await httpGet<ActivityResponse>(
|
||||
@@ -75,29 +68,11 @@ export function ActivityPage() {
|
||||
setActivity(response);
|
||||
}
|
||||
|
||||
async function loadAllProjectDetails() {
|
||||
const { error, response } = await httpGet<PageType[]>(`/pages.json`);
|
||||
|
||||
if (error) {
|
||||
toast.error(error.message || 'Something went wrong');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const allProjects = response.filter((page) => page.group === 'Projects');
|
||||
setProjectDetails(allProjects);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
Promise.allSettled([loadActivity(), loadAllProjectDetails()]).finally(
|
||||
() => {
|
||||
pageProgressMessage.set('');
|
||||
setIsLoading(false);
|
||||
},
|
||||
);
|
||||
loadActivity().finally(() => {
|
||||
pageProgressMessage.set('');
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const learningRoadmaps = activity?.learning.roadmaps || [];
|
||||
@@ -131,17 +106,6 @@ export function ActivityPage() {
|
||||
learningRoadmapsToShow.length !== 0 ||
|
||||
learningBestPracticesToShow.length !== 0;
|
||||
|
||||
const enrichedProjects = activity?.projects.map((project) => {
|
||||
const projectDetail = projectDetails.find(
|
||||
(page) => page.id === project.projectId,
|
||||
);
|
||||
|
||||
return {
|
||||
...project,
|
||||
title: projectDetail?.title || 'N/A',
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActivityCounters
|
||||
@@ -237,19 +201,6 @@ export function ActivityPage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{enrichedProjects && enrichedProjects?.length > 0 && (
|
||||
<div className="mx-0 px-0 py-5 pb-0 md:-mx-10 md:px-8 md:py-8 md:pb-0">
|
||||
<h2 className="mb-3 text-xs uppercase text-gray-400">
|
||||
Your Projects
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2">
|
||||
{enrichedProjects.map((project) => (
|
||||
<ProjectProgress key={project._id} projectStatus={project} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasProgress && (
|
||||
<ActivityStream activities={activity?.activities || []} />
|
||||
)}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import { getUser } from '../../lib/jwt';
|
||||
import { getPercentage } from '../../helper/number';
|
||||
import { ProjectProgressActions } from './ProjectProgressActions';
|
||||
import { cn } from '../../lib/classname';
|
||||
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
|
||||
import { ProjectStatus } from './ProjectStatus';
|
||||
import { ThumbsUp } from 'lucide-react';
|
||||
|
||||
type ProjectProgressType = {
|
||||
projectStatus: ProjectStatusDocument & {
|
||||
title: string;
|
||||
};
|
||||
showActions?: boolean;
|
||||
userId?: string;
|
||||
};
|
||||
|
||||
export function ProjectProgress(props: ProjectProgressType) {
|
||||
const {
|
||||
projectStatus,
|
||||
showActions = true,
|
||||
userId: defaultUserId = getUser()?.id,
|
||||
} = props;
|
||||
|
||||
const shouldShowActions =
|
||||
projectStatus.submittedAt &&
|
||||
projectStatus.submittedAt !== null &&
|
||||
showActions;
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<a
|
||||
className={cn(
|
||||
'group relative flex w-full items-center justify-between overflow-hidden rounded-md border border-gray-300 bg-white px-3 py-2 pr-7 text-left text-sm transition-all hover:border-gray-400',
|
||||
shouldShowActions ? '' : 'pr-3',
|
||||
)}
|
||||
href={`/projects/${projectStatus.projectId}`}
|
||||
target="_blank"
|
||||
>
|
||||
<ProjectStatus projectStatus={projectStatus} />
|
||||
<span className="ml-2 flex-grow truncate">{projectStatus?.title}</span>
|
||||
<span className="inline-flex items-center gap-1 text-xs text-gray-400">
|
||||
{projectStatus.upvotes}
|
||||
<ThumbsUp className="size-2.5 stroke-[2.5px]" />
|
||||
</span>
|
||||
</a>
|
||||
|
||||
{shouldShowActions && (
|
||||
<div className="absolute right-2 top-0 flex h-full items-center">
|
||||
<ProjectProgressActions
|
||||
userId={defaultUserId!}
|
||||
projectId={projectStatus.projectId}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
import { MoreVertical, X } from 'lucide-react';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useOutsideClick } from '../../hooks/use-outside-click';
|
||||
import { useKeydown } from '../../hooks/use-keydown';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { useCopyText } from '../../hooks/use-copy-text';
|
||||
import { CheckIcon } from '../ReactIcons/CheckIcon';
|
||||
import { ShareIcon } from '../ReactIcons/ShareIcon';
|
||||
|
||||
type ProjectProgressActionsType = {
|
||||
userId: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
export function ProjectProgressActions(props: ProjectProgressActionsType) {
|
||||
const { userId, projectId } = props;
|
||||
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const { copyText, isCopied } = useCopyText();
|
||||
|
||||
const projectSolutionUrl = `${import.meta.env.DEV ? 'http://localhost:3000' : 'https://roadmap.sh'}/projects/${projectId}/solutions?u=${userId}`;
|
||||
|
||||
useOutsideClick(dropdownRef, () => {
|
||||
setIsOpen(false);
|
||||
});
|
||||
|
||||
useKeydown('Escape', () => {
|
||||
setIsOpen(false);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="relative h-full" ref={dropdownRef}>
|
||||
<button
|
||||
className="h-full text-gray-400 hover:text-gray-700"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<MoreVertical size={16} />
|
||||
</button>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute right-0 top-8 z-10 w-48 overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg">
|
||||
<button
|
||||
className={cn(
|
||||
'flex w-full items-center gap-1.5 p-2 text-xs font-medium hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-70 sm:text-sm',
|
||||
isCopied ? 'text-green-500' : 'text-gray-500 hover:text-black',
|
||||
)}
|
||||
onClick={() => {
|
||||
copyText(projectSolutionUrl);
|
||||
}}
|
||||
>
|
||||
{isCopied ? (
|
||||
<>
|
||||
<CheckIcon additionalClasses="h-3.5 w-3.5" /> Link Copied
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ShareIcon className="h-3.5 w-3.5 stroke-[2.5px]" /> Share
|
||||
Solution
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { CircleDashed } from 'lucide-react';
|
||||
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
|
||||
import { CheckIcon } from '../ReactIcons/CheckIcon';
|
||||
|
||||
type ProjectStatusType = {
|
||||
projectStatus: ProjectStatusDocument & {
|
||||
title: string;
|
||||
};
|
||||
};
|
||||
|
||||
export function ProjectStatus(props: ProjectStatusType) {
|
||||
const { projectStatus } = props;
|
||||
|
||||
const { submittedAt, repositoryUrl } = projectStatus;
|
||||
const status = submittedAt && repositoryUrl ? 'submitted' : 'started';
|
||||
|
||||
if (status === 'submitted') {
|
||||
return <CheckIcon additionalClasses="size-3 text-gray-500 shrink-0" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<CircleDashed className="size-3 shrink-0 stroke-[2.5px] text-gray-400" />
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import Cookies from 'js-cookie';
|
||||
import type { FormEvent } from 'react';
|
||||
import { useId, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { httpPost } from '../../lib/http';
|
||||
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
|
||||
|
||||
@@ -53,16 +53,13 @@ export function EmailLoginForm(props: EmailLoginFormProps) {
|
||||
setError(error?.message || 'Something went wrong. Please try again later.');
|
||||
};
|
||||
|
||||
const emailFieldId = `form:${useId()}`;
|
||||
const passwordFieldId = `form:${useId()}`;
|
||||
|
||||
return (
|
||||
<form className="w-full" onSubmit={handleFormSubmit}>
|
||||
<label htmlFor={emailFieldId} className="sr-only">
|
||||
<label htmlFor="email" className="sr-only">
|
||||
Email address
|
||||
</label>
|
||||
<input
|
||||
id={emailFieldId}
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
autoComplete="email"
|
||||
@@ -72,11 +69,11 @@ export function EmailLoginForm(props: EmailLoginFormProps) {
|
||||
value={email}
|
||||
onInput={(e) => setEmail(String((e.target as any).value))}
|
||||
/>
|
||||
<label htmlFor={passwordFieldId} className="sr-only">
|
||||
<label htmlFor="password" className="sr-only">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
id={passwordFieldId}
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { type FormEvent, useEffect, useState } from 'react';
|
||||
import { type FormEvent, useState } from 'react';
|
||||
import { httpPost } from '../../lib/http';
|
||||
import { deleteUrlParam, getUrlParams } from '../../lib/browser';
|
||||
import { isLoggedIn, setAIReferralCode } from '../../lib/jwt';
|
||||
|
||||
type EmailSignupFormProps = {
|
||||
isDisabled?: boolean;
|
||||
@@ -11,9 +9,6 @@ type EmailSignupFormProps = {
|
||||
export function EmailSignupForm(props: EmailSignupFormProps) {
|
||||
const { isDisabled, setIsDisabled } = props;
|
||||
|
||||
const { rc: referralCode } = getUrlParams() as {
|
||||
rc?: string;
|
||||
};
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [name, setName] = useState('');
|
||||
@@ -52,16 +47,6 @@ export function EmailSignupForm(props: EmailSignupFormProps) {
|
||||
)}`;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!referralCode || isLoggedIn()) {
|
||||
deleteUrlParam('rc');
|
||||
return;
|
||||
}
|
||||
|
||||
setAIReferralCode(referralCode);
|
||||
deleteUrlParam('rc');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form className="flex w-full flex-col gap-2" onSubmit={onSubmit}>
|
||||
<label htmlFor="name" className="sr-only">
|
||||
@@ -87,7 +72,7 @@ export function EmailSignupForm(props: EmailSignupFormProps) {
|
||||
type="email"
|
||||
autoComplete="email"
|
||||
required
|
||||
className="block w-full rounded-lg border border-gray-300 px-3 py-2 outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
|
||||
className="block w-full rounded-lg border border-gray-300 px-3 py-2 outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
|
||||
placeholder="Email Address"
|
||||
value={email}
|
||||
onInput={(e) => setEmail(String((e.target as any).value))}
|
||||
|
||||
@@ -48,7 +48,6 @@ function handleGuest() {
|
||||
'/team/members',
|
||||
'/team/member',
|
||||
'/team/settings',
|
||||
'/dashboard',
|
||||
];
|
||||
|
||||
showHideAuthElements('hide');
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
import { DateTime } from 'luxon';
|
||||
import type { ChangelogFileType } from '../../lib/changelog';
|
||||
import ChangelogImages from '../ChangelogImages';
|
||||
|
||||
interface Props {
|
||||
changelog: ChangelogFileType;
|
||||
}
|
||||
|
||||
const { changelog } = Astro.props;
|
||||
const { frontmatter } = changelog;
|
||||
|
||||
const formattedDate = DateTime.fromISO(frontmatter.date).toFormat(
|
||||
'dd LLL, yyyy',
|
||||
);
|
||||
---
|
||||
|
||||
<div class='relative mb-6' id={changelog.id}>
|
||||
<span
|
||||
class='absolute -left-6 top-2 h-2 w-2 flex-shrink-0 rounded-full bg-gray-300'
|
||||
></span>
|
||||
|
||||
<div class='mb-3 flex flex-col sm:flex-row items-start sm:items-center gap-0.5 sm:gap-2'>
|
||||
<span class='flex-shrink-0 text-xs tracking-wide text-gray-400'>
|
||||
{formattedDate}
|
||||
</span>
|
||||
<span class='truncate text-base font-medium text-balance'>
|
||||
{changelog.frontmatter.title}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class='rounded-xl border bg-white p-6'>
|
||||
{frontmatter.images && (
|
||||
<div class='mb-5 hidden sm:block -mx-6'>
|
||||
<ChangelogImages images={frontmatter.images} client:load />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
class='prose prose-sm prose-h2:mt-3 prose-h2:text-lg prose-h2:font-medium prose-p:mb-0 prose-blockquote:font-normal prose-blockquote:text-gray-500 prose-ul:my-0 prose-ul:rounded-lg prose-ul:bg-gray-100 prose-ul:px-4 prose-ul:py-4 prose-ul:pl-7 prose-img:mt-0 prose-img:rounded-lg [&>blockquote>p]:mt-0 [&>ul>li]:my-0 [&>ul>li]:mb-1 [&>ul]:mt-3'
|
||||
>
|
||||
<changelog.Content />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
const formattedDate = DateTime.fromISO('2024-09-13').toFormat('dd LLL, yyyy');
|
||||
---
|
||||
|
||||
<div class='relative mb-6'>
|
||||
<span
|
||||
class='absolute -left-6 top-2 h-2 w-2 flex-shrink-0 rounded-full bg-gray-300'
|
||||
></span>
|
||||
|
||||
<div
|
||||
class='mb-3 flex flex-col items-start gap-0.5 sm:flex-row sm:items-center sm:gap-2'
|
||||
>
|
||||
<span class='flex-shrink-0 text-xs tracking-wide text-gray-400'>
|
||||
{formattedDate}
|
||||
</span>
|
||||
<span class='truncate text-balance text-base font-medium'>
|
||||
Changelog page is launched
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class='flex flex-col items-center justify-center gap-2 sm:gap-2 rounded-xl border bg-white px-8 py-12 text-center'
|
||||
>
|
||||
<img src='/images/gifs/rocket.gif' class='w-[70px] mb-4' />
|
||||
<h2 class='text-balance text-xl font-medium'>Changelog page is launched</h2>
|
||||
<p class='font-normal text-balance text-gray-400 text-sm sm:text-base'>
|
||||
We will be sharing a selected list of updates, improvements, and fixes made to
|
||||
the website. Stay tuned!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
import { getAllChangelogs } from '../lib/changelog';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
const allChangelogs = await getAllChangelogs();
|
||||
const top10Changelogs = allChangelogs.slice(0, 10);
|
||||
---
|
||||
|
||||
<div class='border-t bg-white py-6 text-left sm:py-16 sm:text-center'>
|
||||
<div class='container !max-w-[650px]'>
|
||||
<p class='text-2xl font-bold sm:text-5xl'>
|
||||
<img
|
||||
src='/images/gifs/rocket.gif'
|
||||
alt='Rocket'
|
||||
class='mr-2 hidden sm:inline h-12 w-12'
|
||||
/>
|
||||
Actively Maintained
|
||||
</p>
|
||||
<p class='mt-1 mb-2 sm:my-2 text-sm leading-relaxed text-gray-600 sm:my-5 sm:text-lg'>
|
||||
We are always improving our content, adding new resources and adding
|
||||
features to enhance your learning experience.
|
||||
</p>
|
||||
|
||||
<div class='relative mt-2 sm:mt-8 text-left'>
|
||||
<div
|
||||
class='absolute inset-y-0 left-[120px] hidden w-px -translate-x-[0.5px] translate-x-[5.75px] bg-gray-300 sm:block'
|
||||
>
|
||||
</div>
|
||||
<ul class='relative flex flex-col gap-4 py-4'>
|
||||
{
|
||||
top10Changelogs.map((changelog) => {
|
||||
const formattedDate = DateTime.fromISO(
|
||||
changelog.frontmatter.date,
|
||||
).toFormat('dd LLL, yyyy');
|
||||
return (
|
||||
<li class='relative'>
|
||||
<a
|
||||
href={`/changelog#${changelog.id}`}
|
||||
class='flex flex-col sm:flex-row items-start sm:items-center'
|
||||
>
|
||||
<span class='sm:w-[120px] flex-shrink-0 pr-0 sm:pr-4 text-right text-sm tracking-wide text-gray-400'>
|
||||
{formattedDate}
|
||||
</span>
|
||||
<span class='h-3 w-3 flex-shrink-0 rounded-full bg-gray-300 hidden sm:block' />
|
||||
<span class='text-balance sm:pl-8 text-base font-medium text-gray-900'>
|
||||
{changelog.frontmatter.title}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div class='mt-2 sm:mt-8 text-left sm:text-center'>
|
||||
<a
|
||||
href='/changelog'
|
||||
class='inline-block text-sm sm:text-base rounded-lg sm:rounded-full bg-gray-800 px-4 sm:px-6 py-2 text-white transition-colors hover:bg-gray-700'
|
||||
>
|
||||
View Full Changelog
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,108 +0,0 @@
|
||||
import { ChevronLeft, ChevronRight, MoveRight } from 'lucide-react';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
interface ChangelogImagesProps {
|
||||
images: { [key: string]: string };
|
||||
}
|
||||
|
||||
const ChangelogImages: React.FC<ChangelogImagesProps> = ({ images }) => {
|
||||
const [enlargedImage, setEnlargedImage] = useState<string | null>(null);
|
||||
const imageArray = Object.entries(images);
|
||||
|
||||
const handleImageClick = (src: string) => {
|
||||
setEnlargedImage(src);
|
||||
};
|
||||
|
||||
const handleCloseEnlarged = () => {
|
||||
setEnlargedImage(null);
|
||||
};
|
||||
|
||||
const handleNavigation = useCallback(
|
||||
(direction: 'prev' | 'next') => {
|
||||
if (!enlargedImage) return;
|
||||
const currentIndex = imageArray.findIndex(
|
||||
([_, src]) => src === enlargedImage,
|
||||
);
|
||||
let newIndex;
|
||||
if (direction === 'prev') {
|
||||
newIndex = currentIndex > 0 ? currentIndex - 1 : imageArray.length - 1;
|
||||
} else {
|
||||
newIndex = currentIndex < imageArray.length - 1 ? currentIndex + 1 : 0;
|
||||
}
|
||||
setEnlargedImage(imageArray[newIndex][1]);
|
||||
},
|
||||
[enlargedImage, imageArray],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
handleCloseEnlarged();
|
||||
} else if (event.key === 'ArrowLeft') {
|
||||
handleNavigation('prev');
|
||||
} else if (event.key === 'ArrowRight') {
|
||||
handleNavigation('next');
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [handleNavigation]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex gap-3 px-6 pb-1">
|
||||
{imageArray.map(([title, src]) => (
|
||||
<div
|
||||
key={title}
|
||||
className="group relative cursor-pointer overflow-hidden rounded-lg transition hover:scale-105"
|
||||
onClick={() => handleImageClick(src)}
|
||||
>
|
||||
<img
|
||||
src={src}
|
||||
alt={title}
|
||||
className="h-[120px] w-full object-cover object-left-top"
|
||||
/>
|
||||
<span className="absolute group-hover:opacity-0 inset-0 bg-gradient-to-b from-transparent to-black/40" />
|
||||
|
||||
<div className="absolute font-medium inset-x-0 top-full group-hover:inset-y-0 flex items-center justify-center px-2 text-center text-xs bg-black/50 text-white py-0.5 opacity-0 group-hover:opacity-100 cursor-pointer">
|
||||
<span className='bg-black py-0.5 rounded px-1'>{title}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{enlargedImage && (
|
||||
<div
|
||||
className="fixed inset-0 z-[999] flex items-center justify-center bg-black bg-opacity-75"
|
||||
onClick={handleCloseEnlarged}
|
||||
>
|
||||
<img
|
||||
src={enlargedImage}
|
||||
alt="Enlarged view"
|
||||
className="max-h-[90%] max-w-[90%] rounded-xl object-contain"
|
||||
/>
|
||||
<button
|
||||
className="absolute left-4 top-1/2 -translate-y-1/2 transform rounded-full bg-white hover:bg-opacity-100 bg-opacity-50 p-2"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleNavigation('prev');
|
||||
}}
|
||||
>
|
||||
<ChevronLeft size={24} />
|
||||
</button>
|
||||
<button
|
||||
className="absolute right-4 top-1/2 -translate-y-1/2 transform rounded-full bg-white hover:bg-opacity-100 bg-opacity-50 p-2"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleNavigation('next');
|
||||
}}
|
||||
>
|
||||
<ChevronRight size={24} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChangelogImages;
|
||||
@@ -9,31 +9,28 @@ export function ContentConfirmationModal(props: ContentConfirmationModalProps) {
|
||||
const { onClose, onClick } = props;
|
||||
|
||||
return (
|
||||
<Modal onClose={onClose} wrapperClassName="max-w-lg">
|
||||
<Modal onClose={onClose}>
|
||||
<div className="p-4">
|
||||
<h2 className="text-lg font-semibold">
|
||||
Copy Node Details and Resources?
|
||||
</h2>
|
||||
<h2 className="text-lg font-semibold">Roadmap Content</h2>
|
||||
<p className="balanc text-gray-600">
|
||||
This will just copy the roadmap in your team. Would you like to copy
|
||||
the resource links and node details as well?
|
||||
Do you want to copy the content of this roadmap?
|
||||
</p>
|
||||
<div className="mt-4 grid grid-cols-2 gap-2">
|
||||
<button
|
||||
className="rounded-lg border p-2.5 font-normal"
|
||||
className="rounded-lg border p-2.5 font-medium"
|
||||
onClick={() => {
|
||||
onClick(false);
|
||||
}}
|
||||
>
|
||||
No, copy roadmap only
|
||||
No
|
||||
</button>
|
||||
<button
|
||||
className="rounded-lg border bg-black p-2.5 font-normal text-white hover:opacity-80"
|
||||
className="rounded-lg border bg-black p-2.5 font-medium text-white hover:opacity-80"
|
||||
onClick={() => {
|
||||
onClick(true);
|
||||
}}
|
||||
>
|
||||
Yes, also copy resources
|
||||
Yes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,6 @@ import { showLoginPopup } from '../../lib/popup.ts';
|
||||
import { isLoggedIn } from '../../lib/jwt.ts';
|
||||
import { useState } from 'react';
|
||||
import { CreateRoadmapModal } from './CreateRoadmap/CreateRoadmapModal.tsx';
|
||||
import { RoadmapAlert } from '../RoadmapAlert.tsx';
|
||||
|
||||
export function CustomRoadmapAlert() {
|
||||
const [isCreatingRoadmap, setIsCreatingRoadmap] = useState(false);
|
||||
@@ -24,18 +23,33 @@ export function CustomRoadmapAlert() {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="relative mb-5 mt-0 rounded-md border border-yellow-500 bg-yellow-100 p-2 sm:-mt-6 sm:mb-7 sm:p-2.5">
|
||||
<p className="mb-2.5 mt-2 text-sm text-yellow-800 sm:mb-1.5 sm:mt-1 sm:text-base">
|
||||
This is a custom roadmap made by a community member and is not
|
||||
verified by <span className="font-semibold">roadmap.sh</span>
|
||||
</p>
|
||||
<div className="flex flex-col items-start gap-2 sm:flex-row sm:items-center">
|
||||
<a
|
||||
href="/roadmaps"
|
||||
className="inline-flex items-center gap-1.5 text-sm font-semibold text-yellow-700 underline-offset-2 hover:underline"
|
||||
>
|
||||
<BadgeCheck className="h-4 w-4 stroke-[2.5]" />
|
||||
Visit Official Roadmaps
|
||||
</a>
|
||||
<span className="hidden font-black text-yellow-700 sm:block">
|
||||
·
|
||||
</span>
|
||||
<a
|
||||
href="/community"
|
||||
className="inline-flex items-center gap-1.5 text-sm font-semibold text-yellow-700 underline-offset-2 hover:underline"
|
||||
>
|
||||
<HeartHandshake className="h-4 w-4 stroke-[2.5]" />
|
||||
More Community Roadmaps
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<RoadmapAlert
|
||||
title="Community Roadmaps"
|
||||
description={
|
||||
<>
|
||||
This is a custom roadmap made by a community member and is not
|
||||
verified by <span className="font-semibold">roadmap.sh</span>
|
||||
</>
|
||||
}
|
||||
floatingIcon={MessageCircleHeart}
|
||||
className="mb-5 mt-0 sm:-mt-6 sm:mb-7"
|
||||
/>
|
||||
<MessageCircleHeart className="absolute bottom-2 right-2 hidden h-12 w-12 text-yellow-500 opacity-50 sm:block" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
|
||||
import { DashboardCustomProgressCard } from './DashboardCustomProgressCard';
|
||||
import { DashboardCardLink } from './DashboardCardLink';
|
||||
import { useState } from 'react';
|
||||
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal';
|
||||
import { Simulate } from 'react-dom/test-utils';
|
||||
import {
|
||||
ArrowUpRight,
|
||||
Bot,
|
||||
BrainCircuit,
|
||||
Map,
|
||||
PencilRuler,
|
||||
} from 'lucide-react';
|
||||
|
||||
type DashboardAiRoadmapsProps = {
|
||||
roadmaps: {
|
||||
id: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
}[];
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
export function DashboardAiRoadmaps(props: DashboardAiRoadmapsProps) {
|
||||
const { roadmaps, isLoading = false } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-2 mt-6 flex items-center justify-between gap-2">
|
||||
<h2 className="text-xs uppercase text-gray-400">
|
||||
My AI Roadmaps
|
||||
</h2>
|
||||
|
||||
<a
|
||||
href="/ai/explore"
|
||||
className="rounded-full bg-gray-200 px-2.5 py-0.5 text-xs font-medium text-gray-700 hover:bg-gray-300 hover:text-black"
|
||||
>
|
||||
AI Generated Roadmaps
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{!isLoading && roadmaps.length === 0 && (
|
||||
<DashboardCardLink
|
||||
className="mt-0"
|
||||
icon={BrainCircuit}
|
||||
href="/ai"
|
||||
title="Generate Roadmaps with AI"
|
||||
description="You can generate your own roadmap with AI"
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3">
|
||||
{isLoading && (
|
||||
<>
|
||||
{Array.from({ length: 9 }).map((_, index) => (
|
||||
<RoadmapCardSkeleton key={index} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isLoading && roadmaps.length > 0 && (
|
||||
<>
|
||||
{roadmaps.map((roadmap) => (
|
||||
<a
|
||||
href={`/ai/${roadmap.slug}`}
|
||||
className="relative truncate rounded-md border bg-white p-2.5 text-left text-sm shadow-sm hover:border-gray-400 hover:bg-gray-50"
|
||||
>
|
||||
{roadmap.title}
|
||||
</a>
|
||||
))}
|
||||
|
||||
<a
|
||||
className="flex items-center justify-center rounded-lg border border-dashed border-gray-300 bg-white p-2.5 text-sm font-medium text-gray-500 hover:bg-gray-50 hover:text-gray-600"
|
||||
href={'/ai'}
|
||||
>
|
||||
+ Generate New
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type CustomProgressCardSkeletonProps = {};
|
||||
|
||||
function RoadmapCardSkeleton(props: CustomProgressCardSkeletonProps) {
|
||||
return (
|
||||
<div className="h-[42px] w-full animate-pulse rounded-md bg-gray-200" />
|
||||
);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Bookmark } from 'lucide-react';
|
||||
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
|
||||
|
||||
type DashboardBookmarkCardProps = {
|
||||
bookmark: UserProgress;
|
||||
};
|
||||
|
||||
export function DashboardBookmarkCard(props: DashboardBookmarkCardProps) {
|
||||
const {
|
||||
resourceType,
|
||||
resourceId,
|
||||
resourceTitle,
|
||||
roadmapSlug,
|
||||
isCustomResource,
|
||||
} = props.bookmark;
|
||||
|
||||
let url =
|
||||
resourceType === 'roadmap'
|
||||
? `/${resourceId}`
|
||||
: `/best-practices/${resourceId}`;
|
||||
|
||||
if (isCustomResource) {
|
||||
url = `/r/${roadmapSlug}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
href={url}
|
||||
key={resourceId}
|
||||
className="group relative flex flex-row items-center gap-2 rounded-md border border-gray-300 bg-white px-1.5 py-2 text-left text-sm transition-all hover:border-gray-400"
|
||||
>
|
||||
<Bookmark className="size-4 fill-current text-gray-300" />
|
||||
<h4 className="truncate text-gray-900">{resourceTitle}</h4>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { ArrowUpRight, type LucideIcon } from 'lucide-react';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
type DashboardCardLinkProps = {
|
||||
href: string;
|
||||
title: string;
|
||||
icon: LucideIcon;
|
||||
description: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function DashboardCardLink(props: DashboardCardLinkProps) {
|
||||
const { href, title, description, icon: Icon, className } = props;
|
||||
|
||||
return (
|
||||
<a
|
||||
className={cn(
|
||||
'relative mt-4 flex min-h-[220px] flex-col justify-end rounded-lg border border-gray-300 bg-gradient-to-br from-white to-gray-50 py-5 px-6 hover:border-gray-400 hover:from-white hover:to-gray-100',
|
||||
className,
|
||||
)}
|
||||
href={href}
|
||||
>
|
||||
<Icon className="mb-4 size-10 text-gray-300" strokeWidth={1.25} />
|
||||
<h4 className="text-xl font-semibold tracking-wide">{title}</h4>
|
||||
<p className="mt-1 text-gray-500">{description}</p>
|
||||
<ArrowUpRight className="absolute right-3 top-3 size-4" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import { getPercentage } from '../../helper/number';
|
||||
import { getRelativeTimeString } from '../../lib/date';
|
||||
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
|
||||
|
||||
type DashboardCustomProgressCardProps = {
|
||||
progress: UserProgress;
|
||||
};
|
||||
|
||||
export function DashboardCustomProgressCard(props: DashboardCustomProgressCardProps) {
|
||||
const { progress } = props;
|
||||
|
||||
const {
|
||||
resourceType,
|
||||
resourceId,
|
||||
resourceTitle,
|
||||
total: totalCount,
|
||||
done: doneCount,
|
||||
skipped: skippedCount,
|
||||
roadmapSlug,
|
||||
isCustomResource,
|
||||
updatedAt,
|
||||
} = progress;
|
||||
|
||||
let url =
|
||||
resourceType === 'roadmap'
|
||||
? `/${resourceId}`
|
||||
: `/best-practices/${resourceId}`;
|
||||
|
||||
if (isCustomResource) {
|
||||
url = `/r/${roadmapSlug}`;
|
||||
}
|
||||
|
||||
const totalMarked = doneCount + skippedCount;
|
||||
const progressPercentage = getPercentage(totalMarked, totalCount);
|
||||
|
||||
return (
|
||||
<a
|
||||
href={url}
|
||||
className="group relative flex min-h-[80px] w-full flex-col justify-between overflow-hidden rounded-md border bg-white p-3 text-left text-sm shadow-sm transition-all hover:border-gray-400 hover:bg-gray-50"
|
||||
>
|
||||
<h4 className="truncate font-medium text-gray-900">{resourceTitle}</h4>
|
||||
|
||||
<div className="mt-6 flex items-center gap-2">
|
||||
<div className="h-2 w-full overflow-hidden rounded-md bg-black/10">
|
||||
<div
|
||||
className="h-full bg-black/20"
|
||||
style={{ width: `${progressPercentage}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500">
|
||||
{Math.floor(+progressPercentage)}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p className="mt-1 text-xs text-gray-400">
|
||||
{isCustomResource ? (
|
||||
<>Last updated {getRelativeTimeString(updatedAt)}</>
|
||||
) : (
|
||||
<>Last practiced {getRelativeTimeString(updatedAt)}</>
|
||||
)}
|
||||
</p>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $teamList } from '../../stores/team';
|
||||
import type { TeamListResponse } from '../TeamDropdown/TeamDropdown';
|
||||
import { DashboardTab } from './DashboardTab';
|
||||
import { PersonalDashboard, type BuiltInRoadmap } from './PersonalDashboard';
|
||||
import { TeamDashboard } from './TeamDashboard';
|
||||
import { getUser } from '../../lib/jwt';
|
||||
|
||||
type DashboardPageProps = {
|
||||
builtInRoleRoadmaps?: BuiltInRoadmap[];
|
||||
builtInSkillRoadmaps?: BuiltInRoadmap[];
|
||||
builtInBestPractices?: BuiltInRoadmap[];
|
||||
};
|
||||
|
||||
export function DashboardPage(props: DashboardPageProps) {
|
||||
const { builtInRoleRoadmaps, builtInBestPractices, builtInSkillRoadmaps } =
|
||||
props;
|
||||
|
||||
const currentUser = getUser();
|
||||
const toast = useToast();
|
||||
const teamList = useStore($teamList);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [selectedTeamId, setSelectedTeamId] = useState<string>();
|
||||
|
||||
async function getAllTeams() {
|
||||
if (teamList.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { response, error } = await httpGet<TeamListResponse>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-teams`,
|
||||
);
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
return;
|
||||
}
|
||||
|
||||
$teamList.set(response);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getAllTeams().finally(() => setIsLoading(false));
|
||||
}, []);
|
||||
|
||||
const userAvatar =
|
||||
currentUser?.avatar && !isLoading
|
||||
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${currentUser.avatar}`
|
||||
: '/images/default-avatar.png';
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 pb-20 pt-8">
|
||||
<div className="container">
|
||||
<div className="mb-6 flex flex-wrap items-center gap-1.5 sm:mb-8">
|
||||
<DashboardTab
|
||||
label="Personal"
|
||||
isActive={!selectedTeamId}
|
||||
onClick={() => setSelectedTeamId(undefined)}
|
||||
avatar={userAvatar}
|
||||
/>
|
||||
|
||||
{isLoading && (
|
||||
<>
|
||||
<DashboardTabSkeleton />
|
||||
<DashboardTabSkeleton />
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isLoading && (
|
||||
<>
|
||||
{teamList.map((team) => {
|
||||
const { avatar } = team;
|
||||
const avatarUrl = avatar
|
||||
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${avatar}`
|
||||
: '/images/default-avatar.png';
|
||||
return (
|
||||
<DashboardTab
|
||||
key={team._id}
|
||||
label={team.name}
|
||||
isActive={team._id === selectedTeamId}
|
||||
{...(team.status === 'invited'
|
||||
? {
|
||||
href: `/respond-invite?i=${team.memberId}`,
|
||||
}
|
||||
: {
|
||||
href: `/team/activity?t=${team._id}`,
|
||||
// onClick: () => {
|
||||
// setSelectedTeamId(team._id);
|
||||
// },
|
||||
})}
|
||||
avatar={avatarUrl}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<DashboardTab
|
||||
label="+ Create Team"
|
||||
isActive={false}
|
||||
href="/team/new"
|
||||
className="border border-dashed border-gray-300 bg-transparent px-3 text-[13px] text-sm text-gray-500 hover:border-gray-600 hover:text-black"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!selectedTeamId && (
|
||||
<PersonalDashboard
|
||||
builtInRoleRoadmaps={builtInRoleRoadmaps}
|
||||
builtInSkillRoadmaps={builtInSkillRoadmaps}
|
||||
builtInBestPractices={builtInBestPractices}
|
||||
/>
|
||||
)}
|
||||
{selectedTeamId && <TeamDashboard teamId={selectedTeamId} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DashboardTabSkeleton() {
|
||||
return (
|
||||
<div className="h-[30px] w-[114px] animate-pulse rounded-md border bg-white"></div>
|
||||
);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import { getPercentage } from '../../helper/number';
|
||||
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
|
||||
import { ArrowUpRight, ExternalLink } from 'lucide-react';
|
||||
|
||||
type DashboardProgressCardProps = {
|
||||
progress: UserProgress;
|
||||
};
|
||||
|
||||
export function DashboardProgressCard(props: DashboardProgressCardProps) {
|
||||
const { progress } = props;
|
||||
const {
|
||||
resourceType,
|
||||
resourceId,
|
||||
resourceTitle,
|
||||
total: totalCount,
|
||||
done: doneCount,
|
||||
skipped: skippedCount,
|
||||
roadmapSlug,
|
||||
isCustomResource,
|
||||
updatedAt,
|
||||
} = progress;
|
||||
|
||||
let url =
|
||||
resourceType === 'roadmap'
|
||||
? `/${resourceId}`
|
||||
: `/best-practices/${resourceId}`;
|
||||
|
||||
if (isCustomResource) {
|
||||
url = `/r/${roadmapSlug}`;
|
||||
}
|
||||
|
||||
const totalMarked = doneCount + skippedCount;
|
||||
const progressPercentage = getPercentage(totalMarked, totalCount);
|
||||
|
||||
return (
|
||||
<a
|
||||
href={url}
|
||||
key={resourceId}
|
||||
className="group relative flex w-full items-center justify-between overflow-hidden rounded-md border border-gray-300 bg-white px-3 py-2 text-left text-sm transition-all hover:border-gray-400"
|
||||
>
|
||||
<span className="flex-grow truncate">{resourceTitle}</span>
|
||||
<span className="text-xs text-gray-400">
|
||||
{parseInt(progressPercentage, 10)}%
|
||||
</span>
|
||||
|
||||
<span
|
||||
className="absolute left-0 top-0 block h-full cursor-pointer rounded-tl-md bg-black/5 transition-colors group-hover:bg-black/10"
|
||||
style={{
|
||||
width: `${progressPercentage}%`,
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import { Check, CircleCheck, CircleDashed } from 'lucide-react';
|
||||
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
import { getRelativeTimeString } from '../../lib/date.ts';
|
||||
|
||||
type DashboardProjectCardProps = {
|
||||
project: ProjectStatusDocument & {
|
||||
title: string;
|
||||
};
|
||||
};
|
||||
|
||||
export function DashboardProjectCard(props: DashboardProjectCardProps) {
|
||||
const { project } = props;
|
||||
|
||||
const { title, projectId, submittedAt, startedAt, repositoryUrl } = project;
|
||||
|
||||
const url = `/projects/${projectId}`;
|
||||
const status = submittedAt && repositoryUrl ? 'submitted' : 'started';
|
||||
|
||||
return (
|
||||
<a
|
||||
href={url}
|
||||
key={projectId}
|
||||
className="group relative flex w-full items-center gap-2 text-left text-sm underline-offset-2"
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full',
|
||||
{
|
||||
'border border-green-500 bg-green-500 group-hover:border-green-600 group-hover:bg-green-600':
|
||||
status === 'submitted',
|
||||
'border border-dashed border-gray-400 bg-transparent group-hover:border-gray-500':
|
||||
status === 'started',
|
||||
},
|
||||
)}
|
||||
>
|
||||
{status === 'submitted' && (
|
||||
<Check
|
||||
className="relative top-[0.5px] size-3 text-white"
|
||||
strokeWidth={3}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
<span className="flex-grow truncate group-hover:underline">{title.replace(/(System)|(Service)/, '')}</span>
|
||||
<span className="flex-shrink-0 bg-transparent text-xs text-gray-400 no-underline">
|
||||
{!!startedAt &&
|
||||
status === 'started' &&
|
||||
getRelativeTimeString(startedAt)}
|
||||
{!!submittedAt &&
|
||||
status === 'submitted' &&
|
||||
getRelativeTimeString(submittedAt)}
|
||||
</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
type DashboardTabProps = {
|
||||
label: string | ReactNode;
|
||||
isActive: boolean;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
href?: string;
|
||||
avatar?: string;
|
||||
icon?: ReactNode;
|
||||
};
|
||||
|
||||
export function DashboardTab(props: DashboardTabProps) {
|
||||
const { isActive, onClick, label, className, href, avatar, icon } = props;
|
||||
|
||||
const Slot = href ? 'a' : 'button';
|
||||
|
||||
return (
|
||||
<Slot
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
'flex h-[30px] shrink-0 items-center gap-1 rounded-md border bg-white p-1.5 px-2 text-sm leading-none text-gray-600',
|
||||
isActive ? 'border-gray-500 bg-gray-200 text-gray-900' : '',
|
||||
className,
|
||||
)}
|
||||
{...(href ? { href } : {})}
|
||||
>
|
||||
{avatar && (
|
||||
<img
|
||||
src={avatar}
|
||||
alt="avatar"
|
||||
className="h-4 w-4 mr-0.5 rounded-full object-cover"
|
||||
/>
|
||||
)}
|
||||
{icon}
|
||||
{label}
|
||||
</Slot>
|
||||
);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
type EmptyStackMessageProps = {
|
||||
number: number | string;
|
||||
title: string;
|
||||
description: string;
|
||||
buttonText: string;
|
||||
buttonLink: string;
|
||||
bodyClassName?: string;
|
||||
};
|
||||
|
||||
export function EmptyStackMessage(props: EmptyStackMessageProps) {
|
||||
const { number, title, description, buttonText, buttonLink, bodyClassName } =
|
||||
props;
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 flex items-center justify-center rounded-md bg-black/50">
|
||||
<div
|
||||
className={cn(
|
||||
'flex max-w-[200px] flex-col items-center justify-center rounded-md bg-white p-4 shadow-sm',
|
||||
bodyClassName,
|
||||
)}
|
||||
>
|
||||
<span className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-300 text-white">
|
||||
{number}
|
||||
</span>
|
||||
<div className="my-3 text-center">
|
||||
<h3 className="text-sm font-medium text-black">{title}</h3>
|
||||
<p className="text-center text-xs text-gray-500">{description}</p>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href={buttonLink}
|
||||
className="rounded-md bg-black px-3 py-1 text-xs text-white transition-transform hover:scale-105 hover:bg-gray-900"
|
||||
>
|
||||
{buttonText}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
|
||||
import { DashboardCustomProgressCard } from './DashboardCustomProgressCard';
|
||||
import { DashboardCardLink } from './DashboardCardLink';
|
||||
import { useState } from 'react';
|
||||
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal';
|
||||
import { Simulate } from 'react-dom/test-utils';
|
||||
import {
|
||||
ArrowUpRight,
|
||||
Bot,
|
||||
BrainCircuit,
|
||||
Map,
|
||||
PencilRuler,
|
||||
} from 'lucide-react';
|
||||
|
||||
type ListDashboardCustomProgressProps = {
|
||||
progresses: UserProgress[];
|
||||
isLoading?: boolean;
|
||||
isCustomResources?: boolean;
|
||||
isAIGeneratedRoadmaps?: boolean;
|
||||
};
|
||||
|
||||
export function ListDashboardCustomProgress(
|
||||
props: ListDashboardCustomProgressProps,
|
||||
) {
|
||||
const {
|
||||
progresses,
|
||||
isLoading = false,
|
||||
isAIGeneratedRoadmaps = false,
|
||||
} = props;
|
||||
const [isCreateCustomRoadmapModalOpen, setIsCreateCustomRoadmapModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const customRoadmapModal = isCreateCustomRoadmapModalOpen ? (
|
||||
<CreateRoadmapModal
|
||||
onClose={() => setIsCreateCustomRoadmapModalOpen(false)}
|
||||
onCreated={(roadmap) => {
|
||||
window.location.href = `${
|
||||
import.meta.env.PUBLIC_EDITOR_APP_URL
|
||||
}/${roadmap?._id}`;
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{customRoadmapModal}
|
||||
|
||||
<div className="mb-2 mt-6 flex items-center justify-between gap-2">
|
||||
<h2 className="text-xs uppercase text-gray-400">
|
||||
{isAIGeneratedRoadmaps ? 'My AI Roadmaps' : 'My Custom Roadmaps'}
|
||||
</h2>
|
||||
|
||||
<a
|
||||
href="/community"
|
||||
className="rounded-full bg-gray-200 px-2.5 py-0.5 text-xs font-medium text-gray-700 hover:bg-gray-300 hover:text-black"
|
||||
>
|
||||
Community Roadmaps
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{!isLoading && progresses.length === 0 && isAIGeneratedRoadmaps && (
|
||||
<DashboardCardLink
|
||||
className="mt-0"
|
||||
icon={BrainCircuit}
|
||||
href="/ai"
|
||||
title="Generate Roadmaps with AI"
|
||||
description="You can generate your own roadmap with AI"
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isLoading && progresses.length === 0 && !isAIGeneratedRoadmaps && (
|
||||
<DashboardCardLink
|
||||
className="mt-0"
|
||||
icon={PencilRuler}
|
||||
href="https://draw.roadmap.sh"
|
||||
title="Draw your own Roadmap"
|
||||
description="Use our editor to draw your own roadmap"
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-4">
|
||||
{isLoading && (
|
||||
<>
|
||||
{Array.from({ length: 8 }).map((_, index) => (
|
||||
<CustomProgressCardSkeleton key={index} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isLoading && progresses.length > 0 && (
|
||||
<>
|
||||
{progresses.map((progress) => (
|
||||
<DashboardCustomProgressCard
|
||||
key={progress.resourceId}
|
||||
progress={progress}
|
||||
/>
|
||||
))}
|
||||
|
||||
<a
|
||||
className="flex min-h-[80px] items-center justify-center rounded-lg border border-dashed border-gray-300 bg-white p-4 text-sm font-medium text-gray-500 hover:bg-gray-50 hover:text-gray-600"
|
||||
href={'/ai'}
|
||||
onClick={(e) => {
|
||||
if (!isAIGeneratedRoadmaps) {
|
||||
e.preventDefault();
|
||||
setIsCreateCustomRoadmapModalOpen(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isAIGeneratedRoadmaps ? '+ Generate New' : '+ Create New'}
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type CustomProgressCardSkeletonProps = {};
|
||||
|
||||
export function CustomProgressCardSkeleton(
|
||||
props: CustomProgressCardSkeletonProps,
|
||||
) {
|
||||
return (
|
||||
<div className="h-[106px] w-full animate-pulse rounded-md bg-gray-200" />
|
||||
);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
type LoadingProgressProps = {};
|
||||
|
||||
export function LoadingProgress(props: LoadingProgressProps) {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-3">
|
||||
{Array.from({ length: 6 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="h-[38px] w-full animate-pulse rounded-md border border-gray-300 bg-gray-100"
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,395 +0,0 @@
|
||||
import { type JSXElementConstructor, useEffect, useState } from 'react';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
|
||||
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
|
||||
import type { PageType } from '../CommandMenu/CommandMenu';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { getCurrentPeriod } from '../../lib/date';
|
||||
import { ListDashboardCustomProgress } from './ListDashboardCustomProgress';
|
||||
import { RecommendedRoadmaps } from './RecommendedRoadmaps';
|
||||
import { ProgressStack } from './ProgressStack';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $accountStreak, type StreakResponse } from '../../stores/streak';
|
||||
import { CheckEmoji } from '../ReactIcons/CheckEmoji.tsx';
|
||||
import { ConstructionEmoji } from '../ReactIcons/ConstructionEmoji.tsx';
|
||||
import { BookEmoji } from '../ReactIcons/BookEmoji.tsx';
|
||||
import { DashboardAiRoadmaps } from './DashboardAiRoadmaps.tsx';
|
||||
import type { AllowedProfileVisibility } from '../../api/user.ts';
|
||||
import { PencilIcon, type LucideIcon } from 'lucide-react';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
|
||||
type UserDashboardResponse = {
|
||||
name: string;
|
||||
email: string;
|
||||
avatar: string;
|
||||
headline: string;
|
||||
username: string;
|
||||
profileVisibility: AllowedProfileVisibility;
|
||||
progresses: UserProgress[];
|
||||
projects: ProjectStatusDocument[];
|
||||
aiRoadmaps: {
|
||||
id: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
}[];
|
||||
topicDoneToday: number;
|
||||
};
|
||||
|
||||
export type BuiltInRoadmap = {
|
||||
id: string;
|
||||
url: string;
|
||||
title: string;
|
||||
description: string;
|
||||
isFavorite?: boolean;
|
||||
relatedRoadmapIds?: string[];
|
||||
};
|
||||
|
||||
type PersonalDashboardProps = {
|
||||
builtInRoleRoadmaps?: BuiltInRoadmap[];
|
||||
builtInSkillRoadmaps?: BuiltInRoadmap[];
|
||||
builtInBestPractices?: BuiltInRoadmap[];
|
||||
};
|
||||
|
||||
export function PersonalDashboard(props: PersonalDashboardProps) {
|
||||
const {
|
||||
builtInRoleRoadmaps = [],
|
||||
builtInBestPractices = [],
|
||||
builtInSkillRoadmaps = [],
|
||||
} = props;
|
||||
|
||||
const toast = useToast();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [personalDashboardDetails, setPersonalDashboardDetails] =
|
||||
useState<UserDashboardResponse>();
|
||||
const [projectDetails, setProjectDetails] = useState<PageType[]>([]);
|
||||
const accountStreak = useStore($accountStreak);
|
||||
|
||||
const loadAccountStreak = async () => {
|
||||
if (accountStreak) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
const { response, error } = await httpGet<StreakResponse>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-streak`,
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Failed to load account streak');
|
||||
return;
|
||||
}
|
||||
|
||||
$accountStreak.set(response);
|
||||
};
|
||||
|
||||
async function loadProgress() {
|
||||
const { response: progressList, error } =
|
||||
await httpGet<UserDashboardResponse>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-user-dashboard`,
|
||||
);
|
||||
|
||||
if (error || !progressList) {
|
||||
return;
|
||||
}
|
||||
|
||||
progressList?.progresses?.forEach((progress) => {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('mark-favorite', {
|
||||
detail: {
|
||||
resourceId: progress.resourceId,
|
||||
resourceType: progress.resourceType,
|
||||
isFavorite: progress.isFavorite,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
setPersonalDashboardDetails(progressList);
|
||||
}
|
||||
|
||||
async function loadAllProjectDetails() {
|
||||
const { error, response } = await httpGet<PageType[]>(`/pages.json`);
|
||||
|
||||
if (error) {
|
||||
toast.error(error.message || 'Something went wrong');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const allProjects = response.filter((page) => page.group === 'Projects');
|
||||
setProjectDetails(allProjects);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
Promise.allSettled([
|
||||
loadProgress(),
|
||||
loadAllProjectDetails(),
|
||||
loadAccountStreak(),
|
||||
]).finally(() => setIsLoading(false));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('refresh-favorites', loadProgress);
|
||||
return () => window.removeEventListener('refresh-favorites', loadProgress);
|
||||
}, []);
|
||||
|
||||
const learningRoadmapsToShow = (personalDashboardDetails?.progresses || [])
|
||||
.filter((progress) => !progress.isCustomResource)
|
||||
.sort((a, b) => {
|
||||
const updatedAtA = new Date(a.updatedAt);
|
||||
const updatedAtB = new Date(b.updatedAt);
|
||||
|
||||
if (a.isFavorite && !b.isFavorite) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!a.isFavorite && b.isFavorite) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return updatedAtB.getTime() - updatedAtA.getTime();
|
||||
});
|
||||
|
||||
const aiGeneratedRoadmaps = personalDashboardDetails?.aiRoadmaps || [];
|
||||
const customRoadmaps = (personalDashboardDetails?.progresses || [])
|
||||
.filter((progress) => progress.isCustomResource)
|
||||
.sort((a, b) => {
|
||||
const updatedAtA = new Date(a.updatedAt);
|
||||
const updatedAtB = new Date(b.updatedAt);
|
||||
return updatedAtB.getTime() - updatedAtA.getTime();
|
||||
});
|
||||
|
||||
const { avatar, name } = personalDashboardDetails || {};
|
||||
const avatarLink = avatar
|
||||
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${avatar}`
|
||||
: '/images/default-avatar.png';
|
||||
|
||||
const allRoadmapsAndBestPractices = [
|
||||
...builtInRoleRoadmaps,
|
||||
...builtInSkillRoadmaps,
|
||||
...builtInBestPractices,
|
||||
];
|
||||
|
||||
const relatedRoadmapIds = allRoadmapsAndBestPractices
|
||||
// take the ones that user is learning
|
||||
.filter((roadmap) =>
|
||||
learningRoadmapsToShow?.some(
|
||||
(learningRoadmap) => learningRoadmap.resourceId === roadmap.id,
|
||||
),
|
||||
)
|
||||
.flatMap((roadmap) => roadmap.relatedRoadmapIds)
|
||||
// remove the ones that user is already learning or has bookmarked
|
||||
.filter(
|
||||
(roadmapId) =>
|
||||
!learningRoadmapsToShow.some((lr) => lr.resourceId === roadmapId),
|
||||
);
|
||||
|
||||
const recommendedRoadmapIds = new Set(
|
||||
relatedRoadmapIds.length === 0
|
||||
? [
|
||||
'frontend',
|
||||
'backend',
|
||||
'devops',
|
||||
'ai-data-scientist',
|
||||
'full-stack',
|
||||
'api-design',
|
||||
]
|
||||
: relatedRoadmapIds,
|
||||
);
|
||||
|
||||
const recommendedRoadmaps = allRoadmapsAndBestPractices.filter((roadmap) =>
|
||||
recommendedRoadmapIds.has(roadmap.id),
|
||||
);
|
||||
|
||||
const enrichedProjects = personalDashboardDetails?.projects
|
||||
.map((project) => {
|
||||
const projectDetail = projectDetails.find(
|
||||
(page) => page.id === project.projectId,
|
||||
);
|
||||
|
||||
return {
|
||||
...project,
|
||||
title: projectDetail?.title || 'N/A',
|
||||
};
|
||||
})
|
||||
.sort((a, b) => {
|
||||
if (a.repositoryUrl && !b.repositoryUrl) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!a.repositoryUrl && b.repositoryUrl) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
const { username } = personalDashboardDetails || {};
|
||||
|
||||
return (
|
||||
<section>
|
||||
{isLoading ? (
|
||||
<div className="h-7 w-1/4 animate-pulse rounded-lg bg-gray-200"></div>
|
||||
) : (
|
||||
<div className="flex flex-col items-start justify-between gap-1 sm:flex-row sm:items-center">
|
||||
<h2 className="text-lg font-medium">
|
||||
Hi {name}, good {getCurrentPeriod()}!
|
||||
</h2>
|
||||
<a
|
||||
href="/home"
|
||||
className="rounded-full bg-gray-200 px-2.5 py-1 text-xs font-medium text-gray-700 hover:bg-gray-300 hover:text-black"
|
||||
>
|
||||
Visit Homepage
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-4">
|
||||
{isLoading ? (
|
||||
<>
|
||||
<DashboardCardSkeleton />
|
||||
<DashboardCardSkeleton />
|
||||
<DashboardCardSkeleton />
|
||||
<DashboardCardSkeleton />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<DashboardCard
|
||||
imgUrl={avatarLink}
|
||||
title={name!}
|
||||
description={
|
||||
username ? 'View your profile' : 'Setup your profile'
|
||||
}
|
||||
href={username ? `/u/${username}` : '/account/update-profile'}
|
||||
{...(username && {
|
||||
externalLinkIcon: PencilIcon,
|
||||
externalLinkHref: '/account/update-profile',
|
||||
externalLinkText: 'Edit',
|
||||
})}
|
||||
className={
|
||||
!username
|
||||
? 'border-dashed border-gray-500 bg-gray-100 hover:border-gray-500 hover:bg-gray-200'
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
|
||||
<DashboardCard
|
||||
icon={BookEmoji}
|
||||
title="Visit Roadmaps"
|
||||
description="Learn new skills"
|
||||
href="/roadmaps"
|
||||
/>
|
||||
|
||||
<DashboardCard
|
||||
icon={ConstructionEmoji}
|
||||
title="Build Projects"
|
||||
description="Practice what you learn"
|
||||
href="/projects"
|
||||
/>
|
||||
<DashboardCard
|
||||
icon={CheckEmoji}
|
||||
title="Best Practices"
|
||||
description="Do things the right way"
|
||||
href="/best-practices"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ProgressStack
|
||||
progresses={learningRoadmapsToShow}
|
||||
projects={enrichedProjects || []}
|
||||
isLoading={isLoading}
|
||||
accountStreak={accountStreak}
|
||||
topicDoneToday={personalDashboardDetails?.topicDoneToday || 0}
|
||||
/>
|
||||
|
||||
<ListDashboardCustomProgress
|
||||
progresses={customRoadmaps}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
<DashboardAiRoadmaps
|
||||
roadmaps={aiGeneratedRoadmaps}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
<RecommendedRoadmaps
|
||||
roadmaps={recommendedRoadmaps}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
type DashboardCardProps = {
|
||||
icon?: JSXElementConstructor<any>;
|
||||
imgUrl?: string;
|
||||
title: string;
|
||||
description: string;
|
||||
href: string;
|
||||
externalLinkIcon?: LucideIcon;
|
||||
externalLinkText?: string;
|
||||
externalLinkHref?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function DashboardCard(props: DashboardCardProps) {
|
||||
const {
|
||||
icon: Icon,
|
||||
imgUrl,
|
||||
title,
|
||||
description,
|
||||
href,
|
||||
externalLinkHref,
|
||||
externalLinkIcon: ExternalLinkIcon,
|
||||
externalLinkText,
|
||||
className,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'relative overflow-hidden',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<a href={href} className="flex flex-col rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50">
|
||||
{Icon && (
|
||||
<div className="px-4 pb-3 pt-4">
|
||||
<Icon className="size-6" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{imgUrl && (
|
||||
<div className="px-4 pb-1.5 pt-3.5">
|
||||
<img src={imgUrl} alt={title} className="size-8 rounded-full" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex grow flex-col justify-center gap-0.5 p-4">
|
||||
<h3 className="truncate font-medium text-black">{title}</h3>
|
||||
<p className="text-xs text-black">{description}</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
{externalLinkHref && (
|
||||
<a
|
||||
href={externalLinkHref}
|
||||
className="absolute right-1 top-1 flex items-center gap-1.5 rounded-md bg-gray-200 p-1 px-2 text-xs text-gray-600 hover:bg-gray-300 hover:text-black"
|
||||
>
|
||||
{ExternalLinkIcon && <ExternalLinkIcon className="size-3" />}
|
||||
{externalLinkText}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DashboardCardSkeleton() {
|
||||
return (
|
||||
<div className="h-[128px] animate-pulse rounded-lg border border-gray-300 bg-white"></div>
|
||||
);
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
import {
|
||||
ArrowUpRight,
|
||||
Bookmark,
|
||||
FolderKanban,
|
||||
type LucideIcon,
|
||||
Map,
|
||||
} from 'lucide-react';
|
||||
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
|
||||
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
|
||||
import { DashboardBookmarkCard } from './DashboardBookmarkCard';
|
||||
import { DashboardProjectCard } from './DashboardProjectCard';
|
||||
import { useState } from 'react';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { DashboardProgressCard } from './DashboardProgressCard';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $accountStreak, type StreakResponse } from '../../stores/streak';
|
||||
import { EmptyStackMessage } from './EmptyStackMessage.tsx';
|
||||
|
||||
type ProgressStackProps = {
|
||||
progresses: UserProgress[];
|
||||
projects: (ProjectStatusDocument & {
|
||||
title: string;
|
||||
})[];
|
||||
accountStreak?: StreakResponse;
|
||||
isLoading: boolean;
|
||||
topicDoneToday: number;
|
||||
};
|
||||
|
||||
const MAX_PROGRESS_TO_SHOW = 11;
|
||||
const MAX_PROJECTS_TO_SHOW = 8;
|
||||
|
||||
type ProgressLaneProps = {
|
||||
title: string;
|
||||
linkText?: string;
|
||||
linkHref?: string;
|
||||
isLoading?: boolean;
|
||||
isEmpty?: boolean;
|
||||
loadingWrapperClassName?: string;
|
||||
loadingSkeletonCount?: number;
|
||||
loadingSkeletonClassName?: string;
|
||||
children: React.ReactNode;
|
||||
emptyMessage?: string;
|
||||
emptyIcon?: LucideIcon;
|
||||
emptyLinkText?: string;
|
||||
emptyLinkHref?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function ProgressLane(props: ProgressLaneProps) {
|
||||
const {
|
||||
title,
|
||||
linkText,
|
||||
linkHref,
|
||||
isLoading = false,
|
||||
loadingWrapperClassName = '',
|
||||
loadingSkeletonCount = 4,
|
||||
loadingSkeletonClassName = '',
|
||||
children,
|
||||
isEmpty = false,
|
||||
emptyIcon: EmptyIcon = Map,
|
||||
emptyMessage = `No ${title.toLowerCase()} to show`,
|
||||
emptyLinkHref = '/roadmaps',
|
||||
emptyLinkText = 'Explore',
|
||||
className,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-full flex-col rounded-md border bg-white px-4 py-3 shadow-sm',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{isLoading && (
|
||||
<div className={'flex flex-row justify-between'}>
|
||||
<div className="h-[16px] w-[75px] animate-pulse rounded-md bg-gray-100"></div>
|
||||
</div>
|
||||
)}
|
||||
{!isLoading && !isEmpty && (
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<h3 className="text-xs uppercase text-gray-500">{title}</h3>
|
||||
|
||||
{linkText && linkHref && (
|
||||
<a
|
||||
href={linkHref}
|
||||
className="flex items-center gap-1 text-xs text-gray-500 hover:text-black"
|
||||
>
|
||||
<ArrowUpRight size={12} />
|
||||
{linkText}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-4 flex flex-grow flex-col gap-1.5">
|
||||
{isLoading && (
|
||||
<div
|
||||
className={cn('grid grid-cols-2 gap-2', loadingWrapperClassName)}
|
||||
>
|
||||
{Array.from({ length: loadingSkeletonCount }).map((_, index) => (
|
||||
<CardSkeleton key={index} className={loadingSkeletonClassName} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{!isLoading && children}
|
||||
|
||||
{!isLoading && isEmpty && (
|
||||
<div className="flex flex-grow flex-col items-center justify-center text-gray-500">
|
||||
<EmptyIcon
|
||||
size={37}
|
||||
strokeWidth={1.5}
|
||||
className={'mb-3 text-gray-200'}
|
||||
/>
|
||||
<span className="mb-0.5 text-sm">{emptyMessage}</span>
|
||||
<a
|
||||
href={emptyLinkHref}
|
||||
className="text-xs font-medium text-gray-600 underline-offset-2 hover:underline"
|
||||
>
|
||||
{emptyLinkText}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ProgressStack(props: ProgressStackProps) {
|
||||
const { progresses, projects, isLoading, accountStreak, topicDoneToday } =
|
||||
props;
|
||||
|
||||
const [showAllProgresses, setShowAllProgresses] = useState(false);
|
||||
const sortedProgresses = progresses.sort((a, b) => {
|
||||
if (a.isFavorite && !b.isFavorite) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!a.isFavorite && b.isFavorite) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
const userProgressesToShow = showAllProgresses
|
||||
? sortedProgresses
|
||||
: sortedProgresses.slice(0, MAX_PROGRESS_TO_SHOW);
|
||||
|
||||
const [showAllProjects, setShowAllProjects] = useState(false);
|
||||
const projectsToShow = showAllProjects
|
||||
? projects
|
||||
: projects.slice(0, MAX_PROJECTS_TO_SHOW);
|
||||
|
||||
const totalProjectFinished = projects.filter(
|
||||
(project) => project.repositoryUrl,
|
||||
).length;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mt-2 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3">
|
||||
<StatsCard
|
||||
title="Current Streak"
|
||||
value={accountStreak?.count || 0}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<StatsCard
|
||||
title="Topics Done Today"
|
||||
value={topicDoneToday}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<StatsCard
|
||||
title="Projects Finished"
|
||||
value={totalProjectFinished}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-2 grid min-h-[330px] grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3">
|
||||
<div className="relative col-span-2">
|
||||
{!isLoading && userProgressesToShow.length === 0 && (
|
||||
<EmptyStackMessage
|
||||
number={1}
|
||||
title={'Bookmark some Roadmaps'}
|
||||
description={
|
||||
'Bookmark some roadmaps to access them quickly and start updating your progress'
|
||||
}
|
||||
buttonText={'Explore Roadmaps'}
|
||||
buttonLink={'/roadmaps'}
|
||||
bodyClassName="max-w-[280px]"
|
||||
/>
|
||||
)}
|
||||
|
||||
<ProgressLane
|
||||
title="Progress & Bookmarks"
|
||||
isLoading={isLoading}
|
||||
loadingSkeletonCount={MAX_PROGRESS_TO_SHOW}
|
||||
linkHref="/roadmaps"
|
||||
linkText="Roadmaps"
|
||||
isEmpty={userProgressesToShow.length === 0}
|
||||
emptyIcon={Bookmark}
|
||||
emptyMessage={'No bookmarks to show'}
|
||||
emptyLinkHref={'/roadmaps'}
|
||||
emptyLinkText={'Explore Roadmaps'}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{userProgressesToShow.length > 0 && (
|
||||
<>
|
||||
{userProgressesToShow.map((progress) => {
|
||||
const isFavorite =
|
||||
progress.isFavorite &&
|
||||
!progress.done &&
|
||||
!progress.skipped;
|
||||
|
||||
if (isFavorite) {
|
||||
return (
|
||||
<DashboardBookmarkCard
|
||||
key={progress.resourceId}
|
||||
bookmark={progress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DashboardProgressCard
|
||||
key={progress.resourceId}
|
||||
progress={progress}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
|
||||
{sortedProgresses.length > MAX_PROGRESS_TO_SHOW && (
|
||||
<ShowAllButton
|
||||
showAll={showAllProgresses}
|
||||
setShowAll={setShowAllProgresses}
|
||||
count={sortedProgresses.length}
|
||||
maxCount={MAX_PROGRESS_TO_SHOW}
|
||||
className="min-h-[38px] rounded-md border border-dashed leading-none"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ProgressLane>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<ProgressLane
|
||||
title={'Projects'}
|
||||
linkHref={'/projects'}
|
||||
linkText={'Projects'}
|
||||
isLoading={isLoading}
|
||||
loadingWrapperClassName="grid-cols-1"
|
||||
loadingSkeletonClassName={'h-5'}
|
||||
loadingSkeletonCount={8}
|
||||
isEmpty={projectsToShow.length === 0}
|
||||
emptyMessage={'No projects started'}
|
||||
emptyIcon={FolderKanban}
|
||||
emptyLinkText={'Explore Projects'}
|
||||
emptyLinkHref={'/projects'}
|
||||
>
|
||||
{!isLoading && projectsToShow.length === 0 && (
|
||||
<EmptyStackMessage
|
||||
number={2}
|
||||
title={'Build your first project'}
|
||||
description={'Pick a project to practice and start building'}
|
||||
buttonText={'Explore Projects'}
|
||||
buttonLink={'/projects'}
|
||||
/>
|
||||
)}
|
||||
|
||||
{projectsToShow.map((project) => {
|
||||
return (
|
||||
<DashboardProjectCard
|
||||
key={project.projectId}
|
||||
project={project}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{projects.length > MAX_PROJECTS_TO_SHOW && (
|
||||
<ShowAllButton
|
||||
showAll={showAllProjects}
|
||||
setShowAll={setShowAllProjects}
|
||||
count={projects.length}
|
||||
maxCount={MAX_PROJECTS_TO_SHOW}
|
||||
className="mb-0.5 mt-3"
|
||||
/>
|
||||
)}
|
||||
</ProgressLane>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type ShowAllButtonProps = {
|
||||
showAll: boolean;
|
||||
setShowAll: (showAll: boolean) => void;
|
||||
count: number;
|
||||
maxCount: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function ShowAllButton(props: ShowAllButtonProps) {
|
||||
const { showAll, setShowAll, count, maxCount, className } = props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
'flex w-full items-center justify-center text-sm text-gray-500 hover:text-gray-700',
|
||||
className,
|
||||
)}
|
||||
onClick={() => setShowAll(!showAll)}
|
||||
>
|
||||
{!showAll ? <>+ show {count - maxCount} more</> : <>- show less</>}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
type CardSkeletonProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function CardSkeleton(props: CardSkeletonProps) {
|
||||
const { className } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'h-[38px] w-full animate-pulse rounded-md bg-gray-100',
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
type StatsCardProps = {
|
||||
title: string;
|
||||
value: number;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
function StatsCard(props: StatsCardProps) {
|
||||
const { title, value, isLoading = false } = props;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1 rounded-md border bg-white p-4 shadow-sm">
|
||||
<h3 className="mb-1 text-xs uppercase text-gray-500">{title}</h3>
|
||||
{isLoading ? (
|
||||
<CardSkeleton className="h-8" />
|
||||
) : (
|
||||
<span className="text-2xl font-medium text-black">{value}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
import type { BuiltInRoadmap } from './PersonalDashboard';
|
||||
import { ArrowUpRight } from 'lucide-react';
|
||||
import { MarkFavorite } from '../FeaturedItems/MarkFavorite.tsx';
|
||||
|
||||
type RecommendedRoadmapsProps = {
|
||||
roadmaps: BuiltInRoadmap[];
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
export function RecommendedRoadmaps(props: RecommendedRoadmapsProps) {
|
||||
const { roadmaps: roadmapsToShow, isLoading } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-2 mt-8 flex items-center justify-between gap-2">
|
||||
<h2 className="text-xs uppercase text-gray-400">
|
||||
Recommended Roadmaps
|
||||
</h2>
|
||||
|
||||
<a
|
||||
href="/roadmaps"
|
||||
className="rounded-full bg-gray-200 px-2.5 py-0.5 text-xs font-medium text-gray-700 hover:bg-gray-300 hover:text-black"
|
||||
>
|
||||
All Roadmaps
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{isLoading ? (
|
||||
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2 md:grid-cols-3">
|
||||
{Array.from({ length: 9 }).map((_, index) => (
|
||||
<RecommendedCardSkeleton key={index} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2 md:grid-cols-3">
|
||||
{roadmapsToShow.map((roadmap) => (
|
||||
<RecommendedRoadmapCard key={roadmap.id} roadmap={roadmap} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-6 text-sm text-gray-500">
|
||||
Need some help getting started? Check out our{' '}<a href="/get-started" className="text-blue-600 underline">Getting Started Guide</a>.
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type RecommendedRoadmapCardProps = {
|
||||
roadmap: BuiltInRoadmap;
|
||||
};
|
||||
|
||||
export function RecommendedRoadmapCard(props: RecommendedRoadmapCardProps) {
|
||||
const { roadmap } = props;
|
||||
const { title, url, description } = roadmap;
|
||||
|
||||
return (
|
||||
<a
|
||||
href={url}
|
||||
className="font-regular text-sm sm:text-sm group relative block rounded-lg border border-gray-200 bg-white px-2.5 py-2 text-black no-underline hover:border-gray-400 hover:bg-gray-50"
|
||||
>
|
||||
<MarkFavorite className={'opacity-30'} resourceType={'roadmap'} resourceId={roadmap.id} />
|
||||
{title}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
function RecommendedCardSkeleton() {
|
||||
return (
|
||||
<div className="h-[38px] w-full animate-pulse rounded-md bg-gray-200" />
|
||||
);
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { TeamMember } from '../TeamProgress/TeamProgressPage';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { getUser } from '../../lib/jwt';
|
||||
import { LoadingProgress } from './LoadingProgress';
|
||||
import { ResourceProgress } from '../Activity/ResourceProgress';
|
||||
import { TeamActivityPage } from '../TeamActivity/TeamActivityPage';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { Tooltip } from '../Tooltip';
|
||||
|
||||
type TeamDashboardProps = {
|
||||
teamId: string;
|
||||
};
|
||||
|
||||
export function TeamDashboard(props: TeamDashboardProps) {
|
||||
const { teamId } = props;
|
||||
|
||||
const toast = useToast();
|
||||
const currentUser = getUser();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [teamMembers, setTeamMembers] = useState<TeamMember[]>([]);
|
||||
|
||||
async function getTeamProgress() {
|
||||
const { response, error } = await httpGet<TeamMember[]>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-team-progress/${teamId}`,
|
||||
);
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Failed to get team progress');
|
||||
return;
|
||||
}
|
||||
|
||||
setTeamMembers(
|
||||
response.sort((a, b) => {
|
||||
if (a.email === currentUser?.email) {
|
||||
return -1;
|
||||
}
|
||||
if (b.email === currentUser?.email) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!teamId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setTeamMembers([]);
|
||||
getTeamProgress().finally(() => setIsLoading(false));
|
||||
}, [teamId]);
|
||||
|
||||
if (!currentUser) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const currentMember = teamMembers.find(
|
||||
(member) => member.email === currentUser.email,
|
||||
);
|
||||
const learningRoadmapsToShow =
|
||||
currentMember?.progress?.filter(
|
||||
(progress) => progress.resourceType === 'roadmap',
|
||||
) || [];
|
||||
|
||||
const allMembersWithoutCurrentUser = teamMembers.sort((a, b) => {
|
||||
if (a.email === currentUser.email) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (b.email === currentUser.email) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return (
|
||||
<section className="mt-8">
|
||||
<h2 className="mb-3 text-xs uppercase text-gray-400">Roadmaps</h2>
|
||||
{isLoading && <LoadingProgress />}
|
||||
{!isLoading && learningRoadmapsToShow.length > 0 && (
|
||||
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-3">
|
||||
{learningRoadmapsToShow.map((roadmap) => {
|
||||
const learningCount = roadmap.learning || 0;
|
||||
const doneCount = roadmap.done || 0;
|
||||
const totalCount = roadmap.total || 0;
|
||||
const skippedCount = roadmap.skipped || 0;
|
||||
|
||||
return (
|
||||
<ResourceProgress
|
||||
key={roadmap.resourceId}
|
||||
isCustomResource={roadmap?.isCustomResource || false}
|
||||
doneCount={doneCount > totalCount ? totalCount : doneCount}
|
||||
learningCount={
|
||||
learningCount > totalCount ? totalCount : learningCount
|
||||
}
|
||||
totalCount={totalCount}
|
||||
skippedCount={skippedCount}
|
||||
resourceId={roadmap.resourceId}
|
||||
resourceType="roadmap"
|
||||
updatedAt={roadmap.updatedAt}
|
||||
title={roadmap.resourceTitle}
|
||||
showActions={false}
|
||||
roadmapSlug={roadmap.roadmapSlug}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h2 className="mb-3 mt-6 text-xs uppercase text-gray-400">
|
||||
Team Members
|
||||
</h2>
|
||||
{isLoading && <TeamMemberLoading className="mb-6" />}
|
||||
{!isLoading && (
|
||||
<div className="mb-6 flex flex-wrap gap-2">
|
||||
{allMembersWithoutCurrentUser.map((member) => {
|
||||
const avatar = member?.avatar
|
||||
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${member.avatar}`
|
||||
: '/images/default-avatar.png';
|
||||
return (
|
||||
<span className="group relative" key={member.email}>
|
||||
<figure className="relative aspect-square size-8 overflow-hidden rounded-md bg-gray-100">
|
||||
<img
|
||||
src={avatar}
|
||||
alt={member.name || ''}
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
</figure>
|
||||
<Tooltip position="top-center" additionalClass="text-sm">
|
||||
{member.name}
|
||||
</Tooltip>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<TeamActivityPage teamId={teamId} />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
type TeamMemberLoadingProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function TeamMemberLoading(props: TeamMemberLoadingProps) {
|
||||
const { className } = props;
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-wrap gap-2', className)}>
|
||||
{Array.from({ length: 15 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="size-8 animate-pulse rounded-md bg-gray-200"
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ export function FeatureAnnouncement(props: FeatureAnnouncementProps) {
|
||||
</span>
|
||||
Projects are live on the{' '}
|
||||
<a
|
||||
href={'/projects'}
|
||||
href={'/backend/projects'}
|
||||
className="font-medium text-blue-500 underline underline-offset-2"
|
||||
>
|
||||
backend roadmap
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { MouseEvent } from 'react';
|
||||
import type { MouseEvent } from "react";
|
||||
import { httpPatch } from '../../lib/http';
|
||||
import type { ResourceType } from '../../lib/resource-progress';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
@@ -7,7 +7,6 @@ import { showLoginPopup } from '../../lib/popup';
|
||||
import { FavoriteIcon } from './FavoriteIcon';
|
||||
import { Spinner } from '../ReactIcons/Spinner';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
type MarkFavoriteType = {
|
||||
resourceType: ResourceType;
|
||||
@@ -28,9 +27,7 @@ export function MarkFavorite({
|
||||
const toast = useToast();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isFavorite, setIsFavorite] = useState(
|
||||
isAuthenticated
|
||||
? (favorite ?? localStorage.getItem(localStorageKey) === '1')
|
||||
: false,
|
||||
isAuthenticated ? (favorite ?? localStorage.getItem(localStorageKey) === '1') : false
|
||||
);
|
||||
|
||||
async function toggleFavoriteHandler(e: MouseEvent<HTMLButtonElement>) {
|
||||
@@ -51,7 +48,7 @@ export function MarkFavorite({
|
||||
{
|
||||
resourceType,
|
||||
resourceId,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (error) {
|
||||
@@ -71,7 +68,7 @@ export function MarkFavorite({
|
||||
resourceType,
|
||||
isFavorite: !isFavorite,
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
window.dispatchEvent(new CustomEvent('refresh-favorites', {}));
|
||||
@@ -102,18 +99,11 @@ export function MarkFavorite({
|
||||
aria-label={isFavorite ? 'Remove from favorites' : 'Add to favorites'}
|
||||
onClick={toggleFavoriteHandler}
|
||||
tabIndex={-1}
|
||||
className={cn(
|
||||
'absolute right-1.5 top-1.5 z-30 focus:outline-0',
|
||||
isFavorite ? '' : 'opacity-30 hover:opacity-100',
|
||||
className,
|
||||
)}
|
||||
data-is-favorite={isFavorite}
|
||||
className={`${isFavorite ? '' : 'opacity-30 hover:opacity-100'} ${
|
||||
className || 'absolute right-1.5 top-1.5 z-30 focus:outline-0'
|
||||
}`}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Spinner isDualRing={false} />
|
||||
) : (
|
||||
<FavoriteIcon isFavorite={isFavorite} />
|
||||
)}
|
||||
{isLoading ? <Spinner isDualRing={false} /> : <FavoriteIcon isFavorite={isFavorite} />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,9 +11,7 @@ export function ProgressNudge(props: ProgressNudgeProps) {
|
||||
const $totalRoadmapNodes = useStore(totalRoadmapNodes);
|
||||
const $roadmapProgress = useStore(roadmapProgress);
|
||||
|
||||
const done =
|
||||
($roadmapProgress?.done?.length || 0) +
|
||||
($roadmapProgress?.skipped?.length || 0);
|
||||
const done = $roadmapProgress?.done?.length || 0;
|
||||
|
||||
const hasProgress = done > 0;
|
||||
|
||||
@@ -53,8 +51,7 @@ export function ProgressNudge(props: ProgressNudgeProps) {
|
||||
<span className="relative -top-[0.45px] mr-2 text-xs font-medium uppercase text-yellow-400">
|
||||
Progress
|
||||
</span>
|
||||
<span>{done > $totalRoadmapNodes ? $totalRoadmapNodes : done}</span> of{' '}
|
||||
<span>{$totalRoadmapNodes}</span> Done
|
||||
<span>{done > $totalRoadmapNodes ? $totalRoadmapNodes : done}</span> of <span>{$totalRoadmapNodes}</span> Done
|
||||
</span>
|
||||
|
||||
<span
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { BadgeCheck, Bot, Telescope, Wand } from 'lucide-react';
|
||||
import { RoadmapAlert } from '../RoadmapAlert';
|
||||
import { BadgeCheck, Telescope, Wand } from 'lucide-react';
|
||||
|
||||
type AIRoadmapAlertProps = {
|
||||
isListing?: boolean;
|
||||
@@ -9,20 +8,46 @@ export function AIRoadmapAlert(props: AIRoadmapAlertProps) {
|
||||
const { isListing = false } = props;
|
||||
|
||||
return (
|
||||
<RoadmapAlert
|
||||
title={`AI Generated Roadmap${isListing ? 's' : ''}`}
|
||||
badgeText="Beta"
|
||||
description={
|
||||
<>
|
||||
{isListing
|
||||
? 'These are AI generated roadmaps and are not verified by'
|
||||
: 'This is an AI generated roadmap and is not verified by'}{' '}
|
||||
<span className={'font-semibold'}>roadmap.sh</span>. We are currently
|
||||
in beta and working hard to improve the quality of the generated
|
||||
roadmaps.
|
||||
</>
|
||||
}
|
||||
floatingIcon={Bot}
|
||||
/>
|
||||
<div className="mb-3 w-full rounded-xl bg-yellow-100 px-4 py-3 text-yellow-800">
|
||||
<h2 className="flex items-center text-base font-semibold text-yellow-800 sm:text-lg">
|
||||
AI Generated Roadmap{isListing ? 's' : ''}{' '}
|
||||
<span className="ml-1.5 rounded-md border border-yellow-500 bg-yellow-200 px-1.5 text-xs uppercase tracking-wide text-yellow-800">
|
||||
Beta
|
||||
</span>
|
||||
</h2>
|
||||
<p className="mb-2 mt-1">
|
||||
{isListing
|
||||
? 'These are AI generated roadmaps and are not verified by'
|
||||
: 'This is an AI generated roadmap and is not verified by'}{' '}
|
||||
<span className={'font-semibold'}>roadmap.sh</span>. We are currently in
|
||||
beta and working hard to improve the quality of the generated roadmaps.
|
||||
</p>
|
||||
<p className="mb-1.5 mt-2 flex flex-col gap-2 text-sm sm:flex-row">
|
||||
{isListing ? (
|
||||
<a
|
||||
href="/ai"
|
||||
className="flex items-center gap-1.5 rounded-md border border-yellow-600 px-2 py-1 text-yellow-700 transition-colors hover:bg-yellow-300 hover:text-yellow-800"
|
||||
>
|
||||
<Wand size={15} />
|
||||
Create your own Roadmap with AI
|
||||
</a>
|
||||
) : (
|
||||
<a
|
||||
href="/ai/explore"
|
||||
className="flex items-center gap-1.5 rounded-md border border-yellow-600 px-2 py-1 text-yellow-700 transition-colors hover:bg-yellow-300 hover:text-yellow-800"
|
||||
>
|
||||
<Telescope size={15} />
|
||||
Explore other AI Roadmaps
|
||||
</a>
|
||||
)}
|
||||
<a
|
||||
href="/roadmaps"
|
||||
className="flex items-center gap-1.5 rounded-md border border-yellow-600 bg-yellow-200 px-2 py-1 text-yellow-800 transition-colors hover:bg-yellow-300"
|
||||
>
|
||||
<BadgeCheck size={15} />
|
||||
Visit Official Roadmaps
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
import { FavoriteRoadmaps } from './FavoriteRoadmaps';
|
||||
import { FeatureAnnouncement } from "../FeatureAnnouncement";
|
||||
---
|
||||
|
||||
@@ -30,4 +31,5 @@ import { FeatureAnnouncement } from "../FeatureAnnouncement";
|
||||
their career.
|
||||
</p>
|
||||
</div>
|
||||
<FavoriteRoadmaps client:only='react' />
|
||||
</div>
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import type { AppError } from '../../api/api';
|
||||
import { ErrorIcon } from '../ReactIcons/ErrorIcon';
|
||||
|
||||
type ErrorPageProps = {
|
||||
error: AppError;
|
||||
};
|
||||
|
||||
export function ErrorPage(props: ErrorPageProps) {
|
||||
const { error } = props;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container py-10">
|
||||
<div className="flex min-h-[250px] flex-col items-center justify-center px-5 py-3 sm:px-0 sm:py-20">
|
||||
<ErrorIcon additionalClasses="mb-4 h-8 w-8 sm:h-14 sm:w-14" />
|
||||
<h2 className="mb-1 text-lg font-semibold sm:text-xl">
|
||||
Oops! Something went wrong
|
||||
</h2>
|
||||
<p className="mb-3 text-balance text-center text-xs text-gray-800 sm:text-sm">
|
||||
{error?.message || 'An error occurred while fetching'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
import { type ReactNode, useState } from 'react';
|
||||
import type {
|
||||
LeaderboardUserDetails,
|
||||
ListLeaderboardStatsResponse,
|
||||
} from '../../api/leaderboard';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { FolderKanban, GitPullRequest, Users, Users2, Zap } from 'lucide-react';
|
||||
import { TrophyEmoji } from '../ReactIcons/TrophyEmoji';
|
||||
import { SecondPlaceMedalEmoji } from '../ReactIcons/SecondPlaceMedalEmoji';
|
||||
import { ThirdPlaceMedalEmoji } from '../ReactIcons/ThirdPlaceMedalEmoji';
|
||||
|
||||
type LeaderboardPageProps = {
|
||||
stats: ListLeaderboardStatsResponse;
|
||||
};
|
||||
|
||||
export function LeaderboardPage(props: LeaderboardPageProps) {
|
||||
const { stats } = props;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-100">
|
||||
<div className="container pb-5 sm:pb-8">
|
||||
<h1 className="my-5 flex items-center text-lg font-medium text-black sm:mb-4 sm:mt-8">
|
||||
<Users2 className="mr-2 size-5 text-black" />
|
||||
Leaderboard
|
||||
</h1>
|
||||
|
||||
<div className="grid gap-2 sm:gap-3 md:grid-cols-2">
|
||||
<LeaderboardLane
|
||||
title="Longest Visit Streak"
|
||||
tabs={[
|
||||
{
|
||||
title: 'Active',
|
||||
users: stats.streaks?.active || [],
|
||||
emptyIcon: <Zap className="size-16 text-gray-300" />,
|
||||
emptyText: 'No users with streaks yet',
|
||||
},
|
||||
{
|
||||
title: 'Lifetime',
|
||||
users: stats.streaks?.lifetime || [],
|
||||
emptyIcon: <Zap className="size-16 text-gray-300" />,
|
||||
emptyText: 'No users with streaks yet',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<LeaderboardLane
|
||||
title="Projects Completed"
|
||||
tabs={[
|
||||
{
|
||||
title: 'This Month',
|
||||
users: stats.projectSubmissions.currentMonth,
|
||||
emptyIcon: <FolderKanban className="size-16 text-gray-300" />,
|
||||
emptyText: 'No projects submitted this month',
|
||||
},
|
||||
{
|
||||
title: 'Lifetime',
|
||||
users: stats.projectSubmissions.lifetime,
|
||||
emptyIcon: <FolderKanban className="size-16 text-gray-300" />,
|
||||
emptyText: 'No projects submitted yet',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<LeaderboardLane
|
||||
title="Most Referrals"
|
||||
tabs={[
|
||||
{
|
||||
title: 'This Month',
|
||||
users: stats.referrals.currentMonth,
|
||||
emptyIcon: <Users className="size-16 text-gray-300" />,
|
||||
emptyText: 'No referrals this month',
|
||||
},
|
||||
{
|
||||
title: 'Lifetime',
|
||||
users: stats.referrals.lifetime,
|
||||
emptyIcon: <Users className="size-16 text-gray-300" />,
|
||||
emptyText: 'No referrals yet',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<LeaderboardLane
|
||||
title="Top Contributors"
|
||||
subtitle="Past 2 weeks"
|
||||
tabs={[
|
||||
{
|
||||
title: 'This Month',
|
||||
users: stats.githubContributors.currentMonth,
|
||||
emptyIcon: <GitPullRequest className="size-16 text-gray-300" />,
|
||||
emptyText: 'No contributors this month',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type LeaderboardLaneProps = {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
tabs: {
|
||||
title: string;
|
||||
users: LeaderboardUserDetails[];
|
||||
emptyIcon?: ReactNode;
|
||||
emptyText?: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
function LeaderboardLane(props: LeaderboardLaneProps) {
|
||||
const { title, subtitle, tabs } = props;
|
||||
|
||||
const [activeTab, setActiveTab] = useState(tabs[0]);
|
||||
const { users: usersToShow, emptyIcon, emptyText } = activeTab;
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[450px] flex-col overflow-hidden rounded-xl border bg-white shadow-sm">
|
||||
<div className="mb-3 flex items-center justify-between gap-2 px-3 py-3">
|
||||
<h3 className="text-sm font-medium">
|
||||
{title}{' '}
|
||||
{subtitle && (
|
||||
<span className="ml-1 text-sm font-normal text-gray-400">
|
||||
{subtitle}
|
||||
</span>
|
||||
)}
|
||||
</h3>
|
||||
|
||||
{tabs.length > 1 && (
|
||||
<div className="flex items-center gap-1">
|
||||
{tabs.map((tab) => {
|
||||
const isActive = tab === activeTab;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={tab.title}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={cn(
|
||||
'text-xs transition-colors py-0.5 px-2 rounded-full',
|
||||
{
|
||||
'text-white bg-black': isActive,
|
||||
'hover:bg-gray-200': !isActive,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{tab.title}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{usersToShow.length === 0 && emptyText && (
|
||||
<div className="flex flex-grow flex-col items-center justify-center p-8">
|
||||
{emptyIcon}
|
||||
<p className="mt-4 text-sm text-gray-500">{emptyText}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{usersToShow.length > 0 && (
|
||||
<ul className="divide-y divide-gray-100 pb-4">
|
||||
{usersToShow.map((user, counter) => {
|
||||
const avatar = user?.avatar
|
||||
? user?.avatar?.startsWith('http')
|
||||
? user?.avatar
|
||||
: `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${user.avatar}`
|
||||
: '/images/default-avatar.png';
|
||||
|
||||
const rank = counter + 1;
|
||||
const isGitHubUser = avatar?.indexOf('github') > -1;
|
||||
|
||||
return (
|
||||
<li
|
||||
key={user.id}
|
||||
className="flex items-center justify-between gap-1 py-2.5 pl-2 pr-5"
|
||||
>
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<span
|
||||
className={cn(
|
||||
'relative mr-1 flex size-6 shrink-0 items-center justify-center rounded-full text-xs tabular-nums',
|
||||
{
|
||||
'text-black': rank <= 3,
|
||||
'text-gray-400': rank > 3,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{rank}
|
||||
</span>
|
||||
|
||||
<img
|
||||
src={avatar}
|
||||
alt={user.name}
|
||||
className="mr-1 size-7 shrink-0 rounded-full"
|
||||
/>
|
||||
{isGitHubUser ? (
|
||||
<a
|
||||
href={`https://github.com/kamranahmedse/developer-roadmap/pulls?q=is%3Apr+is%3Aclosed+author%3A${user.name}`}
|
||||
target="_blank"
|
||||
className="truncate font-medium underline underline-offset-2"
|
||||
>
|
||||
{user.name}
|
||||
</a>
|
||||
) : (
|
||||
<span className="truncate">{user.name}</span>
|
||||
)}
|
||||
{rank === 1 ? (
|
||||
<TrophyEmoji className="size-5" />
|
||||
) : rank === 2 ? (
|
||||
<SecondPlaceMedalEmoji className="size-5" />
|
||||
) : rank === 3 ? (
|
||||
<ThirdPlaceMedalEmoji className="size-5" />
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
|
||||
<span className="text-sm text-gray-500">{user.count}</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,5 @@
|
||||
---
|
||||
interface Props {
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const { class: className } = Astro.props;
|
||||
---
|
||||
|
||||
<div
|
||||
class:list={[
|
||||
'container prose prose-xl prose-h2:mb-3 prose-h2:mt-10 prose-h2:scroll-mt-5 prose-h2:text-balance prose-h2:text-3xl prose-h3:mt-2 prose-h3:scroll-mt-5 prose-h3:text-balance prose-h4:text-balance prose-h5:text-balance prose-h5:font-medium prose-blockquote:font-normal prose-code:bg-transparent prose-img:mt-1 prose-h2:sm:scroll-mt-10 prose-h3:sm:scroll-mt-10',
|
||||
className,
|
||||
]}
|
||||
class='container prose-h2:text-balance prose-h3:text-balance prose-h4:text-balance prose-h5:text-balance prose prose-xl prose-h2:mb-3 prose-h2:mt-10 prose-h2:scroll-mt-5 prose-h2:text-3xl prose-h3:mt-2 prose-h3:scroll-mt-5 prose-h5:font-medium prose-blockquote:font-normal prose-code:bg-transparent prose-img:mt-1 prose-h2:sm:scroll-mt-10 prose-h3:sm:scroll-mt-10'
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ const links = [
|
||||
Icon: Waypoints,
|
||||
},
|
||||
{
|
||||
link: '/projects',
|
||||
link: '/backend/projects',
|
||||
label: 'Projects',
|
||||
description: 'Skill-up with real-world projects',
|
||||
Icon: FolderKanban,
|
||||
|
||||
@@ -156,7 +156,7 @@ export function PageSponsor(props: PageSponsorProps) {
|
||||
<span>
|
||||
<img
|
||||
src={imageUrl}
|
||||
className="block h-[106px] object-cover sm:h-[153px] sm:w-[118.18px]"
|
||||
className="block h-[106px] object-cover sm:h-[169px] sm:w-[118.18px]"
|
||||
alt="Sponsor Banner"
|
||||
/>
|
||||
</span>
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface ProjectStatusDocument {
|
||||
|
||||
isVisible?: boolean;
|
||||
|
||||
updatedAt: Date;
|
||||
updated1t: Date;
|
||||
}
|
||||
|
||||
const allowedVoteType = ['upvote', 'downvote'] as const;
|
||||
|
||||
@@ -5,13 +5,10 @@ import type {
|
||||
} from '../../lib/project.ts';
|
||||
import { Users } from 'lucide-react';
|
||||
import { formatCommaNumber } from '../../lib/number.ts';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
import { isLoggedIn } from '../../lib/jwt.ts';
|
||||
|
||||
type ProjectCardProps = {
|
||||
project: ProjectFileType;
|
||||
userCount?: number;
|
||||
status?: 'completed' | 'started' | 'none';
|
||||
};
|
||||
|
||||
const badgeVariants: Record<ProjectDifficultyType, string> = {
|
||||
@@ -21,11 +18,9 @@ const badgeVariants: Record<ProjectDifficultyType, string> = {
|
||||
};
|
||||
|
||||
export function ProjectCard(props: ProjectCardProps) {
|
||||
const { project, userCount = 0, status } = props;
|
||||
const { frontmatter, id } = project;
|
||||
const { project, userCount = 0 } = props;
|
||||
|
||||
const isLoadingStatus = status === undefined;
|
||||
const userStartedCount = status !== 'none' && userCount === 0 ? userCount + 1 : userCount;
|
||||
const { frontmatter, id } = project;
|
||||
|
||||
return (
|
||||
<a
|
||||
@@ -39,45 +34,16 @@ export function ProjectCard(props: ProjectCardProps) {
|
||||
/>
|
||||
<Badge variant={'grey'} text={frontmatter.nature} />
|
||||
</span>
|
||||
<span className="my-3 flex min-h-[100px] flex-col">
|
||||
<span className="my-3 flex flex-col">
|
||||
<span className="mb-1 font-medium">{frontmatter.title}</span>
|
||||
<span className="text-sm text-gray-500">{frontmatter.description}</span>
|
||||
</span>
|
||||
<span className="flex min-h-[22px] items-center justify-between gap-2 text-xs text-gray-400">
|
||||
{isLoadingStatus ? (
|
||||
<>
|
||||
<span className="h-5 w-24 animate-pulse rounded bg-gray-200" />{' '}
|
||||
<span className="h-5 w-20 animate-pulse rounded bg-gray-200" />{' '}
|
||||
</>
|
||||
<span className="flex items-center gap-2 text-xs text-gray-400">
|
||||
<Users className="inline-block size-3.5" />
|
||||
{userCount > 0 ? (
|
||||
<>{formatCommaNumber(userCount)} Started</>
|
||||
) : (
|
||||
<>
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Users className="size-3.5" />
|
||||
{userStartedCount > 0 ? (
|
||||
<>{formatCommaNumber(userStartedCount)} Started</>
|
||||
) : (
|
||||
<>Be the first to solve!</>
|
||||
)}
|
||||
</span>
|
||||
|
||||
{status !== 'none' && (
|
||||
<span
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 rounded-full border border-current px-2 py-0.5 capitalize',
|
||||
status === 'completed' && 'text-green-500',
|
||||
status === 'started' && 'text-yellow-500',
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={cn('inline-block h-2 w-2 rounded-full', {
|
||||
'bg-green-500': status === 'completed',
|
||||
'bg-yellow-500': status === 'started',
|
||||
})}
|
||||
/>
|
||||
{status}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
<>Be the first to solve!</>
|
||||
)}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ProjectCard } from './ProjectCard.tsx';
|
||||
import { HeartHandshake, Trash2 } from 'lucide-react';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import {
|
||||
projectDifficulties,
|
||||
type ProjectDifficultyType,
|
||||
@@ -12,8 +12,6 @@ import {
|
||||
getUrlParams,
|
||||
setUrlParams,
|
||||
} from '../../lib/browser.ts';
|
||||
import { httpPost } from '../../lib/http.ts';
|
||||
import { isLoggedIn } from '../../lib/jwt.ts';
|
||||
|
||||
type DifficultyButtonProps = {
|
||||
difficulty: ProjectDifficultyType;
|
||||
@@ -40,11 +38,6 @@ function DifficultyButton(props: DifficultyButtonProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export type ListProjectStatusesResponse = Record<
|
||||
string,
|
||||
'completed' | 'started'
|
||||
>;
|
||||
|
||||
type ProjectsListProps = {
|
||||
projects: ProjectFileType[];
|
||||
userCounts: Record<string, number>;
|
||||
@@ -57,30 +50,6 @@ export function ProjectsList(props: ProjectsListProps) {
|
||||
const [difficulty, setDifficulty] = useState<
|
||||
ProjectDifficultyType | undefined
|
||||
>(urlDifficulty);
|
||||
const [projectStatuses, setProjectStatuses] =
|
||||
useState<ListProjectStatusesResponse>();
|
||||
|
||||
const loadProjectStatuses = async () => {
|
||||
if (!isLoggedIn()) {
|
||||
setProjectStatuses({});
|
||||
return;
|
||||
}
|
||||
|
||||
const projectIds = projects.map((project) => project.id);
|
||||
const { response, error } = await httpPost(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-list-project-statuses`,
|
||||
{
|
||||
projectIds,
|
||||
},
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
setProjectStatuses(response);
|
||||
};
|
||||
|
||||
const projectsByDifficulty: Map<ProjectDifficultyType, ProjectFileType[]> =
|
||||
useMemo(() => {
|
||||
@@ -103,17 +72,12 @@ export function ProjectsList(props: ProjectsListProps) {
|
||||
? projectsByDifficulty.get(difficulty) || []
|
||||
: projects;
|
||||
|
||||
useEffect(() => {
|
||||
loadProjectStatuses().finally();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="my-2.5 flex items-center justify-between">
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{projectDifficulties.map((projectDifficulty) => (
|
||||
<DifficultyButton
|
||||
key={projectDifficulty}
|
||||
onClick={() => {
|
||||
setDifficulty(projectDifficulty);
|
||||
setUrlParams({ difficulty: projectDifficulty });
|
||||
@@ -166,18 +130,7 @@ export function ProjectsList(props: ProjectsListProps) {
|
||||
})
|
||||
.map((matchingProject) => {
|
||||
const count = userCounts[matchingProject?.id] || 0;
|
||||
return (
|
||||
<ProjectCard
|
||||
key={matchingProject.id}
|
||||
project={matchingProject}
|
||||
userCount={count}
|
||||
status={
|
||||
projectStatuses
|
||||
? (projectStatuses?.[matchingProject.id] || 'none')
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
return <ProjectCard project={matchingProject} userCount={count} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { cn } from '../../lib/classname.ts';
|
||||
import { Box, Filter, Group, X } from 'lucide-react';
|
||||
import {
|
||||
deleteUrlParam,
|
||||
getUrlParams,
|
||||
setUrlParams,
|
||||
} from '../../lib/browser.ts';
|
||||
import { CategoryFilterButton } from '../Roadmaps/CategoryFilterButton.tsx';
|
||||
import {
|
||||
projectDifficulties,
|
||||
type ProjectFileType,
|
||||
} from '../../lib/project.ts';
|
||||
import { ProjectCard } from './ProjectCard.tsx';
|
||||
|
||||
type ProjectGroup = {
|
||||
id: string;
|
||||
title: string;
|
||||
projects: ProjectFileType[];
|
||||
};
|
||||
|
||||
type ProjectsPageProps = {
|
||||
roadmapsProjects: ProjectGroup[];
|
||||
userCounts: Record<string, number>;
|
||||
};
|
||||
|
||||
export function ProjectsPage(props: ProjectsPageProps) {
|
||||
const { roadmapsProjects, userCounts } = props;
|
||||
const allUniqueProjectIds = new Set<string>(
|
||||
roadmapsProjects.flatMap((group) =>
|
||||
group.projects.map((project) => project.id),
|
||||
),
|
||||
);
|
||||
const allUniqueProjects = useMemo(
|
||||
() =>
|
||||
Array.from(allUniqueProjectIds)
|
||||
.map((id) =>
|
||||
roadmapsProjects
|
||||
.flatMap((group) => group.projects)
|
||||
.find((project) => project.id === id),
|
||||
)
|
||||
.filter(Boolean) as ProjectFileType[],
|
||||
[allUniqueProjectIds],
|
||||
);
|
||||
|
||||
const [activeGroup, setActiveGroup] = useState<string>('');
|
||||
const [visibleProjects, setVisibleProjects] =
|
||||
useState<ProjectFileType[]>(allUniqueProjects);
|
||||
|
||||
const [isFilterOpen, setIsFilterOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const { g } = getUrlParams() as { g: string };
|
||||
if (!g) {
|
||||
return;
|
||||
}
|
||||
|
||||
setActiveGroup(g);
|
||||
const group = roadmapsProjects.find((group) => group.id === g);
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
setVisibleProjects(group.projects);
|
||||
}, []);
|
||||
|
||||
const sortedVisibleProjects = useMemo(
|
||||
() =>
|
||||
visibleProjects.sort((a, b) => {
|
||||
const projectADifficulty = a?.frontmatter.difficulty || 'beginner';
|
||||
const projectBDifficulty = b?.frontmatter.difficulty || 'beginner';
|
||||
return (
|
||||
projectDifficulties.indexOf(projectADifficulty) -
|
||||
projectDifficulties.indexOf(projectBDifficulty)
|
||||
);
|
||||
}),
|
||||
[visibleProjects],
|
||||
);
|
||||
|
||||
const activeGroupDetail = roadmapsProjects.find(
|
||||
(group) => group.id === activeGroup,
|
||||
);
|
||||
|
||||
const requiredSortOrder = [
|
||||
'Frontend',
|
||||
'Backend',
|
||||
'DevOps',
|
||||
'Full-stack',
|
||||
'JavaScript',
|
||||
'Go',
|
||||
'Python',
|
||||
'Node.js',
|
||||
'Java',
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="border-t bg-gray-100">
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsFilterOpen(!isFilterOpen);
|
||||
}}
|
||||
id="filter-button"
|
||||
className={cn(
|
||||
'-mt-1 flex w-full items-center justify-center bg-gray-300 py-2 text-sm text-black focus:shadow-none focus:outline-0 sm:hidden',
|
||||
{
|
||||
'mb-3': !isFilterOpen,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{!isFilterOpen && <Filter size={13} className="mr-1" />}
|
||||
{isFilterOpen && <X size={13} className="mr-1" />}
|
||||
Categories
|
||||
</button>
|
||||
<div className="container relative flex flex-col gap-4 sm:flex-row">
|
||||
<div
|
||||
className={cn(
|
||||
'hidden w-full flex-col from-gray-100 sm:w-[160px] sm:shrink-0 sm:border-r sm:bg-gradient-to-l sm:pt-6',
|
||||
{
|
||||
'hidden sm:flex': !isFilterOpen,
|
||||
'z-50 flex': isFilterOpen,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div className="absolute top-0 -mx-4 w-full bg-white pb-0 shadow-xl sm:sticky sm:top-10 sm:mx-0 sm:bg-transparent sm:pb-20 sm:shadow-none">
|
||||
<div className="grid grid-cols-1">
|
||||
<CategoryFilterButton
|
||||
onClick={() => {
|
||||
setActiveGroup('');
|
||||
setVisibleProjects(allUniqueProjects);
|
||||
setIsFilterOpen(false);
|
||||
deleteUrlParam('g');
|
||||
}}
|
||||
category={'All Projects'}
|
||||
selected={activeGroup === ''}
|
||||
/>
|
||||
|
||||
{roadmapsProjects
|
||||
.sort((a, b) => {
|
||||
const aIndex = requiredSortOrder.indexOf(a.title);
|
||||
const bIndex = requiredSortOrder.indexOf(b.title);
|
||||
|
||||
if (aIndex === -1 && bIndex === -1) {
|
||||
return a.title.localeCompare(b.title);
|
||||
}
|
||||
|
||||
if (aIndex === -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (bIndex === -1) {
|
||||
return -1;
|
||||
}
|
||||
return aIndex - bIndex;
|
||||
})
|
||||
.map((group) => (
|
||||
<CategoryFilterButton
|
||||
key={group.id}
|
||||
onClick={() => {
|
||||
setActiveGroup(group.id);
|
||||
setIsFilterOpen(false);
|
||||
document
|
||||
?.getElementById('filter-button')
|
||||
?.scrollIntoView();
|
||||
setVisibleProjects(group.projects);
|
||||
setUrlParams({ g: group.id });
|
||||
}}
|
||||
category={group.title}
|
||||
selected={activeGroup === group.id}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-grow flex-col pb-20 pt-2 sm:pt-6">
|
||||
<div className="mb-4 flex items-center justify-between text-sm text-gray-500">
|
||||
<h3 className={'flex items-center'}>
|
||||
<Box size={15} className="mr-1" strokeWidth={2} />
|
||||
{activeGroupDetail
|
||||
? `Projects in ${activeGroupDetail?.title}`
|
||||
: 'All Projects'}
|
||||
</h3>
|
||||
<p className="text-left">
|
||||
Matches found ({sortedVisibleProjects.length})
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2">
|
||||
{sortedVisibleProjects.map((project) => (
|
||||
<ProjectCard
|
||||
key={project.id}
|
||||
project={project}
|
||||
userCount={userCounts[project.id] || 0}
|
||||
status={'none'}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { isLoggedIn } from '../../lib/jwt.ts';
|
||||
import { showLoginPopup } from '../../lib/popup.ts';
|
||||
|
||||
export function ProjectsPageHeader() {
|
||||
return (
|
||||
<div className="bg-white py-3 sm:py-12">
|
||||
<div className="container">
|
||||
<div className="flex flex-col items-start bg-white sm:items-center">
|
||||
<h1 className="text-2xl font-bold sm:text-5xl">Project Ideas</h1>
|
||||
<p className="mt-1 text-sm sm:mt-4 sm:text-lg">
|
||||
Browse the ever-growing list of projects ideas and solutions.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Flag, Play, Send, Share, Square, StopCircle, X } from 'lucide-react';
|
||||
import { Flag, Play, Send, Share } from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { cn } from '../../../lib/classname.ts';
|
||||
import { useStickyStuck } from '../../../hooks/use-sticky-stuck.tsx';
|
||||
import { StepperAction } from './StepperAction.tsx';
|
||||
import { StepperStepSeparator } from './StepperStepSeparator.tsx';
|
||||
import { MilestoneStep } from './MilestoneStep.tsx';
|
||||
import { httpGet, httpPost } from '../../../lib/http.ts';
|
||||
import { httpGet } from '../../../lib/http.ts';
|
||||
import { StartProjectModal } from '../StartProjectModal.tsx';
|
||||
import { getRelativeTimeString } from '../../../lib/date.ts';
|
||||
import { getUser, isLoggedIn } from '../../../lib/jwt.ts';
|
||||
@@ -13,7 +13,6 @@ import { showLoginPopup } from '../../../lib/popup.ts';
|
||||
import { SubmitProjectModal } from '../SubmitProjectModal.tsx';
|
||||
import { useCopyText } from '../../../hooks/use-copy-text.ts';
|
||||
import { CheckIcon } from '../../ReactIcons/CheckIcon.tsx';
|
||||
import { pageProgressMessage } from '../../../stores/page.ts';
|
||||
|
||||
type ProjectStatusResponse = {
|
||||
id?: string;
|
||||
@@ -41,8 +40,6 @@ export function ProjectStepper(props: ProjectStepperProps) {
|
||||
const [isSubmittingProject, setIsSubmittingProject] = useState(false);
|
||||
const { copyText, isCopied } = useCopyText();
|
||||
|
||||
const [isStoppingProject, setIsStoppingProject] = useState(false);
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [activeStep, setActiveStep] = useState<number>(0);
|
||||
const [isLoadingStatus, setIsLoadingStatus] = useState(true);
|
||||
@@ -81,27 +78,6 @@ export function ProjectStepper(props: ProjectStepperProps) {
|
||||
setIsLoadingStatus(false);
|
||||
}
|
||||
|
||||
const stopProject = async () => {
|
||||
setIsStoppingProject(true);
|
||||
const { response, error } = await httpPost(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-stop-project/${projectId}`,
|
||||
{},
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
setIsStoppingProject(false);
|
||||
setError(error?.message || 'Error stopping project');
|
||||
return;
|
||||
}
|
||||
|
||||
pageProgressMessage.set('Update project status');
|
||||
setActiveStep(0);
|
||||
loadProjectStatus().finally(() => {
|
||||
pageProgressMessage.set('');
|
||||
setIsStoppingProject(false);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadProjectStatus().finally(() => {});
|
||||
}, []);
|
||||
@@ -213,20 +189,6 @@ export function ProjectStepper(props: ProjectStepperProps) {
|
||||
</>
|
||||
)}
|
||||
|
||||
{projectStatus?.startedAt && !projectStatus?.submittedAt && (
|
||||
<button
|
||||
className={cn(
|
||||
'ml-auto hidden items-center gap-1.5 text-sm hover:text-black disabled:opacity-50 sm:flex',
|
||||
)}
|
||||
onClick={stopProject}
|
||||
disabled={isStoppingProject}
|
||||
>
|
||||
<Square className="h-3 w-3 fill-current stroke-[2.5px]" />
|
||||
<span className="hidden md:inline">Stop Working</span>
|
||||
<span className="md:hidden">Stop</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{activeStep >= 2 && (
|
||||
<button
|
||||
className={cn(
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import type { SVGProps } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
export function BookEmoji(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 36 36"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="#3e721d"
|
||||
d="M35 26a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V6.313C1 4.104 6.791 0 9 0h20.625C32.719 0 35 2.312 35 5.375z"
|
||||
></path>
|
||||
<path
|
||||
fill="#ccd6dd"
|
||||
d="M33 30a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V6c0-4.119-.021-4 5-4h21a4 4 0 0 1 4 4z"
|
||||
></path>
|
||||
<path
|
||||
fill="#e1e8ed"
|
||||
d="M31 31a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h24a3 3 0 0 1 3 3z"
|
||||
></path>
|
||||
<path
|
||||
fill="#5c913b"
|
||||
d="M31 32a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V10a4 4 0 0 1 4-4h21a4 4 0 0 1 4 4z"
|
||||
></path>
|
||||
<path
|
||||
fill="#77b255"
|
||||
d="M29 32a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V12a4 4 0 0 1 4-4h19.335C27.544 8 29 9.456 29 11.665z"
|
||||
></path>
|
||||
<path
|
||||
fill="#3e721d"
|
||||
d="M6 6C4.312 6 4.269 4.078 5 3.25C5.832 2.309 7.125 2 9.438 2H11V0H8.281C4.312 0 1 2.5 1 5.375V32a4 4 0 0 0 4 4h2V6z"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import React from 'react';
|
||||
import type { SVGProps } from 'react';
|
||||
|
||||
export function BuildEmoji(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 36 36"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="#66757f"
|
||||
d="M28.25 8.513a.263.263 0 0 0-.263-.263h-.475a.263.263 0 0 0-.263.263v11.475c0 .145.117.263.263.263h.475a.263.263 0 0 0 .263-.263z"
|
||||
></path>
|
||||
<g fill="#f19020">
|
||||
<circle cx={27.75} cy={19.75} r={1.5}></circle>
|
||||
<circle cx={27.75} cy={22.25} r={1}></circle>
|
||||
</g>
|
||||
<path
|
||||
fill="#bd2032"
|
||||
d="M33.25 8.25h-4.129L9.946.29L9.944.289h-.001c-.016-.007-.032-.005-.047-.01C9.849.265 9.802.25 9.75.25h-.002a.5.5 0 0 0-.19.038a.5.5 0 0 0-.122.082c-.012.009-.026.014-.037.025a.5.5 0 0 0-.11.164V.56c-.004.009-.003.02-.006.029l-5.541 7.81l-.006.014a.99.99 0 0 0-.486.837v2a1 1 0 0 0 1 1h1.495L2.031 34H.25v2h18.958v-2h-1.74l-3.713-21.75H33.25a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1m-21.769 4L9.75 13.639L8.02 12.25zM9.75 21.3l3.667 2.404l-3.667 2l-3.667-2zm-3.639.71l.474-2.784l1.866 1.223zm4.938-1.561l1.87-1.225l.477 2.789zm-1.299-.866l-2.828-1.885l2.828-2.322l2.828 2.322zm-2.563-3.887l.362-2.127l1.131.928zm3.633-1.198l1.132-.929l.364 2.13zM5.073 8.25L9.25 2.362V6.25h-2a1 1 0 0 0-1 1v1zm.53 16.738l2.73 1.489l-3.29 1.794zM15.443 34H4.067l.686-4.024L9.75 27.25l5.006 2.731zm-1.54-9.015l.562 3.291l-3.298-1.799zM13.25 8.25v-1a1 1 0 0 0-1-1h-2V1.499L26.513 8.25zm2 3h-1.16v-2h1.16zm3 0h-2v-2h2zm3 0h-2v-2h2zm3 0h-2v-2h2zm3 0h-2v-2h2zm3 0h-2v-2h2zm3-.5a.5.5 0 0 1-.5.5h-1.5v-2h1.5a.5.5 0 0 1 .5.5z"
|
||||
></path>
|
||||
<path
|
||||
fill="#4b545d"
|
||||
d="M12.25 7.25h-2a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h3v-4z"
|
||||
></path>
|
||||
<path fill="#cdd7df" d="M11.25 7.25h2v4h-2z"></path>
|
||||
<path
|
||||
fill="#66757f"
|
||||
d="M34.844 24v-1H20.656v1h.844v2.469h-.844v1h14.188v-1H34V24z"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// twitter bulb emoji
|
||||
import type { SVGProps } from 'react';
|
||||
|
||||
type BulbEmojiProps = SVGProps<SVGSVGElement>;
|
||||
|
||||
export function BulbEmoji(props: BulbEmojiProps) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 36 36"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="#FFD983"
|
||||
d="M29 11.06c0 6.439-5 7.439-5 13.44c0 3.098-3.123 3.359-5.5 3.359c-2.053 0-6.586-.779-6.586-3.361C11.914 18.5 7 17.5 7 11.06C7 5.029 12.285.14 18.083.14C23.883.14 29 5.029 29 11.06"
|
||||
></path>
|
||||
<path
|
||||
fill="#CCD6DD"
|
||||
d="M22.167 32.5c0 .828-2.234 2.5-4.167 2.5s-4.167-1.672-4.167-2.5S16.066 32 18 32s4.167-.328 4.167.5"
|
||||
></path>
|
||||
<path
|
||||
fill="#FFCC4D"
|
||||
d="M22.707 10.293a1 1 0 0 0-1.414 0L18 13.586l-3.293-3.293a.999.999 0 1 0-1.414 1.414L17 15.414V26a1 1 0 1 0 2 0V15.414l3.707-3.707a1 1 0 0 0 0-1.414"
|
||||
></path>
|
||||
<path
|
||||
fill="#99AAB5"
|
||||
d="M24 31a2 2 0 0 1-2 2h-8a2 2 0 0 1-2-2v-6h12z"
|
||||
></path>
|
||||
<path
|
||||
fill="#CCD6DD"
|
||||
d="M11.999 32a1 1 0 0 1-.163-1.986l12-2a.994.994 0 0 1 1.15.822a1 1 0 0 1-.822 1.15l-12 2a1 1 0 0 1-.165.014m0-4a1 1 0 0 1-.163-1.986l12-2a.995.995 0 0 1 1.15.822a1 1 0 0 1-.822 1.15l-12 2a1 1 0 0 1-.165.014"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import React from 'react';
|
||||
import type { SVGProps } from 'react';
|
||||
|
||||
export function CheckEmoji(props: SVGProps<SVGSVGElement>) {
|
||||
return (<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 36 36" {...props}><path fill="#77b255" d="M36 32a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4h28a4 4 0 0 1 4 4z"></path><path fill="#fff" d="M29.28 6.362a2.5 2.5 0 0 0-3.458.736L14.936 23.877l-5.029-4.65a2.5 2.5 0 1 0-3.394 3.671l7.209 6.666c.48.445 1.09.665 1.696.665c.673 0 1.534-.282 2.099-1.139c.332-.506 12.5-19.27 12.5-19.27a2.5 2.5 0 0 0-.737-3.458"></path></svg>);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { SVGProps } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
export function ConstructionEmoji(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 36 36"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="#ffcc4d"
|
||||
d="M36 15a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V7a4 4 0 0 1 4-4h28a4 4 0 0 1 4 4z"
|
||||
></path>
|
||||
<path
|
||||
fill="#292f33"
|
||||
d="M6 3H4a4 4 0 0 0-4 4v2zm6 0L0 15c0 1.36.682 2.558 1.72 3.28L17 3zM7 19h5L28 3h-5zm16 0L35.892 6.108A4 4 0 0 0 33.64 3.36L18 19zm13-4v-3l-7 7h3a4 4 0 0 0 4-4"
|
||||
></path>
|
||||
<path fill="#99aab5" d="M4 19h5v14H4zm23 0h5v14h-5z"></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import type { SVGProps } from 'react';
|
||||
|
||||
export function RankBadgeIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width="11"
|
||||
height="11"
|
||||
viewBox="0 0 11 11"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M0 0L11 0V10.0442L5.73392 6.32786L0 10.0442L0 0Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import React from 'react';
|
||||
import type { SVGProps } from 'react';
|
||||
|
||||
export function SecondPlaceMedalEmoji(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 36 36"
|
||||
{...props}
|
||||
>
|
||||
<path fill="#55acee" d="m18 8l-7-8H0l14 17l11.521-4.75z"></path>
|
||||
<path fill="#3b88c3" d="m25 0l-7 8l5.39 7.312l1.227-1.489L36 0z"></path>
|
||||
<path
|
||||
fill="#ccd6dd"
|
||||
d="M23.205 16.026c.08-.217.131-.448.131-.693a2 2 0 0 0-2-2h-6.667a2 2 0 0 0-2 2c0 .245.05.476.131.693c-3.258 1.826-5.464 5.307-5.464 9.307C7.335 31.224 12.111 36 18.002 36s10.667-4.776 10.667-10.667c0-4-2.206-7.481-5.464-9.307"
|
||||
></path>
|
||||
<path
|
||||
fill="#627077"
|
||||
d="M22.002 28.921h-3.543c.878-1.234 2.412-3.234 3.01-4.301c.449-.879.729-1.439.729-2.43c0-2.076-1.57-3.777-4.244-3.777c-2.225 0-3.74 1.832-3.74 1.832c-.131.15-.112.374.019.487l1.141 1.159a.36.36 0 0 0 .523 0c.355-.393 1.047-.935 1.813-.935c1.047 0 1.646.635 1.646 1.346c0 .523-.243 1.047-.486 1.421c-1.104 1.682-3.871 5.441-4.955 6.862v.374c0 .188.149.355.355.355h7.732a.37.37 0 0 0 .355-.355v-1.682a.367.367 0 0 0-.355-.356"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||