Compare commits

..

584 Commits

Author SHA1 Message Date
Arik Chakma
ae08294a9e fix: topic id 2024-07-08 21:14:36 +06:00
Arik Chakma
96e5281c50 fix: remove check 2024-07-08 21:12:02 +06:00
Arik Chakma
83388f07b6 fix: checkbox right click 2024-07-08 20:47:57 +06:00
Arik Chakma
cae841b898 fix: checkbox right click 2024-07-08 20:38:04 +06:00
dsh
3c065338db fixed file name issue and added content to 2-3 trees (#6100) 2024-07-08 14:11:36 +01:00
dsh
cd057508cb correct file name for solr (#6099) 2024-07-08 13:58:51 +01:00
Kamran Ahmed
366bd61562 Fix content file not found 2024-07-08 12:26:52 +01:00
Arik Chakma
9154a57eb9 feat: implement todo and resource button (#6055)
* feat: implement todo and resource button

* feat: add hover color
2024-07-08 12:03:58 +01:00
Martins Gouveia
24f9e0c6ce Update healthkit@Jsu5f6QASpuvpky_W5q-O.md (#6097) 2024-07-08 11:53:13 +01:00
Abdallah Gaber
8b82746676 Adding console.* and more recources to 106-nodejs-command-line-apps (#6098)
* Adding console.* and more recources to 106-nodejs-command-line-apps

* Update src/data/roadmaps/nodejs/content/106-nodejs-command-line-apps/101-printing-output/index.md

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-08 11:24:59 +01:00
dsh
d09962b6a3 Update topic titles (#6096) 2024-07-08 10:58:35 +01:00
Maria
df3dfe9971 updating swiftlint content (#6073)
* updating swiftlint content

* making code review changes

adding standard line and removing installation instructions

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

* Update src/data/roadmaps/ios/content/swiftlint@NnTC3R8goZQTXHmfHKHbM.md

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-08 10:14:16 +01:00
Abdallah Gaber
ec175482bd Updates on 106-nodejs-command-line-apps (#6087)
* updates on 106-nodejs-command-line-apps

* Apply suggestions from code review

Slight style and guideline editting

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-08 10:12:10 +01:00
Damar
5aa67c2e2b Fix typo in final paragraph (#6093) 2024-07-08 10:05:01 +01:00
garvit3835
22290ae0b7 Update terraform-validate@wdYLjB-tKg76B7YcnhevQ.md (#6095)
* Update terraform-validate@wdYLjB-tKg76B7YcnhevQ.md

* Update src/data/roadmaps/terraform/content/terraform-validate@wdYLjB-tKg76B7YcnhevQ.md

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-08 09:59:45 +01:00
Konrad
a8f68371f0 feat(roadmap/angular): add more resources about angular routing (#6089) 2024-07-08 00:15:01 +01:00
Chad Davis
0da2cab0ab Fix capitalization (#6082)
* Replace VCS Hosting with Repo Hosting Services

* Fix capitalization on Repo Hosting Services and GitHub

* Replace VCS Hosting with Repo Hosting Services and fix capitalization on Bitbucket

* Fix capitalization on Bitbucket

* Fix capitalization on Bitbucket

* Fix capitalization on Bitbucket

* Fixed spelling mistake

Changed "None English content." to "Non-English content."
2024-07-07 18:18:52 +01:00
Konrad
bab0ec0a5d fix(roadmap/angular): remove outdated article about angular forms (#6088)
removed link to article was about angularjs version
2024-07-07 14:35:35 +01:00
Karim Safan
36b42dfaa2 Update 102-loops.md (#6071)
bug in the code
2024-07-07 14:02:50 +01:00
dsh
6cd18458db add iOS copy and links (#6059) 2024-07-05 15:57:09 +01:00
Kamran Ahmed
93eb568bbd Migrate android roadmap to new format 2024-07-05 15:47:03 +01:00
Kamran Ahmed
3997641d0b Add android content 2024-07-05 15:47:03 +01:00
Martins Gouveia
3fda008f12 Update arkit@k3uHcF0CsyHr6PK95UwR1.md (#6061)
Add content and resources to Arkit section

- Updated content about ARKit framework
- Included additional links to oficial documentation
2024-07-05 13:39:08 +01:00
Nikhil
7f1f58516e DSA | Updated 105, Sorting Algorithm links (#6063)
* DSA | Updated 105, Sorting Algorithm links

* Update src/data/roadmaps/datastructures-and-algorithms/content/105-sorting-algorithms/101-merge-sort.md

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-05 13:31:23 +01:00
Ruslan Semagin
afb0da4bd6 link to article 'How to write documentation' in 'Rust' (#6058) 2024-07-05 09:49:40 +01:00
mrgsdev
485b3d5c9a Update hig@1I5eFKqFVBxWLAXfpgNXO.md (#6032)
* Update hig@1I5eFKqFVBxWLAXfpgNXO.md

This commit includes to the Human Interface Guidelines (HIG) documentation.

* Update src/data/roadmaps/ios/content/hig@1I5eFKqFVBxWLAXfpgNXO.md

style fix

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-05 08:31:35 +01:00
mrgsdev
78e20d1e85 Update iboutlets@tuUuLInq0p-nhehe2AqPg.md (#6035)
* Update iboutlets@tuUuLInq0p-nhehe2AqPg.md

* Update src/data/roadmaps/ios/content/iboutlets@tuUuLInq0p-nhehe2AqPg.md

fix style

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-05 08:29:45 +01:00
mrgsdev
e7cd703607 Update storyboards@a2CqrCJSxGfHq6_Y9f_re.md (#6036)
* Update storyboards@a2CqrCJSxGfHq6_Y9f_re.md

* Update src/data/roadmaps/ios/content/storyboards@a2CqrCJSxGfHq6_Y9f_re.md

fix styling

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-05 08:29:03 +01:00
mrgsdev
01c78a8cf4 Update auto-layout@j2BL0sf3WjnJZZWa7cCjy.md (#6037)
* Update auto-layout@j2BL0sf3WjnJZZWa7cCjy.md

* Update src/data/roadmaps/ios/content/auto-layout@j2BL0sf3WjnJZZWa7cCjy.md

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-05 08:28:04 +01:00
mrgsdev
cc123f74ea Update lottie@i-T6GTqS0FZ_Llt5v4SvR.md (#6038) 2024-07-05 08:26:09 +01:00
mrgsdev
fed5f722b9 Update mvc@a-QDI7Ei-B5BRHbicFcfG.md (#6039) 2024-07-05 08:25:16 +01:00
mrgsdev
cb4b5a4cc9 Update navigation-view@IBr2P7dknWTnZ2a-fFCqN.md (#6040) 2024-07-05 08:24:24 +01:00
mrgsdev
38be5892d3 Update navigation-stacks@TLm70PlTI0K3Odn1iYxWX.md (#6041)
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-05 08:23:11 +01:00
mrgsdev
24b47d3dd7 Update uikit@-7OW2IgiMk1eot1PaYd7m.md (#6042)
* Update uikit@-7OW2IgiMk1eot1PaYd7m.md

* Update src/data/roadmaps/ios/content/uikit@-7OW2IgiMk1eot1PaYd7m.md

adding content from my draft PR

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-05 08:22:09 +01:00
mrgsdev
783e2400b7 Update navigation@FXUrfyvuIIOH7VDnT_E0z.md (#6043) 2024-07-05 08:20:23 +01:00
mrgsdev
c9390d8612 Update core-data@H4-Dp2WTA6HAZiFRQdLjx.md (#6044)
* Update core-data@H4-Dp2WTA6HAZiFRQdLjx.md

* Update src/data/roadmaps/ios/content/core-data@H4-Dp2WTA6HAZiFRQdLjx.md

swapped to a video as the course wasn't free/was behind a login.

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-05 08:19:18 +01:00
mrgsdev
0cad5890ea Update swift-package-manager@KFkX8_Hv9SCFeCtZMZIQM.md (#6045)
* Update swift-package-manager@KFkX8_Hv9SCFeCtZMZIQM.md

* Update src/data/roadmaps/ios/content/swift-package-manager@KFkX8_Hv9SCFeCtZMZIQM.md

add spacing

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-05 08:17:15 +01:00
mrgsdev
f2297389a7 Update cocoapods@epr1sOEZIAOwlgb8bre7r.md (#6046) 2024-07-05 08:16:32 +01:00
mrgsdev
68906c6cf6 Update core-ml@7s9Elv80TbZX_-NZpyutj.md (#6048) 2024-07-05 08:15:39 +01:00
mrgsdev
d5ea2ed17a Update dynamic-type@0nei6iwP4Pgi_j4vVi_Qt.md (#6049)
* Update dynamic-type@0nei6iwP4Pgi_j4vVi_Qt.md

* Update src/data/roadmaps/ios/content/dynamic-type@0nei6iwP4Pgi_j4vVi_Qt.md

Removing templating

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-05 08:15:15 +01:00
mrgsdev
6118162b03 Update accessibility@1DZYPqvgY6GtwMCS7N2y-.md (#6050) 2024-07-05 08:13:59 +01:00
Florian Schweizer
0a675760ed Add Combine & NavigationStack content to the iOS roadmap (#6052)
Add content to Navigated stacks and combine
2024-07-05 08:13:25 +01:00
mrgsdev
4b5635c5e5 Update voice-over@trAMZVA4tDB7b_qAgiWNW.md (#6051) 2024-07-05 08:12:37 +01:00
mrgsdev
ee298f9959 Update keeping-updated-with-wwdc@fOOQurIL1w3PwH5Mep9x1.md (#6053) 2024-07-05 08:11:29 +01:00
mrgsdev
d09710fee6 Update new-project@BJgoAgH85U6S3RtXq7hHV.md (#6054) 2024-07-05 08:11:00 +01:00
Guilherme Carvalho de Azevedo
7d3d022d5a fix(devops-roadmap): typo fixed (Pometheus to Prometheus) (#6056) 2024-07-05 08:09:43 +01:00
mrgsdev
e81571f7fc Update history-and-why-swift@z4-1Gc95JKYAn2RPFc7hw.md (#6034) 2024-07-05 08:08:00 +01:00
Farzad Mohtasham
ed01ffbefa Tanstack/Router Added to React-Router topic (#6030)
* Added Tanstack-Router to the React Routers section
2024-07-05 08:06:38 +01:00
Farzad Mohtasham
1e5b467124 Added Zustand video tutorial for State-management topic (#6028)
Add Zustand links & Higher Order videos.

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-04 16:13:17 +01:00
Konrad
03b6337388 refactor(roadmap/angular): update links to new angular.dev documentation (#6024)
additionally:
- remove duplicated links in some documentations
- improve titles
2024-07-04 15:46:38 +01:00
dsh
9aed682629 add version management subtopic (#6025)
* add version management subtopic

* Update src/data/roadmaps/terraform/content/version-management@6zFuSNOfbPaYIE_t--2nr.md

correct typo

* Update src/data/roadmaps/terraform/content/version-management@6zFuSNOfbPaYIE_t--2nr.md

Co-authored-by: Konrad <kord.stp@gmail.com>

---------

Co-authored-by: Konrad <kord.stp@gmail.com>
2024-07-04 15:45:31 +01:00
dsh
1c515f1d8f altered title and subtitle to reflect devloper advocate (#6026) 2024-07-04 14:48:27 +01:00
dsh
1ebf850882 Adding links and copy to Terraform roadmap (#5914)
* Adding links and copy to Terraform roadmap

* added hcl content

* add resource topic content

* add tf meta-argument content and copy

* add content for variables and outputs

* added more links to areas that are lacking

* Apply suggestions from code review

Corrected styling
2024-07-04 14:18:57 +01:00
Ankita soni
b7b8a935c1 Update 101-iaas-paas-saas.md (#6023)
Add popular youtube video

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-04 09:31:52 +01:00
Abdallah Gaber
3cf0a7ca8a Roadmap: MORE Node.js Content and Resources Updates and Fixes for Nearly Half the Roadmap (#6022)
Corrected type tags and links, formatted content text, added new links.
2024-07-04 09:28:53 +01:00
Kamran Ahmed
fac090c803 Fix height of the tips box 2024-07-04 01:35:13 +01:00
Kamran Ahmed
adc44ed325 Migrate data analyst roadmap 2024-07-04 01:21:18 +01:00
Kamran Ahmed
2c79d85c67 Add progress caching cleanup 2024-07-03 12:44:36 +01:00
Kamran Ahmed
e24f5dfe6a Add devops roadmap 2024-07-03 12:44:36 +01:00
Kamran Ahmed
ad712b2c4a Redraw devops roadmap with editor 2024-07-03 12:44:35 +01:00
ChuYang
f3fda96c15 chore(docs): fix typos and grammar errors for react.md (#6018) 2024-07-03 10:51:23 +01:00
Farzad Mohtasham
db1ba63e6c feat: Added 3 videos to React-Roadmap, For HOC (#6013) 2024-07-03 09:42:46 +01:00
Abdallah Gaber
f63c59d9ee Roadmap: Node.js Content and Resources Updates and Fixes (#6015)
Corrected type tags, added some extra copy, added new links.
---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-03 09:41:10 +01:00
Arik Chakma
72cc28a436 fix: label line break (#6014) 2024-07-02 22:23:07 +01:00
dsh
58e2405fa0 correct FS link to FS roadmap rather than react (#6010) 2024-07-02 13:59:03 +01:00
Amirali Toori
e5ee35acee Addition: [roadmaps/DataAnalyst] Add article for Finding Outliers (#5999) 2024-07-02 13:28:37 +01:00
Subroto Banerjee
a347c1739b Articles on API security, server security and cyber security (#6001)
Add content links.

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-02 13:27:36 +01:00
Timofey Veretnov
10ac77308d Concurrency in Swift by Apple (#6004)
Updated styling and copy.

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-02 13:20:05 +01:00
Nikhil
de6aaa262b DSA Roadmap | Updated Links for 102 & 105 (#6007)
Update 103-quick-sort.md
2024-07-02 13:15:24 +01:00
xaanxex
1fe5512310 Update 108-exception-handling.md (#6008)
added 2 article links
2024-07-02 13:14:02 +01:00
BANO
96b8e109b1 Add viem library to blockchain roadmap (#5975) (#6009)
* Add viem library to blockchain roadmap (#5975)

* Update src/data/roadmaps/blockchain/content/109-dapps/108-client-libraries/index.md

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-02 13:12:10 +01:00
Kamran Ahmed
64e71574d2 Update shout-out text on devrel roadmap 2024-07-02 11:15:44 +01:00
Kamran Ahmed
5913564d94 Remove console.log 2024-07-02 00:16:48 +01:00
Kamran Ahmed
6686e9361c debug code 2024-07-02 00:07:32 +01:00
Kamran Ahmed
e738936b5e Arrow alignment fixes 2024-07-01 23:53:54 +01:00
Kamran Ahmed
b97e2c7ce1 Fix alignment of devrel engineer 2024-07-01 23:08:49 +01:00
Kamran Ahmed
3e312b6aa7 Fix alignment of devrel engineer 2024-07-01 23:04:38 +01:00
Kamran Ahmed
e8a430db47 Update FAQ for devrel engineer 2024-07-01 23:03:46 +01:00
Kamran Ahmed
47e6f8e926 Add link to devrel engineer roadmap in readme 2024-07-01 22:56:02 +01:00
Kamran Ahmed
fa6f4aa6e3 Add DevRel roadmap assets 2024-07-01 22:52:09 +01:00
Kamran Ahmed
cf0d10eeed Add DevRel roadmap 2024-07-01 22:32:59 +01:00
Amirali Toori
38d96682cf Addition: [roadmaps/DataAnalyst] Add article for Correlation Analysis (#5997) 2024-07-01 16:26:40 +01:00
Mrutyunjay Lodhi
61788edcd0 feat: Added Article for flutter internals (#5995) 2024-07-01 11:54:38 +01:00
mrgsdev
c48907c5e0 Update memory-management (#5992)
* Update memory-management@tqbg8mBJfjuXacdMlIB_L.md

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-01 11:40:34 +01:00
Leni Kirilov
90371b081a Java roadmap - multiple fixes (#5957)
Adding copy, content links & remove broken links.

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-01 10:39:44 +01:00
Leni Kirilov
c80591c1cf Java roadmap: jvm clarifications (#5945)
removed duplicate link
added JVM languages and GraalVM as a popular alternative

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-01 10:34:38 +01:00
mrgsdev
4734a8eb02 Update xcode@la5M4VoH79bhnN8qj5Izp.md (#5967)
Added two official videos from the Apple YouTube channel:
- WWDC24: What’s new in Xcode 16
- WWDC24: Xcode essentials
---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-07-01 10:31:25 +01:00
Amirali Toori
b6ceebae9c Add Article for 101-kurtosis.md in Data Science Road map (#5981)
Add a link (article) that describes the topic of Kurtosis.
2024-07-01 10:24:07 +01:00
Amirali Toori
54459a52f2 Add an article to Data Science roadmap about Skewness concept (#5982)
This is a simple and useful article, which I think might be very useful for understanding the concept of skewness.
2024-07-01 10:22:44 +01:00
Amirali Toori
446373532f Addition: [roadmaps/DataScience] Add article for Dispersion (#5983) 2024-07-01 10:22:00 +01:00
Micael Andrade
a69459ba31 fixing broken link (#5985) 2024-07-01 09:54:57 +01:00
Konrad
7f35c2f6f0 docs(contributing): remove a duplicated opensource type (#5971) 2024-06-30 01:49:53 +06:00
dsh
7e2f9d3e6b add linux permissions article and video (#5964) 2024-06-28 16:04:08 +01:00
fellalli
e4d106904e Corrected / Improved C++ roadmap (#5947)
Updated c++ content with `std::` as this is the recommended method. Added content links where needed and corrected various wording and grammar.
2024-06-28 14:26:20 +01:00
MTRX
7d694f3e56 Fix and add links in Computer Science roadmap (#5960)
Corrected daily.dev feed link & added content links.

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-06-28 14:23:44 +01:00
Jhonatan Mustiola
338bce1308 Update 108-hashmap.md (#5958)
Add Rust Hashmap content links.

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-06-28 14:20:57 +01:00
Nikhil
c9d6b36b34 Fixed Typo for DSA Roadmap (#5962)
Added content links and fixed link syntax error.
---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-06-28 14:17:17 +01:00
Ruslan Semagin
2874eb0a42 add links for 'Rust Macros' (#5963) 2024-06-28 14:12:59 +01:00
Kamran Ahmed
a62ed919c1 Add github URL in redirect 2024-06-28 12:36:29 +01:00
Kamran Ahmed
9ecf4a9d78 Add x URL 2024-06-28 12:33:24 +01:00
Kamran Ahmed
2c373c7574 Add links for discord, twitter and youtubeg 2024-06-28 12:18:33 +01:00
Kamran Ahmed
d9cdc95a79 Update product mangaement SEO description 2024-06-27 22:23:19 +01:00
Kamran Ahmed
3af4bde2ea Fix text wrap on TF roadmap 2024-06-27 22:23:19 +01:00
Arik Chakma
1ee6f0e125 feat: add daily dev link in profile (#5948) 2024-06-27 11:52:03 +01:00
Jhonatan Mustiola
9471bf50f9 Update 107-string.md (#5951)
More links were added to the Vector step in the Rust roadmap
---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-06-27 10:04:51 +01:00
Nikhil
f143d800bd DRAFT: Added link(s) for DSA Roadmap (#5935)
* Added various content links

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-06-27 09:55:38 +01:00
Jacob Penner
f7b42a63bf feat: Add content links to API Design roadmap (#5869)
* Add content links to 'What are APIs' section

* Add content links to 'API Documentation Tools' section

* Add content links to 'HTTP' section

* Add content links to 'HTTP Versions' section

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-06-27 09:46:44 +01:00
Leni Kirilov
212be69582 Update 106-files-and-apis.md (#5943)
added newer Files.* API with articles
2024-06-27 09:42:54 +01:00
MTRX
393eb6c87d Add and correct links in C++ resources section (#5950) 2024-06-27 09:40:02 +01:00
Michael Budnikov
fe6e0830eb All additional links at the end of the articles in C++ roadmap have been wrapped with 'Learn more from the following resources:' line as in contribution docs stated (#5949) 2024-06-27 09:36:19 +01:00
Ye Naing Tun
24c4221591 Add Content links to 'Vue' Section (#5934)
* added Vue content links.
2024-06-27 09:34:11 +01:00
Leni Kirilov
7744363cde added enum and records (#5940) 2024-06-26 14:06:55 +01:00
Leni Kirilov
ce6e2ff71e added lambda functions examples and articles (#5941) 2024-06-26 14:03:38 +01:00
Ruslan Semagin
09e345f48b add useful links for 'Rust Error Handling' (#5936) 2024-06-26 09:58:31 +01:00
Nguyen Trong Toan
5dff9b20e1 feat: Update iOS roadmap link to React Native (#5933)
The iOS roadmap link for React Native was updated to the correct URL. This change ensures that users are directed to the appropriate resource for learning React Native on the iOS roadmap.

Authored-by: kai <trongtoan1609ht@gmail.com>
2024-06-25 21:23:38 +01:00
Michael Budnikov
f1d6cd51cd Update index.md: Article about function pointers was added (#5916)
I added the link to the article about function pointers in C++ that helped me to learn more about it and try it on my own.
2024-06-25 16:04:06 +01:00
GGGamesXDlol
045bab002a Updated 100-spline.md: Added a video about splines (#5930)
* Added a video about splines

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-06-25 13:58:55 +01:00
dsh
08b1b48b5e Dansholds/update contrib doc (#5931)
* update to contribution.md
2024-06-25 13:04:01 +01:00
dsh
0b6da0e076 Correct iOS tool/component names (#5922)
* Correct iOS tool/component names

* forgot AVFoundation
2024-06-25 10:20:51 +01:00
Jhonatan Mustiola
520fa2db45 Update 106-vector.md (#5920)
More links were added to the Vector step in the Rust roadmap
2024-06-25 09:30:55 +01:00
Dale Nash
3c160e8809 Add missing space to about page (#5921)
Very minimal but sure, it's valid.
2024-06-25 09:28:55 +01:00
Kamran Ahmed
f682a6e1a2 Fix content in wrong files 2024-06-24 22:52:20 +01:00
Kamran Ahmed
3f655ad424 Add legends to backend roadmap 2024-06-24 20:03:32 +01:00
Kamran Ahmed
5b108f1fd2 chore: redraw backend roadmaps with our editor (#5917)
* Migrate backend roadmap

* Update frontend roadmap

* Fix typo in backend roadmap

* Remove local caching of progress

* Update migration count
2024-06-24 18:32:31 +01:00
dsh
0064d04ff4 Add external links to intro PM topics (#5867)
* add external links to intro PM topics

* lifecycle and development links
2024-06-24 15:51:55 +01:00
Abdallah Gaber
e98ebcfa11 Fix and Update JavaScript Recourses Links (#5896)
* Update JavaScript-asynchronous Promises resources

* Replace broken link

The previous link to the article was outdated and resulted in a 404 error.

* Fixed the JavaScript typo

* Add Video Resource JS 101-debugging-memory-leaks.md

Added a helpful YouTube video on visualizing memory leaks and debugging them in the console.

* Introduce resource (Chrome Developers) JS-debugging-performance

Added link to Chrome Developers documentation on debugging JavaScript performance in JS 102-debugging-performance.md. This provides valuable insights and best practices for optimizing code performance from the official docs.
2024-06-24 10:01:02 +01:00
Blamowizard
64bbbc2f25 Typo/grammar fixes plus copy edits for Rust roadmap (19 files) (#5906)
* Fix typos Rust/100-integers.md

* Grammatical clarity Rust/101-why-rust.md

* Sentence restructuring for Rust/102-memory-safety.md

* Additional linker explanation for newbies, Rust/103-installing-rust.md

* Wording changes, Rust/103-installing-rust.md

* "tools for debugging" -> "debugging tools" Rust/104-ides-and-rust.md

* Small clarity change Rust/105-rust-repl.md

* "systems" -> "system" (for consistency), Rust/101-why-rust.md

* Clarity, Rust/101-variables.md

* Small sentence edits, Rust/102-control-flow.md

* Another small edit, 102-control-flow.md

* Small changes + added `return` keyword info, Rust/103-functions.md

* Rust/103-functions.md

* Clarity/grammar for Rust/104-pattern-matching.md

* Sentence flow + prose about pattern matching, Rust/100-syntax/index.md

* Wording/paragraph improvements, Rust/100-syntax/index.md

* List-ified, italics-ified for Rust/101-ownership/100-rules.md

* Small changes + bullets for Rust/102-stack-heap.md

* List-ify + small clarity improvements, Rust/101-ownership/index.md

* Sentence flow & clarity for Rust/102-constructs/100-enums.md

* Rewrite of Rust/100-enums.md

* a -> an

* List-ify and small edits for Rust/101-structs.md

* Bold some stuff in Rust/101-structs.md

* Small rewrite for Rust/102-traits.md

* Rewrite Rust/103-impl-blocks.md

* List-ify + clarity edits for Rust/102-constructs/index.md

* More data types explanation for Rust/102-constructs/index.md

* define -> declare

* Update index.md

* Unbolded “traits”

* Unbolded “enum” + replaced em-dashes with commas

* “Rust is a system…”

* Replaced em-dashes with commas

* Update 102-control-flow.md

Replaced more em-dashes with commas

* Unbold “struct”

* Unbold “constructs”
2024-06-24 09:59:19 +01:00
Kamran Ahmed
2da1f61945 Update progress nudge number 2024-06-23 15:40:51 +01:00
Kamran Ahmed
894b66f026 Update progress 2024-06-23 15:22:15 +01:00
Kamran Ahmed
f5fc71aadb Redraw frontend roadmap with our editor (#5897)
* Update frontend roadmap

* Migrate content for frontend roadmap

* Add a button for beginner friendly version

* Frontend roadmap

* Implement beginner version of frontend roadmap

* Clear progress for roadmaps

* Update
2024-06-23 14:55:09 +01:00
Amit Merchant
ec9bebbcda Add article "Unlimited function parameters using Rest" (#5908) 2024-06-23 12:22:26 +01:00
Kamran Ahmed
9cf940e741 Remove badge from backend questions 2024-06-22 03:29:33 +01:00
Kamran Ahmed
f4b157b328 Update new badges 2024-06-22 03:20:05 +01:00
Kamran Ahmed
4c54e20a11 Add terraform assets 2024-06-21 21:26:15 +01:00
Kamran Ahmed
c4cc0630c0 Add basic content for hcl 2024-06-21 21:26:15 +01:00
Kamran Ahmed
a637805a24 Add directory structure for terraform roadmap 2024-06-21 21:26:15 +01:00
Kamran Ahmed
8604810a2e Add directory structure 2024-06-21 21:26:15 +01:00
Kamran Ahmed
a2481f7681 Add terraform roadmap 2024-06-21 21:26:15 +01:00
Nikita Ivanov
88926c9ba5 Fix answer for backend question about eventual consistency (#5904) 2024-06-21 18:47:10 +01:00
Ed Lan
faf12dcf8e Update faqs.astro (#5898)
Copy update
2024-06-20 15:06:09 +01:00
Kamran Ahmed
70d3e6cd39 Redraw frontend roadmap fork 2024-06-20 12:51:27 +01:00
Jhonatan Mustiola
b1d790739f More links to steps in the Rust Roadmap (#5894)
More links were added to the array step in the Rust roadmap
2024-06-20 09:46:26 +01:00
Ante Barić
6d983167c8 feat: daily.dev links (#5860)
* feat: add daily.dev link type

* feat: replace to feed label

* feat: add links to different pages
2024-06-19 15:58:20 +01:00
Kamran Ahmed
c935e2457e Add tracking of topic resource clicks 2024-06-19 12:24:34 +01:00
Ed Lan
d21e01805e Update backend.md (#5890)
Small copy tweak
2024-06-19 09:39:54 +01:00
Kamran Ahmed
b31b4e2a11 Update ios roadmap 2024-06-18 12:17:19 +01:00
Nikita Ivanov
94b245b2cf Fix link to containerization question's answer (#5885)
Corrected content for Containerization question from stateless-http to containerization.
2024-06-17 16:13:53 +01:00
dsh
f37cc57177 Add new authors; Fix devops guide routing issue (#5883)
* add william and kenny as authors

* fixed routing issue from /authors on new devops guides
2024-06-17 15:50:04 +01:00
Ed Lan
533e93e647 Update faqs.astro (#5882)
Tweak to add link to new DevOps guide.
2024-06-17 14:51:45 +01:00
Abdallah Gaber
6f6b942ba4 Update JavaScript-asynchronous resources (#5871)
Added links to two videos for better understanding. One video features comprehensive and visually appealing content, while the other from JSConf provides visuals suited for beginners.
2024-06-17 09:50:03 +01:00
Ebrahim Gamal
5cbbaa61a9 Update 100-jetpack-compose.md (#5873)
I added video resource for jetpack compose.
---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-06-17 09:48:27 +01:00
VIKASH LAL
e0fa460ab9 Add resource for DOM (#5232)
* Update 102-shadow-dom.md

These changes are important from a student's perspective.

* Update src/data/roadmaps/frontend/content/113-web-components/102-shadow-dom.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-06-17 09:40:14 +01:00
dsh
41a3f85ac2 Add iOS media layer topic content (#5846)
* add iOS media layer topic content

* Add copy around Xcode and Media Layer
2024-06-17 09:38:03 +01:00
dsh
8e2515a84b Add devops vs sre guide (#5854) 2024-06-17 09:37:36 +01:00
Snoppy
0e8613daae chore: fix typos (#5881)
Signed-off-by: snoppy <michaleli@foxmail.com>
2024-06-17 07:24:11 +01:00
Kazuki Kurahashi
3dc08388d9 Fix typo 101-hashing-encryption-encoding.md (#5878) 2024-06-16 08:39:44 +01:00
Wesley Blake
714b604546 Update 103-data-transformation.md (#5855)
Additionally, links to examples in Python and R
2024-06-14 22:01:35 +01:00
Jhonatan Mustiola
89d22aa127 Update 101-floats.md (#5859)
Added more links. Content keeps the copy
2024-06-14 14:22:51 +01:00
Ruslan Semagin
cb8f380dc0 link to 'Docker Multi-stage builds' (#5863) 2024-06-13 15:18:13 +01:00
Kamran Ahmed
b4f84b448d Add product manager roadmap to get-started and roadmaps pages 2024-06-12 23:39:07 +01:00
dsh
235c571347 Fix typos in product management roadmap (#5862)
* correct pm goal types

* corrected typos

* swapped project and product

* swap project and product on title

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-06-12 18:49:58 +01:00
fellalli
3025e17e4c Add Product Manager Roadmap to readme.md (#5861) 2024-06-12 18:48:15 +01:00
Kamran Ahmed
86947d83d7 Product vs project management 2024-06-12 18:41:40 +01:00
Yash Deore
0ab46ae861 Update 100-instance-types.md (#5853)
* Update 100-instance-types.md

Updated with ec2 instance types

* Update 100-instance-types.md

added @official@ documentation for ec2 instance types

* Update 100-instance-types.md

* Update src/data/roadmaps/aws/content/101-ec2/100-instance-types.md

Ok sir

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-06-12 16:36:52 +01:00
Kamran Ahmed
2046695479 Add product manager roadmap 2024-06-12 15:30:05 +01:00
Kamran Ahmed
3ed9bdb85e Resolve merge conflicts 2024-06-12 15:29:54 +01:00
Kamran Ahmed
a747a8108d Add product manager roadmap 2024-06-12 15:29:20 +01:00
Wesley Blake
17f5ca3cb0 Add resources for swap (#5850)
I thought these two were the most interesting articles on the subject.

Also provides examples on how to do it.
2024-06-11 12:16:54 +01:00
Grigory
4b12137077 docs(typescript/build-tools): update tsup link (#5851) 2024-06-11 10:56:56 +01:00
dsh
f08eae2632 Add how to become devops engineer guide (#5847)
* add how to become devops engineer guide

* Update src/data/guides/how-to-become-devops-engineer.md

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>

* Update src/data/guides/how-to-become-devops-engineer.md

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>

* Update src/data/guides/how-to-become-devops-engineer.md

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>

* Update src/data/guides/how-to-become-devops-engineer.md

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>

* Update src/data/guides/how-to-become-devops-engineer.md

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>

* add newline before and after all headings

* remove double empty lines

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-06-11 10:56:28 +01:00
Kamran Ahmed
6f4ab78f47 Add sponsor logic for hiding 2024-06-10 18:29:35 +01:00
Kamran Ahmed
855365d897 Topic links refactoring 2024-06-09 18:30:57 +01:00
shohan kazi
8403bf7a04 Update 107-ruby.md (#5841) 2024-06-09 09:18:31 +01:00
Swapnil Sinha
042ba11870 Add content to vercel (#5834)
Documentation addition on vercel platform.
2024-06-08 13:28:25 +01:00
Maciek Sitkowski
2fbec21378 Fix typos in frontend questions about SSR (#5837) 2024-06-08 11:08:50 +01:00
JesusG16
178826683c Update resource link for OOP section (#5833)
One of the links for the introduction to object oriented programming no longer leads to the site it used to but to an unrelated one, I found a link I think would be suitable for this.
2024-06-08 09:59:42 +01:00
Kamran Ahmed
37e5cbf315 Add link to frontend questions 2024-06-07 17:46:31 +01:00
dsh
a836a1c4b5 Add iOS content (#5829)
* add iOS content

* add GitHub article

* Update src/data/roadmaps/ios/content/functional-programming@Pj-hqRZUmwx1WhmTbLoFD.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-06-07 16:42:48 +01:00
Kamran Ahmed
86e3921ca4 Add frontend questions (#5831) 2024-06-07 16:38:06 +01:00
Sauradip Ghosh
e765771500 Update 101-alter-table.md (#5830) 2024-06-07 15:30:46 +01:00
dsh
a4000539f6 Add iOS content (#5828)
* update iOS content, correct tcp/ip title

* add swift interoperability content
2024-06-07 14:02:13 +01:00
Ruslan Semagin
66ff58f42d add useful links for Rust ORM (#5827) 2024-06-07 11:22:04 +01:00
Ruslan Semagin
6a46b9c084 remove broken import (#5824) 2024-06-07 09:53:36 +01:00
Arik Chakma
4254446552 fix: remove is-mobile lib (#5826) 2024-06-07 09:53:26 +01:00
Arik Chakma
caf2f14e54 feat: implement mobile impressions (#5818)
* add resource link for React Native Text component (#5773)

* add resource link for React Native Text component

---------

Co-authored-by: Ruslan Semagin <pixel.365.24@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
Co-authored-by: Ruslan Semagin <53819609+pixel365@users.noreply.github.com>
Co-authored-by: Suman Kisku <sumankisku1@gmail.com>

* feat: add useful links for Rust (#5781)

* feat: add links about testing in Rust (#5791)

* Patch 1 (#5792)

Update 120-real-time-data.md (#5782)

Add links to pages containing brief explanations on the topics listed here.

Update well-architected framework.

---------

Co-authored-by: devgru-3-2 <95485002+devgru-3-2@users.noreply.github.com>
Co-authored-by: Danrley Senegalha Pires <dan.osp@outlook.com>

* Fix: Standardize using "docker container ls" command when referencing listing containers (#5787)

* Add reference link to React Native ImageBackground component (#5795)

* add reference link to React Native ImageBackground component

---------

Co-authored-by: Ruslan Semagin <pixel.365.24@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
Co-authored-by: Ruslan Semagin <53819609+pixel365@users.noreply.github.com>
Co-authored-by: Suman Kisku <sumankisku1@gmail.com>

* add links to the 'Modules and Crates' node in the Rust roadmap (#5797)

* fix broken go topic (#5800)

* feat: change the description and links in the 'log/slog' node (#5798)

* change the description and links in the 'log/slog' node
---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

* add links for 'awk' (#5801)

* add links for 'grep' (#5808)

* Update ci-cd.md (#5807)

* Update Property Binding to most recent link (#5774)

Update 101-property-binding.md

* Added video resources for React Components (#5810)

Signed-off-by: Archit Sharma <74408634+iArchitSharma@users.noreply.github.com>

* Update 104-reference-vars.md (#5775)

* fix: return response status code (#5815)

* feat: implement topic link's label (#5817)

* feat: implement mobile impressions

* fix: add to the body

---------

Signed-off-by: Archit Sharma <74408634+iArchitSharma@users.noreply.github.com>
Co-authored-by: Juan Gerardo Eulufi Salazar <juan.eulufi.sa@gmail.com>
Co-authored-by: Ruslan Semagin <pixel.365.24@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
Co-authored-by: Ruslan Semagin <53819609+pixel365@users.noreply.github.com>
Co-authored-by: Suman Kisku <sumankisku1@gmail.com>
Co-authored-by: Yash Deore <152061059+yashdeored@users.noreply.github.com>
Co-authored-by: devgru-3-2 <95485002+devgru-3-2@users.noreply.github.com>
Co-authored-by: Danrley Senegalha Pires <dan.osp@outlook.com>
Co-authored-by: Anthony Pun <apun97@yahoo.com>
Co-authored-by: Sion Kang <siontama@gmail.com>
Co-authored-by: Liliana Santos <liliana.t.santos@hotmail.com>
Co-authored-by: Archit Sharma <74408634+iArchitSharma@users.noreply.github.com>
2024-06-06 23:48:22 +01:00
Kamran Ahmed
6372990f76 Fix types of resources 2024-06-06 23:31:19 +01:00
Kamran Ahmed
390db65e32 Add types to links 2024-06-06 23:23:55 +01:00
Kamran Ahmed
0a579b4507 Remove geeksforgeeks links 2024-06-06 23:09:32 +01:00
Kamran Ahmed
1b79141b47 fix invalid urls 2024-06-06 23:05:23 +01:00
Kamran Ahmed
dfef66f4b5 Update topic assignment 2024-06-06 22:55:17 +01:00
Kamran Ahmed
458ae33eec Add frontend roadmap node types 2024-06-06 22:52:36 +01:00
Kamran Ahmed
4cc879104f Remove paid resources 2024-06-06 22:40:50 +01:00
Kamran Ahmed
1ac8a86f1c Remove codecademy links 2024-06-06 22:13:25 +01:00
Kamran Ahmed
79e7c10ad9 Change UI for topic labels 2024-06-06 21:32:56 +01:00
José Vivas
03d9e62aaf Add sets to 102-built-in-types.md (#5806)
* Update 120-real-time-data.md (#5782)

Added links to pages containing brief explanations on the topics listed here.

* fix: correct spelling from "Synatx" to "Syntax" (#5784)

* Update 102-built-in-types.md

---------

Co-authored-by: devgru-3-2 <95485002+devgru-3-2@users.noreply.github.com>
Co-authored-by: Danrley Senegalha Pires <dan.osp@outlook.com>
2024-06-06 21:05:11 +01:00
Arik Chakma
c68823c478 feat: implement topic link's label (#5817) 2024-06-06 21:05:11 +01:00
M-DEV-1
3af2a6b6bc added content to api security (#5814)
* added content to api security

Added a link to OWASP API Security Top 10 which lists 10, major problems with API security. It is a good resource to understand API security and the problems associated with it.

* Update src/data/roadmaps/api-design/content/api-security@qIJ6dUppjAjOTA8eQbp0n.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-06-06 19:01:17 +01:00
Kamran Ahmed
6644d8266e Remove deployments on master push 2024-06-06 18:57:45 +01:00
Arik Chakma
d2e3fee99a fix: return response status code (#5815) 2024-06-06 18:57:45 +01:00
Liliana Santos
ed40bf51b0 Update 104-reference-vars.md (#5775) 2024-06-06 18:57:45 +01:00
Archit Sharma
f90630c566 Added video resources for React Components (#5810)
Signed-off-by: Archit Sharma <74408634+iArchitSharma@users.noreply.github.com>
2024-06-06 18:57:45 +01:00
Liliana Santos
c9ce2eedb1 Update Property Binding to most recent link (#5774)
Update 101-property-binding.md
2024-06-06 18:57:45 +01:00
Sion Kang
d5249cc90e Update ci-cd.md (#5807) 2024-06-06 18:57:45 +01:00
Ruslan Semagin
1bc3464102 add links for 'grep' (#5808) 2024-06-06 18:57:45 +01:00
Ruslan Semagin
3c3b0c02a8 add links for 'awk' (#5801) 2024-06-06 18:57:45 +01:00
Ruslan Semagin
bfd0343ee9 feat: change the description and links in the 'log/slog' node (#5798)
* change the description and links in the 'log/slog' node
---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-06-06 18:57:45 +01:00
dsh
3ec301f2f5 fix broken go topic (#5800) 2024-06-06 18:57:45 +01:00
Ruslan Semagin
5a23d4d326 add links to the 'Modules and Crates' node in the Rust roadmap (#5797) 2024-06-06 18:57:45 +01:00
Juan Gerardo Eulufi Salazar
03bf058dd7 Add reference link to React Native ImageBackground component (#5795)
* add reference link to React Native ImageBackground component

---------

Co-authored-by: Ruslan Semagin <pixel.365.24@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
Co-authored-by: Ruslan Semagin <53819609+pixel365@users.noreply.github.com>
Co-authored-by: Suman Kisku <sumankisku1@gmail.com>
2024-06-06 18:57:45 +01:00
Anthony Pun
15b0e33542 Fix: Standardize using "docker container ls" command when referencing listing containers (#5787) 2024-06-06 18:57:45 +01:00
Yash Deore
be6b0128b1 Patch 1 (#5792)
Update 120-real-time-data.md (#5782)

Add links to pages containing brief explanations on the topics listed here.

Update well-architected framework.

---------

Co-authored-by: devgru-3-2 <95485002+devgru-3-2@users.noreply.github.com>
Co-authored-by: Danrley Senegalha Pires <dan.osp@outlook.com>
2024-06-06 18:57:45 +01:00
Ruslan Semagin
b67cb99f41 feat: add links about testing in Rust (#5791) 2024-06-06 18:57:45 +01:00
Ruslan Semagin
d95c1d66f0 feat: add useful links for Rust (#5781) 2024-06-06 18:57:45 +01:00
Juan Gerardo Eulufi Salazar
4a2130d7d0 add resource link for React Native Text component (#5773)
* add resource link for React Native Text component

---------

Co-authored-by: Ruslan Semagin <pixel.365.24@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
Co-authored-by: Ruslan Semagin <53819609+pixel365@users.noreply.github.com>
Co-authored-by: Suman Kisku <sumankisku1@gmail.com>
2024-06-06 18:57:45 +01:00
Danrley Senegalha Pires
a16d781681 fix: correct spelling from "Synatx" to "Syntax" (#5784) 2024-06-01 12:02:51 +01:00
devgru-3-2
65d7d06d2c Update 120-real-time-data.md (#5782)
Added links to pages containing brief explanations on the topics listed here.
2024-06-01 12:02:24 +01:00
Kamran Ahmed
4c615f85e5 Add functionality to go next and back on questions 2024-05-31 01:35:36 +01:00
Ruslan Semagin
a14d8b5f90 feat: Add useful links to documentation on serialization/deserialization in Rust (#5767)
Add Rust web framework doc links.

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-30 09:25:41 +01:00
Kamran Ahmed
eaebe7babd Add missing iOS roadmap to roadmaps and /get-started 2024-05-29 23:48:35 +01:00
Arik Chakma
bab4a1581d fix: show login popup for logged out user (#5764) 2024-05-29 22:54:13 +01:00
Juan Gerardo Eulufi Salazar
bb6d34407d Add link to Angular template statements section (#5763)
* feat: add useful links for learning generics in golang

* highlight teams on mobile view

* feat: add useful links for learning pointers in golang (#5747)

* feat: add useful links for learning pointers in golang

* feat: marking "in progress" should not close popup #5730 (#5742)

* add links to angular section

* add link to Angular template statements section

---------

Co-authored-by: Ruslan Semagin <pixel.365.24@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
Co-authored-by: Ruslan Semagin <53819609+pixel365@users.noreply.github.com>
Co-authored-by: Suman Kisku <sumankisku1@gmail.com>
2024-05-29 21:06:54 +01:00
dsh
0d94d99d4b add copy and links to architecture topics (#5755)
* add copy and links to architecture topics

* made content more concise for iOS architecture topics
2024-05-29 19:31:29 +01:00
Kamran Ahmed
7dc6135416 Update topic detail 2024-05-29 19:31:29 +01:00
Kamran Ahmed
bfea73d372 Fix topic details not loading for custom roadmaps (#5762)
* Update AI example roadmaps

* feat: add useful links to documentation on Rust web frameworks (#5756)

Add Rust web framework doc links.

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

* Add links to readme.md (ios-roadmap, backend-questions) (#5758)

Add iOS roadmap and backend questions to readme.md.

* Fix content not shown for custom roadmaps

* Update topic detail

---------

Co-authored-by: Ruslan Semagin <53819609+pixel365@users.noreply.github.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
Co-authored-by: fellalli <fellalli@web.de>
2024-05-29 19:28:25 +01:00
Kamran Ahmed
e641f06823 Fix content not shown for custom roadmaps 2024-05-29 19:11:59 +01:00
fellalli
0c32730424 Add links to readme.md (ios-roadmap, backend-questions) (#5758)
Add iOS roadmap and backend questions to readme.md.
2024-05-29 19:11:59 +01:00
Ruslan Semagin
b639cfd6d4 feat: add useful links to documentation on Rust web frameworks (#5756)
Add Rust web framework doc links.

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-05-29 19:11:59 +01:00
Kamran Ahmed
c7dc0ae97d Update AI example roadmaps 2024-05-29 01:23:50 +01:00
Kamran Ahmed
e5f7628087 Fix custom roadmap marking as done not working 2024-05-29 01:05:58 +01:00
Kamran Ahmed
158e9b1ed3 Add iOS roadmap 2024-05-29 00:29:50 +01:00
Kamran Ahmed
bb848de581 Add iOS roadmap 2024-05-29 00:29:50 +01:00
fellalli
a3999d04dd Update 104-ides-and-rust.md (#5748) 2024-05-29 00:29:50 +01:00
Juan Gerardo Eulufi Salazar
190a87355e add links to angular section (#5751) 2024-05-29 00:29:50 +01:00
Suman Kisku
4a46e5e170 feat: marking "in progress" should not close popup #5730 (#5742) 2024-05-29 00:29:50 +01:00
Ruslan Semagin
627fb1deb0 feat: add useful links for learning pointers in golang (#5747)
* feat: add useful links for learning pointers in golang
2024-05-29 00:29:50 +01:00
Dan Holdsworth
00ef6bb3a0 highlight teams on mobile view 2024-05-29 00:29:50 +01:00
Ruslan Semagin
a6e8a777e6 feat: add useful links for learning generics in golang 2024-05-29 00:29:50 +01:00
Kamran Ahmed
35ef88e626 Add og images to backend articles 2024-05-27 15:35:04 +01:00
Kamran Ahmed
ba630173b8 Merge branch 'develop' of github.com:kamranahmedse/developer-roadmap into develop 2024-05-27 14:05:52 +01:00
Kamran Ahmed
073ba617ed Add gRPC to api design roadmap 2024-05-27 14:02:56 +01:00
Kamran Ahmed
13744a486a Nudge users to onboard 2024-05-27 14:02:56 +01:00
Kamran Ahmed
16e69a39d5 Add gRPC to api design roadmap 2024-05-27 14:02:18 +01:00
Kamran Ahmed
6cb543ec7d Nudge users to onboard 2024-05-27 12:51:21 +01:00
Kamran Ahmed
268acda75b Add progress nudge on roadmap 2024-05-27 12:38:22 +01:00
Artem745
0167347277 Update built-in modules text (#5737)
The paragraph was about built-in functions, not modules
2024-05-27 12:38:22 +01:00
Kamran Ahmed
8d3c6f946e Update dependencies and fix bug in overlay 2024-05-27 12:38:22 +01:00
Kablys
b2c4bcad34 Fix broken link (#5728) 2024-05-27 12:38:22 +01:00
Kamran Ahmed
6728010173 Add progress nudge on roadmap 2024-05-27 12:37:23 +01:00
Artem745
9895956531 Update built-in modules text (#5737)
The paragraph was about built-in functions, not modules
2024-05-27 11:31:17 +01:00
Kamran Ahmed
0bb784c45b Update dependencies and fix bug in overlay 2024-05-27 10:58:05 +01:00
Kamran Ahmed
0dc6128b8e Fix canonical url 2024-05-27 10:38:57 +01:00
Kamran Ahmed
61eb915fb2 Fix formatting 2024-05-27 10:38:57 +01:00
Dan Holdsworth
04f39d4e91 update contribution doc with develop rules 2024-05-27 10:38:57 +01:00
Kamran Ahmed
f14c945ff9 Fix canonical url 2024-05-27 10:38:45 +01:00
Kamran Ahmed
279aa5c8a7 Fix formatting 2024-05-27 10:38:45 +01:00
Kablys
bbe66a646f Fix broken link (#5728) 2024-05-25 11:22:49 +01:00
dsh
a5a4c9335a Merge pull request #5726 from kamranahmedse/dansholds/update-contrib-doc
update contribution doc with develop rules
2024-05-24 12:29:59 +01:00
Dan Holdsworth
56912f6ed1 update contribution doc with develop rules 2024-05-24 12:27:42 +01:00
Kamran Ahmed
e51ea1ed61 Add backend questions 2024-05-24 12:04:51 +01:00
dsh
ac2b99062e Merge pull request #5724 from wandrey7/patch-1
remove A link is not working 100-event-loop.md
2024-05-24 09:23:25 +01:00
Wandrey
3d17e8f290 Update 100-event-loop.md 2024-05-23 15:47:54 -03:00
dsh
e46ae3bd6e Merge pull request #5722 from zongsforce/patch-2
modify the sample code
2024-05-23 15:03:17 +01:00
zongsforce
38c43c1c95 modify the sample code
the original sample code's complexity is O(n) instead of O(n!)
2024-05-23 21:44:24 +08:00
Kamran Ahmed
7acdbcb4c9 Fix z-indexes 2024-05-23 11:46:24 +01:00
Kamran Ahmed
ee8fb3414a Fix overlaying account button 2024-05-23 11:17:30 +01:00
dsh
ba2f989fa8 Highlight Teams Navigation item over AI item (#5720) 2024-05-23 10:51:35 +01:00
dsh
8c9259fa1d Merge pull request #5716 from Skyth3r/fix-docker-roadmap-intro-typo
Fixed typo in Docker roadmap
2024-05-22 13:36:56 +01:00
Akash Goswami
edb8194707 Fixed typo in Docker roadmap 2024-05-22 13:28:03 +01:00
dsh
83399589c4 Merge pull request #5713 from YutharsanS/patch-1
Change of content in 104-exponential.md
2024-05-22 11:39:30 +01:00
Yutharsan
5b496e8403 Change of content in 104-exponential.md
There's a mistake in the example that has been provided.  According to the complexity analysis, the current example yields the exponential time complexity, not the previous one.
2024-05-22 11:12:31 +05:30
Kamran Ahmed
359e3e1900 Allow transferring roadmap between teams 2024-05-21 17:49:40 +01:00
Arik Chakma
f718d1895f fix: api design rate typo (#5711) 2024-05-21 16:37:51 +01:00
dsh
1b79a91295 Merge pull request #5709 from trueLoving/master
docs(frontend-performance): fix 'cssnano' website link
2024-05-21 15:30:53 +01:00
starsky
4180104402 docs(frontend-performance): fix 'cssnano' website link 2024-05-21 22:02:57 +08:00
dsh
f831258893 Merge pull request #5707 from udohjeremiah/patch-1
Correct link for Expo CLI Quickstart
2024-05-21 11:47:38 +01:00
Udoh Jeremiah
f04e0b2269 Update index.md 2024-05-21 11:47:31 +01:00
Udoh Jeremiah
ad1f1aaa5a Correct link for Expo CLI Quickstart 2024-05-21 11:39:52 +01:00
Kamran Ahmed
1943227f21 Upgrade pnpm 2024-05-21 10:21:30 +01:00
dsh
7aa44d3197 Merge pull request #5705 from BorzooMV/master
Fix Mutex typo in Golang
2024-05-21 10:00:26 +01:00
Borzoo Moazami
452ad7b06b docs(golang): fix Mutex typo 2024-05-20 18:32:11 +03:30
github-actions[bot]
e86b660e05 chore: update dependencies to latest (#5699)
Co-authored-by: kamranahmedse <kamranahmedse@users.noreply.github.com>
2024-05-20 14:40:07 +01:00
Kamran Ahmed
498b653346 Add playwright and remove vue-relay 2024-05-20 14:39:37 +01:00
dsh
303e92dceb Merge pull request #5701 from kamranahmedse/dansholds/5700-missing-ddl-subtopic
add DROP table sub-topic
2024-05-20 10:18:51 +01:00
Dan Holdsworth
f222ebddea add DROP table sub-topic 2024-05-20 10:17:48 +01:00
51L3N7
ca40b403a5 Fix broken formkit domain (#5698)
The old formkit domain has been changed to a new one
2024-05-19 01:10:20 +01:00
dsh
e3a1e1313c Merge pull request #5696 from kamranahmedse/5695-design-patterns-flutter-roadmap
remove broken video link
2024-05-17 22:21:30 +01:00
Dan Holdsworth
814f357021 remove broken video link 2024-05-17 22:20:48 +01:00
Arik Chakma
1af9829c04 Fix renderer not working on local (#5694) 2024-05-17 20:25:46 +01:00
dsh
c277ac3746 Merge pull request #5693 from kamranahmedse/dansholds/5692-links-redirecting-wrong
Remove '!' from youtube links
2024-05-17 19:28:05 +01:00
Dan Holdsworth
9f19229a22 remove '!' from youtube links 2024-05-17 19:25:06 +01:00
Kamran Ahmed
10be8820cb Add API design seo text 2024-05-17 18:07:52 +01:00
Kamran Ahmed
5d909a6023 Update team page URL in onboarding 2024-05-17 17:48:26 +01:00
Kamran Ahmed
ccb57c5ae1 Update API design URLs 2024-05-17 17:38:39 +01:00
Kamran Ahmed
fc277bb32a Add API design roadmap link 2024-05-17 17:37:07 +01:00
Kamran Ahmed
e7a17cf74f Rename API roadmap to API Design roadmap 2024-05-17 17:36:31 +01:00
Kamran Ahmed
5e50ffbc30 feat: add custom renderer for roadmaps (#5691)
* wip

* fix: update packages

* wip

* wip

* feat: editor content generator

* fix: add dimensions

* feat: add renderer

* feat: add progress modal renderer

* Add API design roadmap

* Update API roadmap rendering

* fix: button click

* fix: link item

* feat: render pdf for editor roadmaps

* Add API roadmap

* Fix broken link of full-stack roadmap

* Update content dir

* Fix typos in api roadmap

* Add assets for pdf and svg

* Add content for api roadmap

* Add todo

* fix: close on editor roadmap select

* Update link not working

* Add api roadmap to get-started and roadmaps page

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
2024-05-17 17:28:24 +01:00
Mohamed Salman
375ad931f7 Update local development command (#5687)
* Updated readme.md file with cd command to move into that folder before running further npm command. Also removed roadmap from go

* Addressed review comment - Reverted changes in index.astro file
2024-05-17 12:04:12 +01:00
Wesley Blake
05eab5823e Add DATEDIF disclaimer (#5686)
Asterisk for DATEIF function, doesn't show as a regular function, but can still be used.
2024-05-17 00:02:42 +01:00
月光xia漫步
9b7512bbba Update code example in C++ roadmap (#5680) 2024-05-16 14:27:26 +01:00
Mohammad Kaleaji
3a976663f2 Add resource to tester mindset (#5682)
* Update 101-tester-mindset.md

Adding a reference from ISTQB to the QA Roadmap (Tester Mindset) Section

* Update src/data/roadmaps/qa/content/100-qa-basics/101-tester-mindset.md

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-05-16 14:26:21 +01:00
dsh
ebff5490b3 Merge pull request #5684 from kamranahmedse/dansholds/5683-deprecated-links-in-frontend-http-caching
remove all varvy links
2024-05-16 13:41:43 +01:00
Dan Holdsworth
d5c8a4554c remove all varvy links 2024-05-16 13:40:29 +01:00
dsh
7cd3bddeeb Merge pull request #5679 from Edlan01/patch-5
Updated og image
2024-05-16 10:59:34 +01:00
dsh
8af6a9ae58 Merge pull request #5678 from Edlan01/patch-4
Updated og image
2024-05-16 10:59:05 +01:00
Ed Lan
60d19584ee Updated og image 2024-05-16 10:48:14 +02:00
Ed Lan
ee982bf807 Updated og image 2024-05-16 10:46:33 +02:00
dsh
0467e59b28 Merge pull request #5675 from salmantec/bugfix/move-flutter-and-reactnative-to-skill-roadmap
Move Flutter and React Native to Skill-based roadmap
2024-05-16 07:03:19 +01:00
dsh
aed19d84b5 Merge pull request #5676 from mhdabdurahiman/patch-1
fixing typo Angular 101-why-use-typescript.md
2024-05-16 06:59:07 +01:00
mhdabdurahiman
aee2ca2e47 fixing typo Angular 101-why-use-typescript.md
In the doc, there was a typo, where, a 'C' was missing in the word 'compile'.
2024-05-16 09:53:56 +05:30
Mohamed Salman
b6bfbf3090 Updated Flutter and ReactNative from role to skill based skills in RoadmapsPage.tsx, flutter.md and react-native.md files 2024-05-16 09:14:45 +05:30
dsh
61089c9a09 Update/Refresh contribution guide (#5671)
* Update/Refresh contribution guide

* Add link to draw.roadmap.sh

* Update contributing.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-15 10:20:25 +01:00
Petra Donka
9d943ed773 Fix typos in prisma content file (#5670) 2024-05-15 10:15:41 +01:00
dsh
6e5ba6e892 Merge pull request #5667 from aman-yadav-05/patch-1
Update 101-linked-lists.md
2024-05-15 09:33:02 +01:00
dsh
dced08f0f6 Merge pull request #5669 from MohammadKaleaji/patch-1
Update 103-test-oracles.md
2024-05-15 09:25:29 +01:00
Aman Yadav
1bca8e4bfa Update src/data/roadmaps/datastructures-and-algorithms/content/103-basic-data-structures/101-linked-lists.md
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-05-15 13:55:12 +05:30
dsh
35b99cf6c0 Merge pull request #5668 from mubashir-Interware/patch-1
Update artifacts spelling mistake
2024-05-15 09:23:54 +01:00
Mohammad Kaleaji
37e866ed6e Update 103-test-oracles.md 2024-05-15 10:35:37 +03:00
Aman Yadav
f83ba31af5 added "learn more" and a youtube link along with the article. 2024-05-15 12:32:54 +05:30
mubashir-Interware
f1b7232d37 Update artifacts spelling mistake 2024-05-15 11:28:38 +05:00
Aman Yadav
f910756d35 Update 101-linked-lists.md
Added a link to an article which helped me understand more about the concept of linked list.
2024-05-15 11:23:46 +05:30
dsh
32b0159d9d Merge pull request #5664 from kamranahmedse/dansholds/add-poetry-content
Add Poetry package manager content
2024-05-14 15:23:31 +01:00
Dan Holdsworth
36bef45b5e remove poetry from index.md 2024-05-14 11:35:24 +01:00
Dan Holdsworth
0b177f971f Add Poetry package manager content 2024-05-14 11:31:12 +01:00
dsh
2c54c988ce Merge pull request #5662 from kamranahmedse/dansholds/add-rust-links
add documentation links to data-structure pages
2024-05-14 10:15:40 +01:00
Dan Holdsworth
4883530087 add documentation links to data-structure pages 2024-05-14 10:14:08 +01:00
Kamran Ahmed
2daa7cc327 chore: trigger build 2024-05-14 00:04:17 +01:00
Kamran Ahmed
fdeb6f9cd8 Make /team/member behind auth 2024-05-13 20:35:49 +01:00
Arik Chakma
f8cdd76fa9 feat: onboarding for new users (#5629)
* wip

* feat: add onboarding

* feat: implement onboarding

* Update indicator design

* Update UI for onboarding dropdown

* Changes to onboarding UI

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-13 19:50:43 +01:00
Andrii
67fbba4708 Fix resource links (#5660) 2024-05-13 17:08:57 +01:00
Kamran Ahmed
38cb3d2df6 Add banner for docker 2024-05-13 12:58:12 +01:00
Kamran Ahmed
fa589fd78f Update Vue roadmap 2024-05-13 12:19:13 +01:00
Kamran Ahmed
d53a4e8c79 Add v-model to vue roadmap 2024-05-13 12:08:00 +01:00
Kamran Ahmed
ba3803ab8c Add updates to vue roadmap 2024-05-13 12:03:42 +01:00
Kamran Ahmed
433e53926c Add java date and time 2024-05-13 11:33:53 +01:00
Kamran Ahmed
22d4f18e97 Add script to warmup URLs 2024-05-13 01:38:37 +01:00
Kamran Ahmed
4a40d89783 Add script to warmup og 2024-05-13 01:12:05 +01:00
Kamran Ahmed
fad7133959 Generate PNG images for og 2024-05-13 00:34:05 +01:00
Arik Chakma
6804c6ec00 feat: team member progress modal (#5651)
* feat: restrict members

* feat: member progress modal
2024-05-12 14:48:12 +01:00
Arik Chakma
de89e56a47 fix: edit in editor button (#5652) 2024-05-12 17:29:47 +06:00
Wagner Goulart
97e0059475 Add angular RxJS diagram videos (#5649)
* ADD: Videos explainning how marble diagrams works

Signed-off-by: Wagner <wagnergoulart0@gmail.com>

* Update src/data/roadmaps/angular/content/101-rxjs-basics/102-marble-diagrams.md

---------

Signed-off-by: Wagner <wagnergoulart0@gmail.com>
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-12 02:17:05 +01:00
Kamran Ahmed
29c97964d1 Auto-clear cache on deployment 2024-05-12 02:14:20 +01:00
Kamran Ahmed
2071b92d3e Update suggest changes button 2024-05-11 17:21:23 +01:00
Kamran Ahmed
9674bce96e Add get-x to flutter state management 2024-05-11 17:19:56 +01:00
Kamran Ahmed
72da2d43d8 Update python frameworks 2024-05-11 17:15:39 +01:00
Kamran Ahmed
f22674a0b2 Update logging in golang roadmap 2024-05-11 16:50:49 +01:00
Kamran Ahmed
43ece4c10f Add new topics to MLOps roadmap 2024-05-11 16:33:34 +01:00
Kamran Ahmed
304efd83b6 Add sqlite to backend roadmap 2024-05-11 16:14:46 +01:00
Kamran Ahmed
4697e69e23 Replace Vue CLI with create-vue 2024-05-11 16:09:57 +01:00
Kamran Ahmed
af3bbd9320 Add pointers to golang roadmap 2024-05-11 16:07:21 +01:00
Kamran Ahmed
742b79e473 Add generics to go roadmap 2024-05-11 16:01:47 +01:00
Kamran Ahmed
1a619e1dbd Add standalone components in angular 2024-05-11 14:59:13 +01:00
Kamran Ahmed
2c9bfb3c80 Add changes to go and spring roadmaps 2024-05-11 14:54:09 +01:00
Kamran Ahmed
3102148485 Add prometheus to app monitoring 2024-05-11 14:33:16 +01:00
Kamran Ahmed
f8a7c40c11 Fix broken UI in full-stack roadmap 2024-05-11 14:17:05 +01:00
Kamran Ahmed
7603772075 Label the issues with slug 2024-05-11 11:46:08 +01:00
Dương Hồ Minh Tú
33c8528c1a fix: double scroll bar when searching (#5623)
* fix: double scroll bar when searching

* fix: use cn instead

---------

Co-authored-by: TuDHM <tudhm@ghn.vn>
Co-authored-by: Arik Chakma <arikchangma@gmail.com>
2024-05-11 10:46:37 +06:00
Machalkas
d7978d39c9 Add resource under linux roadmap (#5608)
* add url to introductory resource

* Update src/data/roadmaps/linux/content/100-navigation-basics/103-directory-hierarchy.md

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

* Update src/data/roadmaps/linux/content/100-navigation-basics/103-directory-hierarchy.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-05-11 02:16:10 +01:00
Lily Paczesniak
722b1c60d2 Add video resource for OOP (#5393)
* Add a video resource to 103-oop.md

* Update src/data/roadmaps/flutter/content/106-design-principles/103-oop.md

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-05-11 02:15:00 +01:00
Aditya Trivedi
b0136b0524 Update index.md (#5363)
* Update index.md

* Update src/data/roadmaps/docker/content/101-underlying-technologies/index.md

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-05-11 02:14:36 +01:00
Sakti Sriraj Mishra
7333941a38 Add kotlin resources (#5351)
* Update 100-kotlin.md

Resources to learn Kotlin were added

* Added Kotlin Learning resource to Android Roadmap

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

* Update src/data/roadmaps/android/content/100-pick-a-language/100-kotlin.md

---------

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-11 02:13:57 +01:00
Abdelrahman Eltohamy
27934c1188 Add nexus content (#5336)
* Update 101-nexus.md 

I have added the (definition - role).

* Update src/data/roadmaps/devops/content/118-artifcats/101-nexus.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-11 02:11:29 +01:00
Gustavo Montini de Abreu
247b24e1a3 Add resource (#5219)
* Update 102-spark-airflow-kafka.md

Add link for website 'Spark by Examples'

* Update src/data/roadmaps/mlops/content/105-data-eng-fundamentals/102-spark-airflow-kafka.md

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-05-11 02:09:17 +01:00
Sigma Devops
fb6c56e1aa Add powershell text (#5147)
* Update 101-powershell.md

* Update src/data/roadmaps/devops/content/102-live-in-terminal/scripting/101-powershell.md

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

---------

Co-authored-by: Sigma Devops <muhammadsiddiqbe@gmail.com>
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-05-11 02:08:43 +01:00
Ashutosh Kumar
db4b2487f5 Update queries (#5143)
* Update index.md

added correct syntax for renaming table or column

* Update src/data/roadmaps/sql/content/102-ddl/index.md

* Update src/data/roadmaps/sql/content/102-ddl/index.md

* Update src/data/roadmaps/sql/content/102-ddl/index.md

* Update src/data/roadmaps/sql/content/102-ddl/index.md

* Update src/data/roadmaps/sql/content/102-ddl/index.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-11 02:05:51 +01:00
Ashutosh Kumar
f1fbca6fc9 Fix alter table query (#5137)
* Update 101-alter-table.md

Previously :
     ALTER TABLE tableName
     ALTER COLUMN columnName TYPE newDataType;
results in syntax error

Now:
     ALTER TABLE tableName
     MODIFY COLUMN columnName newDataType;

* Update 101-alter-table.md

Added how to drop an primary key

* Update src/data/roadmaps/sql/content/102-ddl/101-alter-table.md

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-05-11 02:03:28 +01:00
Praneel Maitra
3308387e20 Add resource link (#5108)
* Update linear-algebra-calc-mathana.md

* Update src/data/roadmaps/ai-data-scientist/content/linear-algebra-calc-mathana.md

Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
Co-authored-by: dsh <daniel.s.holdsworth@gmail.com>
2024-05-11 02:02:39 +01:00
Yubraj khatri
ba00c917cf Add resource for useContext (#5033)
* Update 100-context.md

This article help me  a lot to grab the idea about how the context is solving the problem of the prop drilling. I think beginner should start with this article to grasp the knowledge about the problem that trying to solve by the the context

* Update src/data/roadmaps/react/content/106-state-management/100-context.md

* Update src/data/roadmaps/react/content/106-state-management/100-context.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-11 02:00:44 +01:00
Alucard
b476ca0080 Add resources for N+1 Problem (#4952)
Add additional resource links

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-11 01:59:37 +01:00
Jhonatan Mustiola
e9c33a405b Update rust integers section (#4821)
* Update 100-integers.md

More readable, links and more explanations were added

* Update 100-integers.md

Changes in the way the links were coded

* Update 100-integers.md

Incorrect wording corrected

* Update 100-integers.md

Incorrect markdown format corrected

* Update 100-integers.md

Incorrect line spacing was corrected and "-" sign was prepended to each link item

---------

Co-authored-by: Jhonatan Mustiola <152746443+JhonatanMustiolaCas@users.noreply.github.com>
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-11 01:53:54 +01:00
dogukan
56247431de Add resource for function borrowing (#4725)
* Update 100-function-borrowing.md

added stackoverflow example

* Update src/data/roadmaps/javascript/content/111-javascript-this-keyword/100-function-borrowing.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-11 01:50:29 +01:00
Alex
cae46c5db6 Remove duplicate link (#4531)
* Update 101-prototypal-inheritance.md

url "The Modern JavaScript Tutorial" is the same link as "Prototype Inheritance".

* Update src/data/roadmaps/javascript/content/102-javascript-datatypes/101-object/101-prototypal-inheritance.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-11 01:49:14 +01:00
mastercoder8
9cbfbb9231 Add instrumentation link (#3534)
Add open telemetry, which is widely used in the industry and is a the standard for metrics, tracing, logs

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-11 01:48:28 +01:00
Germán González
9f49424e67 Fix typo in android roadmap (#5317) 2024-05-11 01:47:37 +01:00
Arkoh-Addo Ebenezer
f290419694 fix: broken url in minimize-http-requests.md (#5324)
* fix: broken url in minimize-http-requests.md

existing URL points to unavailable resources. I provided a more reliable URL with very good insight to the topic.
Updated description to include a little more context providing enough heads up before visiting the URL to read the details

* Update src/data/best-practices/frontend-performance/content/minimize-http-requests.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-11 01:46:29 +01:00
C E L L
82564712c3 Add up to date resource to learn Next.js (#5325) 2024-05-11 01:45:25 +01:00
Eda
ed1532d1f5 Fix small bug in code example (#5348) 2024-05-11 01:45:11 +01:00
Jakub Kaźmierczak
2b4a3f2281 fix typo in rust ownership section (#5421) 2024-05-11 01:42:37 +01:00
Mindy Flores
e1f32a13ab Fix query (#5424)
Description of having clause filter is incorrect. The current query filters for Customers with a total amount strictly greater than $3000.
2024-05-11 01:42:16 +01:00
Dennis O'Connell
5a2305193b Minor resource title change for clarity (#5434)
Specified link for W3 Schools' Golang tutorial to add clarity
2024-05-11 01:41:54 +01:00
VTolmasov
f8b9d2e271 Add CROSS JOIN in queries (#5447)
Added CROSS JOIN type
2024-05-11 01:41:28 +01:00
Deepak Mardi
a1ced7573b Add content for mongo performance optimization #4538 (#5465)
* Updating the link for DevOps Roadmap to correct URL (https://roadmap.sh/docker) previously set to (https://roadmap.sh/best-practices)

* update

* update

* Updated text content in "Performance Optimization" topic (MongoDB Roadmap)
2024-05-11 01:40:00 +01:00
nikob-dev
0ec50a1ee4 Remove topic text (#5307)
Removing no-context spam at the bottom.
2024-05-11 01:38:25 +01:00
Davee
1d74d0b223 Fix typos (#5302) 2024-05-11 01:38:04 +01:00
Abhay Naik
7333f1357e JavaScript closure explanation (#5298) 2024-05-11 01:37:40 +01:00
Abhishek Santra
82ccd5c755 fix: broken link in "Express Full Guide"(#5259) (#5294) 2024-05-11 01:36:38 +01:00
Alex Marmolejo
577d7af7f8 fix: typo in fullstack chekcpoint (#5292) 2024-05-11 01:36:04 +01:00
Kamran Ahmed
ba7c0f6517 UI design on member detail page 2024-05-11 01:32:50 +01:00
Kamran Ahmed
8c55be23cc Update user progress 2024-05-11 01:27:20 +01:00
Arik Chakma
63ad6fe1e9 feat: team member details (#5598)
* fix: change `topicIds` to `topicTitles`

* fix: comma and gap

* wip: member details page

* fix: team member empty state

* feat: add pagination

* fix: add loading screen
2024-05-11 00:59:28 +01:00
Shizan Shaikh
fb7136e1b0 Update index.md of 'Printing output' in NodeJs Roadmap (#5287)
* Update index.md of 'Printing output' in NodeJs Roadmap

Update index.md of 'Printing output' in NodeJs Roadmap

* Update src/data/roadmaps/nodejs/content/106-nodejs-command-line-apps/101-printing-output/index.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-10 23:37:27 +01:00
Alex Marmolejo
e814eff7e2 fix: Adjusted line height (#5286)
* fix: Adjusted line height

* Adjusted bottom margin

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-10 23:35:40 +01:00
Kamran Ahmed
bb093764ba Add quarkus framework in java 2024-05-10 23:33:13 +01:00
taraferguson
1f5a601370 Fix dead links in prompt hacking (#5280)
* Update 100-style-modifiers.md

Fix 404 link error

* Update index.md

fix 404 error

* Update 100-prompt-injection.md

fix 404 error

* Update 103-defensive-measures.md

fix 404 error

* Update 104-offensive-measures.md

fix 404 error

* Update 100-style-modifiers.md

fix 404 error

* Update 101-quality-boosters.md

add link

* Update 102-weighted-terms.md

add link

* Update 103-deformed-generations.md

add link

* Update src/data/roadmaps/prompt-engineering/content/108-image-prompting/103-deformed-generations.md

* Update src/data/roadmaps/prompt-engineering/content/108-image-prompting/102-weighted-terms.md

* Update src/data/roadmaps/prompt-engineering/content/108-image-prompting/101-quality-boosters.md

* Update src/data/roadmaps/prompt-engineering/content/108-image-prompting/100-style-modifiers.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-10 23:02:14 +01:00
Peter Sarossy
389d431005 Add links to realtime databases (#5279) 2024-05-10 23:00:16 +01:00
Rushikesh Tarapure
d9d8d7891e Fix : typo in suggestion block (#5274)
Co-authored-by: Rushikesh Tarapure <rushikeshtarapure@gofynd.com>
2024-05-10 22:59:44 +01:00
omahs
18631f1a1a Fix broken link (#5263) 2024-05-10 22:58:40 +01:00
boffin-education
67d0f68eb7 Add resources for technical writer (#5189) 2024-05-10 22:24:58 +01:00
dev-aly3n
82de99973c fix: remove duplicate link in smart-contract (#5128) 2024-05-10 22:22:27 +01:00
dev-aly3n
973fbd9fc6 Fix broken URL (#5125)
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-10 22:22:17 +01:00
Sherkhan Azimov
45ab04af04 Fix broken links (#5120)
* fix: link in retry storm (performance antipatterns) section

* fix: link in geode pattern
2024-05-10 22:19:51 +01:00
Luiz Felipe dos Santos Pereira
4d35795899 Fix typos in SQL roadmap (#5112)
The original text misleads the reader. A column subquery shouldn't use the "=" operator, but the "in" operator.
2024-05-10 22:19:14 +01:00
sreyas
6335e51f30 Fix typo in rust roadmap (#5099)
Typo correction from 'Onwnership' to 'Ownership'
2024-05-10 22:17:59 +01:00
murrrda
f5ca535b70 Added Go class by Mat to Backend resources (#5084) 2024-05-10 22:16:50 +01:00
Sherkhan Azimov
6b5cf545df Fix broken links and typos (#5075)
* fix: links in communication section

* fix: typo
2024-05-10 22:16:26 +01:00
Selva Muthu Kumaran Boopalan
62a2b34b38 Add resource (#5068)
javascript-roadmap-hoisting - newvideo-link-added
2024-05-10 22:15:53 +01:00
Oscar T
b61ca66d29 fix(signup.astro): typo unnecessary word (#5060) 2024-05-10 22:15:25 +01:00
RibeiroLucas
0ba3e6e155 Add link to resource (#5046)
Refactoring Guru added to the computer science roadmap Design Patterns section.
2024-05-10 22:15:05 +01:00
Nayan Lonkar
d2a09427ed Remove invalid link from Java roadmap (#5025) 2024-05-10 22:13:02 +01:00
Caio Portugal
752a1d44d7 Add HTTP Caching article from mozilla.org (#5022) 2024-05-10 22:12:30 +01:00
Agustin Velez
8fd4a0bd60 Add links to rust roadmap (#5009)
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-10 22:11:43 +01:00
Mateus Sampaio
8d9605658f feat(roadmap): add new resource to backend roadmap (#4965) 2024-05-10 22:11:03 +01:00
Ali Ashkani Nia
c1fb58dab7 Fix ADL Participation for swap in 105-copy-swap.md (#4925) 2024-05-10 22:09:49 +01:00
Debakar Roy
7c5b49876a Add underscore for dunder methods. (#4920) 2024-05-10 22:09:23 +01:00
Ali Ashkani Nia
5368f9a16a Fix weak pointer details (#4915)
Clarified reference counting for `weak_ptr`s.
2024-05-10 22:09:11 +01:00
Shanelle Marasigan
15f06d1168 Add utility types resources (#4908)
Added a new list of links/resources
2024-05-10 22:05:07 +01:00
Blesy
7f0a5984f3 Add HTML resource link (#4881) 2024-05-10 22:04:12 +01:00
wj-mk
c0f5b00979 Added link to Loops in Dart (#4870)
It felt appropriate to add a link to Loops in Dart as loops are discussed in the preceding text.
2024-05-10 22:01:47 +01:00
Juan Gerardo Eulufi Salazar
61883506b0 Added Information on Angular Interpolation and Resource References (#4852) 2024-05-10 21:59:29 +01:00
Javier Grau
e83538e510 Add description of what is ECMA (#4812)
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-10 21:57:54 +01:00
tombohub
e7c024032a Add a pre-order traversal link (#4805) 2024-05-10 21:56:53 +01:00
Selva Muthu Kumaran Boopalan
f114657607 roadmap-prompt-engineering-llm-intro (#4782)
roadmap-prompt-engineering-llm-intro-new link
fixes: #4754

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-10 21:56:30 +01:00
Marc Thayer
377cbbe8c8 QA - Fix title of Check My Links tool and improve its description (#4742)
* Update grammar in 103-check-my-links.md

* Fixed capitalization in extension name.
2024-05-10 21:53:17 +01:00
Gustavo Corrado
1834703b1e Fx typo in rust roadmap (#4726)
Fixed typo from "Onwership" to "Ownership"
2024-05-10 21:51:02 +01:00
Sadman Sobhan
a75b6b667b Add resource link for FTP Protocol (#4714)
* add spring boot content

* Update content/roadmaps/110-java/content/103-java-web-frameworks/101-spring-boot.md

* Add Java Collection Framework

* Update content/roadmaps/110-java/content/101-java-advanced-topics/102-collection-framework.md

* Add resource link for FTP Protocol

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-10 21:50:36 +01:00
Mohammed Alaiady
ec3ecb832a Change "OCI" to "OSI" (#4681)
There was a typo error for misspelling "OSI" word.

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-10 21:48:25 +01:00
Kamran Ahmed
482b9a291d Fix issues in roadmaps 2024-05-10 21:47:17 +01:00
Paul Wright
0fe8bfe0d3 Replace python 2 with 3 in resources (#4639) 2024-05-10 21:45:32 +01:00
Marco Rubin
914acd201e Fix typo (#4584) 2024-05-10 21:42:45 +01:00
Selva Muthu Kumaran Boopalan
3b88eba110 Add git learning resource (#4562)
backend-roadmap-git-new-link
introduction to git : #4561
2024-05-10 21:42:20 +01:00
Miguel Gargallo
258f800f97 Replace Tailwind with TailwindCSS #4526 (#4529) 2024-05-10 21:40:56 +01:00
Codeguage
71bfe4f03c Added new resources to List Comprehensions (#3580) 2024-05-10 21:35:59 +01:00
Codeguage
d4e5bae03b Added new resource for refs (#3574) 2024-05-10 21:35:36 +01:00
Codeguage
78503c8990 Added new resource (#3573) 2024-05-10 21:35:22 +01:00
Amin Rezaei
cbebb18418 Fix Javascript logical operators Resources (#3544)
Remove duplicate MDN source
2024-05-10 21:33:10 +01:00
Habibov Ulug'bek
9f5081a3a4 Replace deprecated URL (#3524) 2024-05-10 21:30:56 +01:00
Kamran Ahmed
a76413fd33 Fix broken build 2024-05-10 21:17:32 +01:00
Kamran Ahmed
c83a91eec4 Close stale issues script 2024-05-10 21:14:19 +01:00
Kamran Ahmed
7c68830b45 Add a script to auto-label issues 2024-05-10 21:14:19 +01:00
dsh
fbecabf3fa Merge pull request #5313 from AbrorPatidinov/patch-1
Update C++ snippet to correct indentation
2024-05-10 21:03:09 +01:00
dsh
0476b725f4 Merge pull request #5314 from AbrorPatidinov/patch-2
Correct C++ indentation standard
2024-05-10 21:01:56 +01:00
Kamran Ahmed
1733371a90 Make it deploy only when changes are in src or public folders 2024-05-10 21:00:05 +01:00
Kamran Ahmed
d0766a3865 Add labels to topic change suggestion issue 2024-05-10 20:58:13 +01:00
dsh
d2715b5978 Merge pull request #5322 from akarsanth/patch-2
Improve 102-typeof-operator.md definition
2024-05-10 20:55:47 +01:00
marvin
dd053ac706 Update 109-friction.md (#5386)
IB diploma vid discussing friction basics
2024-05-10 20:48:50 +01:00
Jean-François Greffier
04336fedae Add official Playwright get started videos (#5580)
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2024-05-10 19:18:59 +01:00
Jean-François Greffier
0bc9ae66ed chore: harmonize Playwright tutorial link (#5581) 2024-05-10 19:18:15 +01:00
Kamran Ahmed
622766fea3 Fix details for game ai 2024-05-10 19:15:16 +01:00
bitblocksplicer
bd76e760d4 Fix Typo in Shell Scripting Literal Description (#5613)
Fixed a typo in the definition of boolean literals in shell scripts that could be misleading
2024-05-10 19:12:09 +01:00
marvin
540d5030a4 Adding videos and a slightly altered description of arrays (#5637)
To give credit where credit is due, the description change is from an issue (#5600) I picked up by GitHub user 'andremarko';

https://github.com/kamranahmedse/developer-roadmap/issues/5600

im going to try to provide more videos for all languages across the data structures and algorithms flowchart
2024-05-10 19:10:51 +01:00
Kamran Ahmed
d9466717a7 Change google container registry to artifact registry 2024-05-10 18:50:46 +01:00
Kamran Ahmed
edbc22e02f Close empty issues 2024-05-10 16:44:14 +01:00
Kamran Ahmed
6c6f7021d1 Add github action to close PRs with feedback and no action 2024-05-10 16:31:38 +01:00
Utkarsh Tiwari
8862239a11 Fix #5533 Software Architect Roadmap (#5605)
Updated SAP ERP spelling
2024-05-10 16:27:34 +01:00
dsh
ca2088f553 Add go schedular content and roadmap node (#5635) 2024-05-10 15:30:00 +01:00
Rachit Agrawal
67edf2ce4d Add Video Lecture on OSI Model to CyberSecurity (#5392)
Add OSI lecture video to OSI content.
2024-05-10 14:59:33 +01:00
dsh
9857a0b981 Merge pull request #5332 from bobralks/patch-1
Add Bun as a task runner
2024-05-10 14:38:17 +01:00
dsh
d1429efaa8 Merge pull request #5626 from Edlan01/patch-1
Update backend - adjusted copy
2024-05-10 14:10:13 +01:00
dsh
223b6ae096 Merge pull request #5627 from chefemaster/patch-1
Add Link of Effective Go about functions
2024-05-10 14:06:46 +01:00
Eric
e2e40d1fdc Add w3school content (#4807)
Added W3schools resource to learn react with create react app. w3schools does a pretty great job at explaining stuff.
2024-05-10 14:02:05 +01:00
dsh
73e117e693 Merge pull request #5579 from includeSimon/patch-1
Fix typo in cocpy
2024-05-10 13:01:30 +01:00
dsh
a587503160 Merge pull request #5460 from JDeepD/patch-1
Replace article link
2024-05-10 12:59:46 +01:00
dsh
ca9aabaa63 Merge pull request #5455 from FaiqMahmood/patch-1
Add interactive shell tutorial
2024-05-10 12:52:53 +01:00
dsh
3e4f5fbfdf Merge pull request #5381 from pronob1010/patch-1
Add caching analogies to db caching copy
2024-05-10 12:33:12 +01:00
dsh
ab34fe725c Merge pull request #5377 from olawuwo/patch-1
Add SQL identifier to enable highlighting in snippet
2024-05-10 12:31:10 +01:00
dsh
70f6fcc722 Merge pull request #5352 from Abderrahmane07/patch-3
Fix typo in 103-lazy-eager-explicit-loading.md
2024-05-10 12:26:34 +01:00
dsh
10287bd9a5 Merge pull request #5582 from shto/patch-1
Fix type in copy
2024-05-10 12:07:43 +01:00
dsh
91bd69f9d1 Merge pull request #5211 from rahulbaghel007/addingZeroCostAbstractionToRust
expand rust zero cost abstractions content
2024-05-10 11:49:14 +01:00
dsh
d2de4eac41 Merge pull request #5155 from iribama/patch-1
Add developer focussed blogging platforms
2024-05-10 11:40:48 +01:00
dsh
cf206240cd Merge pull request #5149 from rishabhv2003/patch-2
Add int and std clarifications
2024-05-10 11:35:47 +01:00
dsh
09043deecc Merge pull request #5148 from rishabhv2003/patch-1
Add license disclaimer to CLion IDE
2024-05-10 11:30:30 +01:00
dsh
d686ed208f Merge pull request #5146 from Basliel25/patch-1
Add third OCI specification
2024-05-10 11:20:37 +01:00
dsh
a607a23abb Merge pull request #5145 from Vitruvius21/patch-1
Update 114-cli-apps.md rm redundant "take"
2024-05-10 11:18:40 +01:00
dsh
0603ec56ce Merge pull request #5118 from cyrionp/patch-1
Update 101-singlar-core.md to .NET 8.0
2024-05-10 11:06:47 +01:00
dsh
6de052df6b Merge pull request #5114 from MaharshiChoksi/patch-1
Add Natural Join as an example of the types of join
2024-05-10 11:05:45 +01:00
dsh
588440dcc1 Merge pull request #5095 from MarcinKozak005/patch-3
Remove duplicated link in 104-firewall.md
2024-05-10 10:57:27 +01:00
dsh
794614f6e0 Merge pull request #5092 from selvamuthukumaran1/kubernetes-roadmap
Remove inactive content link
2024-05-10 10:56:47 +01:00
dsh
f85b6f9644 Merge pull request #5072 from kamalogudah/patch-1
Fix typo in content heading
2024-05-10 10:50:32 +01:00
dsh
74629f47d9 Merge pull request #5067 from KishoreAnanth18/patch-1
Add link to AWS documentation for CPU Credits
2024-05-10 10:49:25 +01:00
dsh
d60fc67da7 Merge pull request #5065 from jafzak/patch-1
https://assets.gradlehero.com/get-going-with-gradle/get-going-with-gradle-book.pdf
2024-05-10 10:47:28 +01:00
dsh
16a2a48a88 Merge pull request #5064 from jafzak/patch-2
Add android tutorial article
2024-05-10 10:42:49 +01:00
dsh
840bb4e31a Merge pull request #5057 from timofeevAV/patch-1
Remove duplication of TouchableHighlight
2024-05-10 10:33:47 +01:00
dsh
f1212118d8 Merge pull request #5038 from Ahmad-Alsaleh/patch-1
fix grammatical error in copy
2024-05-10 10:10:56 +01:00
dsh
8cb38d3c3f Merge pull request #5031 from theMuhammadKhalid/patch-1
Add react native storage copy
2024-05-10 09:52:49 +01:00
dsh
aec54a4565 Merge pull request #5008 from TaviotBaptiste/patch-2
Add depends_on to dockerfile example
2024-05-10 09:43:56 +01:00
mufasa
88b4344a90 Add Rust enum example (#4962)
Add Rust enum example
2024-05-10 09:35:17 +01:00
dsh
476400a02e add rust video to rust intro section (#5634) 2024-05-10 09:27:23 +01:00
Deepak Mardi
bb9a911e59 Added HATEOAS resources (#5438)
Added Resource
2024-05-09 22:36:13 +01:00
Deepak Mardi
fb77e54d54 Update broken React Router resource link (#5444)
Update broken React Router resource link
2024-05-09 22:34:24 +01:00
Deepak Mardi
a4d699b3d7 Fix broken resources in javascript and nodejs Page (#5445)
Updated broken link in javascript and nodejs roadmap for settimeout Explained in 5 minutes!
2024-05-09 22:32:17 +01:00
Deepak Mardi
ec31ad339e fixes : Updated MonogDB > Collection and Methods > Validate (#5466)
removed `background` from Mongodb snippet.
2024-05-09 22:29:32 +01:00
dsh
dfa91cd085 Merge pull request #5632 from BeardedOwl1357/patch-1
Fixed link display of 101-why-rust.md
2024-05-09 22:25:01 +01:00
Sanchay Joshi
424f1d061a Update 101-why-rust.md
Unnecessary tabs makes markdown render them as "code" instead of links
2024-05-10 02:16:58 +05:30
dsh
bc52c0cfbe Merge pull request #4953 from Tiago-Vier-Preto/patch-1
Improve readability of 'Why us typescript' section
2024-05-09 20:35:39 +01:00
dsh
2d3ca43e01 Merge pull request #4901 from mehmetumutmutlu/patch-3
Capitalized 'French'
2024-05-09 19:56:21 +01:00
dsh
0bc4a11fc5 Merge pull request #4899 from mehmetumutmutlu/patch-1
Correct LLM abbreviation
2024-05-09 19:54:49 +01:00
dsh
dc63c2e9d4 Merge pull request #4876 from nsk6704/patch-1
Improve readability of 'What is technical writing' section
2024-05-09 19:49:36 +01:00
dsh
46e56ac315 Merge pull request #4834 from anavalo/patch-1
Update pagespeed insights url version
2024-05-09 19:43:49 +01:00
dsh
1903674147 Merge pull request #4816 from AliMaazKhan/patch-1
Add youtube video link on 'Vectors'
2024-05-09 19:32:17 +01:00
dsh
79023f35cb Merge pull request #4794 from ethan-butler-alight/patch-1
Fix spelling error on 100-basic-prompting.md
2024-05-09 16:36:12 +01:00
dsh
615188cba6 Merge pull request #4784 from iMuhammad3/patch-1
Update enable-compression.md
2024-05-09 16:35:16 +01:00
dsh
437973a2ba Merge pull request #4774 from kan1shq/patch-1
Add video content to the 'why rust' section
2024-05-09 16:29:59 +01:00
dsh
cd68a12b71 Merge pull request #4951 from lazydoug/patch-1
Update 100-primitive-types.md
2024-05-09 15:45:07 +01:00
dsh
d34525776d Merge pull request #5628 from kamranahmedse/dansholds/fix-appdynamics-node
fix incorrect path in JSON for appdynamics node
2024-05-09 15:38:15 +01:00
Dan Holdsworth
cb4b9c82c8 fix incorrect path in JSON for appdynamics node 2024-05-09 15:36:10 +01:00
dsh
f303b466c9 Merge pull request #4769 from collegedude9300/patch-7
Update 103-unity-3d.md
2024-05-09 15:25:38 +01:00
dsh
93ff9402b1 Merge pull request #4762 from PetroKabina/patch-1
Update index.md
2024-05-09 15:20:29 +01:00
Jeferson Martins Bruno
27c5626ef6 Add Link of Effective Go about functions 2024-05-09 10:46:44 -03:00
dsh
636192af87 Merge pull request #4749 from rishilahoti/patch-1
Update next.js youtube video link to latest
2024-05-09 14:25:18 +01:00
dsh
c84694b3bb Merge pull request #4739 from Nozarno/patch-1
Add new design pattern link
2024-05-09 14:14:24 +01:00
dsh
e825f47d0a Merge pull request #4736 from bilalrahim/patch-1
Update 101-posa-patterns.md
2024-05-09 14:08:10 +01:00
Ed Lan
fcc88b389e Update backend - adjusted copy
Adjusted copy for the Q "What does a Backend Developer do?"
2024-05-09 14:44:54 +02:00
dsh
22bd61580b Merge pull request #5625 from kamranahmedse/dansholds/fix-vulkan-node-typo
fixed vulkan typo in advanced render topic
2024-05-09 13:38:00 +01:00
Dan Holdsworth
eab0bf9494 fixed vulkan typo in advanced render topic 2024-05-09 13:36:30 +01:00
dsh
41e6682f66 Merge pull request #4696 from whitezom7/patch-1
Fix typo of Vulcan to Vulkan
2024-05-09 13:32:39 +01:00
Kamran Ahmed
aabc8e12b0 Add backend project ideas guide 2024-05-09 12:15:44 +01:00
dsh
a2487aeea8 Merge pull request #3507 from bhavuu/patch-1
Add official JavaScript documentation link
2024-05-09 09:56:47 +01:00
dsh
1e04a6cc0a Merge pull request #5624 from kamranahmedse/dansholds/4197-typo-in-input-section-of-api-security-best-practices
Fix typo in input section of API Security Best Practice Roadmap
2024-05-09 09:24:17 +01:00
Dan Holdsworth
8ed874d4ea fix typo in input section of API Security Best Practice Roadmap 2024-05-09 09:20:19 +01:00
Kamran Ahmed
2117fda50f Fix duplicate orientation/curve nodes 2024-05-08 16:56:54 +01:00
Kamran Ahmed
da1a5f6506 Change activity title 2024-05-08 11:59:59 +01:00
dsh
803f87de38 Merge pull request #5618 from kamranahmedse/dansholds/5400-removed-article-backend
Removed empty AWS Content Link
2024-05-08 11:49:28 +01:00
dsh
67948002fd Merge pull request #5619 from kamranahmedse/dansholds/4330-remove-the-tensorflow-content-from-the-c++-roadmap
Removed Python Code from C++ Content Example
2024-05-08 11:49:05 +01:00
dsh
e76617c9a9 Removed Python Code from C++ Content Example
The Tensorflow node in the C++ Roadmap used Python code as examples, I have removed this.
2024-05-08 11:39:54 +01:00
dsh
cc4fd82fef Merge pull request #4598 from hernanes338/python-roadmap-lambdas
Fix Python Lambda Functions hyperlink
2024-05-08 11:24:22 +01:00
dsh
05d379da08 Merge pull request #4923 from sonvir249/issue-4740
issue-4740: Updated the dead oracle network link.
2024-05-08 11:19:01 +01:00
dsh
8ab7f2c8b3 Merge pull request #5435 from belikedeep/deepakmardii/fix-resource-unavailable
Blockchain Rust resource updated
2024-05-08 10:19:37 +01:00
dsh
a1d0129f36 Removed casino link from Image Optimization Content (#5616)
A previously useful linke (image.guide) is now linking to a casino website.

I have removed this link.
2024-05-08 10:18:25 +01:00
dsh
0c54816b3f Removed empty AWS Content Link
The linked content now results in a 404.
2024-05-08 10:15:45 +01:00
Arik Chakma
e1c35d299d fix: update activity stream design (#5615) 2024-05-08 01:41:45 +01:00
Andrei Patru
c6648655cf Update 100-permissions.md
fix incorrect sentence
2024-05-01 17:12:27 +02:00
Pirlog Simon
d139df6a2c Update index.md
fixed spelling from "use" to "used"
2024-05-01 16:27:18 +03:00
Jaydeep Das
235567400e Update 100-horizontal-vs-vertical-scaling.md
The previous URL redirects to a whole different website which has nothing related to the topic.
2024-04-03 19:41:10 +05:30
Faiq Mahmood
e5e03c76a3 Update 100-bash-scripting.md
Added link to an interactive shell scripting tutorial
2024-04-02 23:20:33 +02:00
Deepak Mardi
58960eb6d4 update 2024-04-01 01:54:27 +05:30
Deepak Mardi
675f90adc6 Updated resource URL from figment.io(resource deleted) to Near docs(tutorial in rust) 2024-03-31 23:41:38 +05:30
Deepak Mardi
dbdfb2226b Keyboard Navigation only on vissible elements 2024-03-31 23:05:15 +05:30
Deepak Mardi
d4eef5ecd0 Fixed typo at Lazy Eager Explicit Loading topic 2024-03-31 22:47:03 +05:30
Deepak Mardi
ecf904d99f Merge pull request #1 from deepakmardii/deepakmardii/fix-docker-link
Updating the link for DevOps Roadmap to correct URL (https://roadmap.…
2024-03-31 22:31:52 +05:30
Deepak Mardi
5d43f4b1e6 Updating the link for DevOps Roadmap to correct URL (https://roadmap.sh/docker) previously set to (https://roadmap.sh/best-practices) 2024-03-31 22:21:41 +05:30
Pronob Mozumder
f1874c7637 Update 103-database-caching.md
Added a simplified explanation of database caching using analogies for easier understanding.
2024-03-20 18:27:14 +06:00
Tai
78be705f70 Update 103-avg.md
Added SQL language identifier to enable syntax highlighting
2024-03-19 19:43:36 +00:00
Abderrahmane Larchi
00df91f30d Typo - Update 103-lazy-eager-explicit-loading.md 2024-03-15 18:01:52 +01:00
Bob Ralks
64070616c0 Update index.md
add bun for running package.json scripts
2024-03-12 01:37:07 -04:00
Aakarshan Thapa
99e15b5a9b Improve 102-typeof-operator.md definition
Improved the definition of typeof operator.
2024-03-10 16:01:41 -04:00
Artorias - The Abyss Walker
f33af1dcf3 Update index.md
Just adjusted a space, now it's more readable
2024-03-08 23:14:04 +05:00
Artorias - The Abyss Walker
2a54ebb091 Update index.md 2024-03-08 22:55:49 +05:00
Rahul Baghel
b5ce2a9d36 Update 102-memory-safety.md
Add missing content of Zero Cost Abstraction in Rust
2024-02-19 11:34:22 +05:30
Rejoice Anodo
0379edc684 Update 106-blogging-platforms.md 2024-02-09 18:29:40 +01:00
Rishabh Verma
d781568f93 Update 102-first-program.md
Added additional information about the functions and return values about the program.
2024-02-06 19:58:41 +05:45
Rishabh Verma
cc95998339 Update 101-code-editors.md
A minor change to inform new users that this IDE is not pre unlike others.
2024-02-06 19:48:41 +05:45
Basliel25
1b364ae3de Propose to add content to 103-docker-and-oci.md
The OCI-specification has three core specifications while on the description written only two are mentioned. 

I added the *Distribution Specification (distribution-spec)*.

According to the website, the distro-spec defines a standardized API protocol for distributing container images and other content. It is primarly designed to ensure interoperability and consistency across different container registries and tools, regardless of their specific implementation.

I attach below links to the website of OCI and the github repo
- [https://github.com/opencontainers/distribution-spec](On the github repo of the OCI) 
- [https://opencontainers.org/about/overview/](Website of the OCI)
2024-02-06 12:22:42 +01:00
ᚷᛁᛟᚱᚷᛁ ᛒᚨᛚᚨᚲᚻᚨᛞᛉᛖ
f1a4d8d38b Update 114-cli-apps.md rm redundant "take" 2024-02-06 14:49:08 +04:00
Abdulkadir Durmaz
1b333f774a Update 101-singlar-core.md to .NET 8.0 2024-02-01 10:56:12 +03:00
Maharshi Choksi
ccbaa1fe6d Update index.md
Added a kind of join (Natural Join).
2024-01-30 17:39:45 -07:00
Marcin Kozak
78bb3155e0 Remove duplicated link in 104-firewall.md 2024-01-26 17:59:20 +01:00
Selva Muthu Kumaran Boopalan
89bad8cb11 Update 101-choosing-a-managed-provider.md 2024-01-25 22:31:10 +05:30
KISHORE ANANTH N
f8d8776667 Update 101-cpu-credits.md
Formatted the link
2024-01-23 11:05:42 +05:30
Paul Oguda
36ae1b521b Update index.md
Fix typo
2024-01-22 13:03:40 +03:00
KISHORE ANANTH N
48187393a8 Update 101-cpu-credits.md
Added a reference link.
2024-01-22 10:05:37 +05:30
Jafar Zakariya
a38961ad84 Update 105-hello-world-app.md
Added resource for creating first android app
2024-01-21 21:00:34 +01:00
Jafar Zakariya
1d6957d263 Update 104-using-gradle.md
Added PDF resource for gradle
2024-01-21 20:57:39 +01:00
Timofeev Artyom
53c9279049 Update 100-touchables.md 2024-01-20 00:38:56 +05:00
Ahmad Alsaleh
c2458fff8e Update 100-builtin-modules.md
Fixed a grammatical mistake
2024-01-15 22:55:28 +04:00
Muhammad Khalid
77fbf8a745 Update 102-storage.md 2024-01-14 23:04:12 +05:00
Baptiste TAVIOT
d90cd01fab Update 101-docker-compose.md
update : add depends_on
2024-01-09 15:02:59 +01:00
Tiago Vier
d5772901d9 Update 101-why-use-typescript.md 2023-12-31 11:42:48 -03:00
Osarodion Douglas Idumwonyi
8984d9e166 Update 100-primitive-types.md
This update highlights the distinction between primitive data types and non-primitives based on the values they hold and the properties and methods they possess.
2023-12-31 10:46:03 +01:00
sonvir249
b633702747 issue-4740: Updated the dead oracle netwrok link. 2023-12-26 18:21:13 +05:30
Umut
ea2884ed60 Update index.md
Capitalized the "French" word in the first example chunk.
2023-12-22 22:59:14 +02:00
Umut
c95919ba7f Update 100-what-are-llms.md
The definition of LLM was changed from Language Learning Models to Large Language Models.
2023-12-22 22:19:48 +02:00
Saketh Kashyap Nagendra
c8dc730fb7 Update 101-what-technical-writing.md
Added 2 points, made the layout easier to read
2023-12-18 06:07:31 +05:30
Tasos Tsournos
45462c49da Update use-non-blocking-javascript.md
v4 is deprecated
2023-12-07 16:18:13 +02:00
AliMaazKhan
a191948675 Update 101-vector.md
Added a video on the introduction to "Vectors" for game development".
2023-12-03 15:40:22 +05:30
Ethan Butler
8154a398a8 Fix spelling error on 100-basic-prompting.md 2023-11-29 20:34:51 -05:00
Muhammad Auwal
ef353e1c8f Update enable-compression.md
Fix grammar error
2023-11-28 18:58:57 +01:00
kan1shq
aaacc41c82 Update 101-why-rust.md
added links to beginner friendly videos on choosing rust
2023-11-27 13:11:48 +05:30
collegedude9300
863758b49f Update 103-unity-3d.md 2023-11-26 15:18:16 -08:00
PetroKabina
5fe66a1e4f Update index.md
ls command added to `docker container` and `docker image` to be more specific
2023-11-25 12:07:17 +02:00
Rishi
7e5c0a5716 Update 101-next-js.md
Updated to latest video of JextJS from freecodecamp
2023-11-22 22:22:14 +05:30
Nozarno
41d182e987 Update index.md
Add resource site https://refactoring.guru/
2023-11-21 23:33:38 +01:00
Bilal Rahim
bd553fa630 Update 101-posa-patterns.md
The correct abbreviation for POSA is Pattern-Oriented Software Architecture not Patterns of Scalable and Adaptable Software Architecture.

Thanks
2023-11-21 09:16:32 +05:00
Taylor
d4f48a3ebd Update and rename 101-vulcan-ray-tracing.md to 101-vulkan-ray-tracing.md
Typo in Vulkan
2023-11-14 22:37:27 +01:00
hernanes338
b8fe4e2b35 Fix Python Lambda Functions hyperlink 2023-10-18 15:22:00 +02:00
bhavuu
7f14e99fbf Update 105-javascript.md
Added official Javascript documentation link for it to learn in depth.
2023-02-28 16:11:19 +05:30
4161 changed files with 151671 additions and 152868 deletions

View File

@@ -3,6 +3,6 @@
"enabled": false
},
"_variables": {
"lastUpdateCheck": 1714413381505
"lastUpdateCheck": 1720119515249
}
}

View File

@@ -1,6 +1,6 @@
name: "✍️ Suggest Changes"
name: "✍️ Missing or Deprecated Roadmap Topics"
description: Help us improve the roadmaps by suggesting changes
labels: [suggestion]
labels: [topic-change]
assignees: []
body:
- type: markdown

50
.github/workflows/close-feedback-pr.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Close PRs with Feedback
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
jobs:
close-pr:
runs-on: ubuntu-latest
steps:
- name: Close PR if it has label "feedback left" and no changes in 7 days
uses: actions/github-script@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: pullRequests } = await github.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
base: 'master',
});
for (const pullRequest of pullRequests) {
const { data: labels } = await github.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
});
const feedbackLabel = labels.find((label) => label.name === 'feedback left');
if (feedbackLabel) {
const lastUpdated = new Date(pullRequest.updated_at);
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
if (lastUpdated < sevenDaysAgo) {
await github.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
body: 'Closing this PR because there has been no activity for the past 7 days. Feel free to reopen if you have any feedback.',
});
await github.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pullRequest.number,
state: 'closed',
});
}
}
}

View File

@@ -1,10 +1,6 @@
name: Clears Cloudfront Cache
on:
# Allow manual Run
workflow_dispatch:
# Run at midnight utc
schedule:
- cron: '0 0 * * *'
jobs:
aws_costs:
runs-on: ubuntu-latest

View File

@@ -1,9 +1,6 @@
name: Deploy to EC2
on:
workflow_dispatch: # allow manual run
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
@@ -59,4 +56,17 @@ jobs:
key: ${{ secrets.EC2_PRIVATE_KEY }}
script: |
cd /var/www/roadmap.sh
sudo pm2 restart web-roadmap
sudo pm2 restart web-roadmap
# --------------------
# Clear cloudfront cache
# --------------------
- name: Clear Cloudfront Caching
run: |
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GH_PAT }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/roadmapsh/infra-ansible/actions/workflows/playbook.yml/dispatches \
-d '{ "ref":"master", "inputs": { "playbook": "roadmap_web.yml", "tags": "cloudfront", "is_verbose": false } }'

38
.github/workflows/label-issue.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: Label Issue
on:
issues:
types: [ opened, edited ]
jobs:
label-topic-change-issue:
runs-on: ubuntu-latest
steps:
- name: Add roadmap slug to issue as label
uses: actions/github-script@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const issue = context.payload.issue;
const roadmapUrl = issue.body.match(/https?:\/\/roadmap.sh\/[^ ]+/);
// if the issue is labeled as a topic-change, add the roadmap slug as a label
if (issue.labels.some(label => label.name === 'topic-change')) {
if (roadmapUrl) {
const roadmapSlug = new URL(roadmapUrl[0]).pathname.replace(/\//, '');
github.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: [roadmapSlug]
});
}
// Close the issue if it has no roadmap URL
if (!roadmapUrl) {
github.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed'
});
}
}

2
.gitignore vendored
View File

@@ -31,3 +31,5 @@ tests-examples
/editor/*
!/editor/readonly-editor.tsx
!/editor/renderer/renderer.ts
!/editor/renderer/index.tsx

View File

@@ -11,6 +11,9 @@ import react from '@astrojs/react';
// https://astro.build/config
export default defineConfig({
site: 'https://roadmap.sh/',
experimental: {
rewriting: true,
},
markdown: {
shikiConfig: {
theme: 'dracula',

View File

@@ -2,40 +2,99 @@
First of all thank you for considering to contribute. Please look at the details below:
- [Contribution](#contribution)
- [New Roadmaps](#new-roadmaps)
- [Existing Roadmaps](#existing-roadmaps)
- [Adding Content](#adding-content)
- [Guidelines](#guidelines)
- [New Roadmaps](#new-roadmaps)
- [Existing Roadmaps](#existing-roadmaps)
- [Adding Content](#adding-content)
- [Guidelines](#guidelines)
## New Roadmaps
For new roadmaps, submit a roadmap by providing [a textual roadmap similar to this roadmap](https://gist.github.com/kamranahmedse/98758d2c73799b3a6ce17385e4c548a5) in an issue.
For new roadmaps, you can either:
- Submit a roadmap by providing [a textual roadmap similar to this roadmap](https://gist.github.com/kamranahmedse/98758d2c73799b3a6ce17385e4c548a5) in an [issue](https://github.com/kamranahmedse/developer-roadmap/issues).
- Create an interactive roadmap yourself using [our roadmap editor](https://draw.roadmap.sh/) & submit the link to that roadmap in an [issue](https://github.com/kamranahmedse/developer-roadmap/issues).
## Existing Roadmaps
For the existing roadmaps, please follow the details listed for the nature of contribution:
- **Fixing Typos** — Make your changes in the [roadmap JSON file](https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/roadmaps)
- **Adding or Removing Nodes** — Please open an issue with your suggestion.
- **Fixing Typos** — Make your changes in the [roadmap JSON file](https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/roadmaps) and submit a [PR](https://github.com/kamranahmedse/developer-roadmap/pulls).
- **Adding or Removing Nodes** — Please open an [issue](https://github.com/kamranahmedse/developer-roadmap/issues) with your suggestion.
**Note:** Please note that our goal is not to have the biggest list of items. Our goal is to list items or skills most relevant today.
**Note:** Please note that our goal is <strong>not to have the biggest list of items</strong>. Our goal is to list items or skills most relevant today.
## Adding Content
Find [the content directory inside the relevant roadmap](https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/roadmaps). Please keep the following guidelines in mind when submitting content:
- Content must be in English.
- Put a brief description about the topic on top of the file and the a list of links below with each link having title of the URL.
- Maximum of 8 links per topic.
- Follow the below style guide for content.
### How To Structure Content
Please adhere to the following style when adding content to a topic:
```
# Topic Title
(Content)
Visit the following resources to learn more:
- [@type@Description of link](Link)
```
`@type@` must be one of the following and describes the type of content you are adding:
- `@official@`
- `@opensource@`
- `@article@`
- `@course@`
- `@podcast@`
- `@video@`
It's important to add a valid type, this will help us categorize the content and display it properly on the roadmap.
## Guidelines
- <p><strong>Please don't use the project for self-promotion!</strong><br />
We believe this project is a valuable asset to the developer community and it includes numerous helpful resources. We kindly ask you to avoid submitting pull requests for the sole purpose of self-promotion. We appreciate contributions that genuinely add value, such as guides from maintainers of well-known frameworks, and will consider accepting these even if they're self authored. Thank you for your understanding and cooperation!
- <p><strong>Adding everything available out there is not the goal!</strong><br />
The roadmaps represent the skillset most valuable today, i.e., if you were to enter any of the listed fields today, what would you learn?! There might be things that are of-course being used today but prioritize the things that are most in demand today, e.g., agreed that lots of people are using angular.js today but you wouldn't want to learn that instead of React, Angular, or Vue. Use your critical thinking to filter out non-essential stuff. Give honest arguments for why the resource should be included.</p>
The roadmaps represent the skillset most valuable today, i.e., if you were to enter any of the listed fields today, what would you learn? There might be things that are of-course being used today but prioritize the things that are most in demand today, e.g., agreed that lots of people are using angular.js today but you wouldn't want to learn that instead of React, Angular, or Vue. Use your critical thinking to filter out non-essential stuff. Give honest arguments for why the resource should be included.</p>
- <p><strong>Do not add things you have not evaluated personally!</strong><br />
Use your critical thinking to filter out non-essential stuff. Give honest arguments for why the resource should be included. Have you read this book? Can you give a short article?</p>
- <p><strong>Create a Single PR for Content Additions</strong></p>
If you are planning to contribute by adding content to the roadmaps, I recommend you to clone the repository, add content to the [content directory of the roadmap](./src/data/roadmaps/) and create a single PR to make it easier for me to review and merge the PR.
- Write meaningful commit messages
- Look at the existing issues/pull requests before opening new ones
- <p><strong>Write meaningful commit messages</strong><br >
Meaningful commit messages help speed up the review process as well as help other contributors in gaining a good overview of the repositories commit history without having to dive into every commit.
(See the following guide on how to write good [commit messages](https://www.freecodecamp.org/news/how-to-write-better-git-commit-messages/)).
</p>
- <p><strong>Look at the existing issues/pull requests before opening new ones</strong></p>
### Good vs Not So Good Contributions
<strong>Good</strong>
- New Roadmaps.
- Engaging, fresh content links.
- Typos and grammatical fixes.
- Content copy in topics that do not have any (or minimal copy exists).
<strong>Not So Good</strong>
- Adding whitespace that doesn't add to the readability of the content.
- Rewriting content in a way that doesn't add any value.
- Non-English content.
- PR's that don't follow our style guide, have no description and a default title.
- Links to your own blog articles.

14
editor/renderer/index.tsx Normal file
View File

@@ -0,0 +1,14 @@
export function Renderer(props: any) {
return (
<div className="fixed bottom-0 left-0 right-0 top-0 z-[9999] border bg-white p-5 text-black">
<h2 className="mb-2 text-xl font-semibold">Private Component</h2>
<p className="mb-4">
Renderer is a private component. If you are a collaborator and have
access to it. Run the following command:
</p>
<code className="mt-5 rounded-md bg-gray-800 p-2 text-white">
npm run generate-renderer
</code>
</div>
);
}

View File

@@ -0,0 +1,5 @@
export function renderFlowJSON(data: any, options?: any) {
console.warn("renderFlowJSON is not implemented");
console.warn("run the following command to generate the renderer:");
console.warn("> npm run generate-renderer");
}

View File

@@ -9,30 +9,36 @@
"build": "astro build",
"preview": "astro preview",
"format": "prettier --write .",
"gh-labels": "./scripts/create-roadmap-labels.sh",
"astro": "astro",
"deploy": "NODE_DEBUG=gh-pages gh-pages -d dist -t",
"upgrade": "ncu -u",
"roadmap-links": "node scripts/roadmap-links.cjs",
"roadmap-dirs": "node scripts/roadmap-dirs.cjs",
"roadmap-assets": "tsx scripts/editor-roadmap-assets.ts",
"editor-roadmap-dirs": "tsx scripts/editor-roadmap-dirs.ts",
"editor-roadmap-content": "tsx scripts/editor-roadmap-content.ts",
"roadmap-content": "node scripts/roadmap-content.cjs",
"generate-renderer": "sh scripts/generate-renderer.sh",
"best-practice-dirs": "node scripts/best-practice-dirs.cjs",
"best-practice-content": "node scripts/best-practice-content.cjs",
"generate:og": "node ./scripts/generate-og-images.mjs",
"warm:urls": "sh ./scripts/warm-urls.sh https://roadmap.sh/sitemap-0.xml",
"compress:images": "tsx ./scripts/compress-images.ts",
"test:e2e": "playwright test"
},
"dependencies": {
"@astrojs/node": "^8.2.5",
"@astrojs/react": "^3.3.1",
"@astrojs/sitemap": "^3.1.4",
"@astrojs/react": "^3.4.0",
"@astrojs/sitemap": "^3.1.5",
"@astrojs/tailwind": "^5.1.0",
"@fingerprintjs/fingerprintjs": "^4.3.0",
"@nanostores/react": "^0.7.2",
"@napi-rs/image": "^1.9.2",
"@resvg/resvg-js": "^2.6.2",
"@types/react": "^18.3.1",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.3.0",
"astro": "^4.7.0",
"astro": "^4.9.1",
"clsx": "^2.1.1",
"dayjs": "^1.11.11",
"dom-to-image": "^2.6.0",
@@ -40,26 +46,27 @@
"gray-matter": "^4.0.3",
"htm": "^3.1.1",
"image-size": "^1.1.1",
"jose": "^5.2.4",
"jose": "^5.3.0",
"js-cookie": "^3.0.5",
"lucide-react": "^0.376.0",
"lucide-react": "^0.378.0",
"nanoid": "^5.0.7",
"nanostores": "^0.10.3",
"node-html-parser": "^6.1.13",
"npm-check-updates": "^16.14.20",
"playwright": "^1.44.0",
"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-tooltip": "^5.26.4",
"reactflow": "^11.11.2",
"reactflow": "^11.11.3",
"rehype-external-links": "^3.0.0",
"remark-parse": "^11.0.0",
"roadmap-renderer": "^1.0.6",
"satori": "^0.10.13",
"satori-html": "^0.3.2",
"sharp": "^0.33.3",
"sharp": "^0.33.4",
"slugify": "^1.6.6",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.3",
@@ -67,20 +74,20 @@
"zustand": "^4.5.2"
},
"devDependencies": {
"@playwright/test": "^1.43.1",
"@playwright/test": "^1.44.0",
"@tailwindcss/typography": "^0.5.13",
"@types/dom-to-image": "^2.6.7",
"@types/js-cookie": "^3.0.6",
"@types/prismjs": "^1.26.3",
"@types/prismjs": "^1.26.4",
"@types/react-calendar-heatmap": "^1.6.7",
"csv-parser": "^3.0.0",
"gh-pages": "^6.1.1",
"js-yaml": "^4.1.0",
"markdown-it": "^14.1.0",
"openai": "^4.38.5",
"openai": "^4.47.1",
"prettier": "^3.2.5",
"prettier-plugin-astro": "^0.13.0",
"prettier-plugin-tailwindcss": "^0.5.14",
"tsx": "^4.7.3"
"tsx": "^4.10.5"
}
}

9763
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

BIN
public/roadmaps/devrel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

BIN
public/roadmaps/ios.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

View File

@@ -36,13 +36,16 @@ Here is the list of available roadmaps with more being actively worked upon.
- [Backend Roadmap](https://roadmap.sh/backend) / [Backend Beginner Roadmap](https://roadmap.sh/backend?r=backend-beginner)
- [DevOps Roadmap](https://roadmap.sh/devops) / [DevOps Beginner Roadmap](https://roadmap.sh/devops?r=devops-beginner)
- [Full Stack Roadmap](https://roadmap.sh/full-stack)
- [API Design Roadmap](https://roadmap.sh/api-design)
- [Computer Science Roadmap](https://roadmap.sh/computer-science)
- [Data Structures and Algorithms Roadmap](https://roadmap.sh/datastructures-and-algorithms)
- [AI and Data Scientist Roadmap](https://roadmap.sh/ai-data-scientist)
- [AWS Roadmap](https://roadmap.sh/aws)
- [Linux Roadmap](https://roadmap.sh/linux)
- [Terraform Roadmap](https://roadmap.sh/terraform)
- [Data Analyst Roadmap](https://roadmap.sh/data-analyst)
- [MLOps Roadmap](https://roadmap.sh/mlops)
- [Product Manager Roadmap](https://roadmap.sh/product-manager)
- [QA Roadmap](https://roadmap.sh/qa)
- [Python Roadmap](https://roadmap.sh/python)
- [Software Architect Roadmap](https://roadmap.sh/software-architect)
@@ -58,6 +61,7 @@ Here is the list of available roadmaps with more being actively worked upon.
- [Node.js Roadmap](https://roadmap.sh/nodejs)
- [GraphQL Roadmap](https://roadmap.sh/graphql)
- [Android Roadmap](https://roadmap.sh/android)
- [iOS Roadmap](https://roadmap.sh/ios)
- [Flutter Roadmap](https://roadmap.sh/flutter)
- [Go Roadmap](https://roadmap.sh/golang)
- [Rust Roadmap](https://roadmap.sh/rust)
@@ -76,6 +80,7 @@ Here is the list of available roadmaps with more being actively worked upon.
- [Docker Roadmap](https://roadmap.sh/docker)
- [Prompt Engineering Roadmap](https://roadmap.sh/prompt-engineering)
- [Technical Writer Roadmap](https://roadmap.sh/technical-writer)
- [DevRel Engineer Roadmap](https://roadmap.sh/devrel)
There are also interactive best practices:
@@ -90,6 +95,8 @@ There are also interactive best practices:
- [JavaScript Questions](https://roadmap.sh/questions/javascript)
- [Node.js Questions](https://roadmap.sh/questions/nodejs)
- [React Questions](https://roadmap.sh/questions/react)
- [Backend Questions](https://roadmap.sh/questions/backend)
- [Frontend Questions](https://roadmap.sh/questions/frontend)
![](https://i.imgur.com/waxVImv.png)
@@ -109,6 +116,7 @@ Clone the repository, install the dependencies and start the application
```bash
git clone git@github.com:kamranahmedse/developer-roadmap.git
cd developer-roadmap
npm install
npm run dev
```

View File

@@ -0,0 +1,189 @@
const fs = require('node:fs');
const path = require('node:path');
const allRoadmapDirs = fs.readdirSync(
path.join(__dirname, '../src/data/roadmaps'),
);
allRoadmapDirs.forEach((roadmapId) => {
const roadmapDir = path.join(
__dirname,
`../src/data/roadmaps/${roadmapId}/content`,
);
function getHostNameWithoutTld(hostname) {
const parts = hostname.split('.');
return parts.slice(0, parts.length - 1).join('.');
}
function isOfficialWebsite(hostname, fileName, roadmapId) {
fileName = fileName.replace('/index.md', '').replace('.md', '');
const parts = fileName.split('/');
const lastPart = parts[parts.length - 1];
const normalizedFilename = lastPart.replace(/\d+/g, '').replace(/-/g, '');
const normalizedHostname = getHostNameWithoutTld(hostname);
if (normalizedFilename === normalizedHostname) {
return true;
}
if (normalizedFilename.includes(normalizedHostname)) {
return true;
}
return !!roadmapId.includes(normalizedHostname);
}
// websites are educational websites that are of following types:
// - @official@
// - @article@
// - @course@
// - @opensource@
// - @podcast@
// - @video@
// - @website@
// content is only educational websites
function getTypeFromHostname(hostname, fileName, roadmapId) {
hostname = hostname.replace('www.', '');
const videoHostnames = ['youtube.com', 'vimeo.com', 'youtu.be'];
const courseHostnames = ['coursera.org', 'udemy.com', 'edx.org'];
const podcastHostnames = ['spotify.com', 'apple.com'];
const opensourceHostnames = ['github.com', 'gitlab.com'];
const articleHostnames = [
'neilpatel.com',
'learningseo.io',
'htmlreference.io',
'docs.gitlab.com',
'docs.github.com',
'skills.github.com',
'cloudflare.com',
'w3schools.com',
'medium.com',
'dev.to',
'web.dev',
'css-tricks.com',
'developer.mozilla.org',
'smashingmagazine.com',
'freecodecamp.org',
'cs.fyi',
'thenewstack.io',
'html5rocks.com',
'html.com',
'javascript.info',
'css-tricks.com',
'developer.apple.com',
];
if (articleHostnames.includes(hostname)) {
return 'article';
}
if (videoHostnames.includes(hostname)) {
return 'video';
}
if (courseHostnames.includes(hostname)) {
return 'course';
}
if (podcastHostnames.includes(hostname)) {
return 'podcast';
}
if (opensourceHostnames.includes(hostname)) {
return 'opensource';
}
if (hostname === 'roadmap.sh') {
return 'roadmap.sh';
}
if (isOfficialWebsite(hostname, fileName, roadmapId)) {
return 'official';
}
return 'article';
}
function readNestedMarkdownFiles(dir, files = []) {
const dirEnts = fs.readdirSync(dir, { withFileTypes: true });
for (const dirent of dirEnts) {
const fullPath = path.join(dir, dirent.name);
if (dirent.isDirectory()) {
readNestedMarkdownFiles(fullPath, files);
} else {
if (path.extname(fullPath) === '.md') {
files.push(fullPath);
}
}
}
return files;
}
const files = readNestedMarkdownFiles(roadmapDir);
// for each of the files, assign the type of link to the beginning of each markdown link
// i.e. - [@article@abc](xyz) where @article@ is the type of link. Possible types:
// - @official@
// - @opensource@
// - @article@
// - @course@
// - @opensource@
// - @podcast@
// - @video@
files.forEach((file) => {
const content = fs.readFileSync(file, 'utf-8');
const lines = content.split('\n');
const newContent = lines
.map((line) => {
if (line.startsWith('- [') && !line.startsWith('- [@')) {
const type = line.match(/@(\w+)@/);
if (type) {
return line;
}
let urlMatches = line.match(/\((https?:\/\/[^)]+)\)/);
let fullUrl = urlMatches?.[1];
if (!fullUrl) {
// is it slashed URL i.e. - [abc](/xyz)
fullUrl = line.match(/\((\/[^)]+)\)/)?.[1];
if (fullUrl) {
fullUrl = `https://roadmap.sh${fullUrl}`;
}
if (!fullUrl) {
console.error('Invalid URL found in:', file);
return line;
}
}
const url = new URL(fullUrl);
const hostname = url.hostname;
let urlType = getTypeFromHostname(hostname, file, roadmapId);
const linkText = line.match(/\[([^\]]+)\]/)[1];
if (
linkText.toLowerCase().startsWith('visit dedicated') &&
linkText.toLowerCase().endsWith('roadmap')
) {
urlType = 'roadmap';
}
return line.replace('- [', `- [@${urlType}@`).replace('](', '](');
}
return line;
})
.join('\n');
fs.writeFileSync(file, newContent);
});
});

31
scripts/close-issues.sh Executable file
View File

@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Fetch issues JSON data and parse it properly
issues=$(gh issue list --repo kamranahmedse/developer-roadmap --search "sort:created-asc" --state open --limit 500 --json number,title,createdAt,updatedAt,state,url,comments,reactionGroups,body | jq -c '.[]')
# Loop through the issues and delete the ones created in 2022 and not updated in the past year
while IFS= read -r issue; do
created_at=$(echo "$issue" | jq -r '.createdAt')
updated_at=$(echo "$issue" | jq -r '.updatedAt')
issue_number=$(echo "$issue" | jq -r '.number')
issue_title=$(echo "$issue" | jq -r '.title')
reaction_groups=$(echo "$issue" | jq -r '.reactionGroups')
has_reactions=$(echo "$issue" | jq -r '.reactionGroups | length')
comment_count=$(echo "$issue" | jq -r '.comments | length')
body_characters=$(echo "$issue" | jq -r '.body | length')
# if has empty body
if [[ "$created_at" == 2024-01* ]]; then
comment="Hey there!
Looks like this issue has been hanging around for a bit without much action. Our roadmaps have evolved quite a bit since then, and a bunch of older issues aren't really applicable anymore. So, we're tidying things up by closing out the older ones to keep our issue tracker nice and organized for future feedback.
If you still think this problem needs addressing, don't hesitate to reopen the issue. We're here to help!
Thanks a bunch!"
gh issue comment "$issue_number" --body "$comment"
gh issue close "$issue_number"
fi
done <<< "$issues"

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# get all the folder names inside src/data/roadmaps
roadmap_ids=$(ls src/data/roadmaps)
# create a label for each roadmap name on github issues using gh cli
for roadmap_id in $roadmap_ids
do
random_color=$(openssl rand -hex 3)
gh label create "$roadmap_id" --color $random_color --description "Roadmap: $roadmap_id"
done

View File

@@ -0,0 +1,76 @@
import playwright from 'playwright';
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
// ERROR: `__dirname` is not defined in ES module scope
// https://iamwebwiz.medium.com/how-to-fix-dirname-is-not-defined-in-es-module-scope-34d94a86694d
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Usage: tsx ./scripts/editor-roadmap-dirs.ts <roadmapId>
// Directory containing the roadmaps
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/data/roadmaps');
const roadmapId = process.argv[2];
const allowedRoadmapIds = await fs.readdir(ROADMAP_CONTENT_DIR);
if (!roadmapId) {
console.error('Roadmap Id is required');
process.exit(1);
}
if (!allowedRoadmapIds.includes(roadmapId)) {
console.error(`Invalid roadmap key ${roadmapId}`);
console.error(`Allowed keys are ${allowedRoadmapIds.join(', ')}`);
process.exit(1);
}
const roadmapFrontmatterDir = path.join(
ROADMAP_CONTENT_DIR,
roadmapId,
`${roadmapId}.md`,
);
const roadmapFrontmatterRaw = await fs.readFile(roadmapFrontmatterDir, 'utf-8');
const { data } = matter(roadmapFrontmatterRaw);
const roadmapFrontmatter = data as RoadmapFrontmatter;
if (!roadmapFrontmatter) {
console.error('Invalid roadmap frontmatter');
process.exit(1);
}
if (roadmapFrontmatter.renderer !== 'editor') {
console.error('Only Editor Rendered Roadmaps are allowed');
process.exit(1);
}
console.log(`Launching chromium`);
const browser = await playwright.chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
const pageUrl = `http://localhost:3000/${roadmapId}/svg`;
console.log(`Opening page ${pageUrl}`);
await page.goto(pageUrl);
await page.waitForSelector('#resource-svg-wrap');
await page.waitForTimeout(5000);
console.log(`Generating PDF ${pageUrl}`);
await page.pdf({
path: `./public/pdfs/roadmaps/${roadmapId}.pdf`,
margin: { top: 0, right: 0, bottom: 0, left: 0 },
height: roadmapFrontmatter?.dimensions?.height || 2000,
width: roadmapFrontmatter?.dimensions?.width || 968,
});
// @todo generate png from the pdf
console.log(`Generating png ${pageUrl}`);
await page.locator('#resource-svg-wrap>svg').screenshot({
path: `./public/roadmaps/${roadmapId}.png`,
type: 'png',
scale: 'device',
});
await browser.close();

View File

@@ -0,0 +1,185 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Edge, Node } from 'reactflow';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
import { slugify } from '../src/lib/slugger';
import OpenAI from 'openai';
import { runPromisesInBatchSequentially } from '../src/lib/promise';
// ERROR: `__dirname` is not defined in ES module scope
// https://iamwebwiz.medium.com/how-to-fix-dirname-is-not-defined-in-es-module-scope-34d94a86694d
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Usage: tsx ./scripts/editor-roadmap-content.ts <roadmapId>
const OPEN_AI_API_KEY = process.env.OPEN_AI_API_KEY;
console.log('OPEN_AI_API_KEY:', OPEN_AI_API_KEY);
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/data/roadmaps');
const roadmapId = process.argv[2];
const allowedRoadmapIds = await fs.readdir(ROADMAP_CONTENT_DIR);
if (!roadmapId) {
console.error('Roadmap Id is required');
process.exit(1);
}
if (!allowedRoadmapIds.includes(roadmapId)) {
console.error(`Invalid roadmap key ${roadmapId}`);
console.error(`Allowed keys are ${allowedRoadmapIds.join(', ')}`);
process.exit(1);
}
const roadmapFrontmatterDir = path.join(
ROADMAP_CONTENT_DIR,
roadmapId,
`${roadmapId}.md`,
);
const roadmapFrontmatterRaw = await fs.readFile(roadmapFrontmatterDir, 'utf-8');
const { data } = matter(roadmapFrontmatterRaw);
const roadmapFrontmatter = data as RoadmapFrontmatter;
if (!roadmapFrontmatter) {
console.error('Invalid roadmap frontmatter');
process.exit(1);
}
if (roadmapFrontmatter.renderer !== 'editor') {
console.error('Only Editor Rendered Roadmaps are allowed');
process.exit(1);
}
const roadmapDir = path.join(
ROADMAP_CONTENT_DIR,
roadmapId,
`${roadmapId}.json`,
);
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
let { nodes, edges } = JSON.parse(roadmapContent) as {
nodes: Node[];
edges: Edge[];
};
const enrichedNodes = nodes
.filter(
(node) =>
node?.type &&
['topic', 'subtopic'].includes(node.type) &&
node.data?.label,
)
.map((node) => {
// Because we only need the parent id and title for subtopics
if (node.type !== 'subtopic') {
return node;
}
const parentNodeId =
edges.find((edge) => edge.target === node.id)?.source || '';
const parentNode = nodes.find((n) => n.id === parentNodeId);
return {
...node,
parentId: parentNodeId,
parentTitle: parentNode?.data?.label || '',
};
}) as (Node & { parentId?: string; parentTitle?: string })[];
const roadmapContentDir = path.join(ROADMAP_CONTENT_DIR, roadmapId, 'content');
const stats = await fs.stat(roadmapContentDir).catch(() => null);
if (!stats || !stats.isDirectory()) {
await fs.mkdir(roadmapContentDir, { recursive: true });
}
let openai: OpenAI | undefined;
if (OPEN_AI_API_KEY) {
openai = new OpenAI({
apiKey: OPEN_AI_API_KEY,
});
}
function writeTopicContent(
roadmapTitle: string,
childTopic: string,
parentTopic?: string,
) {
let prompt = `I will give you a topic and you need to write a brief introduction for that with regards to "${roadmapTitle}". Your format should be as follows and be in strictly markdown format:
# (Put a heading for the topic without adding parent "Subtopic in Topic" or "Topic in Roadmap" or "Subtopic under XYZ" etc.)
(Briefly explain the topic in one paragraph using simple english with regards to "${roadmapTitle}". Don't start with explaining how important the topic is with regard to "${roadmapTitle}". Don't say something along the lines of "XYZ plays a crucial role in ${roadmapTitle}". Don't include anything saying "In the context of ${roadmapTitle}". Instead, start with a simple explanation of the topic itself. For example, if the topic is "React", you can start with "React is a JavaScript library for building user interfaces." and then you can explain how it is used in "${roadmapTitle}".)
`;
if (!parentTopic) {
prompt += `First topic is: ${childTopic}`;
} else {
prompt += `First topic is: ${childTopic} under ${parentTopic}`;
}
return new Promise((resolve, reject) => {
openai?.chat.completions
.create({
model: 'gpt-4',
messages: [
{
role: 'user',
content: prompt,
},
],
})
.then((response) => {
const article = response.choices[0].message.content;
resolve(article);
})
.catch((err) => {
reject(err);
});
});
}
async function writeNodeContent(node: Node & { parentTitle?: string }) {
const nodeDirPattern = `${slugify(node.data.label)}@${node.id}.md`;
if (!roadmapContentFiles.includes(nodeDirPattern)) {
console.log(`Missing file for: ${nodeDirPattern}`);
return;
}
const nodeDir = path.join(roadmapContentDir, nodeDirPattern);
const nodeContent = await fs.readFile(nodeDir, 'utf-8');
const isFileEmpty = !nodeContent.replace(`# ${node.data.label}`, '').trim();
if (!isFileEmpty) {
console.log(`❌ Ignoring ${nodeDirPattern}. Not empty.`);
return;
}
const topic = node.data.label;
const parentTopic = node.parentTitle;
console.log(`⏳ Generating content for ${topic}...`);
let newContentFile = '';
if (OPEN_AI_API_KEY) {
newContentFile = (await writeTopicContent(
roadmapFrontmatter.title,
topic,
parentTopic,
)) as string;
} else {
newContentFile = `# ${topic}`;
}
await fs.writeFile(nodeDir, newContentFile, 'utf-8');
console.log(`✅ Content generated for ${topic}`);
}
let roadmapContentFiles = await fs.readdir(roadmapContentDir, {
recursive: true,
});
if (!OPEN_AI_API_KEY) {
console.log('----------------------------------------');
console.log('OPEN_AI_API_KEY not found. Skipping openai api calls...');
console.log('----------------------------------------');
}
const promises = enrichedNodes.map((node) => () => writeNodeContent(node));
await runPromisesInBatchSequentially(promises, 20);
console.log('✅ All content generated');

View File

@@ -0,0 +1,86 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Node } from 'reactflow';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
import { slugify } from '../src/lib/slugger';
// ERROR: `__dirname` is not defined in ES module scope
// https://iamwebwiz.medium.com/how-to-fix-dirname-is-not-defined-in-es-module-scope-34d94a86694d
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Usage: tsx ./scripts/editor-roadmap-dirs.ts <roadmapId>
// Directory containing the roadmaps
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/data/roadmaps');
const roadmapId = process.argv[2];
const allowedRoadmapIds = await fs.readdir(ROADMAP_CONTENT_DIR);
if (!roadmapId) {
console.error('Roadmap Id is required');
process.exit(1);
}
if (!allowedRoadmapIds.includes(roadmapId)) {
console.error(`Invalid roadmap key ${roadmapId}`);
console.error(`Allowed keys are ${allowedRoadmapIds.join(', ')}`);
process.exit(1);
}
const roadmapFrontmatterDir = path.join(
ROADMAP_CONTENT_DIR,
roadmapId,
`${roadmapId}.md`,
);
const roadmapFrontmatterRaw = await fs.readFile(roadmapFrontmatterDir, 'utf-8');
const { data } = matter(roadmapFrontmatterRaw);
const roadmapFrontmatter = data as RoadmapFrontmatter;
if (!roadmapFrontmatter) {
console.error('Invalid roadmap frontmatter');
process.exit(1);
}
if (roadmapFrontmatter.renderer !== 'editor') {
console.error('Only Editor Rendered Roadmaps are allowed');
process.exit(1);
}
const roadmapDir = path.join(
ROADMAP_CONTENT_DIR,
roadmapId,
`${roadmapId}.json`,
);
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
let { nodes } = JSON.parse(roadmapContent) as {
nodes: Node[];
};
nodes = nodes.filter(
(node) =>
node?.type && ['topic', 'subtopic'].includes(node.type) && node.data?.label,
);
const roadmapContentDir = path.join(ROADMAP_CONTENT_DIR, roadmapId, 'content');
const stats = await fs.stat(roadmapContentDir).catch(() => null);
if (!stats || !stats.isDirectory()) {
await fs.mkdir(roadmapContentDir, { recursive: true });
}
const roadmapContentFiles = await fs.readdir(roadmapContentDir, {
recursive: true,
});
nodes.forEach(async (node, index) => {
const nodeDirPattern = `${slugify(node.data.label)}@${node.id}.md`;
if (roadmapContentFiles.includes(nodeDirPattern)) {
console.log(`Skipping ${nodeDirPattern}`);
return;
}
await fs.writeFile(
path.join(roadmapContentDir, nodeDirPattern),
`# ${node.data.label}`,
);
});

View File

@@ -475,8 +475,6 @@ function getRoadmapDefaultTemplate({ title, description }) {
function getRoadmapImageTemplate({ title, description, image, height, width }) {
return html`<div tw="bg-white relative flex flex-col h-full w-full">
<div tw="absolute flex top-0 left-0 w-full h-[18px] bg-black"></div>
<div tw="flex flex-col px-[90px] pt-[90px]">
<div tw="flex flex-col pb-0">
<div tw="text-[70px] leading-[70px] tracking-tight">

View File

@@ -29,4 +29,6 @@ done
# ignore the worktree changes for the editor directory
git update-index --assume-unchanged editor/readonly-editor.tsx || true
git update-index --assume-unchanged editor/readonly-editor.tsx || true
git update-index --assume-unchanged editor/renderer/index.tsx || true
git update-index --assume-unchanged editor/renderer/renderer.ts || true

41
scripts/label-issues.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Fetch issues JSON data and parse it properly
issues=$(gh issue list --repo kamranahmedse/developer-roadmap --search "sort:created-asc" --state open --limit 500 --json number,title,createdAt,updatedAt,state,url,comments,reactionGroups,body | jq -c '.[]')
# checks the body of issue, identifies the slug from the roadmap URLs
# and labels the issue with the corresponding slug
while IFS= read -r issue; do
created_at=$(echo "$issue" | jq -r '.createdAt')
updated_at=$(echo "$issue" | jq -r '.updatedAt')
issue_number=$(echo "$issue" | jq -r '.number')
issue_title=$(echo "$issue" | jq -r '.title')
reaction_groups=$(echo "$issue" | jq -r '.reactionGroups')
has_reactions=$(echo "$issue" | jq -r '.reactionGroups | length')
comment_count=$(echo "$issue" | jq -r '.comments | length')
body_characters=$(echo "$issue" | jq -r '.body | length')
# If the issue has no body, then skip it
if [ "$body_characters" -eq 0 ]; then
continue
fi
# Extract the roadmap URLs from the issue body
roadmap_urls=$(echo "$issue" | jq -r '.body' | grep -o 'https://roadmap\.sh/[^ ]*')
# If no roadmap URLs found, then skip it
if [ -z "$roadmap_urls" ]; then
continue
fi
# URL is like https://roadmap.sh/frontend
# Extract the slug from the URL
slug_of_first_url=$(echo "$roadmap_urls" | head -n 1 | sed 's/https:\/\/roadmap\.sh\///')
if [ -z "$slug_of_first_url" ]; then
continue
fi
# Label the issue with the slug
gh issue edit "$issue_number" --add-label "$slug_of_first_url"
done <<< "$issues"

45
scripts/warm-urls.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
# Usage: warm-urls.sh <sitemap-url>
# Example: warm-urls.sh https://www.example.com/sitemap.xml
# Check if sitemap url is provided
if [ -z "$1" ]; then
echo "Please provide sitemap URL" >&2
exit 1
fi
# Get all URLs from sitemap
urls=$(curl -s "$1" | grep -o "<loc>[^<]*</loc>" | sed 's#<loc>\(.*\)</loc>#\1#')
failed_urls=()
# Warm up URLs
for url in $urls; do
# Fetch the og:image URL from the meta tags
og_image_url=$(curl -s "$url" | grep -o "<meta property=\"og:image\" content=\"[^\"]*\"" | sed 's#<meta property="og:image" content="\([^"]*\)"#\1#')
# warm the URL
echo "Warming up URL: $url"
if ! curl -s -I "$url" > /dev/null; then
failed_urls+=("$url")
fi
# Warm up the og:image URL
if [ -n "$og_image_url" ]; then
echo "Warming up OG: $og_image_url"
if ! curl -s -I "$og_image_url" > /dev/null; then
failed_urls+=("$og_image_url")
fi
else
echo "No og:image found for $url"
fi
done
# Print failed URLs
if [ ${#failed_urls[@]} -gt 0 ]; then
echo "Failed to warm up the following URLs:" >&2
for failed_url in "${failed_urls[@]}"; do
echo "$failed_url" >&2
done
fi

View File

@@ -18,6 +18,9 @@ export const allowedProfileVisibility = ['public', 'private'] as const;
export type AllowedProfileVisibility =
(typeof allowedProfileVisibility)[number];
export const allowedOnboardingStatus = ['done', 'pending', 'ignored'] as const;
export type AllowedOnboardingStatus = (typeof allowedOnboardingStatus)[number];
export interface UserDocument {
_id?: string;
name: string;
@@ -41,6 +44,7 @@ export interface UserDocument {
github?: string;
linkedin?: string;
twitter?: string;
dailydev?: string;
website?: string;
};
username?: string;
@@ -56,6 +60,18 @@ export interface UserDocument {
};
resetPasswordCodeAt: string;
verifiedAt: string;
// Onboarding fields
onboardingStatus?: AllowedOnboardingStatus;
onboarding?: {
updateProgress: AllowedOnboardingStatus;
publishProfile: AllowedOnboardingStatus;
customRoadmap: AllowedOnboardingStatus;
addFriends: AllowedOnboardingStatus;
roadCard: AllowedOnboardingStatus;
inviteTeam: AllowedOnboardingStatus;
};
createdAt: string;
updatedAt: string;
}

View File

@@ -1,10 +1,11 @@
import { useMemo, useState } from 'react';
import { useState } from 'react';
import { getRelativeTimeString } from '../../lib/date';
import type { ResourceType } from '../../lib/resource-progress';
import { EmptyStream } from './EmptyStream';
import { ActivityTopicsModal } from './ActivityTopicsModal.tsx';
import { ChevronsDown, ChevronsUp } from 'lucide-react';
import { ActivityTopicTitles } from './ActivityTopicTitles.tsx';
import { cn } from '../../lib/classname.ts';
export const allowedActivityActionType = [
'in_progress',
@@ -29,10 +30,16 @@ export type UserStreamActivity = {
type ActivityStreamProps = {
activities: UserStreamActivity[];
className?: string;
onResourceClick?: (
resourceId: string,
resourceType: ResourceType,
isCustomResource: boolean,
) => void;
};
export function ActivityStream(props: ActivityStreamProps) {
const { activities } = props;
const { activities, className, onResourceClick } = props;
const [showAll, setShowAll] = useState(false);
const [selectedActivity, setSelectedActivity] =
@@ -48,7 +55,7 @@ export function ActivityStream(props: ActivityStreamProps) {
.slice(0, showAll ? activities.length : 10);
return (
<div className="mx-0 px-0 py-5 md:-mx-10 md:px-8 md:py-8">
<div className={cn('mx-0 px-0 py-5 md:-mx-10 md:px-8 md:py-8', className)}>
<h2 className="mb-3 text-xs uppercase text-gray-400">
Learning Activity
</h2>
@@ -78,6 +85,7 @@ export function ActivityStream(props: ActivityStreamProps) {
updatedAt,
topicTitles,
isCustomResource,
resourceSlug,
} = activity;
const resourceUrl =
@@ -86,18 +94,28 @@ export function ActivityStream(props: ActivityStreamProps) {
: resourceType === 'best-practice'
? `/best-practices/${resourceId}`
: isCustomResource && resourceType === 'roadmap'
? `/r/${resourceId}`
? `/r/${resourceSlug}`
: `/${resourceId}`;
const resourceLinkComponent = (
<a
className="font-medium underline transition-colors hover:cursor-pointer hover:text-black"
target="_blank"
href={resourceUrl}
>
{resourceTitle}
</a>
);
const resourceLinkComponent =
onResourceClick && resourceType !== 'question' ? (
<button
className="font-medium underline transition-colors hover:cursor-pointer hover:text-black"
onClick={() =>
onResourceClick(resourceId, resourceType, isCustomResource!)
}
>
{resourceTitle}
</button>
) : (
<a
className="font-medium underline transition-colors hover:cursor-pointer hover:text-black"
target="_blank"
href={resourceUrl}
>
{resourceTitle}
</a>
);
const topicCount = topicTitles?.length || 0;

View File

@@ -26,16 +26,16 @@ export function ActivityTopicTitles(props: ActivityTopicTitlesProps) {
)}
>
{filteredTopicTitles.map((topicTitle, index) => (
<span key={index} className="rounded-md bg-gray-200 px-1">
<span key={index} className="rounded-md bg-gray-200 px-1.5">
{topicTitle}
</span>
))}
{shouldShowButton && (
{shouldShowButton && !showAll && (
<button
onClick={() => setShowAll(!showAll)}
className="text-gray-600 underline underline-offset-2 hover:text-black"
className="bg-white border border-black text-black rounded-md px-1.5 hover:bg-black text-xs h-[20px] hover:text-white"
>
{showAll ? '- Show less' : `+${topicTitles.length - 3} more`}
{showAll ? '- Show less' : `+${topicTitles.length - 3}`}
</button>
)}
</div>

View File

@@ -1,6 +1,7 @@
import { getUser } from '../../lib/jwt';
import { getPercentage } from '../../helper/number';
import { ResourceProgressActions } from './ResourceProgressActions';
import { cn } from '../../lib/classname';
type ResourceProgressType = {
resourceType: 'roadmap' | 'best-practice';
@@ -15,10 +16,17 @@ type ResourceProgressType = {
showClearButton?: boolean;
isCustomResource: boolean;
roadmapSlug?: string;
showActions?: boolean;
onResourceClick?: () => void;
};
export function ResourceProgress(props: ResourceProgressType) {
const { showClearButton = true, isCustomResource } = props;
const {
showClearButton = true,
isCustomResource,
showActions = true,
onResourceClick,
} = props;
const userId = getUser()?.id;
@@ -47,12 +55,23 @@ export function ResourceProgress(props: ResourceProgressType) {
const totalMarked = doneCount + skippedCount;
const progressPercentage = getPercentage(totalMarked, totalCount);
const Slot = onResourceClick ? 'button' : 'a';
return (
<div className="relative">
<a
target="_blank"
href={url}
className="group relative flex 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"
<Slot
{...(onResourceClick
? {
onClick: onResourceClick,
}
: {
href: url,
target: '_blank',
})}
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 text-left text-sm transition-all hover:border-gray-400',
showActions ? 'pr-7' : '',
)}
>
<span className="flex-grow truncate">{title}</span>
<span className="text-xs text-gray-400">
@@ -65,18 +84,20 @@ export function ResourceProgress(props: ResourceProgressType) {
width: `${progressPercentage}%`,
}}
></span>
</a>
</Slot>
<div className="absolute right-2 top-0 flex h-full items-center">
<ResourceProgressActions
userId={userId!}
resourceType={resourceType}
resourceId={resourceId}
isCustomResource={isCustomResource}
onCleared={onCleared}
showClearButton={showClearButton}
/>
</div>
{showActions && (
<div className="absolute right-2 top-0 flex h-full items-center">
<ResourceProgressActions
userId={userId!}
resourceType={resourceType}
resourceId={resourceId}
isCustomResource={isCustomResource}
onCleared={onCleared}
showClearButton={showClearButton}
/>
</div>
)}
</div>
);
}

View File

@@ -46,6 +46,7 @@ function handleGuest() {
'/team/roadmaps',
'/team/new',
'/team/members',
'/team/member',
'/team/settings',
];

View File

@@ -87,7 +87,7 @@ const isBestPracticeReady = !isUpcoming;
{
isBestPracticeReady && (
<a
href={`https://github.com/kamranahmedse/developer-roadmap/issues/new?title=[Suggestion] ${title}`}
href={`https://github.com/kamranahmedse/developer-roadmap/issues/new/choose`}
target="_blank"
class="inline-flex items-center justify-center rounded-md bg-gray-500 px-3 py-1.5 text-xs font-medium text-white hover:bg-gray-600 sm:text-sm"
aria-label="Suggest Changes"

View File

@@ -17,6 +17,8 @@ import { ClipboardIcon } from '../ReactIcons/ClipboardIcon.tsx';
import { GuideIcon } from '../ReactIcons/GuideIcon.tsx';
import { HomeIcon } from '../ReactIcons/HomeIcon.tsx';
import { VideoIcon } from '../ReactIcons/VideoIcon.tsx';
import { cn } from '../../lib/classname.ts';
import type { AllowedRoadmapRenderer } from '../../lib/roadmap.ts';
export type PageType = {
id: string;
@@ -26,6 +28,7 @@ export type PageType = {
icon?: ReactElement;
isProtected?: boolean;
metadata?: Record<string, any>;
renderer?: AllowedRoadmapRenderer;
};
const defaultPages: PageType[] = [
@@ -190,7 +193,7 @@ export function CommandMenu() {
return (
<div className="fixed left-0 right-0 top-0 z-50 flex h-full justify-center overflow-y-auto overflow-x-hidden bg-black/50">
<div className="relative top-0 h-full w-full max-w-lg p-2 sm:top-20 md:h-auto">
<div className="relative top-0 h-full w-full max-w-lg p-2 sm:mt-20 md:h-auto">
<div className="relative rounded-lg bg-white shadow" ref={modalRef}>
<input
ref={inputRef}
@@ -245,9 +248,10 @@ export function CommandMenu() {
<div className="border-b border-gray-100"></div>
)}
<a
className={`flex w-full items-center rounded p-2 text-sm ${
counter === activeCounter ? 'bg-gray-100' : ''
}`}
className={cn(
'flex w-full items-center rounded p-2 text-sm',
counter === activeCounter ? 'bg-gray-100' : '',
)}
onMouseOver={() => setActiveCounter(counter)}
href={page.url}
>

View File

@@ -24,6 +24,7 @@ export type TeamResourceConfig = {
topics?: number;
sharedTeamMemberIds: string[];
sharedFriendIds: string[];
defaultRoadmapId?: string;
}[];
type RoadmapSelectorProps = {
@@ -106,6 +107,7 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
}
pageProgressMessage.set(`Adding roadmap to team`);
const renderer = allRoadmaps.find((r) => r.id === roadmapId)?.renderer;
const { error, response } = await httpPut<TeamResourceConfig>(
`${
import.meta.env.PUBLIC_API_URL
@@ -115,6 +117,7 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
resourceId: roadmapId,
resourceType: 'roadmap',
removed: [],
renderer: renderer || 'balsamiq',
},
);
@@ -124,6 +127,9 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
}
setTeamResources(response);
if (renderer === 'editor') {
setShowSelectRoadmapModal(false);
}
}
useEffect(() => {

View File

@@ -68,7 +68,7 @@ export function SelectRoadmapModal(props: SelectRoadmapModalProps) {
);
return (
<div className="fixed left-0 right-0 top-0 z-50 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div className="fixed left-0 right-0 top-0 z-[100] h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div className="relative mx-auto h-full w-full max-w-2xl p-4 md:h-auto">
<div
ref={popupBodyEl}

View File

@@ -148,7 +148,7 @@ export function UpdateTeamResourceModal(props: ProgressMapProps) {
}, []);
return (
<div className="fixed left-0 right-0 top-0 z-50 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div className="fixed left-0 right-0 top-0 z-[100] h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div className="relative mx-auto h-full w-full max-w-4xl p-4 md:h-auto">
<div
id={'customized-roadmap'}

View File

@@ -15,6 +15,10 @@ export const allowedLinkTypes = [
'course',
'website',
'podcast',
'roadmap.sh',
'official',
'roadmap',
'feed'
] as const;
export type AllowedLinkTypes = (typeof allowedLinkTypes)[number];

View File

@@ -0,0 +1,15 @@
import type { SVGProps } from 'react';
export function DailyDevIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg viewBox="0 0 32 18" xmlns="http://www.w3.org/2000/svg" {...props}>
<g fill="currentColor" fillRule="nonzero">
<path
d="M26.633 8.69l-3.424-3.431 1.711-3.43 5.563 5.575c.709.71.709 1.861 0 2.572l-6.847 6.86c-.709.711-1.858.711-2.567 0a1.821 1.821 0 010-2.571l5.564-5.575z"
fillOpacity="0.64"
></path>
<path d="M21.07.536a1.813 1.813 0 012.568 0l1.283 1.286L9.945 16.83c-.709.71-1.858.71-2.567 0l-1.284-1.287L21.071.536zm-6.418 4.717l-2.567 2.572-3.424-3.43-4.28 4.288 3.424 3.43-1.71 3.43L.531 9.97a1.821 1.821 0 010-2.572L7.378.537A1.813 1.813 0 019.945.535l4.707 4.717z"></path>
</g>
</svg>
);
}

View File

@@ -0,0 +1,106 @@
import { useEffect, useState, type CSSProperties } from 'react';
import {
EditorRoadmapRenderer,
type RoadmapRendererProps,
} from './EditorRoadmapRenderer';
import { Spinner } from '../ReactIcons/Spinner';
import {
clearMigratedRoadmapProgress,
type ResourceType,
} from '../../lib/resource-progress';
import { httpGet } from '../../lib/http';
import { ProgressNudge } from '../FrameRenderer/ProgressNudge';
import { getUrlParams } from '../../lib/browser.ts';
import { cn } from '../../lib/classname.ts';
import { getUser } from '../../lib/jwt.ts';
type EditorRoadmapProps = {
resourceId: string;
resourceType?: ResourceType;
dimensions: {
width: number;
height: number;
};
};
export function EditorRoadmap(props: EditorRoadmapProps) {
const { resourceId, resourceType = 'roadmap', dimensions } = props;
const [hasSwitchedRoadmap, setHasSwitchedRoadmap] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [roadmapData, setRoadmapData] = useState<
Omit<RoadmapRendererProps, 'resourceId'> | undefined
>(undefined);
const loadRoadmapData = async () => {
setIsLoading(true);
const { r: switchRoadmapId } = getUrlParams();
const { response, error } = await httpGet<
Omit<RoadmapRendererProps, 'resourceId'>
>(`/${switchRoadmapId || resourceId}.json`);
if (error) {
console.error(error);
return;
}
setRoadmapData(response);
setIsLoading(false);
setHasSwitchedRoadmap(!!switchRoadmapId);
};
useEffect(() => {
clearMigratedRoadmapProgress(resourceType, resourceId);
loadRoadmapData().finally();
}, [resourceId]);
const aspectRatio = dimensions.width / dimensions.height;
if (!roadmapData || isLoading) {
return (
<div
style={
!hasSwitchedRoadmap
? ({
'--aspect-ratio': aspectRatio,
} as CSSProperties)
: undefined
}
className={
'flex aspect-[var(--aspect-ratio)] w-full flex-col justify-center'
}
>
<div className="flex w-full justify-center">
<Spinner
innerFill="#2563eb"
outerFill="#E5E7EB"
className="h-6 w-6 animate-spin sm:h-12 sm:w-12"
/>
</div>
</div>
);
}
return (
<div
style={
!hasSwitchedRoadmap
? ({
'--aspect-ratio': aspectRatio,
} as CSSProperties)
: undefined
}
className={
'flex aspect-[var(--aspect-ratio)] w-full flex-col justify-center'
}
>
<EditorRoadmapRenderer
{...roadmapData}
dimensions={dimensions}
resourceId={resourceId}
/>
<ProgressNudge resourceId={resourceId} resourceType={resourceType} />
</div>
);
}

View File

@@ -0,0 +1,59 @@
svg text tspan {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeSpeed;
}
svg > g[data-type='topic'],
svg > g[data-type='subtopic'],
svg g[data-type='link-item'],
svg > g[data-type='button'],
svg > g[data-type='resourceButton'],
svg > g[data-type='todo-checkbox'],
svg > g[data-type='todo'] {
cursor: pointer;
}
svg > g[data-type='topic']:hover > rect {
fill: var(--hover-color);
}
svg > g[data-type='subtopic']:hover > rect {
fill: var(--hover-color);
}
svg g[data-type='button']:hover,
svg g[data-type='link-item']:hover,
svg g[data-type='resourceButton']:hover,
svg g[data-type='todo-checkbox']:hover {
opacity: 0.8;
}
svg .done rect {
fill: #cbcbcb !important;
}
svg .done text,
svg .skipped text {
text-decoration: line-through;
}
svg > g[data-type='topic'].learning > rect + text,
svg > g[data-type='topic'].done > rect + text {
fill: black;
}
svg > g[data-type='subtipic'].done > rect + text,
svg > g[data-type='subtipic'].learning > rect + text {
fill: #cbcbcb;
}
svg .learning rect {
fill: #dad1fd !important;
}
svg .learning text {
text-decoration: underline;
}
svg .skipped rect {
fill: #496b69 !important;
}

View File

@@ -0,0 +1,218 @@
import { useCallback, useEffect, useRef } from 'react';
import './EditorRoadmapRenderer.css';
import {
renderResourceProgress,
updateResourceProgress,
type ResourceProgressType,
renderTopicProgress,
refreshProgressCounters,
} from '../../lib/resource-progress';
import { pageProgressMessage } from '../../stores/page';
import { useToast } from '../../hooks/use-toast';
import type { Edge, Node } from 'reactflow';
import { Renderer } from '../../../editor/renderer';
import { slugify } from '../../lib/slugger';
import { isLoggedIn } from '../../lib/jwt';
import { showLoginPopup } from '../../lib/popup';
export type RoadmapRendererProps = {
resourceId: string;
nodes: Node[];
edges: Edge[];
dimensions: {
width: number;
height: number;
};
};
type RoadmapNodeDetails = {
nodeId: string;
nodeType: string;
targetGroup: SVGElement;
title?: string;
};
function getNodeDetails(svgElement: SVGElement): RoadmapNodeDetails | null {
const targetGroup = (svgElement?.closest('g') as SVGElement) || {};
const nodeId = targetGroup?.dataset?.nodeId;
const nodeType = targetGroup?.dataset?.type;
const title = targetGroup?.dataset?.title;
if (!nodeId || !nodeType) {
return null;
}
return { nodeId, nodeType, targetGroup, title };
}
const allowedNodeTypes = [
'topic',
'subtopic',
'button',
'link-item',
'resourceButton',
'todo',
'todo-checkbox',
];
export function EditorRoadmapRenderer(props: RoadmapRendererProps) {
const { resourceId, nodes = [], edges = [] } = props;
const roadmapRef = useRef<HTMLDivElement>(null);
const toast = useToast();
async function updateTopicStatus(
topicId: string,
newStatus: ResourceProgressType,
) {
pageProgressMessage.set('Updating progress');
updateResourceProgress(
{
resourceId,
resourceType: 'roadmap',
topicId,
},
newStatus,
)
.then(() => {
renderTopicProgress(topicId, newStatus);
})
.catch((err) => {
toast.error('Something went wrong, please try again.');
console.error(err);
})
.finally(() => {
pageProgressMessage.set('');
refreshProgressCounters();
});
return;
}
const handleSvgClick = useCallback((e: MouseEvent) => {
const target = e.target as SVGElement;
const { nodeId, nodeType, targetGroup, title } =
getNodeDetails(target) || {};
if (!nodeId || !nodeType || !allowedNodeTypes.includes(nodeType)) {
return;
}
if (
nodeType === 'button' ||
nodeType === 'link-item' ||
nodeType === 'resourceButton'
) {
const link = targetGroup?.dataset?.link || '';
const isExternalLink = link.startsWith('http');
if (isExternalLink) {
window.open(link, '_blank');
} else {
window.location.href = link;
}
return;
}
const isCurrentStatusLearning = targetGroup?.classList.contains('learning');
const isCurrentStatusSkipped = targetGroup?.classList.contains('skipped');
if (nodeType === 'todo-checkbox') {
e.preventDefault();
if (!isLoggedIn()) {
showLoginPopup();
return;
}
const newStatus = targetGroup?.classList.contains('done')
? 'pending'
: 'done';
updateTopicStatus(nodeId, newStatus);
return;
}
if (e.shiftKey) {
e.preventDefault();
if (!isLoggedIn()) {
showLoginPopup();
return;
}
updateTopicStatus(
nodeId,
isCurrentStatusLearning ? 'pending' : 'learning',
);
return;
} else if (e.altKey) {
e.preventDefault();
if (!isLoggedIn()) {
showLoginPopup();
return;
}
updateTopicStatus(nodeId, isCurrentStatusSkipped ? 'pending' : 'skipped');
return;
}
if (!title) {
return;
}
const detailsPattern = `${slugify(title)}@${nodeId}`;
window.dispatchEvent(
new CustomEvent('roadmap.node.click', {
detail: {
topicId: detailsPattern,
resourceId,
resourceType: 'roadmap',
},
}),
);
}, []);
const handleSvgRightClick = useCallback((e: MouseEvent) => {
e.preventDefault();
const target = e.target as SVGElement;
const { nodeId, nodeType, targetGroup } = getNodeDetails(target) || {};
if (!nodeId || !nodeType || !allowedNodeTypes.includes(nodeType)) {
return;
}
if (nodeType === 'button') {
return;
}
if (!isLoggedIn()) {
showLoginPopup();
return;
}
const isCurrentStatusDone = targetGroup?.classList.contains('done');
updateTopicStatus(nodeId, isCurrentStatusDone ? 'pending' : 'done');
}, []);
useEffect(() => {
if (!roadmapRef?.current) {
return;
}
roadmapRef?.current?.addEventListener('click', handleSvgClick);
roadmapRef?.current?.addEventListener('contextmenu', handleSvgRightClick);
return () => {
roadmapRef?.current?.removeEventListener('click', handleSvgClick);
roadmapRef?.current?.removeEventListener(
'contextmenu',
handleSvgRightClick,
);
};
}, []);
return (
<Renderer
ref={roadmapRef}
roadmap={{ nodes, edges }}
onRendered={() => {
roadmapRef.current?.setAttribute('data-renderer', 'editor');
renderResourceProgress('roadmap', resourceId).finally();
}}
/>
);
}

View File

@@ -1,35 +1,47 @@
---
import type { GuideFileType } from '../lib/guide';
import GuideListItem from './GuideListItem.astro';
import { QuestionGroupType } from '../lib/question-group';
export interface Props {
heading: string;
guides: GuideFileType[];
questions: QuestionGroupType[];
}
const { heading, guides } = Astro.props;
const { heading, guides, questions = [] } = Astro.props;
const sortedGuides: (QuestionGroupType | GuideFileType)[] = [
...guides,
...questions,
].sort((a, b) => {
const aDate = new Date(a.frontmatter.date);
const bDate = new Date(b.frontmatter.date);
return bDate.getTime() - aDate.getTime();
});
---
<div class='container'>
<h2 class='text-2xl sm:text-3xl font-bold block'>{heading}</h2>
<h2 class='block text-2xl font-bold sm:text-3xl'>{heading}</h2>
<div class='mt-3 sm:my-5'>
{guides.map((guide) => <GuideListItem guide={guide} />)}
{sortedGuides.map((guide) => <GuideListItem guide={guide} />)}
</div>
<a
href='/guides'
class='hidden sm:inline transition-colors py-2 px-3 text-xs font-medium rounded-full bg-gradient-to-r from-slate-600 to-black hover:from-blue-600 hover:to-blue-800 text-white'
class='hidden rounded-full bg-gradient-to-r from-slate-600 to-black px-3 py-2 text-xs font-medium text-white transition-colors hover:from-blue-600 hover:to-blue-800 sm:inline'
>
View All Guides &rarr;
</a>
<div class='block sm:hidden mt-3'>
<div class='mt-3 block sm:hidden'>
<a
href='/guides'
class='text-sm font-regular block p-2 border border-black text-black rounded-md text-center hover:bg-black hover:text-gray-50'
class='font-regular block rounded-md border border-black p-2 text-center text-sm text-black hover:bg-black hover:text-gray-50'
>
View All Guides &nbsp;&rarr;
</a>
</div>
</div>
</div>

View File

@@ -52,7 +52,7 @@ svg .done rect {
fill: #cbcbcb !important;
}
svg .done rect[stroke="rgb(255,229,153)"] {
svg .done rect[stroke='rgb(255,229,153)'] {
stroke: #cbcbcb !important;
}
@@ -133,10 +133,12 @@ svg .removed path {
}
}
#customized-roadmap #resource-svg-wrap g:not([class]),
#customized-roadmap #resource-svg-wrap circle,
#customized-roadmap #resource-svg-wrap path[stroke='#fff'],
#customized-roadmap #resource-svg-wrap g[data-group-id$='-note'] {
#customized-roadmap #resource-svg-wrap:not([data-renderer]) g:not([class]),
#customized-roadmap #resource-svg-wrap:not([data-renderer]) circle,
#customized-roadmap #resource-svg-wrap:not([data-renderer]) path[stroke='#fff'],
#customized-roadmap
#resource-svg-wrap:not([data-renderer])
g[data-group-id$='-note'] {
display: none;
}

View File

@@ -51,7 +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}</span> of <span>{$totalRoadmapNodes}</span> Done
<span>{done > $totalRoadmapNodes ? $totalRoadmapNodes : done}</span> of <span>{$totalRoadmapNodes}</span> Done
</span>
<span

View File

@@ -152,6 +152,10 @@ export class Renderer {
return;
}
if (/^check:/.test(topicId)) {
topicId = topicId.replace('check:', '');
}
pageProgressMessage.set('Updating progress');
updateResourceProgress(
{

View File

@@ -7,12 +7,14 @@ import { useToast } from '../../hooks/use-toast';
import { TrashIcon } from '../ReactIcons/TrashIcon';
import { AddedUserIcon } from '../ReactIcons/AddedUserIcon';
import { AddUserIcon } from '../ReactIcons/AddUserIcon';
import type { AllowedRoadmapRenderer } from '../../lib/roadmap';
type FriendProgressItemProps = {
friend: ListFriendsResponse[0];
onShowResourceProgress: (
resourceId: string,
isCustomResource?: boolean
isCustomResource?: boolean,
renderer?: AllowedRoadmapRenderer,
) => void;
onReload: () => void;
};
@@ -27,7 +29,7 @@ export function FriendProgressItem(props: FriendProgressItemProps) {
pageProgressMessage.set('Please wait...');
const { response, error } = await httpDelete(
`${import.meta.env.PUBLIC_API_URL}/v1-delete-friend/${userId}`,
{}
{},
);
if (error || !response) {
@@ -43,7 +45,7 @@ export function FriendProgressItem(props: FriendProgressItemProps) {
pageProgressMessage.set('Please wait...');
const { response, error } = await httpPost(
`${import.meta.env.PUBLIC_API_URL}/v1-add-friend/${userId}`,
{}
{},
);
if (error || !response) {
@@ -92,7 +94,8 @@ export function FriendProgressItem(props: FriendProgressItemProps) {
onClick={() =>
onShowResourceProgress(
progress.resourceId,
progress.isCustomResource
progress.isCustomResource,
progress?.renderer,
)
}
className="group relative overflow-hidden rounded-md border p-2 hover:border-gray-300 hover:text-black focus:outline-none"
@@ -160,7 +163,7 @@ export function FriendProgressItem(props: FriendProgressItemProps) {
deleteFriend(friend.userId, 'Friend removed').finally(
() => {
pageProgressMessage.set('');
}
},
);
}}
>
@@ -198,7 +201,7 @@ export function FriendProgressItem(props: FriendProgressItemProps) {
addFriend(friend.userId, 'Friend request accepted').finally(
() => {
pageProgressMessage.set('');
}
},
);
}}
>
@@ -225,7 +228,7 @@ export function FriendProgressItem(props: FriendProgressItemProps) {
deleteFriend(friend.userId, 'Friend request removed').finally(
() => {
pageProgressMessage.set('');
}
},
);
}}
>
@@ -267,7 +270,7 @@ export function FriendProgressItem(props: FriendProgressItemProps) {
onClick={() => {
deleteFriend(
friend.userId,
'Friend request withdrawn'
'Friend request withdrawn',
).finally(() => {
pageProgressMessage.set('');
});
@@ -304,7 +307,7 @@ export function FriendProgressItem(props: FriendProgressItemProps) {
addFriend(friend.userId, 'Friend request accepted').finally(
() => {
pageProgressMessage.set('');
}
},
);
}}
className="mb-1 block w-full max-w-[150px] rounded-md bg-black py-1.5 text-sm text-white"
@@ -316,7 +319,7 @@ export function FriendProgressItem(props: FriendProgressItemProps) {
onClick={() => {
deleteFriend(
friend.userId,
'Friend request rejected'
'Friend request rejected',
).finally(() => {
pageProgressMessage.set('');
});

View File

@@ -11,6 +11,7 @@ import { UserProgressModal } from '../UserProgress/UserProgressModal';
import { InviteFriendPopup } from './InviteFriendPopup';
import { UserCustomProgressModal } from '../UserProgress/UserCustomProgressModal';
import { UserIcon } from 'lucide-react';
import type { AllowedRoadmapRenderer } from '../../lib/roadmap';
type FriendResourceProgress = {
updatedAt: string;
@@ -22,6 +23,7 @@ type FriendResourceProgress = {
skipped: number;
done: number;
total: number;
renderer?: AllowedRoadmapRenderer;
};
export type ListFriendsResponse = {
@@ -55,6 +57,7 @@ export function FriendsPage() {
resourceId: string;
friend: ListFriendsResponse[0];
isCustomResource?: boolean;
renderer?: AllowedRoadmapRenderer;
}>();
const [isLoading, setIsLoading] = useState(true);
@@ -92,8 +95,8 @@ export function FriendsPage() {
(grouping) => grouping.value === selectedGrouping,
);
const filteredFriends = friends.filter(
(friend) => selectedGroupingType?.statuses.includes(friend.status),
const filteredFriends = friends.filter((friend) =>
selectedGroupingType?.statuses.includes(friend.status),
);
const receivedRequests = friends.filter(
@@ -124,6 +127,7 @@ export function FriendsPage() {
resourceType={'roadmap'}
onClose={() => setShowFriendProgress(undefined)}
isCustomResource={showFriendProgress?.isCustomResource}
renderer={showFriendProgress?.renderer}
/>
);
@@ -182,11 +186,16 @@ export function FriendsPage() {
{filteredFriends.map((friend) => (
<FriendProgressItem
friend={friend}
onShowResourceProgress={(resourceId, isCustomResource) => {
onShowResourceProgress={(
resourceId,
isCustomResource,
renderer,
) => {
setShowFriendProgress({
resourceId,
friend,
isCustomResource,
renderer,
});
}}
key={friend.userId}

View File

@@ -25,20 +25,14 @@ import { Ban, Cog, Download, PenSquare, Save, Wand } from 'lucide-react';
import { ShareRoadmapButton } from '../ShareRoadmapButton.tsx';
import { httpGet, httpPost } from '../../lib/http.ts';
import { pageProgressMessage } from '../../stores/page.ts';
import {
deleteUrlParam,
getUrlParams,
setUrlParams,
} from '../../lib/browser.ts';
import { deleteUrlParam, getUrlParams } from '../../lib/browser.ts';
import { downloadGeneratedRoadmapImage } from '../../helper/download-image.ts';
import { showLoginPopup } from '../../lib/popup.ts';
import { cn } from '../../lib/classname.ts';
import { RoadmapTopicDetail } from './RoadmapTopicDetail.tsx';
import { AIRoadmapAlert } from './AIRoadmapAlert.tsx';
import { OpenAISettings } from './OpenAISettings.tsx';
import { IS_KEY_ONLY_ROADMAP_GENERATION } from '../../lib/ai.ts';
import { AITermSuggestionInput } from './AITermSuggestionInput.tsx';
import { useParams } from '../../hooks/use-params.ts';
import { IncreaseRoadmapLimit } from './IncreaseRoadmapLimit.tsx';
import { AuthenticationForm } from '../AuthenticationFlow/AuthenticationForm.tsx';
@@ -294,7 +288,10 @@ export function GenerateRoadmap(props: GenerateRoadmapProps) {
setIsLoading(false);
pageProgressMessage.set('');
return response.roadmapSlug;
return {
roadmapId: response.roadmapId,
roadmapSlug: response.roadmapSlug,
};
};
const downloadGeneratedRoadmapContent = async () => {
@@ -686,9 +683,9 @@ export function GenerateRoadmap(props: GenerateRoadmapProps) {
<button
className="inline-flex items-center justify-center gap-2 rounded-md bg-gray-200 py-1.5 pl-2.5 pr-3 text-xs font-medium text-black transition-colors duration-300 hover:bg-gray-300 sm:text-sm"
onClick={async () => {
const roadmapSlug = await saveAIRoadmap();
if (roadmapSlug) {
window.location.href = `/r/${roadmapSlug}`;
const response = await saveAIRoadmap();
if (response?.roadmapSlug) {
window.location.href = `/r/${response.roadmapSlug}`;
}
}}
disabled={isLoading}
@@ -703,10 +700,10 @@ export function GenerateRoadmap(props: GenerateRoadmapProps) {
<button
className="hidden items-center justify-center gap-2 rounded-md bg-gray-200 py-1.5 pl-2.5 pr-3 text-xs font-medium text-black transition-colors duration-300 hover:bg-gray-300 sm:inline-flex sm:text-sm"
onClick={async () => {
const roadmapId = await saveAIRoadmap();
if (roadmapId) {
const response = await saveAIRoadmap();
if (response?.roadmapId) {
window.open(
`${import.meta.env.PUBLIC_EDITOR_APP_URL}/${roadmapId}`,
`${import.meta.env.PUBLIC_EDITOR_APP_URL}/${response?.roadmapId}`,
'_blank',
);
}

View File

@@ -42,7 +42,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
setIsAuthenticatedUser(isLoggedIn());
}, []);
const randomTerms = ['OAuth', 'APIs', 'UX Design', 'gRPC'];
const randomTerms = ['OAuth', 'UI / UX', 'SRE', 'DevRel'];
return (
<div className="flex flex-grow flex-col items-center px-4 py-6 sm:px-6 md:my-24 lg:my-32">

View File

@@ -124,7 +124,7 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
const openAIKey = getOpenAIKey();
return (
<div className={'relative z-50'}>
<div className={'relative z-[90]'}>
<div
ref={topicRef}
tabIndex={0}

View File

@@ -1,22 +1,39 @@
---
import type { GuideFileType } from '../lib/guide';
import type { GuideFileType, GuideFrontmatter } from '../lib/guide';
import { replaceVariables } from '../lib/markdown';
import { QuestionGroupType } from '../lib/question-group';
export interface Props {
guide: GuideFileType;
guide: GuideFileType | QuestionGroupType;
}
function isQuestionGroupType(
guide: GuideFileType | QuestionGroupType,
): guide is QuestionGroupType {
return (guide as QuestionGroupType).questions !== undefined;
}
const { guide } = Astro.props;
const { frontmatter, id } = guide;
let pageUrl = '';
let guideType = '';
if (isQuestionGroupType(guide)) {
pageUrl = `/questions/${id}`;
guideType = 'Questions';
} else {
const excludedBySlug = (frontmatter as GuideFrontmatter).excludedBySlug;
pageUrl = excludedBySlug ? excludedBySlug : `/guides/${id}`;
guideType = (frontmatter as GuideFrontmatter).type;
}
---
<a
class:list={[
'text-md group block flex items-center justify-between border-b py-2 text-gray-600 no-underline hover:text-blue-600',
]}
href={frontmatter.excludedBySlug
? frontmatter.excludedBySlug
: `/guides/${id}`}
href={pageUrl}
>
<span
class='text-sm transition-transform group-hover:translate-x-2 md:text-base'
@@ -38,7 +55,7 @@ const { frontmatter, id } = guide;
}
</span>
<span class='hidden text-xs capitalize text-gray-500 sm:block'>
{frontmatter.type}
{guideType}
</span>
<span class='block text-xs text-gray-400 sm:hidden'> &raquo;</span>

View File

@@ -10,12 +10,12 @@ import { AIAnnouncement } from "../AIAnnouncement";
class='container px-5 py-6 pb-14 text-left transition-opacity duration-300 sm:px-0 sm:py-20 sm:text-center'
id='hero-text'
>
<p class='-mt-4 mb-7 sm:-mt-10'>
<p class='-mt-4 mb-7 sm:-mt-10 sm:mb-4'>
<AIAnnouncement />
</p>
<h1
class='mb-2 bg-gradient-to-b from-amber-50 to-purple-500 bg-clip-text text-2xl font-bold text-transparent sm:mb-4 sm:text-5xl'
class='mb-2 bg-gradient-to-b from-amber-50 to-purple-500 bg-clip-text text-2xl font-bold text-transparent sm:mb-4 sm:text-5xl sm:leading-tight'
>
Developer Roadmaps
</h1>

View File

@@ -1,68 +1,173 @@
import { useRef, useState } from 'react';
import { ChevronDown } from 'lucide-react';
import { isLoggedIn } from '../../lib/jwt';
import { useEffect, useRef, useState } from 'react';
import { ChevronDown, User } from 'lucide-react';
import { getUser, isLoggedIn } from '../../lib/jwt';
import { AccountDropdownList } from './AccountDropdownList';
import { DropdownTeamList } from './DropdownTeamList';
import { useOutsideClick } from '../../hooks/use-outside-click';
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal.tsx';
import { OnboardingModal } from './OnboardingModal.tsx';
import { httpGet } from '../../lib/http.ts';
import { useToast } from '../../hooks/use-toast.ts';
import type { UserDocument } from '../../api/user.ts';
import { NotificationIndicator } from './NotificationIndicator.tsx';
import { OnboardingNudge } from '../OnboardingNudge.tsx';
export type OnboardingConfig = Pick<
UserDocument,
'onboarding' | 'onboardingStatus'
>;
export function AccountDropdown() {
const toast = useToast();
const dropdownRef = useRef(null);
const [showDropdown, setShowDropdown] = useState(false);
const [isTeamsOpen, setIsTeamsOpen] = useState(false);
const [isCreatingRoadmap, setIsCreatingRoadmap] = useState(false);
const [isConfigLoading, setIsConfigLoading] = useState(false);
const [isOnboardingModalOpen, setIsOnboardingModalOpen] = useState(false);
const [onboardingConfig, setOnboardingConfig] = useState<
OnboardingConfig | undefined
>(undefined);
const currentUser = getUser();
const shouldShowOnboardingStatus =
currentUser?.onboardingStatus === 'pending' ||
onboardingConfig?.onboardingStatus === 'pending';
const loadOnboardingConfig = async () => {
if (!isLoggedIn() || !shouldShowOnboardingStatus) {
return;
}
setIsConfigLoading(true);
const { response, error } = await httpGet<OnboardingConfig>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-onboarding-config`,
);
if (error || !response) {
toast.error(error?.message || 'Failed to load onboarding config');
}
setOnboardingConfig(response);
};
useOutsideClick(dropdownRef, () => {
setShowDropdown(false);
setIsTeamsOpen(false);
setIsConfigLoading(true);
});
useEffect(() => {
if (!isLoggedIn() || !showDropdown) {
return;
}
loadOnboardingConfig().finally(() => {
setIsConfigLoading(false);
});
}, [showDropdown]);
useEffect(() => {
const loadConfig = () => {
loadOnboardingConfig().finally(() => {
setIsConfigLoading(false);
});
};
window.addEventListener('visibilitychange', loadConfig);
return () => {
window.removeEventListener('visibilitychange', loadConfig);
};
}, []);
if (!isLoggedIn()) {
return null;
}
const onboardingDoneCount = Object.values(
onboardingConfig?.onboarding || {},
).filter((status) => status !== 'pending').length;
const onboardingCount = Object.keys(
onboardingConfig?.onboarding || {},
).length;
return (
<div className="relative z-50 animate-fade-in">
{isCreatingRoadmap && (
<CreateRoadmapModal
onClose={() => {
setIsCreatingRoadmap(false);
<>
{shouldShowOnboardingStatus && !isOnboardingModalOpen && (
<OnboardingNudge
onStartOnboarding={() => {
loadOnboardingConfig().then(() => {
setIsOnboardingModalOpen(true);
});
}}
/>
)}
<button
className="flex h-8 w-40 items-center justify-center gap-1.5 rounded-full bg-gradient-to-r from-purple-500 to-purple-700 px-4 py-2 text-sm font-medium text-white hover:from-purple-500 hover:to-purple-600"
onClick={() => {
setIsTeamsOpen(false);
setShowDropdown(!showDropdown);
}}
>
<span className="inline-flex items-center">
Account&nbsp;<span className="text-gray-300">/</span>&nbsp;Teams
</span>
<ChevronDown className="h-4 w-4 shrink-0 stroke-[2.5px]" />
</button>
<div className="relative z-[90] animate-fade-in">
{isOnboardingModalOpen && onboardingConfig && (
<OnboardingModal
onboardingConfig={onboardingConfig}
onClose={() => {
setIsOnboardingModalOpen(false);
}}
onIgnoreTask={(taskId, status) => {
loadOnboardingConfig().finally(() => {});
}}
/>
)}
{isCreatingRoadmap && (
<CreateRoadmapModal
onClose={() => {
setIsCreatingRoadmap(false);
}}
/>
)}
{showDropdown && (
<div
ref={dropdownRef}
className="absolute right-0 z-50 mt-2 min-h-[152px] w-48 rounded-md bg-slate-800 py-1 shadow-xl"
<button
className="relative flex h-8 w-40 items-center justify-center gap-1.5 rounded-full bg-gradient-to-r from-purple-500 to-purple-700 px-4 py-2 text-sm font-medium text-white hover:from-purple-500 hover:to-purple-600"
onClick={() => {
setIsTeamsOpen(false);
setShowDropdown(!showDropdown);
}}
>
{isTeamsOpen ? (
<DropdownTeamList setIsTeamsOpen={setIsTeamsOpen} />
) : (
<AccountDropdownList
onCreateRoadmap={() => {
setIsCreatingRoadmap(true);
setShowDropdown(false);
}}
setIsTeamsOpen={setIsTeamsOpen}
/>
<span className="inline-flex items-center">
Account&nbsp;<span className="text-gray-300">/</span>&nbsp;Teams
</span>
<ChevronDown className="h-4 w-4 shrink-0 stroke-[2.5px]" />
{shouldShowOnboardingStatus && !showDropdown && (
<NotificationIndicator />
)}
</div>
)}
</div>
</button>
{showDropdown && (
<div
ref={dropdownRef}
className="absolute right-0 z-50 mt-2 min-h-[152px] w-48 rounded-md bg-slate-800 py-1 shadow-xl"
>
{isTeamsOpen ? (
<DropdownTeamList setIsTeamsOpen={setIsTeamsOpen} />
) : (
<AccountDropdownList
onCreateRoadmap={() => {
setIsCreatingRoadmap(true);
setShowDropdown(false);
}}
setIsTeamsOpen={setIsTeamsOpen}
onOnboardingClick={() => {
setIsOnboardingModalOpen(true);
setShowDropdown(false);
}}
shouldShowOnboardingStatus={shouldShowOnboardingStatus}
isConfigLoading={isConfigLoading}
onboardingConfigCount={onboardingCount}
doneConfigCount={onboardingDoneCount}
/>
)}
</div>
)}
</div>
</>
);
}

View File

@@ -6,21 +6,67 @@ import {
SquareUserRound,
User2,
Users2,
Handshake,
} from 'lucide-react';
import { logout } from './navigation';
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal.tsx';
import { useState } from 'react';
import { cn } from '../../lib/classname.ts';
import { NotificationIndicator } from './NotificationIndicator.tsx';
import { Spinner } from '../ReactIcons/Spinner.tsx';
import { CheckIcon } from '../ReactIcons/CheckIcon.tsx';
type AccountDropdownListProps = {
onCreateRoadmap: () => void;
setIsTeamsOpen: (isOpen: boolean) => void;
onOnboardingClick: () => void;
isConfigLoading: boolean;
shouldShowOnboardingStatus?: boolean;
onboardingConfigCount: number;
doneConfigCount: number;
};
export function AccountDropdownList(props: AccountDropdownListProps) {
const { setIsTeamsOpen, onCreateRoadmap } = props;
const {
setIsTeamsOpen,
onCreateRoadmap,
onOnboardingClick,
isConfigLoading = true,
shouldShowOnboardingStatus = false,
onboardingConfigCount,
doneConfigCount,
} = props;
return (
<ul>
{shouldShowOnboardingStatus && (
<li className="mb-1 px-1">
<button
className={cn(
'flex h-9 w-full items-center rounded py-1 pl-3 pr-2 text-sm font-medium text-slate-100 hover:opacity-80',
isConfigLoading
? 'striped-loader-darker flex border-slate-800 opacity-70'
: 'border-slate-600 bg-slate-700',
)}
onClick={onOnboardingClick}
disabled={isConfigLoading}
>
<NotificationIndicator className="-left-0.5 -top-0.5" />
{isConfigLoading ? (
<></>
) : (
<>
<Handshake className="mr-2 h-4 w-4 text-slate-400 group-hover:text-white" />
<span>Onboarding</span>
<span className="ml-auto flex items-center gap-1.5 text-xs text-slate-400">
{doneConfigCount} of {onboardingConfigCount}
</span>
</>
)}
</button>
</li>
)}
<li className="px-1">
<a
href="/account"

View File

@@ -17,10 +17,10 @@ import { AccountDropdown } from './AccountDropdown';
</a>
<a
href='/ai'
href='/teams'
class='group inline sm:hidden relative !mr-2 text-blue-300 hover:text-white'
>
AI Roadmaps&nbsp;
Teams
<span class='absolute -right-[11px] top-0'>
<span class='relative flex h-2 w-2'>
@@ -39,13 +39,11 @@ import { AccountDropdown } from './AccountDropdown';
<a href='/get-started' class='text-gray-400 hover:text-white'>
Start Here
</a>
<a href='/teams' class='text-gray-400 hover:text-white'> Teams</a>
<a
href='/ai'
<a
href='/teams'
class='group relative !mr-2 text-blue-300 hover:text-white'
>
AI Roadmaps
Teams
<span class='absolute -right-[11px] top-0'>
<span class='relative flex h-2 w-2'>
<span
@@ -56,6 +54,8 @@ import { AccountDropdown } from './AccountDropdown';
</span>
</span>
</a>
<a
href='/ai' class='text-gray-400 hover:text-white'> AI Roadmaps</a>
<button
data-command-menu
class='hidden items-center rounded-md border border-gray-800 px-2.5 py-1.5 text-sm text-gray-400 hover:cursor-pointer hover:bg-gray-800 md:flex'

View File

@@ -0,0 +1,20 @@
import { cn } from '../../lib/classname.ts';
type NotificationIndicatorProps = {
className?: string;
};
export function NotificationIndicator(props: NotificationIndicatorProps) {
const { className = '' } = props;
return (
<span
className={cn(
'absolute -top-1 right-0 h-3 w-3 text-xs uppercase tracking-wider',
className,
)}
>
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
<span className="relative inline-flex h-3 w-3 rounded-full bg-green-500"></span>
</span>
);
}

View File

@@ -0,0 +1,253 @@
import { ArrowUpRight, Check } from 'lucide-react';
import { Modal } from '../Modal';
import { cn } from '../../lib/classname';
import { useEffect, useMemo, useState } from 'react';
import type { AllowedOnboardingStatus } from '../../api/user';
import { pageProgressMessage } from '../../stores/page';
import { httpPatch } from '../../lib/http';
import { useToast } from '../../hooks/use-toast';
import type { OnboardingConfig } from './AccountDropdown';
import { setAuthToken } from '../../lib/jwt';
import { NUDGE_ONBOARDING_KEY } from '../OnboardingNudge.tsx';
type Task = {
id: string;
title: string;
description: string;
status: AllowedOnboardingStatus;
url: string;
urlText: string;
onClick?: () => void;
};
type OnboardingModalProps = {
onClose: () => void;
onboardingConfig: OnboardingConfig;
onIgnoreTask?: (taskId: string, status: AllowedOnboardingStatus) => void;
};
export function OnboardingModal(props: OnboardingModalProps) {
const { onboardingConfig, onClose, onIgnoreTask } = props;
const toast = useToast();
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
const tasks = useMemo(() => {
return [
{
id: 'updateProgress',
title: 'Update your Progress',
description: 'Mark your progress on roadmaps',
status: onboardingConfig?.onboarding?.updateProgress || 'pending',
url: '/roadmaps',
urlText: 'Roadmaps List',
},
{
id: 'publishProfile',
title: 'Claim a Username',
description: 'Optionally create a public profile to share your skills',
status: onboardingConfig?.onboarding?.publishProfile || 'pending',
url: '/account/update-profile',
urlText: 'Update Profile',
},
{
id: 'customRoadmap',
title: 'Custom Roadmaps',
description: 'Create your own roadmap from scratch',
status: onboardingConfig?.onboarding?.customRoadmap || 'pending',
url: import.meta.env.DEV
? 'http://localhost:4321'
: 'https://draw.roadmap.sh',
urlText: 'Create Roadmap',
},
{
id: 'addFriends',
title: 'Invite your Friends',
description: 'Invite friends to join you on roadmaps',
status: onboardingConfig?.onboarding?.addFriends || 'pending',
url: '/account/friends',
urlText: 'Add Friends',
onClick: () => {
ignoreOnboardingTask(
'addFriends',
'done',
'Updating status..',
).finally(() => pageProgressMessage.set(''));
},
},
{
id: 'roadCard',
title: 'Create your Roadmap Card',
description: 'Embed your skill card on your github or website',
status: onboardingConfig?.onboarding?.roadCard || 'pending',
url: '/account/road-card',
urlText: 'Create Road Card',
onClick: () => {
ignoreOnboardingTask('roadCard', 'done', 'Updating status..').finally(
() => pageProgressMessage.set(''),
);
},
},
{
id: 'inviteTeam',
title: 'Invite your Team',
description: 'Invite your team to collaborate on roadmaps',
status: onboardingConfig?.onboarding?.inviteTeam || 'pending',
url: '/team/new',
urlText: 'Create Team',
},
];
}, [onboardingConfig]);
const ignoreOnboardingTask = async (
taskId: string,
status: AllowedOnboardingStatus,
message: string = 'Ignoring Task',
) => {
pageProgressMessage.set(message);
const { response, error } = await httpPatch(
`${import.meta.env.PUBLIC_API_URL}/v1-update-onboarding-config`,
{
id: taskId,
status,
},
);
if (error || !response) {
toast.error(error?.message || 'Failed to ignore task');
return;
}
onIgnoreTask?.(taskId, status);
setSelectedTask(null);
};
const ignoreForever = async () => {
const { response, error } = await httpPatch<{ token: string }>(
`${import.meta.env.PUBLIC_API_URL}/v1-ignore-onboarding-forever`,
{},
);
if (error || !response) {
toast.error(error?.message || 'Failed to ignore onboarding');
return;
}
setAuthToken(response.token);
window.location.reload();
};
const isAllTasksDone = tasks.every(
(task) => task.status === 'done' || task.status === 'ignored',
);
useEffect(() => {
if (!isAllTasksDone) {
return;
}
pageProgressMessage.set('Finishing Onboarding');
ignoreForever().finally(() => {});
}, [isAllTasksDone]);
return (
<Modal onClose={onClose} bodyClassName="text-black h-auto">
<div className="px-4 pb-2 pl-11 pt-4">
<h2 className="mb-0.5 text-xl font-semibold">Welcome to roadmap.sh</h2>
<p className="text-balance text-sm text-gray-500">
Complete the tasks below to get started!
</p>
</div>
<ul
className={cn('flex flex-col divide-y', {
'border-b': tasks[tasks.length - 1]?.status === 'done',
})}
>
{/*sort to put completed tasks at the end */}
{tasks.map((task, taskCounter) => {
const isDone = task.status === 'done';
const isActive = selectedTask?.id === task.id;
return (
<li
key={task.id}
data-active={isActive}
data-status={task.status}
className={cn('group/task px-4 py-2.5', {
'bg-gray-100': isDone,
'border-t': taskCounter === 0 && isDone,
})}
>
<div
className={cn('flex items-start gap-2', {
'opacity-50': task.status === 'done',
})}
>
<span className="relative top-px flex h-5 w-5 items-center justify-center">
{isDone ? (
<Check className="h-4 w-4 stroke-[3px] text-green-500" />
) : (
<div
className={cn(
'h-4 w-4 rounded-md border border-gray-300',
task.status === 'ignored'
? 'bg-gray-200'
: 'bg-transparent',
)}
/>
)}
</span>
<div className="group-data-[status=ignored]/task:text-gray-400">
<h3 className="flex items-center text-sm font-semibold group-data-[status=done]/task:line-through">
{task.title}
<a
href={task.url}
target="_blank"
className={cn(
'ml-1 inline-block rounded-xl border border-black bg-white pl-1.5 pr-1 text-xs font-normal text-black hover:bg-black hover:text-white',
)}
aria-label="Open task in new tab"
onClick={() => {
if (!task?.onClick) {
return;
}
task.onClick();
}}
>
{task.urlText}
<ArrowUpRight className="relative -top-[0.5px] ml-0.5 inline-block h-3.5 w-3.5 stroke-[2px]" />
</a>
</h3>
<p className="text-xs text-gray-500 group-data-[status=ignored]/task:text-gray-400">
{task.description}
</p>
</div>
</div>
</li>
);
})}
</ul>
<div className="mt-2 px-11 pb-5">
<button
className="w-full rounded-md bg-gradient-to-r from-purple-500 to-purple-700 px-4 py-2 text-sm font-medium text-white hover:from-purple-500 hover:to-purple-600"
onClick={onClose}
>
Do it later
</button>
<button
className="mt-3 text-sm text-gray-500 underline underline-offset-2 hover:text-black"
onClick={() => {
pageProgressMessage.set('Ignoring Onboarding');
ignoreForever().finally();
}}
>
Ignore forever
</button>
</div>
</Modal>
);
}

View File

@@ -0,0 +1,69 @@
import { cn } from '../lib/classname.ts';
import { memo, useEffect, useState } from 'react';
import { useScrollPosition } from '../hooks/use-scroll-position.ts';
import { X } from 'lucide-react';
type OnboardingNudgeProps = {
onStartOnboarding: () => void;
};
export const NUDGE_ONBOARDING_KEY = 'should_nudge_onboarding';
export function OnboardingNudge(props: OnboardingNudgeProps) {
const { onStartOnboarding } = props;
const [isLoading, setIsLoading] = useState(false);
const { y: scrollY } = useScrollPosition();
useEffect(() => {
if (localStorage.getItem(NUDGE_ONBOARDING_KEY) === null) {
localStorage.setItem(NUDGE_ONBOARDING_KEY, 'true');
}
}, []);
if (localStorage.getItem(NUDGE_ONBOARDING_KEY) !== 'true') {
return null;
}
if (scrollY < 100) {
return null;
}
return (
<div
className={cn(
'fixed left-0 right-0 top-0 z-[91] flex w-full items-center justify-center bg-yellow-300 border-b border-b-yellow-500/30 pt-1.5 pb-2',
{
'striped-loader': isLoading,
},
)}
>
<p className="text-base font-semibold text-yellow-950">
Welcome! Please take a moment to{' '}
<button
type="button"
onClick={() => {
setIsLoading(true);
localStorage.setItem(NUDGE_ONBOARDING_KEY, 'false');
onStartOnboarding();
}}
className="underline"
>
complete onboarding
</button>
<button
type="button"
className="relative top-[3px] ml-1 px-1 py-1 text-yellow-600 hover:text-yellow-950"
onClick={(e) => {
e.stopPropagation();
localStorage.setItem(NUDGE_ONBOARDING_KEY, 'false');
setIsLoading(true);
}}
>
<X className="h-4 w-4" strokeWidth={3} />
</button>
</p>
</div>
);
}

View File

@@ -24,7 +24,7 @@ const discordInfo = await getDiscordInfo();
class='mt-5 grid grid-cols-1 justify-between gap-2 divide-x-0 sm:my-11 sm:grid-cols-3 sm:gap-0 sm:divide-x mb-4 sm:mb-0'
>
<OpenSourceStat text='GitHub Stars' value={starCount} />
<OpenSourceStat text='Registered Users' value={'850k'} />
<OpenSourceStat text='Registered Users' value={'+1M'} />
<OpenSourceStat
text='Discord Members'
value={discordInfo.totalFormatted}

View File

@@ -28,7 +28,7 @@ const isDiscordMembers = text.toLowerCase() === 'discord members';
{
isRegistered && (
<p class='flex items-center text-sm text-blue-500 sm:flex'>
<span class='mr-1.5 rounded-md bg-blue-500 px-1 text-white'>+55k</span>
<span class='mr-1.5 rounded-md bg-blue-500 px-1 text-white'>+75k</span>
every month
</p>
)
@@ -44,7 +44,7 @@ const isDiscordMembers = text.toLowerCase() === 'discord members';
}
<div class="flex flex-row items-center sm:flex-col my-1 sm:my-0">
<p
class='relative my-0 sm:my-4 mr-1 sm:mr-0 text-base font-bold lowercase sm:w-auto sm:text-5xl'
class='relative my-0 sm:my-4 mr-1 sm:mr-0 text-base font-bold sm:w-auto sm:text-5xl'
>
{value}
</p>

View File

@@ -28,7 +28,7 @@ export function PageProgress(props: Props) {
return (
<div>
{/* Tailwind based spinner for full page */}
<div className="fixed left-0 top-0 z-50 flex h-full w-full items-center justify-center bg-white bg-opacity-75">
<div className="fixed left-0 top-0 z-[100] flex h-full w-full items-center justify-center bg-white bg-opacity-75">
<div className="flex items-center justify-center rounded-md border bg-white px-4 py-2 ">
<Spinner
className="h-4 w-4 sm:h-4 sm:w-4"

View File

@@ -4,6 +4,8 @@ import { sponsorHidden } from '../stores/page';
import { useStore } from '@nanostores/react';
import { X } from 'lucide-react';
import { setViewSponsorCookie } from '../lib/jwt';
import { isMobile } from '../lib/is-mobile';
import Cookies from 'js-cookie';
export type PageSponsorType = {
company: string;
@@ -25,6 +27,22 @@ type PageSponsorProps = {
gaPageIdentifier?: string;
};
const CLOSE_SPONSOR_KEY = 'sponsorClosed';
function markSponsorHidden(sponsorId: string) {
Cookies.set(`${CLOSE_SPONSOR_KEY}-${sponsorId}`, '1', {
path: '/',
expires: 1,
sameSite: 'lax',
secure: true,
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
}
function isSponsorMarkedHidden(sponsorId: string) {
return Cookies.get(`${CLOSE_SPONSOR_KEY}-${sponsorId}`) === '1';
}
export function PageSponsor(props: PageSponsorProps) {
const { gaPageIdentifier } = props;
const $isSponsorHidden = useStore(sponsorHidden);
@@ -50,6 +68,7 @@ export function PageSponsor(props: PageSponsorProps) {
`${import.meta.env.PUBLIC_API_URL}/v1-get-sponsor`,
{
href: window.location.pathname,
mobile: isMobile() ? 'true' : 'false',
},
);
@@ -58,12 +77,16 @@ export function PageSponsor(props: PageSponsorProps) {
return;
}
if (!response?.sponsor) {
if (
!response?.sponsor ||
!response.id ||
isSponsorMarkedHidden(response.id)
) {
return;
}
setSponsor(response.sponsor);
setSponsorId(response?.id || null);
setSponsorId(response.id);
window.fireEvent({
category: 'SponsorImpression',
@@ -75,9 +98,15 @@ export function PageSponsor(props: PageSponsorProps) {
};
const clickSponsor = async (sponsorId: string) => {
const { response, error } = await httpPatch<{ status: 'ok' }>(
const clickUrl = new URL(
`${import.meta.env.PUBLIC_API_URL}/v1-view-sponsor/${sponsorId}`,
{},
);
const { response, error } = await httpPatch<{ status: 'ok' }>(
clickUrl.toString(),
{
mobile: isMobile(),
},
);
if (error || !response) {
@@ -103,7 +132,7 @@ export function PageSponsor(props: PageSponsorProps) {
href={url}
target="_blank"
rel="noopener sponsored nofollow"
className="fixed bottom-[15px] right-[15px] z-50 flex max-w-[350px] bg-white shadow-lg outline-0 outline-transparent"
className="fixed bottom-0 left-0 right-0 z-50 flex bg-white shadow-lg outline-0 outline-transparent sm:bottom-[15px] sm:left-auto sm:right-[15px] sm:max-w-[350px]"
onClick={async () => {
window.fireEvent({
category: 'SponsorClick',
@@ -114,26 +143,32 @@ export function PageSponsor(props: PageSponsorProps) {
}}
>
<span
className="absolute right-1.5 top-1.5 text-gray-300 hover:text-gray-800"
className="absolute right-1 top-1 text-gray-400 hover:text-gray-800 sm:right-1.5 sm:top-1.5 sm:text-gray-300"
aria-label="Close"
onClick={(e) => {
e.preventDefault();
markSponsorHidden(sponsorId || '');
sponsorHidden.set(true);
}}
>
<X className="h-4 w-4" />
<X className="h-5 w-5 sm:h-4 sm:w-4" />
</span>
<img
src={imageUrl}
className="block h-[150px] object-cover lg:h-[169px] lg:w-[118.18px]"
alt="Sponsor Banner"
/>
<span className="flex flex-1 flex-col justify-between text-sm">
<span>
<img
src={imageUrl}
className="block h-[106px] object-cover sm:h-[169px] sm:w-[118.18px]"
alt="Sponsor Banner"
/>
</span>
<span className="flex flex-1 flex-col justify-between text-xs sm:text-sm">
<span className="p-[10px]">
<span className="mb-0.5 block font-semibold">{title}</span>
<span className="block text-gray-500">{description}</span>
</span>
<span className="sponsor-footer">Partner Content</span>
<span className="sponsor-footer hidden sm:block">Partner Content</span>
<span className="block pb-1 text-center text-[10px] uppercase text-gray-400 sm:hidden">
Partner Content
</span>
</span>
</a>
);

View File

@@ -13,28 +13,19 @@ type ProgressStatButtonProps = {
icon: ReactNode;
label: string;
count: number;
onClick: () => void;
};
function ProgressStatButton(props: ProgressStatButtonProps) {
const { icon, label, count, onClick, isDisabled = false } = props;
function ProgressStatLabel(props: ProgressStatButtonProps) {
const { icon, label, count } = props;
return (
<button
disabled={isDisabled}
onClick={onClick}
className="group relative flex flex-1 items-center overflow-hidden rounded-md border border-gray-300 bg-white px-2 py-2 text-sm text-black transition-colors hover:border-black disabled:pointer-events-none disabled:opacity-50 sm:rounded-xl sm:px-4 sm:py-3 sm:text-base"
>
<span className="group relative flex flex-1 items-center overflow-hidden rounded-md border border-gray-300 bg-white px-2 py-2 text-sm text-black transition-colors disabled:opacity-50 sm:rounded-xl sm:px-4 sm:py-3 sm:text-base">
{icon}
<span className="flex flex-grow justify-between">
<span>{label}</span>
<span>{count}</span>
</span>
<span className="absolute left-0 right-0 top-full flex h-full items-center justify-center border border-black bg-black text-white transition-all duration-200 group-hover:top-0">
Restart Asking
</span>
</button>
</span>
);
}
@@ -43,12 +34,11 @@ type QuestionFinishedProps = {
didNotKnowCount: number;
skippedCount: number;
totalCount: number;
onReset: (type: QuestionProgressType | 'reset') => void;
onReset: () => void;
};
export function QuestionFinished(props: QuestionFinishedProps) {
const { knowCount, didNotKnowCount, skippedCount, totalCount, onReset } =
props;
const { knowCount, didNotKnowCount, skippedCount, onReset } = props;
return (
<div className="relative flex flex-grow flex-col items-center justify-center px-4 sm:px-0">
@@ -63,31 +53,25 @@ export function QuestionFinished(props: QuestionFinishedProps) {
</p>
<div className="mb-5 mt-5 flex w-full flex-col gap-1.5 px-2 sm:flex-row sm:gap-3 sm:px-16">
<ProgressStatButton
<ProgressStatLabel
icon={<ThumbsUp className="mr-1 h-4" />}
label="Knew"
count={knowCount}
isDisabled={knowCount === 0}
onClick={() => onReset('know')}
/>
<ProgressStatButton
<ProgressStatLabel
icon={<Sparkles className="mr-1 h-4" />}
label="Learned"
count={didNotKnowCount}
isDisabled={didNotKnowCount === 0}
onClick={() => onReset('dontKnow')}
/>
<ProgressStatButton
<ProgressStatLabel
icon={<SkipForward className="mr-1 h-4" />}
label="Skipped"
count={skippedCount}
isDisabled={skippedCount === 0}
onClick={() => onReset('skip')}
/>
</div>
<div className="mb-4 mt-2 text-sm sm:mb-0">
<button
onClick={() => onReset('reset')}
onClick={() => onReset()}
className="flex items-center gap-0.5 text-sm text-red-700 hover:text-black sm:text-base"
>
<RefreshCcw className="mr-1 h-4" />

View File

@@ -0,0 +1,154 @@
---
import {
getGuideTableOfContent,
type GuideFileType,
HeadingGroupType,
} from '../../lib/guide';
import MarkdownFile from '../MarkdownFile.astro';
import { TableOfContent } from '../TableOfContent/TableOfContent';
import { markdownToHtml, replaceVariables } from '../../lib/markdown';
import { QuestionGroupType } from '../../lib/question-group';
import { QuestionsList } from './QuestionsList';
interface Props {
questionGroup: QuestionGroupType;
}
const { questionGroup } = Astro.props;
const allHeadings = questionGroup.getHeadings();
const tableOfContent: HeadingGroupType[] = [
...getGuideTableOfContent(allHeadings),
{
depth: 2,
title: 'Test with Flashcards',
children: [],
slug: 'test-with-flashcards',
text: 'Test yourself with Flashcards',
},
{
depth: 2,
title: 'Questions List',
children: [
{
depth: 2,
title: 'Beginner Level',
children: [],
slug: 'beginner-level',
text: 'Beginner Level',
} as HeadingGroupType,
{
depth: 2,
title: 'Intermediate Level',
children: [],
slug: 'intermediate-level',
text: 'Intermediate Level',
} as HeadingGroupType,
{
depth: 2,
title: 'Advanced Level',
children: [],
slug: 'advanced-level',
text: 'Advanced Level',
} as HeadingGroupType,
],
slug: 'questions-list',
text: 'Questions List',
},
];
const showTableOfContent = tableOfContent.length > 0;
const { frontmatter: guideFrontmatter, author } = questionGroup;
---
<article class='lg:grid lg:max-w-full lg:grid-cols-[1fr_minmax(0,700px)_1fr]'>
{
showTableOfContent && (
<div class='bg-gradient-to-r from-gray-50 py-0 lg:col-start-3 lg:col-end-4 lg:row-start-1'>
<TableOfContent toc={tableOfContent} client:load />
</div>
)
}
<div
class:list={[
'col-start-2 col-end-3 row-start-1 mx-auto max-w-[700px] py-5 sm:py-10',
{
'lg:border-r': showTableOfContent,
},
]}
>
<MarkdownFile>
<h1 class='mb-3 text-balance text-4xl font-bold'>
{replaceVariables(guideFrontmatter.title)}
</h1>
{
author && (
<p class='my-0 flex items-center justify-start text-sm text-gray-400'>
<a
href={`/authors/${author?.id}`}
class='inline-flex items-center font-medium underline-offset-2 hover:text-gray-600 hover:underline'
>
<img
alt={author.frontmatter.name}
src={author.frontmatter.imageUrl}
class='mb-0 mr-2 inline h-5 w-5 rounded-full'
/>
{author.frontmatter.name}
</a>
<span class='mx-2 hidden sm:inline'>&middot;</span>
<a
class='hidden underline-offset-2 hover:text-gray-600 sm:inline'
href={`https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/question-groups/${questionGroup.id}`}
target='_blank'
>
Improve this Guide
</a>
</p>
)
}
<questionGroup.Content />
<h2 id='test-with-flashcards'>Test yourself with Flashcards</h2>
<p>
You can either use these flashcards or jump to the questions list
section below to see them in a list format.
</p>
<div class='mx-0 sm:-mb-32'>
<QuestionsList
groupId={questionGroup.id}
questions={questionGroup.questions}
client:load
/>
</div>
<h2 id='questions-list'>Questions List</h2>
<p>
If you prefer to see the questions in a list format, you can find them
below.
</p>
{
['beginner', 'intermediate', 'advanced'].map((questionLevel) => (
<div class='mb-5'>
<h3 id={`${questionLevel}-level`} class='mb-0 capitalize'>
{questionLevel} Level
</h3>
{questionGroup.questions
.filter((q) => {
return q.topics
.map((t) => t.toLowerCase())
.includes(questionLevel);
})
.map((q) => (
<div class='mb-5'>
<h4>{q.question}</h4>
<div set:html={markdownToHtml(q.answer, false)} />
</div>
))}
</div>
))
}
</MarkdownFile>
</div>
</article>

View File

@@ -24,14 +24,14 @@ type QuestionsListProps = {
};
export function QuestionsList(props: QuestionsListProps) {
const { questions: unshuffledQuestions, groupId } = props;
const { questions: defaultQuestions, groupId } = props;
const toast = useToast();
const [questions, setQuestions] = useState(defaultQuestions);
const [isLoading, setIsLoading] = useState(true);
const [showConfetti, setShowConfetti] = useState(false);
const [questions, setQuestions] = useState<QuestionType[]>();
const [pendingQuestions, setPendingQuestions] = useState<QuestionType[]>([]);
const [currQuestionIndex, setCurrQuestionIndex] = useState(0);
const [userProgress, setUserProgress] = useState<UserQuestionProgress>();
const containerRef = useRef<HTMLDivElement>(null);
@@ -57,7 +57,7 @@ export function QuestionsList(props: QuestionsListProps) {
return response;
}
async function loadQuestions() {
async function prepareProgress() {
const userProgress = await fetchUserProgress();
setUserProgress(userProgress);
@@ -65,7 +65,7 @@ export function QuestionsList(props: QuestionsListProps) {
const didNotKnowQuestions = userProgress?.dontKnow || [];
const skipQuestions = userProgress?.skip || [];
const pendingQuestions = unshuffledQuestions.filter((question) => {
const pendingQuestionIndex = questions.findIndex((question) => {
return (
!knownQuestions.includes(question.id) &&
!didNotKnowQuestions.includes(question.id) &&
@@ -73,30 +73,21 @@ export function QuestionsList(props: QuestionsListProps) {
);
});
// Shuffle and set pending questions
setPendingQuestions(pendingQuestions.sort(() => Math.random() - 0.5));
setQuestions(unshuffledQuestions);
setCurrQuestionIndex(pendingQuestionIndex);
setIsLoading(false);
}
async function resetProgress(type: QuestionProgressType | 'reset' = 'reset') {
async function resetProgress() {
let knownQuestions = userProgress?.know || [];
let didNotKnowQuestions = userProgress?.dontKnow || [];
let skipQuestions = userProgress?.skip || [];
if (!isLoggedIn()) {
if (type === 'know') {
knownQuestions = [];
} else if (type === 'dontKnow') {
didNotKnowQuestions = [];
} else if (type === 'skip') {
skipQuestions = [];
} else if (type === 'reset') {
knownQuestions = [];
didNotKnowQuestions = [];
skipQuestions = [];
}
setQuestions(defaultQuestions);
knownQuestions = [];
didNotKnowQuestions = [];
skipQuestions = [];
} else {
setIsLoading(true);
@@ -105,7 +96,7 @@ export function QuestionsList(props: QuestionsListProps) {
import.meta.env.PUBLIC_API_URL
}/v1-reset-question-progress/${groupId}`,
{
status: type,
status: 'reset',
},
);
@@ -119,21 +110,13 @@ export function QuestionsList(props: QuestionsListProps) {
skipQuestions = response?.skip || [];
}
const pendingQuestions = unshuffledQuestions.filter((question) => {
return (
!knownQuestions.includes(question.id) &&
!didNotKnowQuestions.includes(question.id) &&
!skipQuestions.includes(question.id)
);
});
setCurrQuestionIndex(0);
setUserProgress({
know: knownQuestions,
dontKnow: didNotKnowQuestions,
skip: skipQuestions,
});
setPendingQuestions(pendingQuestions.sort(() => Math.random() - 0.5));
setIsLoading(false);
}
@@ -172,30 +155,29 @@ export function QuestionsList(props: QuestionsListProps) {
newProgress = response;
}
const updatedQuestionList = pendingQuestions.filter(
(q) => q.id !== questionId,
);
const nextQuestionIndex = currQuestionIndex + 1;
setUserProgress(newProgress);
setPendingQuestions(updatedQuestionList);
setIsLoading(false);
if (updatedQuestionList.length === 0) {
if (!nextQuestionIndex || !questions[nextQuestionIndex]) {
setShowConfetti(true);
}
setCurrQuestionIndex(nextQuestionIndex);
}
useEffect(() => {
loadQuestions().then(() => null);
}, [unshuffledQuestions]);
prepareProgress().then(() => null);
}, [questions]);
const knowCount = userProgress?.know.length || 0;
const dontKnowCount = userProgress?.dontKnow.length || 0;
const skipCount = userProgress?.skip.length || 0;
const hasProgress = knowCount > 0 || dontKnowCount > 0 || skipCount > 0;
const currQuestion = pendingQuestions[0];
const hasFinished = !isLoading && hasProgress && !currQuestion;
const currQuestion = questions[currQuestionIndex];
const hasFinished = !isLoading && hasProgress && currQuestionIndex === -1;
return (
<div className="mb-0 gap-3 text-center sm:mb-40">
@@ -203,11 +185,37 @@ export function QuestionsList(props: QuestionsListProps) {
knowCount={knowCount}
didNotKnowCount={dontKnowCount}
skippedCount={skipCount}
totalCount={unshuffledQuestions?.length || questions?.length}
totalCount={questions?.length}
isLoading={isLoading}
showLoginAlert={!isLoggedIn() && hasProgress}
onResetClick={() => {
resetProgress('reset').finally(() => null);
resetProgress().finally(() => null);
}}
onNextClick={() => {
if (
currQuestionIndex !== -1 &&
currQuestionIndex < questions.length - 1
) {
updateQuestionStatus('skip', currQuestion.id).finally(() => null);
}
}}
onPrevClick={() => {
if (currQuestionIndex > 0) {
const prevQuestion = questions[currQuestionIndex - 1];
// remove last question from the progress of the user
const tempUserProgress = {
know:
userProgress?.know.filter((id) => id !== prevQuestion.id) || [],
dontKnow:
userProgress?.dontKnow.filter((id) => id !== prevQuestion.id) ||
[],
skip:
userProgress?.skip.filter((id) => id !== prevQuestion.id) || [],
};
setUserProgress(tempUserProgress);
setCurrQuestionIndex(currQuestionIndex - 1);
}
}}
/>
@@ -227,12 +235,12 @@ export function QuestionsList(props: QuestionsListProps) {
>
{hasFinished && (
<QuestionFinished
totalCount={unshuffledQuestions?.length || questions?.length || 0}
totalCount={questions?.length || 0}
knowCount={knowCount}
didNotKnowCount={dontKnowCount}
skippedCount={skipCount}
onReset={(type: QuestionProgressType | 'reset') => {
resetProgress(type).finally(() => null);
onReset={() => {
resetProgress().finally(() => null);
}}
/>
)}

View File

@@ -1,4 +1,11 @@
import { CheckCircle, RotateCcw, SkipForward, Sparkles } from 'lucide-react';
import {
CheckCircle,
ChevronLeft,
ChevronRight,
RotateCcw,
SkipForward,
Sparkles,
} from 'lucide-react';
import { showLoginPopup } from '../../lib/popup';
type QuestionsProgressProps = {
@@ -9,6 +16,8 @@ type QuestionsProgressProps = {
totalCount?: number;
skippedCount?: number;
onResetClick?: () => void;
onPrevClick?: () => void;
onNextClick?: () => void;
};
export function QuestionsProgress(props: QuestionsProgressProps) {
@@ -20,6 +29,8 @@ export function QuestionsProgress(props: QuestionsProgressProps) {
totalCount = 0,
skippedCount = 0,
onResetClick = () => null,
onPrevClick = () => null,
onNextClick = () => null,
} = props;
const totalSolved = knowCount + didNotKnowCount + skippedCount;
@@ -36,8 +47,22 @@ export function QuestionsProgress(props: QuestionsProgressProps) {
}}
/>
</div>
<span className="ml-3 text-sm">
{totalSolved} / {totalCount}
<span className="ml-3 flex items-center text-sm">
<button
onClick={onPrevClick}
className="text-zinc-400 hover:text-black"
>
<ChevronLeft className="h-4" strokeWidth={3} />
</button>
<span className="block min-w-[41px] text-center">
<span className="tabular-nums">{totalSolved}</span> / {totalCount}
</span>
<button
onClick={onNextClick}
className="text-zinc-400 hover:text-black"
>
<ChevronRight className="h-4" strokeWidth={3} />
</button>
</span>
</div>
@@ -46,9 +71,7 @@ export function QuestionsProgress(props: QuestionsProgressProps) {
<CheckCircle className="mr-1 h-4" />
<span>Knew</span>
<span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black">
<span className="tabular-nums">{knowCount}</span>{' '}
<span className="hidden lg:inline">Questions</span>
<span className="inline sm:hidden">Questions</span>
<span className="tabular-nums">{knowCount}</span> Items
</span>
</span>
@@ -56,9 +79,7 @@ export function QuestionsProgress(props: QuestionsProgressProps) {
<Sparkles className="mr-1 h-4" />
<span>Learnt</span>
<span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black">
<span className="tabular-nums">{didNotKnowCount}</span>{' '}
<span className="hidden lg:inline">Questions</span>
<span className="inline sm:hidden">Questions</span>
<span className="tabular-nums">{didNotKnowCount}</span> Items
</span>
</span>
@@ -66,9 +87,7 @@ export function QuestionsProgress(props: QuestionsProgressProps) {
<SkipForward className="mr-1 h-4" />
<span>Skipped</span>
<span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black">
<span className="tabular-nums">{skippedCount}</span>{' '}
<span className="hidden lg:inline">Questions</span>
<span className="inline sm:hidden">Questions</span>
<span className="tabular-nums">{skippedCount}</span> Items
</span>
</span>

View File

@@ -1,6 +1,6 @@
---
import { getQuestionGroupsByIds } from '../lib/question-group';
import { getRoadmapsByIds, RoadmapFrontmatter } from '../lib/roadmap';
import { getRoadmapsByIds, type RoadmapFrontmatter } from '../lib/roadmap';
import { Map, Clipboard } from 'lucide-react';
export interface Props {
@@ -24,9 +24,6 @@ const relatedQuestionDetails = await getQuestionGroupsByIds(relatedQuestions);
<span class='text-md flex items-center rounded-md border bg-white px-3 py-1 font-medium'>
<Clipboard className='mr-1.5 text-black' size='17px' />
Test your Knowledge
<span class='ml-2 rounded-md border border-yellow-300 bg-yellow-100 px-1 py-0.5 text-xs uppercase'>
New
</span>
</span>
<a
href='/questions'
@@ -59,14 +56,19 @@ const relatedQuestionDetails = await getQuestionGroupsByIds(relatedQuestions);
{
relatedRoadmaps.length && (
<div class:list={['border-t bg-gray-100', {
'mt-8': !relatedQuestionDetails.length
}]}>
<div
class:list={[
'border-t bg-gray-100',
{
'mt-8': !relatedQuestionDetails.length,
},
]}
>
<div class='container'>
<div class='relative -top-5 flex justify-between'>
<span class='text-md flex items-center rounded-md border bg-white px-3 py-1 font-medium'>
<Map className='text-black mr-1.5' size='17px' />
Related Roadmaps
Related <span class='hidden sm:inline'>Roadmaps</span>
</span>
<a
href='/roadmaps'

View File

@@ -4,10 +4,11 @@ import { CopyIcon } from 'lucide-react';
type EditorProps = {
title: string;
text: string;
onCopy?: () => void;
};
export function Editor(props: EditorProps) {
const { text, title } = props;
const { text, title, onCopy } = props;
const { isCopied, copyText } = useCopyText();
@@ -17,7 +18,13 @@ export function Editor(props: EditorProps) {
<span className="text-xs uppercase leading-none text-gray-400">
{title}
</span>
<button className="flex items-center" onClick={() => copyText(text)}>
<button
className="flex items-center"
onClick={() => {
copyText(text);
onCopy?.();
}}
>
{isCopied && (
<span className="mr-1 text-xs leading-none text-gray-700">
Copied!&nbsp;
@@ -33,6 +40,7 @@ export function Editor(props: EditorProps) {
onClick={(e: any) => {
e.target.select();
copyText(e.target.value);
onCopy?.();
}}
value={text}
/>

View File

@@ -9,6 +9,8 @@ import { SelectionButton } from './SelectionButton';
import { StepCounter } from './StepCounter';
import { Editor } from './Editor';
import { CopyIcon } from 'lucide-react';
import { httpPatch } from '../../lib/http';
import { useToast } from '../../hooks/use-toast';
type StepLabelProps = {
label: string;
@@ -24,11 +26,28 @@ function StepLabel(props: StepLabelProps) {
}
export function RoadCardPage() {
const user = useAuth();
const toast = useToast();
const { isCopied, copyText } = useCopyText();
const [roadmaps, setRoadmaps] = useState<string[]>([]);
const [version, setVersion] = useState<'tall' | 'wide'>('tall');
const [variant, setVariant] = useState<'dark' | 'light'>('dark');
const user = useAuth();
const markRoadCardDone = async () => {
const { error } = await httpPatch(
`${import.meta.env.PUBLIC_API_URL}/v1-update-onboarding-config`,
{
id: 'roadCard',
status: 'done',
},
);
if (error) {
toast.error(error?.message || 'Something went wrong');
}
};
if (!user) {
return null;
}
@@ -131,20 +150,24 @@ export function RoadCardPage() {
<div className="mt-3 grid grid-cols-2 gap-2">
<button
className="flex items-center justify-center rounded border border-gray-300 p-1.5 px-2 text-sm font-medium"
onClick={() =>
onClick={() => {
downloadImage({
url: badgeUrl.toString(),
name: 'road-card',
scale: 4,
})
}
});
markRoadCardDone();
}}
>
Download
</button>
<button
disabled={isCopied}
className="flex cursor-pointer items-center justify-center rounded border border-gray-300 p-1.5 px-2 text-sm font-medium disabled:bg-blue-50"
onClick={() => copyText(badgeUrl.toString())}
onClick={() => {
copyText(badgeUrl.toString());
markRoadCardDone();
}}
>
<CopyIcon size={16} className="mr-1 inline-block h-4 w-4" />
@@ -156,11 +179,13 @@ export function RoadCardPage() {
<Editor
title={'HTML'}
text={`<a href="https://roadmap.sh"><img src="${badgeUrl}" alt="roadmap.sh"/></a>`.trim()}
onCopy={() => markRoadCardDone()}
/>
<Editor
title={'Markdown'}
text={`[![roadmap.sh](${badgeUrl})](https://roadmap.sh)`.trim()}
onCopy={() => markRoadCardDone()}
/>
</div>

View File

@@ -157,7 +157,7 @@ const hasTnsBanner = !!tnsBannerLink;
{
isRoadmapReady && (
<a
href={`https://github.com/kamranahmedse/developer-roadmap/issues/new?title=[Suggestion] ${title}`}
href={`https://github.com/kamranahmedse/developer-roadmap/issues/new/choose`}
target='_blank'
class='inline-flex items-center justify-center rounded-md bg-gray-500 px-3 py-1.5 text-xs font-medium text-white hover:bg-gray-600 sm:text-sm'
aria-label='Suggest Changes'

View File

@@ -23,7 +23,7 @@ const hasTnsBanner = !!tnsBannerLink;
<div
class:list={[
'mt-4 sm:mt-7 border-0 sm:border rounded-md mb-0 bg-white',
'mb-0 mt-4 rounded-md border-0 bg-white sm:mt-7 sm:border',
...(hasTnsBanner
? [
{
@@ -42,7 +42,7 @@ const hasTnsBanner = !!tnsBannerLink;
<ResourceProgressStats
resourceId={roadmapId}
resourceType='roadmap'
hasSecondaryBanner={hasTitleQuestion}
hasSecondaryBanner={Boolean(hasTitleQuestion)}
/>
{

View File

@@ -21,7 +21,7 @@ export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
return (
<div className="relative hidden border-t text-sm font-medium sm:block">
{isAnswerVisible && (
<div className="fixed left-0 right-0 top-0 z-50 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50"></div>
<div className="fixed left-0 right-0 top-0 z-[100] h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50"></div>
)}
<h2
className="z-50 flex cursor-pointer items-center px-2 py-2.5 text-base font-medium"
@@ -41,7 +41,7 @@ export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
</h2>
<div
className={`absolute left-0 right-0 top-0 z-50 mt-0 rounded-md border bg-white ${
className={`absolute left-0 right-0 top-0 z-[100] mt-0 rounded-md border bg-white ${
isAnswerVisible ? 'block' : 'hidden'
}`}
ref={ref}

View File

@@ -77,6 +77,12 @@ const groups: GroupType[] = [
type: 'role',
otherGroups: ['Web Development', 'Absolute Beginners'],
},
{
title: 'API Design',
link: '/api-design',
type: 'role',
otherGroups: ['Web Development'],
},
{
title: 'QA',
link: '/qa',
@@ -216,6 +222,12 @@ const groups: GroupType[] = [
type: 'skill',
otherGroups: ['Web Development'],
},
{
title: 'Terraform',
link: '/terraform',
type: 'skill',
otherGroups: ['Web Development'],
},
],
},
{
@@ -226,15 +238,20 @@ const groups: GroupType[] = [
link: '/android',
type: 'role',
},
{
title: 'iOS',
link: '/ios',
type: 'role',
},
{
title: 'React Native',
link: '/react-native',
type: 'role',
type: 'skill',
},
{
title: 'Flutter',
link: '/flutter',
type: 'role',
type: 'skill',
},
],
},
@@ -299,6 +316,16 @@ const groups: GroupType[] = [
link: '/technical-writer',
type: 'role',
},
{
title: 'Product Manager',
link: '/product-manager',
type: 'role',
},
{
title: 'DevRel Engineer',
link: '/devrel',
type: 'role',
},
],
},
{

View File

@@ -53,6 +53,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
const toast = useToast();
const [isLoading, setIsLoading] = useState(false);
const [isTransferringToTeam, setIsTransferringToTeam] = useState(false);
const [isSettingsUpdated, setIsSettingsUpdated] = useState(false);
const [friends, setFriends] = useState<ListFriendsResponse>([]);
const [teams, setTeams] = useState<UserTeamItem[]>([]);
@@ -71,13 +72,12 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
);
const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
const canTransferRoadmap = visibility === 'team' && !teamId;
let isUpdateDisabled = false;
// Disable update button if there are no friends to share with
if (visibility === 'friends' && sharedFriendIds.length === 0) {
isUpdateDisabled = true;
// Disable update button if there are no team to transfer
} else if (canTransferRoadmap && !selectedTeamId) {
} else if (isTransferringToTeam && !selectedTeamId) {
isUpdateDisabled = true;
// Disable update button if there are no members to share with
} else if (
@@ -198,6 +198,8 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
</div>
<ShareOptionTabs
isTransferringToTeam={isTransferringToTeam}
setIsTransferringToTeam={setIsTransferringToTeam}
visibility={visibility}
setVisibility={setVisibility}
teamId={teamId}
@@ -226,48 +228,52 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
/>
<div className="mt-4 flex grow flex-col">
{visibility === 'public' && (
<div className="flex h-full flex-grow flex-col items-center justify-center rounded-md border bg-gray-50 text-center">
<Globe2 className="mb-3 h-10 w-10 text-gray-300" />
<p className="font-medium text-gray-500">
Anyone with the link can access.
</p>
</div>
)}
{visibility === 'me' && (
<div className="flex h-full flex-grow flex-col items-center justify-center rounded-md border bg-gray-50 text-center">
<Lock className="mb-3 h-10 w-10 text-gray-300" />
<p className="font-medium text-gray-500">
Only you will be able to access.
</p>
</div>
{!isTransferringToTeam && (
<>
{visibility === 'public' && (
<div className="flex h-full flex-grow flex-col items-center justify-center rounded-md border bg-gray-50 text-center">
<Globe2 className="mb-3 h-10 w-10 text-gray-300" />
<p className="font-medium text-gray-500">
Anyone with the link can access.
</p>
</div>
)}
{visibility === 'me' && (
<div className="flex h-full flex-grow flex-col items-center justify-center rounded-md border bg-gray-50 text-center">
<Lock className="mb-3 h-10 w-10 text-gray-300" />
<p className="font-medium text-gray-500">
Only you will be able to access.
</p>
</div>
)}
{/* For Personal Roadmap */}
{visibility === 'friends' && (
<ShareFriendList
friends={friends}
setFriends={setFriends}
sharedFriendIds={sharedFriendIds}
setSharedFriendIds={setSharedFriendIds}
/>
)}
{/* For Team Roadmap */}
{visibility === 'team' && teamId && (
<ShareTeamMemberList
teamId={teamId}
sharedTeamMemberIds={sharedTeamMemberIds}
setSharedTeamMemberIds={setSharedTeamMemberIds}
membersCache={membersCache}
isTeamMembersLoading={isTeamMembersLoading}
setIsTeamMembersLoading={setIsTeamMembersLoading}
/>
)}
</>
)}
{/* For Personal Roadmap */}
{visibility === 'friends' && (
<ShareFriendList
friends={friends}
setFriends={setFriends}
sharedFriendIds={sharedFriendIds}
setSharedFriendIds={setSharedFriendIds}
/>
)}
{/* For Team Roadmap */}
{visibility === 'team' && teamId && (
<ShareTeamMemberList
teamId={teamId}
sharedTeamMemberIds={sharedTeamMemberIds}
setSharedTeamMemberIds={setSharedTeamMemberIds}
membersCache={membersCache}
isTeamMembersLoading={isTeamMembersLoading}
setIsTeamMembersLoading={setIsTeamMembersLoading}
/>
)}
{canTransferRoadmap && (
{isTransferringToTeam && (
<>
<TransferToTeamList
currentTeamId={teamId}
teams={teams}
setTeams={setTeams}
selectedTeamId={selectedTeamId}
@@ -319,7 +325,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
Close
</button>
{canTransferRoadmap && (
{isTransferringToTeam && (
<UpdateAction
disabled={
isUpdateDisabled || isLoading || sharedTeamMemberIds.length === 0
@@ -335,7 +341,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
</UpdateAction>
)}
{!canTransferRoadmap && (
{!isTransferringToTeam && (
<UpdateAction
disabled={isUpdateDisabled || isLoading}
onClick={() => {

View File

@@ -8,6 +8,8 @@ import {
} from 'lucide-react';
import type { AllowedRoadmapVisibility } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal';
import { cn } from '../../lib/classname';
import { $teamList } from '../../stores/team.ts';
import { useStore } from '@nanostores/react';
export const allowedVisibilityLabels: {
id: AllowedRoadmapVisibility;
@@ -44,15 +46,29 @@ export const allowedVisibilityLabels: {
type ShareOptionTabsProps = {
visibility: AllowedRoadmapVisibility;
setVisibility: (visibility: AllowedRoadmapVisibility) => void;
isTransferringToTeam: boolean;
setIsTransferringToTeam: (isTransferringToTeam: boolean) => void;
teamId?: string;
onChange: (visibility: AllowedRoadmapVisibility) => void;
};
export function ShareOptionTabs(props: ShareOptionTabsProps) {
const { visibility, setVisibility, teamId, onChange } = props;
const {
isTransferringToTeam,
setIsTransferringToTeam,
visibility,
setVisibility,
teamId,
onChange,
} = props;
const handleClick = (visibility: AllowedRoadmapVisibility) => {
const teamList = useStore($teamList);
const handleTabClick = (visibility: AllowedRoadmapVisibility) => {
setIsTransferringToTeam(false);
setVisibility(visibility);
onChange(visibility);
};
@@ -63,11 +79,9 @@ export function ShareOptionTabs(props: ShareOptionTabsProps) {
{allowedVisibilityLabels.map((v) => {
if (v.id === 'friends' && teamId) {
return null;
} else if (v.id === 'team' && !teamId) {
return null;
}
const isActive = v.id === visibility;
const isActive = !isTransferringToTeam && v.id === visibility;
return (
<li key={v.id}>
<OptionTab
@@ -75,21 +89,21 @@ export function ShareOptionTabs(props: ShareOptionTabsProps) {
isActive={isActive}
icon={v.icon}
onClick={() => {
handleClick(v.id);
handleTabClick(v.id);
}}
/>
</li>
);
})}
</ul>
{!teamId && (
{(!teamId || teamList.length > 1) && (
<div className="grow">
<OptionTab
label="Transfer to team"
icon={ArrowLeftRight}
isActive={visibility === 'team'}
isActive={isTransferringToTeam}
onClick={() => {
handleClick('team');
setIsTransferringToTeam(true);
}}
className='border-red-300 text-red-600 hover:border-red-200 hover:bg-red-50 data-[active="true"]:border-red-600 data-[active="true"]:bg-red-600 data-[active="true"]:text-white'
/>
@@ -115,7 +129,7 @@ function OptionTab(props: OptionTabProps) {
className={cn(
'flex items-center justify-center gap-2 rounded-md border px-3 py-2 text-sm text-black hover:border-gray-300 hover:bg-gray-100',
'data-[active="true"]:border-gray-500 data-[active="true"]:bg-gray-200 data-[active="true"]:text-black',
className
className,
)}
data-active={isActive}
disabled={isActive}

View File

@@ -82,25 +82,24 @@ export function ShareSuccess(props: ShareSuccessProps) {
</p>
)}
<div className="mt-2 border-t pt-2">
<p className="text-sm text-gray-400">
You can also embed this roadmap on your website.
</p>
<div className="mt-2">
<input
onClick={(e) => {
e.currentTarget.select();
copyText(embedHtml);
}}
readOnly={true}
className="w-full resize-none rounded-md border bg-gray-50 p-2 text-sm"
value={embedHtml}
/>
</div>
</div>
{visibility === 'public' && (
<>
<div className="mt-2 border-t pt-2">
<p className="text-sm text-gray-400">
You can also embed this roadmap on your website.
</p>
<div className="mt-2">
<input
onClick={(e) => {
e.currentTarget.select();
copyText(embedHtml);
}}
readOnly={true}
className="w-full resize-none rounded-md border bg-gray-50 p-2 text-sm"
value={embedHtml}
/>
</div>
</div>
<div className="-mx-4 mt-4 flex items-center gap-1.5">
<span className="h-px grow bg-gray-300" />
<span className="px-2 text-xs uppercase text-gray-400">Or</span>

View File

@@ -9,6 +9,7 @@ type TransferToTeamListProps = {
teams: UserTeamItem[];
setTeams: (teams: UserTeamItem[]) => void;
currentTeamId?: string;
selectedTeamId: string | null;
setSelectedTeamId: (teamId: string | null) => void;
@@ -24,6 +25,7 @@ export function TransferToTeamList(props: TransferToTeamListProps) {
selectedTeamId,
setSelectedTeamId,
isTeamMembersLoading,
currentTeamId,
setIsTeamMembersLoading,
onTeamChange,
} = props;
@@ -38,7 +40,7 @@ export function TransferToTeamList(props: TransferToTeamListProps) {
}
const { response, error } = await httpGet<UserTeamItem[]>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-teams`
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-teams`,
);
if (error || !response) {
toast.error(error?.message || 'Something went wrong');
@@ -46,7 +48,7 @@ export function TransferToTeamList(props: TransferToTeamListProps) {
}
setTeams(
response.filter((team) => ['admin', 'manager'].includes(team.role))
response.filter((team) => ['admin', 'manager'].includes(team.role)),
);
}
@@ -80,13 +82,16 @@ export function TransferToTeamList(props: TransferToTeamListProps) {
<ul className="mt-2 grid grid-cols-3 gap-1.5">
{teams.map((team) => {
const isSelected = team._id === selectedTeamId;
if (team._id === currentTeamId) {
return null;
}
return (
<li key={team._id}>
<button
className={cn(
'relative flex w-full items-center gap-2.5 rounded-lg border p-2.5 disabled:cursor-not-allowed disabled:opacity-70',
isSelected && 'border-gray-500 bg-gray-100 text-black'
isSelected && 'border-gray-500 bg-gray-100 text-black',
)}
disabled={isTeamMembersLoading}
onClick={() => {

View File

@@ -4,6 +4,8 @@ import type { TeamStreamActivity } from './TeamActivityPage';
import { ChevronsDown, ChevronsUp } from 'lucide-react';
import { ActivityTopicTitles } from '../Activity/ActivityTopicTitles';
import { cn } from '../../lib/classname';
import { useStore } from '@nanostores/react';
import { $currentTeam } from '../../stores/team';
type TeamActivityItemProps = {
onTopicClick?: (activity: TeamStreamActivity) => void;
@@ -14,6 +16,7 @@ type TeamActivityItemProps = {
name: string;
avatar?: string | undefined;
username?: string | undefined;
memberId?: string;
};
};
@@ -21,6 +24,7 @@ export function TeamActivityItem(props: TeamActivityItemProps) {
const { user, onTopicClick, teamId } = props;
const { activities } = user;
const currentTeam = useStore($currentTeam);
const [showAll, setShowAll] = useState(false);
const resourceLink = (activity: TeamStreamActivity) => {
@@ -61,15 +65,33 @@ export function TeamActivityItem(props: TeamActivityItemProps) {
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${user.avatar}`
: '/images/default-avatar.png';
const isPersonalProgressOnly =
currentTeam?.personalProgressOnly &&
currentTeam.role === 'member' &&
user.memberId !== currentTeam.memberId;
const username = (
<>
<a
href={`/team/member?t=${teamId}&m=${user?.memberId}`}
className={cn(
'inline-flex items-center gap-1.5 underline underline-offset-2 hover:underline',
isPersonalProgressOnly
? 'pointer-events-none cursor-default no-underline'
: '',
)}
onClick={(e) => {
if (isPersonalProgressOnly) {
e.preventDefault();
}
}}
aria-disabled={isPersonalProgressOnly}
>
<img
className="mr-1 inline-block h-5 w-5 rounded-full"
className="inline-block h-5 w-5 rounded-full"
src={userAvatar}
alt={user.name}
/>
<span className="font-medium">{user?.name || 'Unknown'}</span>
</>
</a>
);
if (activities.length === 1) {
@@ -80,11 +102,11 @@ export function TeamActivityItem(props: TeamActivityItemProps) {
return (
<li
key={user._id}
className="flex flex-wrap items-center rounded-md border px-2 py-2.5 text-sm"
className="flex flex-wrap items-center gap-1 rounded-md border px-2 py-2.5 text-sm"
>
{actionType === 'in_progress' && (
<>
<p className="mb-1">
<p className="mb-1 flex w-full flex-wrap items-center">
{username}&nbsp;started&nbsp;
{topicCount}&nbsp;topic{topicCount > 1 ? 's' : ''}&nbsp;in&nbsp;
{resourceLink(activity)}&nbsp;
@@ -98,7 +120,7 @@ export function TeamActivityItem(props: TeamActivityItemProps) {
)}
{actionType === 'done' && (
<>
<p className="mb-1">
<p className="mb-1 flex w-full flex-wrap items-center">
{username}&nbsp;completed&nbsp;
{topicCount}&nbsp;topic{topicCount > 1 ? 's' : ''}&nbsp;in&nbsp;
{resourceLink(activity)}&nbsp;
@@ -112,7 +134,7 @@ export function TeamActivityItem(props: TeamActivityItemProps) {
)}
{actionType === 'answered' && (
<>
<p className="mb-1">
<p className="mb-1 flex w-full flex-wrap items-center">
{username}&nbsp;answered&nbsp;
{topicCount}&nbsp;question{topicCount > 1 ? 's' : ''}
&nbsp;in&nbsp;
@@ -138,8 +160,8 @@ export function TeamActivityItem(props: TeamActivityItemProps) {
return (
<li key={user._id} className="overflow-hidden rounded-md border">
<h3 className="flex flex-wrap items-center gap-1 bg-gray-100 px-2 py-2.5 text-sm">
{username} has {activities.length} updates in {uniqueResourcesCount}{' '}
resource(s)
{username} has {activities.length} updates in {uniqueResourcesCount}
&nbsp;resource(s)
</h3>
<div className="py-3">
<ul className="ml-2 flex flex-col divide-y pr-2 sm:ml-[36px]">

View File

@@ -39,6 +39,7 @@ type GetTeamActivityResponse = {
name: string;
avatar?: string;
username?: string;
memberId?: string;
}[];
activities: TeamActivityStreamDocument[];
};
@@ -188,10 +189,10 @@ export function TeamActivityPage() {
Team Activity
</h3>
<ul className="mb-4 mt-2 flex flex-col gap-3">
{usersWithActivities.map((user) => {
{usersWithActivities.map((user, index) => {
return (
<TeamActivityItem
key={user._id}
key={`${user._id}-${index}`}
user={user}
teamId={teamId}
onTopicClick={setSelectedActivity}

View File

@@ -23,6 +23,7 @@ export type UserTeamItem = {
role: AllowedRoles;
status: AllowedMemberStatus;
memberId: string;
personalProgressOnly?: boolean;
};
export type TeamListResponse = UserTeamItem[];

View File

@@ -0,0 +1,215 @@
import { useEffect, useState } from 'react';
import { httpGet } from '../../lib/http';
import { pageProgressMessage } from '../../stores/page';
import { getUrlParams } from '../../lib/browser';
import { useToast } from '../../hooks/use-toast';
import type { TeamMemberDocument } from '../TeamMembers/TeamMembersPage';
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
import type { TeamActivityStreamDocument } from '../TeamActivity/TeamActivityPage';
import { ResourceProgress } from '../Activity/ResourceProgress';
import { ActivityStream } from '../Activity/ActivityStream';
import { MemberRoleBadge } from '../TeamMembers/RoleBadge';
import { TeamMemberEmptyPage } from './TeamMemberEmptyPage';
import { Pagination } from '../Pagination/Pagination';
import type { ResourceType } from '../../lib/resource-progress';
import { MemberProgressModal } from '../TeamProgress/MemberProgressModal';
import { useStore } from '@nanostores/react';
import { $currentTeam } from '../../stores/team';
import { MemberCustomProgressModal } from '../TeamProgress/MemberCustomProgressModal';
type GetTeamMemberProgressesResponse = TeamMemberDocument & {
name: string;
avatar: string;
email: string;
progresses: UserProgress[];
};
type GetTeamMemberActivityResponse = {
data: TeamActivityStreamDocument[];
totalCount: number;
totalPages: number;
currPage: number;
perPage: number;
};
export function TeamMemberDetailsPage() {
const { t: teamId, m: memberId } = getUrlParams() as { t: string; m: string };
const toast = useToast();
const currentTeam = useStore($currentTeam);
const [memberProgress, setMemberProgress] =
useState<GetTeamMemberProgressesResponse | null>(null);
const [memberActivity, setMemberActivity] =
useState<GetTeamMemberActivityResponse | null>(null);
const [currPage, setCurrPage] = useState(1);
const [selectedResource, setSelectedResource] = useState<{
resourceId: string;
resourceType: ResourceType;
isCustomResource?: boolean;
} | null>(null);
const loadMemberProgress = async () => {
const { response, error } = await httpGet<GetTeamMemberProgressesResponse>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-team-member-progresses/${teamId}/${memberId}`,
);
if (error || !response) {
pageProgressMessage.set('');
toast.error(error?.message || 'Failed to load team member');
return;
}
setMemberProgress(response);
};
const loadMemberActivity = async (currPage: number = 1) => {
const { response, error } = await httpGet<GetTeamMemberActivityResponse>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-team-member-activity/${teamId}/${memberId}`,
{
currPage,
},
);
if (error || !response) {
pageProgressMessage.set('');
toast.error(error?.message || 'Failed to load team member activity');
return;
}
setMemberActivity(response);
setCurrPage(response?.currPage || 1);
};
useEffect(() => {
if (!teamId) {
return;
}
Promise.allSettled([loadMemberProgress(), loadMemberActivity()]).finally(
() => {
pageProgressMessage.set('');
},
);
}, [teamId]);
if (!teamId || !memberId || !memberProgress || !memberActivity) {
return null;
}
const avatarUrl = memberProgress?.avatar
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${memberProgress?.avatar}`
: '/images/default-avatar.png';
const ProgressModal =
selectedResource && !selectedResource.isCustomResource
? MemberProgressModal
: MemberCustomProgressModal;
return (
<>
{selectedResource && (
<ProgressModal
teamId={teamId}
member={{
...memberProgress,
_id: memberId,
updatedAt: new Date(memberProgress.updatedAt).toISOString(),
progress: memberProgress.progresses,
}}
resourceId={selectedResource.resourceId}
resourceType={selectedResource.resourceType}
isCustomResource={selectedResource.isCustomResource}
onClose={() => setSelectedResource(null)}
onShowMyProgress={() => {
window.location.href = `/team/member?t=${teamId}&m=${currentTeam?.memberId}`;
}}
/>
)}
<div className="mb-8 flex items-center gap-3">
<img
src={avatarUrl}
alt={memberProgress?.name}
className="h-14 w-14 rounded-full"
/>
<div>
<h1 className="mt-1 text-2xl font-medium">{memberProgress?.name}</h1>
<p className="text-sm text-gray-500">{memberProgress?.email}</p>
</div>
</div>
{memberProgress?.progresses && memberProgress?.progresses?.length > 0 ? (
<>
<h2 className="mb-3 text-xs uppercase text-gray-400">
Progress Overview
</h2>
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2">
{memberProgress?.progresses?.map((progress) => {
const learningCount = progress.learning || 0;
const doneCount = progress.done || 0;
const totalCount = progress.total || 0;
const skippedCount = progress.skipped || 0;
return (
<ResourceProgress
key={progress.resourceId}
isCustomResource={progress.isCustomResource!}
doneCount={doneCount > totalCount ? totalCount : doneCount}
learningCount={
learningCount > totalCount ? totalCount : learningCount
}
totalCount={totalCount}
skippedCount={skippedCount}
resourceId={progress.resourceId}
resourceType={'roadmap'}
updatedAt={progress.updatedAt}
title={progress.resourceTitle}
roadmapSlug={progress.roadmapSlug}
showActions={false}
onResourceClick={() => {
setSelectedResource({
resourceId: progress.resourceId,
resourceType: progress.resourceType,
isCustomResource: progress.isCustomResource,
});
}}
/>
);
})}
</div>
</>
) : (
<TeamMemberEmptyPage teamId={teamId} />
)}
{memberActivity?.data && memberActivity?.data?.length > 0 ? (
<>
<ActivityStream
className="mt-8 p-0 md:m-0 md:mb-4 md:mt-8 md:p-0"
activities={
memberActivity?.data?.flatMap((act) => act.activity) || []
}
onResourceClick={(resourceId, resourceType, isCustomResource) => {
setSelectedResource({
resourceId,
resourceType,
isCustomResource,
});
}}
/>
<Pagination
currPage={currPage}
totalPages={memberActivity?.totalPages || 1}
totalCount={memberActivity?.totalCount || 0}
perPage={memberActivity?.perPage || 10}
onPageChange={(page) => {
pageProgressMessage.set('Loading Activity');
loadMemberActivity(page).finally(() => {
pageProgressMessage.set('');
});
}}
/>
</>
) : null}
</>
);
}

View File

@@ -0,0 +1,29 @@
import { RoadmapIcon } from '../ReactIcons/RoadmapIcon';
type TeamMemberEmptyPageProps = {
teamId: string;
};
export function TeamMemberEmptyPage(props: TeamMemberEmptyPageProps) {
const { teamId } = props;
return (
<div className="rounded-md">
<div className="flex flex-col items-center p-7 text-center">
<RoadmapIcon className="mb-2 h-[60px] w-[60px] opacity-10 sm:h-[120px] sm:w-[120px]" />
<h2 className="text-lg font-bold sm:text-xl">No Progress</h2>
<p className="my-1 max-w-[400px] text-balance text-sm text-gray-500 sm:my-2 sm:text-base">
Progress will appear here as they start tracking their{' '}
<a
href={`/team/roadmaps?t=${teamId}`}
className="mt-4 text-blue-500 hover:underline"
>
Roadmaps
</a>{' '}
progress.
</p>
</div>
</div>
);
}

View File

@@ -1,12 +1,23 @@
import { cn } from '../../lib/classname';
import type { AllowedRoles } from '../CreateTeam/RoleDropdown';
export function MemberRoleBadge({ role }: { role: AllowedRoles }) {
type RoleBadgeProps = {
role: AllowedRoles;
className?: string;
};
export function MemberRoleBadge(props: RoleBadgeProps) {
const { role, className } = props;
return (
<span
className={`rounded-full px-2 py-0.5 text-xs sm:flex items-center capitalize ${['admin'].includes(role)
? 'bg-blue-100 text-blue-700 '
: 'bg-gray-100 text-gray-700 '
} ${['manager'].includes(role) ? 'bg-green-100 text-green-700' : ''}`}
className={cn(
`items-center rounded-full px-2 py-0.5 text-xs capitalize sm:flex ${
['admin'].includes(role)
? 'bg-blue-100 text-blue-700 '
: 'bg-gray-100 text-gray-700 '
} ${['manager'].includes(role) ? 'bg-green-100 text-green-700' : ''}`,
className,
)}
>
{role}
</span>

View File

@@ -2,8 +2,10 @@ import { MailIcon } from '../ReactIcons/MailIcon';
import { MemberActionDropdown } from './MemberActionDropdown';
import { MemberRoleBadge } from './RoleBadge';
import type { TeamMemberItem } from './TeamMembersPage';
import { $canManageCurrentTeam } from '../../stores/team';
import { $canManageCurrentTeam, $currentTeam } from '../../stores/team';
import { useStore } from '@nanostores/react';
import { useAuth } from '../../hooks/use-auth';
import { cn } from '../../lib/classname';
type TeamMemberProps = {
member: TeamMemberItem;
@@ -29,6 +31,7 @@ export function TeamMemberItem(props: TeamMemberProps) {
onSendProgressReminder,
} = props;
const currentTeam = useStore($currentTeam);
const canManageTeam = useStore($canManageCurrentTeam);
const showNoProgressBadge = !member.hasProgress && member.status === 'joined';
const allowProgressReminder =
@@ -36,6 +39,10 @@ export function TeamMemberItem(props: TeamMemberProps) {
!member.hasProgress &&
member.status === 'joined' &&
member.userId !== userId;
const isPersonalProgressOnly =
currentTeam?.personalProgressOnly &&
currentTeam.role === 'member' &&
String(member._id) !== currentTeam.memberId;
return (
<div
@@ -59,7 +66,23 @@ export function TeamMemberItem(props: TeamMemberProps) {
</div>
<div className="flex items-center">
<h3 className="inline-grid grid-cols-[auto_auto_auto] items-center font-medium">
<span className="truncate">{member.name}</span>
<a
href={`/team/member?t=${member.teamId}&m=${member._id}`}
className={cn(
'truncate',
isPersonalProgressOnly
? 'pointer-events-none cursor-default no-underline'
: '',
)}
onClick={(e) => {
if (isPersonalProgressOnly) {
e.preventDefault();
}
}}
aria-disabled={isPersonalProgressOnly}
>
{member.name}
</a>
{showNoProgressBadge && (
<span className="ml-2 rounded-full bg-red-400 px-2 py-0.5 text-xs font-normal text-white">
No Progress
@@ -109,4 +132,4 @@ export function TeamMemberItem(props: TeamMemberProps) {
</div>
</div>
);
}
}

View File

@@ -215,7 +215,7 @@ export function MemberCustomProgressModal(props: ProgressMapProps) {
}, []);
return (
<div className="fixed left-0 right-0 top-0 z-50 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div className="fixed left-0 right-0 top-0 z-[100] h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div
id="original-roadmap"
className="relative mx-auto h-full w-full max-w-4xl p-4 md:h-auto"
@@ -237,6 +237,7 @@ export function MemberCustomProgressModal(props: ProgressMapProps) {
<div className="px-4 pb-2">
<ReadonlyEditor
variant="modal"
hasMinimap={false}
roadmap={roadmap!}
className="min-h-[400px]"
onRendered={() => {

View File

@@ -1,23 +1,39 @@
import { useStore } from '@nanostores/react';
import type { TeamMember } from './TeamProgressPage';
import { useState } from 'react';
import { $currentTeam } from '../../stores/team';
import { cn } from '../../lib/classname';
type MemberProgressItemProps = {
member: TeamMember;
onShowResourceProgress: (
resourceId: string,
isCustomResource: boolean
isCustomResource: boolean,
) => void;
isMyProgress?: boolean;
teamId: string;
};
export function MemberProgressItem(props: MemberProgressItemProps) {
const { member, onShowResourceProgress, isMyProgress = false } = props;
const {
member,
onShowResourceProgress,
isMyProgress = false,
teamId,
} = props;
const currentTeam = useStore($currentTeam);
const memberProgress = member?.progress?.sort((a, b) => {
return b.done - a.done;
});
const [showAll, setShowAll] = useState(false);
const isPersonalProgressOnly =
currentTeam?.personalProgressOnly &&
currentTeam.role === 'member' &&
String(member._id) !== currentTeam.memberId;
const memberDetailsUrl = `/team/member?t=${teamId}&m=${member._id}`;
return (
<>
<div
@@ -36,11 +52,43 @@ export function MemberProgressItem(props: MemberProgressItemProps) {
/>
<div className="inline-grid w-full">
{!isMyProgress && (
<h3 className="truncate font-medium">{member.name}</h3>
<a
href={memberDetailsUrl}
className={cn(
'truncate font-medium',
isPersonalProgressOnly
? 'pointer-events-none cursor-default no-underline'
: '',
)}
onClick={(e) => {
if (isPersonalProgressOnly) {
e.preventDefault();
}
}}
aria-disabled={isPersonalProgressOnly}
>
{member.name}
</a>
)}
{isMyProgress && (
<div className="inline-grid grid-cols-[auto,32px] items-center gap-1.5">
<h3 className="truncate font-medium">{member.name}</h3>
<a
href={memberDetailsUrl}
className={cn(
'truncate font-medium',
isPersonalProgressOnly
? 'pointer-events-none cursor-default no-underline'
: '',
)}
onClick={(e) => {
if (isPersonalProgressOnly) {
e.preventDefault();
}
}}
aria-disabled={isPersonalProgressOnly}
>
{member.name}
</a>
<span className="rounded-md bg-red-500 px-1 py-0.5 text-xs text-white">
You
</span>
@@ -57,7 +105,7 @@ export function MemberProgressItem(props: MemberProgressItemProps) {
onClick={() =>
onShowResourceProgress(
progress.resourceId,
progress.isCustomResource!
progress.isCustomResource!,
)
}
className="group relative overflow-hidden rounded-md border p-2 hover:border-gray-300 hover:text-black focus:outline-none"
@@ -81,7 +129,7 @@ export function MemberProgressItem(props: MemberProgressItemProps) {
/>
</button>
);
}
},
)}
{memberProgress.length > 4 && !showAll && (

View File

@@ -260,7 +260,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
}, [member]);
return (
<div className="fixed left-0 right-0 top-0 z-50 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div className="fixed left-0 right-0 top-0 z-[100] h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div
id={'customized-roadmap'}
className="relative mx-auto h-full w-full max-w-4xl p-4 md:h-auto"

View File

@@ -227,6 +227,7 @@ export function TeamProgressPage() {
<MemberProgressItem
key={member._id}
member={member}
teamId={teamId}
isMyProgress={member?.email === user?.email}
onShowResourceProgress={(resourceId, isCustomResource) => {
setShowMemberProgress({

View File

@@ -147,6 +147,7 @@ export function TeamRoadmaps() {
toast.loading('Adding roadmap');
pageProgressMessage.set('Adding roadmap');
setIsLoading(true);
const roadmap = allRoadmaps.find((r) => r.id === roadmapId);
const { error, response } = await httpPut<TeamResourceConfig>(
`${
import.meta.env.PUBLIC_API_URL
@@ -156,6 +157,7 @@ export function TeamRoadmaps() {
resourceId: roadmapId,
resourceType: 'roadmap',
removed: [],
renderer: roadmap?.renderer || 'balsamiq',
},
);
@@ -166,6 +168,9 @@ export function TeamRoadmaps() {
setTeamResources(response);
toast.success('Roadmap added');
if (roadmap?.renderer === 'editor') {
setIsAddingRoadmap(false);
}
}
async function onRemove(resourceId: string) {
@@ -219,11 +224,14 @@ export function TeamRoadmaps() {
/>
);
const filteredAllRoadmaps = allRoadmaps.filter(
(r) => !teamResources.find((c) => c?.defaultRoadmapId === r.id),
);
const addRoadmapModal = isAddingRoadmap && (
<SelectRoadmapModal
onClose={() => setIsAddingRoadmap(false)}
teamResourceConfig={teamResources}
allRoadmaps={allRoadmaps}
allRoadmaps={filteredAllRoadmaps}
teamId={teamId}
onRoadmapAdd={(roadmapId: string) => {
onAdd(roadmapId).finally(() => {

View File

@@ -106,6 +106,10 @@ export function TeamVersions(props: TeamVersionsProps) {
}, []);
useEffect(() => {
if (!selectedTeamVersion) {
return;
}
clearResourceProgress();
// teams have customizations. Assigning #customized-roadmap to roadmapSvgWrap

Some files were not shown because too many files have changed in this diff Show More