Compare commits

...

175 Commits

Author SHA1 Message Date
Arik Chakma
21046347da fix: add a default subject 2025-05-06 03:47:01 +06:00
Arik Chakma
6525b1bdee fix: hide the announcement for 3 days (#8595) 2025-05-05 21:22:34 +01:00
github-actions[bot]
afc6f2d5ae chore: update roadmap content json (#8580)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-05-05 19:55:57 +06:00
Kamran Ahmed
871d43c295 Fix CSS issues on AI tutor 2025-05-04 13:04:59 +01:00
Arik Chakma
bc32dc780b feat: add ai course creator id (#8592) 2025-05-03 23:14:18 +01:00
Matthew Bill
5685b30c42 Mention book tag (#8588)
added the @book@ tag as per reply on discord from Kamran that this is a valid type.
2025-05-03 13:56:02 +01:00
Arik Chakma
7e3508cdf4 feat: implement ai tutor in topics (#8546)
* wip

* feat: implement ai tutor

* fix: add style

* feat: ai course subjects

* fix: remove tree json

* wip

* Topic chat

* Refactor topic popup

* Improve UI for navigation

* Update contribution URL

* Improve topic popup

* Update UI

* feat: predefined messages

* fix: ui changes

* fix: add summarise

* fix: add explain topic

* Topic AI changes

* feat: predefined message group

* Refactor actions logic

* Implement topic ai changes

* Improve actions buttons

* Add new explainer action

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2025-05-02 21:12:04 +01:00
Kamran Ahmed
2ba3e64c1c Update ai agents roadmap links 2025-05-01 22:30:50 +01:00
Vedansh
ca30955e9f Adjust few topics in CyberSecurity roadmap (#8537) 2025-05-01 13:29:47 +01:00
Vedansh
c2e1d8fd28 cleanup and refactor Computer Science roadmap topics (#8561) 2025-05-01 13:29:23 +01:00
Vedansh
ab5cc9dd6d refactor (ai-agents): improve topic content resources (#8581)
* refactor first 27 topics.

* refactor next 28 topics

* finalize remaining 44 topics.

* Update src/data/roadmaps/ai-agents/content/acting--tool-invocation@sHYd4KsKlmw5Im3nQ19W8.md

* Update src/data/roadmaps/ai-agents/content/acting--tool-invocation@sHYd4KsKlmw5Im3nQ19W8.md

* Update src/data/roadmaps/ai-agents/content/agent-loop@Eih4eybuYB3C2So8K0AT3.md

* Update src/data/roadmaps/ai-agents/content/basic-backend-development@VPI89s-m885r2YrXjYxdd.md

* Update src/data/roadmaps/ai-agents/content/database-queries@sV1BnA2-qBnXoKpUn-8Ub.md

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2025-05-01 13:27:01 +01:00
Arik Chakma
74267a6061 fix: handle syntax error (#8506) 2025-04-30 16:32:12 +01:00
github-actions[bot]
9c2c06affd chore: update roadmap content json (#8567)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-04-30 16:31:44 +01:00
David Willis-Owen
0e0eea635b Update jailbreak content (#8577) 2025-04-30 13:20:37 +01:00
Kamran Ahmed
82400cd7a6 Update content 2025-04-30 12:27:24 +01:00
Kamran Ahmed
55fdc9f957 Add PDF and PNG for ai-agents 2025-04-29 23:47:41 +01:00
Kamran Ahmed
f8b9b58968 Add content to AI Agents 2025-04-29 23:37:39 +01:00
Kamran Ahmed
b79411c49e Fix height for ai-agents roadmap 2025-04-29 23:20:20 +01:00
Kamran Ahmed
5ba951e555 Add AI Agents roadmap on get-started and roadmaps pages 2025-04-29 23:20:20 +01:00
Kamran Ahmed
464bcb6c66 Add content for AI Agents roadmap 2025-04-29 23:20:20 +01:00
Kamran Ahmed
b9aee719b2 Add ai-agents roadmap 2025-04-29 23:20:20 +01:00
Kamran Ahmed
5d9eecc2b9 Add ai-agents roadmap 2025-04-29 23:20:20 +01:00
David Willis-Owen
80a0caba2f Update resources in AI Red Teaming Roadmap (#8570)
* Update why-red-team-ai-systems@fNTb9y3zs1HPYclAmu_Wv.md

* Update prompt-engineering@gx4KaFqKgJX9n9_ZGMqlZ.md

* Update generative-models@3XJ-g0KvHP75U18mxCqgw.md

* Update prompt-hacking@1Xr7mxVekeAHzTL7G4eAZ.md

* Update jailbreak-techniques@Ds8pqn4y9Npo7z6ubunvc.md

* Update countermeasures@G1u_Kq4NeUsGX2qnUTuJU.md

* Update forums@Smncq-n1OlnLAY27AFQOO.md

* Update lab-environments@MmwwRK4I9aRH_ha7duPqf.md

* Update ctf-challenges@2Imb64Px3ZQcBpSQjdc_G.md

* Update ctf-challenges@2Imb64Px3ZQcBpSQjdc_G.md

* Update industry-credentials@HHjsFR6wRDqUd66PMDE_7.md

* Update agentic-ai-security@FVsKivsJrIb82B0lpPmgw.md

* Update responsible-disclosure@KAcCZ3zcv25R6HwzAsfUG.md

* Update benchmark-datasets@et1Xrr8ez-fmB0mAq8W_a.md

* Update adversarial-examples@xjlttOti-_laPRn8a2fVy.md

* Update large-language-models@8K-wCn2cLc7Vs_V4sC3sE.md

* Update introduction@HFJIYcI16OMyM77fAw9af.md

* Update ethical-considerations@1gyuEV519LjN-KpROoVwv.md

* Update role-of-red-teams@Irkc9DgBfqSn72WaJqXEt.md

* Update threat-modeling@RDOaTBWP3aIJPUp_kcafm.md

* Update direct@5zHow4KZVpfhch5Aabeft.md

* Update indirect@3_gJRtJSdm2iAfkwmcv0e.md

* Update model-vulnerabilities@uBXrri2bXVsNiM8fIHHOv.md

* Update model-weight-stealing@QFzLx5nc4rCCD8WVc20mo.md

* Update unauthorized-access@DQeOavZCoXpF3k_qRDABs.md

* Update data-poisoning@nD0_64ELEeJSN-0aZiR7i.md

* Update model-inversion@iE5PcswBHnu_EBFIacib0.md

* Update code-injection@vhBu5x8INTtqvx6vcYAhE.md

* Update remote-code-execution@kgDsDlBk8W2aM6LyWpFY8.md

* Update api-protection@Tszl26iNBnQBdBEWOueDA.md

* Update authentication@J7gjlt2MBx7lOkOnfGvPF.md

* Update white-box-testing@Mrk_js5UVn4dRDw-Yco3Y.md

* Update white-box-testing@Mrk_js5UVn4dRDw-Yco3Y.md

* Update white-box-testing@Mrk_js5UVn4dRDw-Yco3Y.md

* Update automated-vs-manual@LVdYN9hyCyNPYn2Lz1y9b.md

* Update specialized-courses@s1xKK8HL5-QGZpcutiuvj.md
2025-04-28 13:12:11 +01:00
Jawher Kl
2937923fb1 Fix wrong URL (#8429) 2025-04-28 09:26:41 +01:00
Vedansh
a33018de1d fix(cybersecurity): resource link adjustments (#8544)
* adjustments.

* Squashed commit of the following:

commit ab55b0972edca0ea36bd87e93a54e38750b15208
Author: Vedansh <superuser.ntsystems@outlook.com>
Date:   Tue Apr 22 17:32:07 2025 +0530

    adjust remaining topics.

* Update src/data/roadmaps/cyber-security/content/dig@D2YYv1iTRGken75sHO0Gt.md

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
2025-04-27 19:08:05 +06:00
Soumik Sarker
c236e863a0 Fixed link of Experimental Design Article of AI Data Scientist roadmap (#8562) 2025-04-27 12:42:25 +01:00
github-actions[bot]
b7f94a7679 chore: update roadmap content json (#8564)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-04-27 01:41:31 +01:00
Vedansh
107fa4842c cybersecurity: adjust few topic links (#8520)
* adjust content in few topics.

* Update src/data/roadmaps/cyber-security/content/antimalware@9QtY1hMJ7NKLFztYK-mHY.md

* Update antimalware@9QtY1hMJ7NKLFztYK-mHY.md

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
2025-04-26 13:06:51 +01:00
Kamran Ahmed
3e059cc3e9 Fix broken URLs 2025-04-26 12:56:24 +01:00
Kamran Ahmed
ebd34612a2 Refactor red teaming resources (#8560) 2025-04-26 12:47:04 +01:00
Kamran Ahmed
ed54dd663a Add AI red teaming roadmap 2025-04-26 12:18:22 +01:00
Vedansh
bb0df94afa fix: topic links and content (#8536) 2025-04-26 14:13:05 +06:00
github-actions[bot]
be1b041acb chore: update roadmap content json (#8540)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-04-26 14:11:20 +06:00
Vedansh
5f53b3ef1e refactor: (cybersecurity) content & link adjustments. (#8549)
* refactor 40 topics quickly.

* finally.
2025-04-25 14:59:19 +01:00
David Willis-Owen
c83309b7db AI Red Teaming Roadmap - Initial Commit (#8553)
* Initial commit

* Editing formatting
2025-04-25 14:57:24 +01:00
Kamran Ahmed
3a49ad556a Update react router resources 2025-04-24 19:18:12 +01:00
Kamran Ahmed
4447f4841c Add hubspot snippet 2025-04-24 19:15:23 +01:00
Kamran Ahmed
6351969d3c Update frontend explanation video 2025-04-24 12:56:52 +01:00
Vedansh
599da5a153 refactor(cybersecurity): adjust topic content (#8527) 2025-04-23 20:11:19 +06:00
Vedansh
0a2e098c44 cleanup all 95 topics. phew (#8519) 2025-04-23 20:09:31 +06:00
github-actions[bot]
514377da63 chore: update roadmap content json (#8524)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-04-23 19:47:54 +06:00
Kamran Ahmed
9dea34869a Fix progress nudge not centered 2025-04-22 22:55:20 +01:00
Kamran Ahmed
db7d02b3c2 Update pnpm lock 2025-04-22 18:34:16 +01:00
Kamran Ahmed
cf09986ffc Update isNew flags 2025-04-22 18:31:47 +01:00
Kamran Ahmed
1ff5aa1e05 Add AI red teaming roadmap 2025-04-22 18:30:14 +01:00
Kamran Ahmed
36021ce4a5 Add AI red teaming roadmap 2025-04-22 18:28:03 +01:00
Kamran Ahmed
5409deeaa8 Add golang interview questions link 2025-04-22 12:44:42 +01:00
Kamran Ahmed
555cd15a8e Add golang interview questions 2025-04-22 12:44:42 +01:00
Arik Chakma
d36af2d3fa feat: project without submission (#8530) 2025-04-22 11:36:36 +01:00
Arik Chakma
05db236a3c fix: package lock file (#8528) 2025-04-21 23:25:23 +06:00
Kamran Ahmed
cd4f099ca8 Add FAQ backlink 2025-04-21 17:08:13 +01:00
Kamran Ahmed
c5694be86b Add og images to question guides 2025-04-21 15:57:09 +01:00
Kamran Ahmed
8a1960eae8 Add data analyst questions guide 2025-04-21 15:52:04 +01:00
Kamran Ahmed
649384ac45 Page sponsors removal 2025-04-21 11:44:50 +01:00
Vedansh
d3ccfb9ced refactor(cyber-security): adjust content for cyber securities (#8521) 2025-04-20 23:12:57 +06:00
Vedansh
eb5d2fbc3a refractor(redis): cleanup redis db roadmap (#8518)
* refractor 45 topics

* refractor remaining 46 topics
2025-04-20 20:00:25 +06:00
Bryan Wilches
a822f00a70 chore(topic): add help example (#8514)
* Update command-help.md

Feat: Adding the --help flag explanation

* Update src/data/roadmaps/linux/content/shell-basics/command-help.md

* Update command-help.md

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
2025-04-20 14:02:23 +06:00
github-actions[bot]
08d0301181 chore: update roadmap content json (#8511)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-04-20 13:53:43 +06:00
Satyaakam Goswami
62adbe49be feat(topic): public speaking content (#8420)
* Update public-speaking@LRZ8yxTfEGCXsYp4N1_uD.md

adding some content to the public speaking stub

* Update src/data/roadmaps/devrel/content/public-speaking@LRZ8yxTfEGCXsYp4N1_uD.md

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
2025-04-19 01:10:00 +06:00
Allen Terescenco
a0784e1695 fix: misspelling of 'Cucumber' in Java Course (#8457)
Co-authored-by: Allen Terescenco <allen.terescenco@gmail.com>
2025-04-19 01:07:34 +06:00
Instinct
556588c303 fix: official supabase vector reference (#8460)
current reference is https://supabase.com/docs/guides/ai/vector-columns

previous reference was https://supabase.com/vector which is now a 404
2025-04-19 01:05:56 +06:00
Instinct
c7ec3f08ee fix(topic): openai image understanding docs(#8467)
Previous article gives 404 (https://platform.openai.com/docs/guides/vision/low-or-high-fidelity-image-understanding)
New article linked in this PR (https://platform.openai.com/docs/guides/images)
2025-04-19 01:02:29 +06:00
Arnab Sarkar
ec0f129783 feat(topic): add content to control flow in cpp roadmap (#8480)
* added content to if-else/switch/goto under control flow & statements inside c++ roadmap

* added content to if-else/switch/goto under control flow & statements inside c++ roadmap

* added content to control flow in cpp roadmap fixed format error

* chore: update roadmap content json
2025-04-19 01:01:13 +06:00
web-svb
5f9dca9a1e fix(topic): postgresql docs link (#8491)
Removed outdated link to PostgreSQL 7.1 documentation.
Updated remaining link from version 8.1 to current to ensure long-term accuracy and relevance.
This change ensures that users are directed to the latest stable PostgreSQL documentation.
2025-04-19 00:59:36 +06:00
Amir Babaei
256ad44e60 feat(topic): add differential calculus resource (#8496)
Added link to the 3blue1brown very relevant "essence of calculus" youtube playlist - following the contrib guidelines and the style in the "Linear algebra" content in the roadmap
2025-04-19 00:57:44 +06:00
Silicon27
85b9eaa9f7 feat(topic): add virtual functions topic content (#8497) 2025-04-19 00:55:48 +06:00
Frank
5831588cd8 fix(topic): incorrect resource link (#8502)
**Fix the incorrect link to the Flutter documentation.**  
The current link points to the documentation for the `OutlineButton` widget, but it should link to the documentation about the Flutter widget tree, since the topic is about the hierarchy of widgets, NOT the `OutlineButton`.
2025-04-19 00:50:27 +06:00
Bryan Wilches
cd33b7aa92 feat(topic): linux file system (#8508)
* Update directory-hierarchy.md

Adding a youtube link about the Linux file system

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

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
2025-04-19 00:47:47 +06:00
Bryan Wilches
6193eaf176 feat: replace with a mini exercise (#8509)
Adding a mini exercise about bash scripting
2025-04-18 23:10:17 +06:00
Arik Chakma
f279aea1cb fix: pagination number (#8504) 2025-04-17 17:25:00 +06:00
Kamran Ahmed
8b69b266d5 Fix flicker of paid plan 2025-04-17 11:41:41 +01:00
Kamran Ahmed
3f0db1526d Add upgrade button on ai page 2025-04-17 10:57:35 +01:00
Kamran Ahmed
69d9dd23b2 Add upgrade button on ai page 2025-04-17 10:56:00 +01:00
github-actions[bot]
3e1bc34d4a chore: update roadmap content json (#8493)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-04-17 14:54:58 +06:00
Kamran Ahmed
dea689b068 Email login to trigger purchase 2025-04-17 09:31:37 +01:00
Kamran Ahmed
de237ec6fc Keep button loading when redirecting 2025-04-17 09:21:06 +01:00
Kamran Ahmed
5ec61cc32f Add AI vs data science guide 2025-04-14 20:08:57 +01:00
Kamran Ahmed
7bffc1004d Add new guide about software engineering 2025-04-14 20:05:25 +01:00
Kamran Ahmed
c06218910d Add new guide data science vs cyber security 2025-04-14 19:52:25 +01:00
Kamran Ahmed
130e381054 Remove testing from course demo 2025-04-14 18:14:47 +01:00
Kamran Ahmed
d5d38ee919 Add protip 2025-04-14 14:29:40 +01:00
Kamran Ahmed
6b7138b8d8 Add protip 2025-04-14 14:21:52 +01:00
Kamran Ahmed
242e40ddd8 Add protip 2025-04-14 14:18:54 +01:00
Kamran Ahmed
9ea70fcc97 Update placeholder 2025-04-14 14:08:37 +01:00
Kamran Ahmed
823c31eac4 AI Tutor - Explore page, sidebar better search (#8476)
* Add sidebar to ai-tutor

* wip

* wip

* Fix mistakes and refacctor

* AI landing page changes

* Update sidebar design

* wip

* wip

* Update AI tutor sidebar

* wip

* Add ai-course dropdown

* Update

* fix: ai chat window position

* Course explanation changes

* Update course

* Tutor sidebar changes

* Refactor staff picks and community

* Update UI for a course

* Improve pagination

* Implement pagination of ai tutor ai courses

* AI explore page with search

* Fix pagination of tutor

* Update tutor header design

* Responsiveness of AI

* Fork alert changes

* Responsiveness of actions

* Forking functionality changes

* Fork confirmation changes

* Add upgrade indicator in sidebar

* fix: ai course access

* fix: next lesson

* Add login to view functionality

* Add search to my picks

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
2025-04-14 10:54:38 +01:00
Kamran Ahmed
d4a1180c4d Add google tag manager 2025-04-11 14:28:00 +01:00
Kamran Ahmed
483c942338 Inline script 2025-04-11 13:04:41 +01:00
Kamran Ahmed
f28b018e99 Add varify 2025-04-11 12:58:35 +01:00
Sepand
c683db2757 Add resources for regression testing (#8482)
The QA roadmap does not have any resources for Regression Testing. I have added three resources for it.
2025-04-11 11:47:50 +01:00
Vedansh
6dd8f29bff Refactor spring boot roadmap (#8484) 2025-04-11 11:47:07 +01:00
Vedansh
671b59c0ac fix some topic content. (#8485) 2025-04-11 11:46:30 +01:00
sukalaper
1197a0fd6d doc: Proc Priorities Under Process Management (#8486) 2025-04-11 11:46:15 +01:00
web-svb
9ebb288f9b Remove broken link (#8489)
Removed obsolete link to outdated PostgreSQL 7.1 docs that no longer reflect current row behavior.
2025-04-11 11:45:40 +01:00
Kamran Ahmed
ca38c0cede Fix broken UI 2025-04-11 11:45:20 +01:00
Kamran Ahmed
ff7c981f2f Add loading delay 2025-04-10 18:32:19 +01:00
Kamran Ahmed
3455e6ef1c Add varify tracking 2025-04-10 18:17:11 +01:00
Kamran Ahmed
f7f409ca90 Add demo button 2025-04-10 18:12:05 +01:00
Kamran Ahmed
2538db4786 Implement course demo page (#8477)
* Add course demo button

* Read sample button on page

* GA event for buy button

* Add isTesting link
2025-04-10 17:55:18 +01:00
Kamran Ahmed
d5a8814add Handle incomplete 2025-04-10 12:46:07 +01:00
Kamran Ahmed
0cadde1092 Remove hubspot snippet 2025-04-09 19:28:17 +01:00
Kamran Ahmed
3f4bbef211 Treat expired as cancelled 2025-04-09 19:23:11 +01:00
Kamran Ahmed
715352eeab Add tracking code for hubspot 2025-04-09 11:22:39 +01:00
github-actions[bot]
e5e43de98a chore: update roadmap content json (#8471)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-04-08 18:18:53 +06:00
Kamran Ahmed
f085a226ba Fix broken best practices page 2025-04-08 09:21:01 +01:00
Kamran Ahmed
2e90823af4 Update premium pricing modal text 2025-04-07 20:24:43 +01:00
Kamran Ahmed
50df3eda0f Fix issue where in complete is shown as active subscription 2025-04-07 19:58:58 +01:00
Kamran Ahmed
69b0d7abb3 Improve course button visibility 2025-04-07 19:30:43 +01:00
Kamran Ahmed
c4af3c57f0 Add courses tab 2025-04-07 19:27:00 +01:00
Kamran Ahmed
2cee3a8859 Remove console.log 2025-04-07 17:11:46 +01:00
Kamran Ahmed
7f28a755dc Add 404 handling 2025-04-07 16:39:09 +01:00
Kamran Ahmed
a2e83e909e Fix pre-rendered 404 2025-04-07 16:28:09 +01:00
Kamran Ahmed
e4f53ed90e Fix path 2025-04-07 16:20:04 +01:00
Kamran Ahmed
5e836ab7a5 Fix path 2025-04-07 16:11:25 +01:00
Kamran Ahmed
9851978dbd Add debug info 2025-04-07 16:05:17 +01:00
Kamran Ahmed
82c52aca7e chore: upgrade dependencies (#8468)
* Upgrade paths

* Update topic rendering

* Fix file names

* Remove courses file
2025-04-07 15:52:48 +01:00
Kamran Ahmed
0d62847053 Add courses functionality 2025-04-07 13:49:43 +01:00
Kamran Ahmed
7a00234f9a Add courses tag 2025-04-07 13:49:43 +01:00
Kamran Ahmed
64a65fa2e9 Migrate to Tailwind 4 + Editor Upgrade (#8465)
* wip

* fix: roadmap editor

* fix: padding

* wip

* fix: remove editor package

* wip

* fix: update pnpm lock

* Add contribution docs

* UI changes for TW4

* Update deployment workflow

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
2025-04-07 12:53:25 +01:00
jj
09d8c709d4 Fix SQL roadmap content (#8459)
- fix typo in query-optimization@Ps9Yv2s-bKvEegGAbPsiA.md
- fix content in reducing-subqueries@UVTgbZrqpbYl1bQvQejcF.md - the existing content is duplicate with recursive-queries
2025-04-06 23:13:58 +01:00
github-actions[bot]
6a14170e64 chore: update roadmap content json (#8455)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-04-06 00:37:01 +06:00
Kamran Ahmed
ac3ebb2162 Update editor 2025-04-05 12:45:24 +01:00
Kamran Ahmed
56ea91b11c Update generate-renderer 2025-04-05 00:42:03 +01:00
Kamran Ahmed
5a1f52892e Add generate-renderer and dummy renderer 2025-04-05 00:36:58 +01:00
Kamran Ahmed
74781d6e7b Add generate-renderer and dummy renderer 2025-04-05 00:28:41 +01:00
Kamran Ahmed
06bdfc42d2 Update deployment flow 2025-04-05 00:12:50 +01:00
Kamran Ahmed
0a42ea6f41 Add dummy generate renderer 2025-04-04 23:42:08 +01:00
Kamran Ahmed
2dc4041228 Add editor 2025-04-04 23:41:05 +01:00
Kamran Ahmed
4b7eab66da Make deployment script accept pat 2025-04-04 21:16:00 +01:00
Kamran Ahmed
999f6b09a8 Make deployment script accept pat 2025-04-04 21:15:13 +01:00
Kamran Ahmed
a9cd557dd3 Make deployment script accept pat 2025-04-04 21:11:47 +01:00
Kamran Ahmed
3d3423f8e5 Make deployment script accept pat 2025-04-04 21:07:40 +01:00
Kamran Ahmed
a5eb5231cb Make deployment script accept pat 2025-04-04 21:03:12 +01:00
Kamran Ahmed
8662416c96 Make deployment script accept pat 2025-04-04 20:59:44 +01:00
Kamran Ahmed
7564895d7a Make deployment script accept pat 2025-04-04 20:55:35 +01:00
Kamran Ahmed
7b15ed39a3 Make deployment script accept pat 2025-04-04 20:54:24 +01:00
Kamran Ahmed
e72622f2b2 Make deployment script accept pat 2025-04-04 20:52:20 +01:00
Kamran Ahmed
deb9aaafc2 Migrate roadmaps 2025-04-04 20:46:39 +01:00
Kamran Ahmed
63b6d471a2 Update generate-renderer 2025-04-04 20:41:35 +01:00
Arik Chakma
2485b716dd feat: xyflow upgrade (#7803)
* wip

* fix: reset the sizes

* fix: update zustand

* fix: update

* fix: add additional width

* wip

* fix: remove hacky code

* wip

* wip

* wip

* wip

* wip

* fix: try pre-commit

* fix: add check pre-commit

* fix: remove xyflow

* fix: remove unnecessary files

* fix: update packages

* Update scripts/generate-renderer.sh

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2025-04-04 20:33:01 +01:00
Kamran Ahmed
dc2142dde0 Default path to be considered home 2025-04-04 11:38:18 +01:00
Kamran Ahmed
d8466634a1 Fix: Lesson generation fails 2025-04-04 11:12:34 +01:00
Kamran Ahmed
7900130f1c Set last page to home if not exists 2025-04-04 10:21:09 +01:00
Sarvar
13c4aee26a Add resources for minimal APIs (#8446) 2025-04-03 23:12:29 +01:00
Instinct
294e76b52d Fix broken resource link (#8447)
the [previous link](https://platform.openai.com/docs/guides/text-generation/managing-context-for-text-generation) directed to a 404 page. new link corrects this
2025-04-03 23:12:06 +01:00
Kamran Ahmed
d6e9602c0b Store page where user signed up from 2025-04-03 23:11:06 +01:00
Kamran Ahmed
b84e75a0d4 Update data scientist roadmap title 2025-04-03 19:37:47 +01:00
Kamran Ahmed
81bb0bac83 Remove new badge from projects 2025-04-03 18:16:21 +01:00
Kamran Ahmed
d2d7e54c74 Put SQL roadmap first 2025-04-03 18:03:11 +01:00
Kamran Ahmed
8a70a944e6 Add sql course in burger menu 2025-04-03 17:58:08 +01:00
Kamran Ahmed
175ac4d097 Setup a /ai-tutor to /ai redirect 2025-04-03 17:42:18 +01:00
Kamran Ahmed
b75d36a8c0 Changes to the ai tutor URL 2025-04-03 17:41:46 +01:00
Pavan Kumar Balijepalli
0d9374285b Fix md syntax issue (#8452) 2025-04-03 15:51:51 +01:00
Arik Chakma
f798035f0f feat: remove ai roadmap limit for pro user (#8442) 2025-04-03 15:50:22 +01:00
Kalvin Chakma
7a988cbbea fix: label typo (#8454) 2025-04-03 15:48:07 +01:00
Kamran Ahmed
b576ad5f86 Fix typo in file name 2025-04-03 15:47:20 +01:00
Kamran Ahmed
2124ce96dc Add changelog entry 2025-04-03 15:44:17 +01:00
Kamran Ahmed
27cbdd18fd Add full stack roadmap FAQs 2025-04-03 14:52:04 +01:00
Kamran Ahmed
ef242e8747 Add Java FAQs 2025-04-03 13:54:23 +01:00
Kamran Ahmed
614429283f Add java title faq 2025-04-03 13:44:36 +01:00
Kamran Ahmed
b4e6336732 Add data analyst FAQs 2025-04-03 13:34:59 +01:00
Kamran Ahmed
35aa2d07f0 Add missing FAQs to ai roadmap 2025-04-03 13:05:16 +01:00
Kamran Ahmed
d570618d32 Fix golang title question 2025-04-03 12:48:24 +01:00
Kamran Ahmed
1a5b6976c2 Wiggle animation on the course banner 2025-04-01 18:09:39 +01:00
Kamran Ahmed
45059998c0 Add support for replacing TNS partner banner 2025-04-01 15:05:28 +01:00
Kamran Ahmed
f3eed4d010 Update og image 2025-04-01 14:48:08 +01:00
Kamran Ahmed
0f1e1962db Update announcement banner 2025-04-01 12:24:11 +01:00
Kamran Ahmed
84de2a7c9d Update title of course 2025-04-01 12:22:14 +01:00
Arik Chakma
dfd54b35b0 feat: ai course chat (#8426)
* feat: ai course chat

* wip: remove old code

* wip

* feat: responsiveness of ai chat

* fix: key warning

* feat: make chat resizeable

* wip

* wip: default questions

* wip

* fix: fixed position

* fix: hide button

* Fix scroll issue

* Improve questions UI

* Refactor UI

* Add close icon

* Update UI for course chat

* Close AI chat question

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2025-04-01 12:09:14 +01:00
github-actions[bot]
981af58fa9 chore: update roadmap content json (#8441)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-03-31 17:00:47 +06:00
sickpoitew
e5802eaeaf feat: update resource video (#8431)
Replace Git & GitHub Crash Course For Beginner with Git & GitHub Crash Course For Beginner 2025 made by the same guy
2025-03-30 16:50:28 +06:00
github-actions[bot]
64fd0e28aa chore: update roadmap content json (#8427)
Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com>
2025-03-30 16:48:05 +06:00
Kamran Ahmed
0a442493f1 Update sticky course banner 2025-03-28 19:54:54 +00:00
Kamran Ahmed
77b4e78348 Update sticky course banner 2025-03-28 19:52:27 +00:00
Kamran Ahmed
56c9faabe8 Add ai tutor inside ai roadmap topic 2025-03-28 18:15:20 +00:00
Kamran Ahmed
a68ed2e0b8 Remove terms of sale link 2025-03-27 21:21:04 +00:00
2441 changed files with 147882 additions and 23889 deletions

View File

@@ -3,6 +3,6 @@
"enabled": false
},
"_variables": {
"lastUpdateCheck": 1742812122664
"lastUpdateCheck": 1745231680828
}
}

View File

@@ -2,7 +2,7 @@ name: Clears API Cloudfront Cache
on:
workflow_dispatch:
jobs:
aws_costs:
cloudfront_api_cache:
runs-on: ubuntu-latest
steps:
- name: Clear Cloudfront Caching

View File

@@ -2,7 +2,7 @@ name: Clears Frontend Cloudfront Cache
on:
workflow_dispatch:
jobs:
aws_costs:
cloudfront_fe_cache:
runs-on: ubuntu-latest
steps:
- name: Clear Cloudfront Caching

View File

@@ -1,23 +0,0 @@
name: ❤️ Greetings
on:
issues:
types: [opened]
pull_request_target:
branches: [master]
types: [opened]
jobs:
greet:
name: Greet New Contributors
runs-on: ubuntu-latest
steps:
- uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: |
Thank you for your first ever contribution to [roadmap.sh](https://roadmap.sh)! 🎉
Please make sure to follow the [contribution guidelines](https://github.com/kamranahmedse/developer-roadmap/blob/master/contributing.md) when contributing to this project. Any PRs that don't follow the guidelines will be closed.
Thanks for choosing to contribute, and for helping make this project better! 🌟

7
.gitignore vendored
View File

@@ -28,9 +28,6 @@ pnpm-debug.log*
/playwright-report/
/playwright/.cache/
tests-examples
*.csv
*.csveditor/
/editor/*
!/editor/readonly-editor.tsx
!/editor/renderer/renderer.ts
!/editor/renderer/index.tsx
packages/editor

View File

@@ -1,10 +1,10 @@
// https://astro.build/config
import sitemap from '@astrojs/sitemap';
import tailwind from '@astrojs/tailwind';
import node from '@astrojs/node';
import { defineConfig } from 'astro/config';
import rehypeExternalLinks from 'rehype-external-links';
import { serializeSitemap, shouldIndexPage } from './sitemap.mjs';
import tailwindcss from '@tailwindcss/vite';
import react from '@astrojs/react';
@@ -16,6 +16,10 @@ export default defineConfig({
status: 301,
destination: '/devops',
},
'/ai-tutor': {
status: 301,
destination: '/ai',
},
},
vite: {
server: {
@@ -51,21 +55,22 @@ export default defineConfig({
],
],
},
output: 'hybrid',
output: 'server',
adapter: node({
mode: 'standalone',
}),
trailingSlash: 'never',
integrations: [
tailwind({
config: {
applyBaseStyles: false,
},
}),
sitemap({
filter: shouldIndexPage,
serialize: serializeSitemap,
}),
react(),
],
vite: {
plugins: [tailwindcss()],
ssr: {
noExternal: [/^@roadmapsh\/editor.*$/],
},
},
});

View File

@@ -79,6 +79,7 @@ Visit the following resources to learn more:
- `@course@`
- `@podcast@`
- `@video@`
- `@book@`
It's important to add a valid type, this will help us categorize the content and display it properly on the roadmap. The order of the links based on type is same as above.
@@ -125,6 +126,22 @@ It's important to add a valid type, this will help us categorize the content and
- PR's that don't follow our style guide, have no description, and a default title.
- Links to your own blog articles.
## Local Development
For local development, you can use the following commands:
```bash
git clone git@github.com:kamranahmedse/developer-roadmap.git --depth 1
cd developer-roadmap
pnpm add @roadmapsh/editor@npm:@roadmapsh/dummy-editor -w
pnpm install
```
Run the development server with:
```bash
pnpm dev
```
***
Have a look at the [License](./license) file.

View File

@@ -1,14 +0,0 @@
export function ReadonlyEditor(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

@@ -1,14 +0,0 @@
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

@@ -1,5 +0,0 @@
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

@@ -20,90 +20,94 @@
"editor-roadmap-content": "tsx scripts/editor-roadmap-content.ts",
"roadmap-content": "node scripts/roadmap-content.cjs",
"generate-renderer": "sh scripts/generate-renderer.sh",
"generate-renderer-dummy": "sh scripts/generate-renderer-dummy.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",
"generate:roadmap-content-json": "tsx ./scripts/editor-roadmap-content-json.ts",
"migrate:editor-roadmaps": "tsx ./scripts/migrate-editor-roadmap.ts",
"test:e2e": "playwright test"
},
"dependencies": {
"@astrojs/node": "^8.3.4",
"@astrojs/react": "^3.6.2",
"@astrojs/sitemap": "^3.2.0",
"@astrojs/tailwind": "^5.1.2",
"@fingerprintjs/fingerprintjs": "^4.5.0",
"@astrojs/node": "^9.2.0",
"@astrojs/react": "^4.2.4",
"@astrojs/sitemap": "^3.3.0",
"@fingerprintjs/fingerprintjs": "^4.6.2",
"@microsoft/clarity": "^1.0.0",
"@nanostores/react": "^0.8.0",
"@nanostores/react": "^1.0.0",
"@napi-rs/image": "^1.9.2",
"@resvg/resvg-js": "^2.6.2",
"@tanstack/react-query": "^5.59.16",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"astro": "^4.16.1",
"@roadmapsh/editor": "workspace:*",
"@tailwindcss/vite": "^4.1.4",
"@tanstack/react-query": "^5.74.4",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"astro": "^5.7.4",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"dom-to-image": "^2.6.0",
"dracula-prism": "^2.1.16",
"gray-matter": "^4.0.3",
"htm": "^3.1.1",
"image-size": "^1.1.1",
"jose": "^5.9.4",
"image-size": "^2.0.2",
"jose": "^6.0.10",
"js-cookie": "^3.0.5",
"lucide-react": "^0.452.0",
"luxon": "^3.5.0",
"markdown-it-async": "^2.0.0",
"nanoid": "^5.0.7",
"nanostores": "^0.11.3",
"node-html-parser": "^6.1.13",
"npm-check-updates": "^17.1.3",
"playwright": "^1.48.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-textarea-autosize": "^8.5.7",
"react-tooltip": "^5.28.0",
"reactflow": "^11.11.4",
"lucide-react": "^0.503.0",
"luxon": "^3.6.1",
"markdown-it-async": "^2.2.0",
"nanoid": "^5.1.5",
"nanostores": "^1.0.1",
"node-html-parser": "^7.0.1",
"npm-check-updates": "^18.0.0",
"playwright": "^1.52.0",
"prismjs": "^1.30.0",
"react": "^19.1.0",
"react-calendar-heatmap": "^1.10.0",
"react-confetti": "^6.4.0",
"react-dom": "^19.1.0",
"react-resizable-panels": "^2.1.8",
"react-textarea-autosize": "^8.5.9",
"react-tooltip": "^5.28.1",
"rehype-external-links": "^3.0.0",
"remark-parse": "^11.0.0",
"roadmap-renderer": "^1.0.6",
"sanitize-html": "^2.13.1",
"satori": "^0.11.2",
"roadmap-renderer": "^1.0.7",
"sanitize-html": "^2.16.0",
"satori": "^0.12.2",
"satori-html": "^0.3.2",
"sharp": "^0.33.5",
"shiki": "^3.1.0",
"sharp": "^0.34.1",
"shiki": "^3.2.2",
"slugify": "^1.6.6",
"tailwind-merge": "^2.5.3",
"tailwindcss": "^3.4.13",
"tailwind-merge": "^3.2.0",
"tailwindcss": "^4.1.4",
"tiptap-markdown": "^0.8.10",
"turndown": "^7.2.0",
"unified": "^11.0.5",
"zustand": "^4.5.5"
"zustand": "^5.0.3"
},
"devDependencies": {
"@ai-sdk/google": "^1.1.19",
"@playwright/test": "^1.48.0",
"@tailwindcss/typography": "^0.5.15",
"@ai-sdk/google": "^1.2.12",
"@playwright/test": "^1.52.0",
"@tailwindcss/typography": "^0.5.16",
"@types/dom-to-image": "^2.6.7",
"@types/js-cookie": "^3.0.6",
"@types/luxon": "^3.4.2",
"@types/prismjs": "^1.26.4",
"@types/react-calendar-heatmap": "^1.6.7",
"@types/luxon": "^3.6.2",
"@types/markdown-it": "^14.1.2",
"@types/prismjs": "^1.26.5",
"@types/react-calendar-heatmap": "^1.9.0",
"@types/react-slick": "^0.23.13",
"@types/sanitize-html": "^2.13.0",
"@types/sanitize-html": "^2.15.0",
"@types/turndown": "^5.0.5",
"ai": "^4.1.51",
"csv-parser": "^3.0.0",
"gh-pages": "^6.2.0",
"ai": "^4.3.9",
"csv-parser": "^3.2.0",
"gh-pages": "^6.3.0",
"js-yaml": "^4.1.0",
"markdown-it": "^14.1.0",
"openai": "^4.67.3",
"prettier": "^3.3.3",
"openai": "^4.95.1",
"prettier": "^3.5.3",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.6.8",
"tsx": "^4.19.1"
"prettier-plugin-tailwindcss": "^0.6.11",
"tsx": "^4.19.3"
}
}

0
packages/.gitkeep Normal file
View File

4379
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

2
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,2 @@
packages:
- packages/*

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -49,6 +49,11 @@
"title": "Algebra and Differential Calculus for Data Science",
"url": "https://imp.i384100.net/LX5M7M",
"type": "article"
},
{
"title": "Calculus Youtube Course",
"url": "https://www.youtube.com/playlist?list=PLZHQObOWTQDMsr9K-rj53DwVRMYO3t5Yr",
"type": "video"
}
]
},
@@ -106,7 +111,7 @@
},
{
"title": "Step by Step Process for Planning an A/B Test",
"url": "https://towardsdatascience.com/step-by-step-for-planning-an-a-b-test-ef3c93143c0b",
"url": "https://medium.com/data-science/step-by-step-for-planning-an-a-b-test-ef3c93143c0b",
"type": "article"
},
{

View File

@@ -328,7 +328,7 @@
"links": [
{
"title": "Managing Context",
"url": "https://platform.openai.com/docs/guides/text-generation/managing-context-for-text-generation",
"url": "https://platform.openai.com/docs/guides/conversation-state?api-mode=responses#managing-context-for-text-generation",
"type": "article"
},
{
@@ -1265,7 +1265,7 @@
"links": [
{
"title": "Supabase Vector",
"url": "https://supabase.com/vector",
"url": "https://supabase.com/docs/guides/ai",
"type": "article"
},
{
@@ -1685,7 +1685,7 @@
"links": [
{
"title": "Low or High Fidelity Image Understanding - OpenAI",
"url": "https://platform.openai.com/docs/guides/vision/low-or-high-fidelity-image-understanding",
"url": "https://platform.openai.com/docs/guides/images",
"type": "article"
}
]

File diff suppressed because it is too large Load Diff

View File

@@ -594,8 +594,19 @@
},
"ipABerBcM9zCte9pYaIse": {
"title": "Minimal APIs",
"description": "",
"links": []
"description": "Minimal APIs is a lightweight approach to building HTTP APIs in .NET with minimal ceremony. It is designed for simplicity and performance, making it ideal for microservices, serverless applications, and small web services. Minimal APIs provide a streamlined way to define routes, handle requests, and return responses without requiring controllers or extensive configuration. They leverage top-level statements, reducing boilerplate code while maintaining flexibility and scalability.\n\nMinimal APIs support dependency injection, middleware, model binding, and validation. They also integrate seamlessly with OpenAPI (Swagger) for API documentation. Their simplicity makes them an excellent choice for building fast and efficient web applications with .NET.\n\nTo learn more, visit the following resources:",
"links": [
{
"title": "Minimal APIs in .NET 8: A Simplified Approach to Build Services",
"url": "https://medium.com/codenx/minimal-apis-in-net-8-a-simplified-approach-to-build-services-eb50df56819f",
"type": "article"
},
{
"title": "Introduction to ASP.NET Core Minimal APIs",
"url": "https://blog.jetbrains.com/dotnet/2023/04/25/introduction-to-asp-net-core-minimal-apis/",
"type": "article"
}
]
},
"POQPoN98eqOH2873ZI6Hm": {
"title": "Object Relational Mapping",

View File

@@ -3,6 +3,11 @@
"title": "Pick a Language",
"description": "You need to pick a programming language to learn the Computer Science concepts. My personal recommendation would be to pick C++ or C and the reason for that is:\n\n* They allow you to deal with pointers and memory allocation/deallocation, so you feel the data structures and algorithms in your bones. In higher level languages like Python or Java, these are hidden from you. In day to day work, that's terrific, but when you're learning how these low-level data structures are built, it's great to feel close to the metal.\n* You will be able to find a lot of resources for the topics listed in this roadmap using C or C++. You can find a lot of resources for Python and Java, but they are not as abundant as C++ and C.\n\nGiven below is the list of resources; pick ones relevant to the language of your choice.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Visit Dedicated C++ Roadmap",
"url": "https://roadmap.sh/cpp",
"type": "article"
},
{
"title": "Learn C++ - W3Schools",
"url": "https://www.w3schools.com/cpp/",
@@ -126,7 +131,7 @@
"type": "article"
},
{
"title": "W3Schools Go Tutorial ",
"title": "W3Schools Go Tutorial",
"url": "https://www.w3schools.com/go/",
"type": "article"
},
@@ -219,7 +224,7 @@
"description": "C++ is a powerful general-purpose programming language. It can be used to develop operating systems, browsers, games, and so on. C++ supports different ways of programming like procedural, object-oriented, functional, and so on. This makes C++ powerful as well as flexible.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "C++ Roadmap",
"title": "Visit Dedicated C++ Roadmap",
"url": "https://roadmap.sh/cpp",
"type": "article"
},
@@ -301,7 +306,7 @@
"type": "article"
},
{
"title": "Java Website",
"title": "Java",
"url": "https://www.java.com/",
"type": "article"
},
@@ -455,6 +460,11 @@
"title": "Hash Table",
"description": "Hash Table, Map, HashMap, Dictionary or Associative are all the names of the same data structure. It is one of the most commonly used data structures.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Hash Tables - Princeton University",
"url": "https://algs4.cs.princeton.edu/34hash/",
"type": "article"
},
{
"title": "Hash Table | Illustrated Data Structures",
"url": "https://www.youtube.com/watch?v=jalSiaIi8j4",
@@ -470,26 +480,6 @@
"url": "https://www.youtube.com/watch?v=0M_kIqhwbFo&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=9",
"type": "video"
},
{
"title": "Table Doubling, Karp-Rabin",
"url": "https://www.youtube.com/watch?v=BRO7mVIFt08&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=10",
"type": "video"
},
{
"title": "Open Addressing, Cryptographic Hashing",
"url": "https://www.youtube.com/watch?v=rvdJDijO2Ro&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=11",
"type": "video"
},
{
"title": "PyCon 2010: The Mighty Dictionary",
"url": "https://www.youtube.com/watch?v=C4Kc8xzcA68",
"type": "video"
},
{
"title": "PyCon 2017: The Dictionary Even Mightier",
"url": "https://www.youtube.com/watch?v=66P5FMkWoVU",
"type": "video"
},
{
"title": "(Advanced) Randomization: Universal & Perfect Hashing",
"url": "https://www.youtube.com/watch?v=z0lJ2k0sl1g&list=PLUl4u3cNGP6317WaSNfmCvGym2ucw3oGp&index=11",
@@ -512,9 +502,9 @@
"type": "course"
},
{
"title": "Dynamic Arrays - Coursera",
"url": "https://www.coursera.org/lecture/data-structures/dynamic-arrays-EwbnV",
"type": "course"
"title": "What is Array in Data Structure? Types & Syntax",
"url": "https://www.simplilearn.com/tutorials/data-structure-tutorial/arrays-in-data-structure",
"type": "article"
},
{
"title": "Array Data Structure | Illustrated Data Structures",
@@ -522,8 +512,8 @@
"type": "video"
},
{
"title": "UC Berkeley CS61B - Linear and Multi-Dim Arrays (Start watching from 15m 32s)",
"url": "https://archive.org/details/ucberkeley_webcast_Wp8oiO_CZZE",
"title": "Jagged Arrays",
"url": "https://www.youtube.com/watch?v=1jtrQqYpt7g",
"type": "video"
},
{
@@ -537,8 +527,8 @@
"type": "video"
},
{
"title": "Jagged Arrays",
"url": "https://www.youtube.com/watch?v=1jtrQqYpt7g",
"title": "UC Berkeley CS61B - Linear and Multi-Dim Arrays (Start watching from 15m 32s)",
"url": "https://archive.org/details/ucberkeley_webcast_Wp8oiO_CZZE",
"type": "video"
}
]
@@ -547,6 +537,11 @@
"title": "Tree",
"description": "A tree is non-linear and a hierarchical data structure consisting of a collection of nodes such that each node of the tree stores a value and a list of references to other nodes (the “children”).\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Tree Data Structure",
"url": "https://www.programiz.com/dsa/trees",
"type": "article"
},
{
"title": "Tree | Illustrated Data Structures",
"url": "https://www.youtube.com/watch?v=S2W3SXGPVyU",
@@ -674,8 +669,13 @@
"description": "An unbalanced binary tree is one that is not balanced.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Balanced Binary Tree",
"url": "https://www.programiz.com/dsa/balanced-binary-tree",
"title": "Balanced vs Unbalanced Binary Tree",
"url": "https://stackoverflow.com/questions/59206128/balanced-vs-unbalanced-binary-tree-clarification-needed",
"type": "article"
},
{
"title": "Unbalanced Binary Tree",
"url": "https://eng.libretexts.org/Bookshelves/Computer_Science/Databases_and_Data_Structures/Open_Data_Structures_-_An_Introduction_(Morin)/06%3A_Binary_Trees/6.02%3A_BinarySearchTree_-_An_Unbalanced_Binary_Search_Treee",
"type": "article"
}
]
@@ -815,6 +815,11 @@
"url": "https://www.coursera.org/lecture/data-structures/introduction-2OpTs",
"type": "course"
},
{
"title": "Heap Data Structure",
"url": "https://www.programiz.com/dsa/heap-data-structure",
"type": "article"
},
{
"title": "CS 61B Lecture 24: Priority Queues",
"url": "https://archive.org/details/ucberkeley_webcast_yIUFT6AKBGE",
@@ -878,7 +883,12 @@
"description": "The Big O notation can be used to describe how the running time of an algorithm scales with the growth of the input size, ignoring implementation details such as programming language and computer speed. Specifically, it denotes the upper bound of the growth rate of a function that relates the running time of an algorithm to its input size. It can be used to compare algorithms and determine which one is better.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "moviesCS 61B Lecture 19: Asymptotic Analysis",
"title": "Big-O Notation: A Simple Explanation with Examples",
"url": "https://medium.com/better-programming/big-o-notation-a-simple-explanation-with-examples-a56347d1daca",
"type": "article"
},
{
"title": "CS 61B Lecture 19: Asymptotic Analysis",
"url": "https://archive.org/details/ucberkeley_webcast_VIS4YDpuP98",
"type": "article"
},
@@ -982,6 +992,11 @@
"title": "Linear",
"description": "Linear algorithms are algorithms that have a runtime that is directly proportional to the size of the input. This means that the runtime of the algorithm will increase linearly with the size of the input. For example, if the input size is 10, the runtime will be 10 times the runtime of the algorithm when the input size is 1. If the input size is 100, the runtime will be 100 times the runtime of the algorithm when the input size is 1.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Linear Search Algorithm",
"url": "https://www.geeksforgeeks.org/linear-search/",
"type": "article"
},
{
"title": "Big O Notation — Calculating Time Complexity",
"url": "https://www.youtube.com/watch?v=Z0bH0cMY0E8",
@@ -1028,8 +1043,19 @@
},
"m0umGQNdvg95UiNpQZsQN": {
"title": "Factorial",
"description": "Factorial complexity algorithms have a runtime of `O(n!)`. This is the worst case scenario for an algorithm. Factorial complexity algorithms are very inefficient and should be avoided.\n\n def generate_permutations(s):\n # Base case: If the string length is 1, return a list containing the string\n if len(s) == 1:\n return [s]\n \n # Initialize the result list\n permutations = []\n \n # Recursively generate all permutations\n for i in range(len(s)):\n # Current character\n current_char = s[i]\n # Remaining characters\n remaining_chars = s[:i] + s[i + 1 :]\n # Generate all permutations of the remaining characters\n for perm in generate_permutations(remaining_chars):\n # Add the current character to the front of each generated permutation\n permutations.append(current_char + perm)\n \n return permutations",
"links": []
"description": "Factorial complexity algorithms have a runtime of `O(n!)`. This is the worst case scenario for an algorithm. Factorial complexity algorithms are very inefficient and should be avoided.\n\n def generate_permutations(s):\n # Base case: If the string length is 1, return a list containing the string\n if len(s) == 1:\n return [s]\n \n # Initialize the result list\n permutations = []\n \n # Recursively generate all permutations\n for i in range(len(s)):\n # Current character\n current_char = s[i]\n # Remaining characters\n remaining_chars = s[:i] + s[i + 1 :]\n # Generate all permutations of the remaining characters\n for perm in generate_permutations(remaining_chars):\n # Add the current character to the front of each generated permutation\n permutations.append(current_char + perm)\n \n return permutations\n \n\nVisit the following resources to learn more:",
"links": [
{
"title": "Big O Cheat Sheet - Time Complexity Chart",
"url": "https://www.freecodecamp.org/news/big-o-cheat-sheet-time-complexity-chart/",
"type": "article"
},
{
"title": "Factorial Explained",
"url": "https://www.youtube.com/watch?v=pxh__ugRKz8",
"type": "video"
}
]
},
"7a6-AnBI-3tAU1dkOvPkx": {
"title": "Common Algorithms",
@@ -1203,6 +1229,11 @@
"url": "https://www.coursera.org/lecture/algorithms-part1/selection-UQxFT",
"type": "course"
},
{
"title": "Selection Sort",
"url": "https://en.wikipedia.org/wiki/Selection_sort",
"type": "article"
},
{
"title": "Selection Sort in 3 Minutes",
"url": "https://www.youtube.com/watch?v=g-PGLbMth_g",
@@ -1215,7 +1246,7 @@
"description": "Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time by comparisons. It is much less efficient on large lists than more advanced algorithms such as quicksort, heapsort, or merge sort.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Insertion Sort Algorithm",
"title": "Insertion Sort",
"url": "https://www.programiz.com/dsa/insertion-sort",
"type": "article"
},
@@ -1324,6 +1355,11 @@
"title": "In-Order Traversal",
"description": "In-order traversal is a tree traversal algorithm that visits the left subtree, the root, and then the right subtree. This is the most common way to traverse a binary search tree. It is also used to create a sorted list of nodes in a binary search tree.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Tree Traversal Techniques",
"url": "https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/",
"type": "article"
},
{
"title": "Tree | Illustrated Data Structures",
"url": "https://www.youtube.com/watch?v=S2W3SXGPVyU",
@@ -1335,6 +1371,11 @@
"title": "Post Order Traversal",
"description": "Post-order traversal is a type of tree traversal that visits the left subtree, then the right subtree, and finally the root node. This is the opposite of pre-order traversal, which visits the root node first, then the left subtree, and finally the right subtree.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Postorder Traversal of Binary Tree",
"url": "https://www.geeksforgeeks.org/postorder-traversal-of-binary-tree/",
"type": "article"
},
{
"title": "Tree | Illustrated Data Structures",
"url": "https://www.youtube.com/watch?v=S2W3SXGPVyU",
@@ -1552,7 +1593,7 @@
"type": "article"
},
{
"title": "Knights Tour Proble",
"title": "Knights Tour Problem",
"url": "https://www.codesdope.com/course/algorithms-knights-tour-problem/",
"type": "article"
},
@@ -1708,6 +1749,11 @@
"title": "LFU Cache",
"description": "LFU Cache is a data structure that stores key-value pairs. It has a fixed size and when it is full, it removes the least frequently used key-value pair. It is a variation of the LRU Cache and is used in many applications such as caching web pages, caching database queries, and caching images.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Least Frequently Used (LFU) Cache Implementation",
"url": "https://www.geeksforgeeks.org/least-frequently-used-lfu-cache-implementation/",
"type": "article"
},
{
"title": "1117. Data Structure - LFU Cache",
"url": "https://jojozhuang.github.io/algorithm/data-structure-lfu-cache/",
@@ -1720,7 +1766,7 @@
"description": "String search and manipulation is a very important topic in computer science. It is used in many different applications, such as searching or replacing a specific pattern, word or character in a string.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "String-searching algorithm",
"title": "String-searching Algorithm",
"url": "https://en.wikipedia.org/wiki/String-searching_algorithm",
"type": "article"
}
@@ -1734,6 +1780,11 @@
"title": "Search Pattern in Text",
"url": "https://www.coursera.org/learn/data-structures/lecture/tAfHI/search-pattern-in-text",
"type": "course"
},
{
"title": "Pattern Searching",
"url": "https://www.geeksforgeeks.org/pattern-searching/",
"type": "article"
}
]
},
@@ -1747,7 +1798,12 @@
"type": "course"
},
{
"title": "Suffix array introduction",
"title": "Suffix Arrays - Princeton University",
"url": "https://algs4.cs.princeton.edu/63suffix/",
"type": "article"
},
{
"title": "Suffix Array Introduction",
"url": "https://www.youtube.com/watch?v=zqKlL3ZpTqs",
"type": "video"
},
@@ -1757,7 +1813,7 @@
"type": "video"
},
{
"title": "Suffix arrays: building",
"title": "Suffix Arrays: building",
"url": "https://www.youtube.com/watch?v=ZWlbhBjjwyA",
"type": "video"
}
@@ -1773,7 +1829,7 @@
"type": "course"
},
{
"title": "A beginner guide to Brute Force Algorithm for substring search",
"title": "A Beginner Guide to Brute Force Algorithm for Substring Search",
"url": "https://nulpointerexception.com/2019/02/10/a-beginner-guide-to-brute-force-algorithm-for-substring-search/",
"type": "article"
},
@@ -1808,6 +1864,11 @@
"title": "Boyer Moore Algorithm",
"url": "https://www.coursera.org/learn/algorithms-part2/lecture/CYxOT/boyer-moore",
"type": "course"
},
{
"title": "Boyer-Moore String-search Algorithm",
"url": "https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm",
"type": "article"
}
]
},
@@ -1921,7 +1982,7 @@
"description": "Little Endian is a way of storing data in memory. It is the opposite of Big Endian. In Little Endian, the least significant byte is stored first. In Big Endian, the most significant byte is stored first.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Big Endian vs Little Endian.mp4",
"title": "Big Endian vs Little Endian",
"url": "https://www.youtube.com/watch?v=JrNF0KRAlyo",
"type": "video"
},
@@ -1936,6 +1997,11 @@
"title": "Common UML Diagrams",
"description": "UML is a standard way of visualizing a software system. It is a general-purpose, developmental, modeling language in the field of software engineering that is intended to provide a standard way to visualize the design of a system.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Unified Modeling Language (UML) Description",
"url": "https://www.uml-diagrams.org/",
"type": "article"
},
{
"title": "UML Diagrams Full Course (Unified Modeling Language)",
"url": "https://www.youtube.com/watch?v=WnMQ8HlmeXc",
@@ -2091,7 +2157,7 @@
"type": "opensource"
},
{
"title": "Design Patterns - Wikipedia",
"title": "Design Patterns",
"url": "https://en.wikipedia.org/wiki/Software_design_pattern",
"type": "article"
},
@@ -2314,7 +2380,7 @@
"description": "Combinatorics is the study of counting. It is a branch of mathematics that is used to solve problems in a variety of fields, including computer science, statistics, and physics. In computer science, combinatorics is used to solve problems related to counting the number of possible outcomes of a given problem. For example, if you are given a set of 10 objects, how many different ways can you arrange them? Or, if you are given a set of 10 objects, how many different ways can you choose 3 objects from that set? These are examples of combinatorial problems.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Probability and Combinatorics Topic",
"title": "Probability and Combinatorics",
"url": "https://www.khanacademy.org/math/probability/probability-and-combinatorics-topic",
"type": "article"
},
@@ -2368,21 +2434,6 @@
"title": "Greedy Algs. II & Intro to NP Completeness",
"url": "https://youtu.be/qcGnJ47Smlo?list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&t=2939",
"type": "video"
},
{
"title": "NP Completeness II & Reductions",
"url": "https://www.youtube.com/watch?v=e0tGC6ZQdQE&index=16&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
"type": "video"
},
{
"title": "NP Completeness III",
"url": "https://www.youtube.com/watch?v=fCX1BGT3wjE&index=17&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
"type": "video"
},
{
"title": "NP Completeness IV",
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
"type": "video"
}
]
},
@@ -2419,21 +2470,6 @@
"title": "Greedy Algs. II & Intro to NP Completeness",
"url": "https://youtu.be/qcGnJ47Smlo?list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&t=2939",
"type": "video"
},
{
"title": "NP Completeness II & Reductions",
"url": "https://www.youtube.com/watch?v=e0tGC6ZQdQE&index=16&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
"type": "video"
},
{
"title": "NP Completeness III",
"url": "https://www.youtube.com/watch?v=fCX1BGT3wjE&index=17&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
"type": "video"
},
{
"title": "NP Completeness IV",
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
"type": "video"
}
]
},
@@ -2558,21 +2594,6 @@
"url": "https://www.youtube.com/watch?v=YX40hbAHx3s",
"type": "video"
},
{
"title": "Complexity: Approximation Algorithms",
"url": "https://www.youtube.com/watch?v=MEz1J9wY2iM&list=PLUl4u3cNGP6317WaSNfmCvGym2ucw3oGp&index=24",
"type": "video"
},
{
"title": "Complexity: Fixed-Parameter Algorithms",
"url": "https://www.youtube.com/watch?v=4q-jmGrmxKs&index=25&list=PLUl4u3cNGP6317WaSNfmCvGym2ucw3oGp",
"type": "video"
},
{
"title": "Lecture 23: Computational Complexity",
"url": "https://www.youtube.com/watch?v=moPtwq_cVH8&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=24",
"type": "video"
},
{
"title": "Greedy Algs. II & Intro to NP Completeness",
"url": "https://youtu.be/qcGnJ47Smlo?list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&t=2939",
@@ -2610,6 +2631,11 @@
"title": "Travelling Salesman Problem",
"description": "The Travelling Salesman Problem (TSP) is a classic problem in computer science. It is a problem that is NP-complete, which means that it is a problem that is hard to solve. It is also a problem that is used to test the efficiency of algorithms.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Traveling Salesman Problem",
"url": "https://en.wikipedia.org/wiki/Travelling_salesman_problem",
"type": "article"
},
{
"title": "What is the Traveling Salesman Problem?",
"url": "https://www.youtube.com/watch?v=1pmBjIZ20pE",
@@ -2715,7 +2741,7 @@
"description": "Balanced search trees are a type of data structure that allow for fast insertion, deletion, and lookup of data. They are a type of self-balancing binary search tree, which means that they are a binary tree that maintains the binary search tree property while also keeping the tree balanced. This means that the tree is always approximately balanced, which allows for fast insertion, deletion, and lookup of data.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Self-balancing binary search tree - Wikipedia",
"title": "Self-balancing Binary Search Tree - Wikipedia",
"url": "https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree",
"type": "article"
},
@@ -2750,6 +2776,11 @@
"url": "https://www.coursera.org/learn/data-structures/lecture/22BgE/split-and-merge",
"type": "course"
},
{
"title": "AVL Tree - Programiz",
"url": "https://www.programiz.com/dsa/avl-tree",
"type": "article"
},
{
"title": "MIT AVL Trees / AVL Sort",
"url": "https://www.youtube.com/watch?v=FNeL18KsWPc&list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&index=6",
@@ -2809,26 +2840,10 @@
}
]
},
"IaPd_zuLbiOCwoSHQLoIG": {
"2-3-4-trees@IaPd_zuLbiOCwoSHQLoIG.md": {
"title": "2 3 4 Trees",
"description": "In practice: For every 2-4 tree, there are corresponding redblack trees with data elements in the same order. The insertion and deletion operations on 2-4 trees are also equivalent to color-flipping and rotations in redblack trees. This makes 2-4 trees an important tool for understanding the logic behind redblack trees, and this is why many introductory algorithm texts introduce 2-4 trees just before redblack trees, even though 2-4 trees are not often used in practice.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "CS 61B Lecture 26: Balanced Search Trees",
"url": "https://archive.org/details/ucberkeley_webcast_zqrqYXkth6Q",
"type": "article"
},
{
"title": "Bottom Up 234-Trees",
"url": "https://www.youtube.com/watch?v=DQdMYevEyE4&index=4&list=PLA5Lqm4uh9Bbq-E0ZnqTIa8LRaL77ica6",
"type": "video"
},
{
"title": "Top Down 234-Trees",
"url": "https://www.youtube.com/watch?v=2679VQ26Fp4&list=PLA5Lqm4uh9Bbq-E0ZnqTIa8LRaL77ica6&index=5",
"type": "video"
}
]
"description": "",
"links": []
},
"UOYeM-hqIKCrB9hGez4Q_": {
"title": "K-ary / M-ary Tree",
@@ -2887,7 +2902,7 @@
"type": "opensource"
},
{
"title": "System Design: The complete course",
"title": "System Design: The Complete Course",
"url": "https://dev.to/karanpratapsingh/system-design-the-complete-course-10fo",
"type": "article"
},
@@ -2907,7 +2922,7 @@
"type": "video"
},
{
"title": "System design interview: Scale to 1 million users",
"title": "System Design interview: Scale to 1 million users",
"url": "https://www.youtube.com/watch?v=YkGHxOg9d3M",
"type": "video"
}
@@ -2938,6 +2953,11 @@
"title": "Load Balancing",
"description": "Load balancing is the process of distributing network or application traffic across a cluster of servers. Load balancing is used to improve responsiveness and reliability of applications, maximize throughput, minimize response time, and avoid overload of any single server.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "What is Load Balancing? | How load balancers work",
"url": "https://www.cloudflare.com/learning/performance/what-is-load-balancing/",
"type": "article"
},
{
"title": "Load Balancers 101",
"url": "https://www.youtube.com/watch?v=galcDRNd5Ow",
@@ -3003,13 +3023,13 @@
"description": "A proxy server is an intermediary piece of hardware/software sitting between the client and the backend server. It receives requests from clients and relays them to the origin servers. Typically, proxies are used to filter requests, log requests, or sometimes transform requests (by adding/removing headers, encrypting/decrypting, or compression).\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Proxy - System Design",
"url": "https://dev.to/karanpratapsingh/system-design-the-complete-course-10fo#proxy",
"title": "Proxy Servers",
"url": "https://roadmap.sh/guides/proxy-servers",
"type": "article"
},
{
"title": "Proxy Servers",
"url": "https://roadmap.sh/guides/proxy-servers",
"title": "Proxy - System Design",
"url": "https://dev.to/karanpratapsingh/system-design-the-complete-course-10fo#proxy",
"type": "article"
}
]
@@ -3019,7 +3039,7 @@
"description": "The CAP theorem states that it is impossible for a distributed data store to simultaneously provide more than two out of Consistency, Availability and Partition Tolerance.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "CAP Theorem - Wikipedia",
"title": "CAP Theorem",
"url": "https://en.wikipedia.org/wiki/CAP_theorem",
"type": "article"
},
@@ -3056,7 +3076,7 @@
"description": "Architectural patterns are the fundamental organization of a system, defining how the system is composed and how its components interact. Architectural patterns are identified by their name, like client-server, peer-to-peer, and layered.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "List of software architecture styles and patterns",
"title": "List of Software Architecture Styles and Patterns",
"url": "https://en.wikipedia.org/wiki/List_of_software_architecture_styles_and_patterns",
"type": "article"
}
@@ -3097,6 +3117,16 @@
"url": "https://roadmap.sh/graphql",
"type": "article"
},
{
"title": "GraphQL",
"url": "https://graphql.org/",
"type": "article"
},
{
"title": "GraphQL Documentation",
"url": "https://graphql.org/learn/",
"type": "article"
},
{
"title": "Apollo GraphQL Tutorials",
"url": "https://www.apollographql.com/tutorials/",
@@ -3397,8 +3427,19 @@
},
"q3nRhTYS5wg9tYnQe2sCF": {
"title": "BASE",
"description": "The rise in popularity of NoSQL databases provided a flexible and fluidity with ease to manipulate data and as a result, a new database model was designed, reflecting these properties. The acronym BASE is slightly more confusing than ACID but however, the words behind it suggest ways in which the BASE model is different and acronym BASE stands for:-\n\n* **B**asically **A**vailable\n* **S**oft state\n* **E**ventual consistency",
"links": []
"description": "The rise in popularity of NoSQL databases provided a flexible and fluidity with ease to manipulate data and as a result, a new database model was designed, reflecting these properties. The acronym BASE is slightly more confusing than ACID but however, the words behind it suggest ways in which the BASE model is different and acronym BASE stands for:-\n\n* **B**asically **A**vailable\n* **S**oft state\n* **E**ventual consistency\n\nVisit the following resources to learn more:",
"links": [
{
"title": "ACID vs. BASE Database Model",
"url": "https://phoenixnap.com/kb/acid-vs-base",
"type": "article"
},
{
"title": "What Is BASE in Database Engineering?",
"url": "https://www.lifewire.com/abandoning-acid-in-favor-of-base-1019674",
"type": "article"
}
]
},
"uqfeiQ9K--QkGNwks4kjk": {
"title": "CAP Theorem",
@@ -3446,6 +3487,11 @@
"title": "Indexes",
"description": "An index is a data structure that you build and assign on top of an existing table that basically looks through your table and tries to analyze and summarize so that it can create shortcuts.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Database Indexes Explained",
"url": "https://www.essentialsql.com/what-is-a-database-index/",
"type": "article"
},
{
"title": "Database Indexing Explained",
"url": "https://www.youtube.com/watch?v=-qNSXK7s7_w",
@@ -3477,6 +3523,11 @@
"title": "What are Transactions?",
"url": "https://fauna.com/blog/database-transaction",
"type": "article"
},
{
"title": "Database Transaction",
"url": "https://en.wikipedia.org/wiki/Database_transaction",
"type": "article"
}
]
},
@@ -3688,6 +3739,16 @@
"title": "TLS & HTTPS",
"description": "TLS (Transport Layer Security) is a cryptographic protocol that provides privacy and data integrity between two communicating applications. It is widely used to secure HTTP, although it can be used with any protocol. TLS is often used in combination with HTTPS, which is HTTP over TLS.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "What is TLS & How Does it Work? - Internet Society",
"url": "https://www.internetsociety.org/deploy360/tls/basics/",
"type": "article"
},
{
"title": "What is TLS (Transport Layer Security)? - Cloudflare",
"url": "https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/",
"type": "article"
},
{
"title": "SSL and HTTPS",
"url": "https://www.youtube.com/watch?v=S2iBR2ZlZf0",
@@ -3808,6 +3869,11 @@
"title": "Hashing Algorithms",
"description": "Hashing algorithms are used to generate a unique value for a given input. This value is called a hash. Hashing algorithms are used to verify the integrity of data, to store passwords, and to generate unique identifiers for data.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Hashing Algorithm Overview:",
"url": "https://www.okta.com/identity-101/hashing-algorithms/",
"type": "article"
},
{
"title": "Explore top posts about Algorithms",
"url": "https://app.daily.dev/tags/algorithms?ref=roadmapsh",
@@ -3866,12 +3932,12 @@
"description": "Computers are everywhere. They are in our phones, our cars, our homes, and even in our pockets. But how do they actually work? How do they take in information, and how do they output information?\n\nVisit the following resources to learn more:",
"links": [
{
"title": "How CPU executes a program",
"title": "How CPU Executes A Program",
"url": "https://www.youtube.com/watch?v=XM4lGflQFvA",
"type": "video"
},
{
"title": "How computers calculate - ALU",
"title": "How Computers Calculate - ALU",
"url": "https://youtu.be/1I5ZMmrOfnA",
"type": "video"
},
@@ -3913,7 +3979,12 @@
"description": "Computers calculate using the binary system, where all data is represented as 0s and 1s. These binary states correspond to the ON/OFF positions of transistors, which are the building blocks of logic gates (AND, OR, NOT). Numbers, characters, and instructions are broken into binary sequences (bits), and grouped into bytes (8 bits). Arithmetic operations like addition are performed through logic gates, which combine binary values. The CPU executes these calculations by following a fetch-decode-execute cycle. Complex calculations, such as handling decimals, use floating-point representation. Programs written in high-level languages are compiled into machine code for the CPU to execute.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "How computers calculate - ALU",
"title": "How Does A Computer Calculate Numbers?",
"url": "https://www.sciencing.com/computer-calculate-numbers-4705975/",
"type": "article"
},
{
"title": "How Computers Calculate - ALU",
"url": "https://youtu.be/1I5ZMmrOfnA",
"type": "video"
}
@@ -3921,8 +3992,13 @@
},
"U3379F4AO1KSmGtVmPr27": {
"title": "Registers and RAM",
"description": "**_Registers_** are the smallest data-holding elements built into the processor itself. Registers are the memory locations that are directly accessible by the processor. The registers hold the instruction or operands currently accessed by the CPU.\n\nRegisters are the high-speed accessible storage elements. The processor accesses the registers within one CPU clock cycle. The processor can decode the instructions and perform operations on the register contents at more than one operation per CPU clock cycle.\n\n**_Memory_** is a hardware device that stores computer programs, instructions, and data. The memory that is internal to the processor is primary memory (RAM), and the memory that is external to the processor is secondary (**Hard Drive**). Primary memory or RAM is a volatile memory, meaning the primary memory data exist when the system's power is on, and the data vanishes as the system is switched off. The primary memory contains the data required by the currently executing program in the CPU. If the data required by the processor is not in primary memory, then the data is transferred from secondary storage to primary memory, and then it is fetched by the processor.\n\nVisit the following resources to learn more:",
"description": "**_Registers_** are the smallest data-holding elements built into the processor itself. Registers are the memory locations that are directly accessible by the processor. The registers hold the instruction or operands currently accessed by the CPU.\n\nRegisters are the high-speed accessible storage elements. The processor accesses the registers within one CPU clock cycle. The processor can decode the instructions and perform operations on the register contents at more than one operation per CPU clock cycle.\n\n**_Memory_** is a hardware device that stores computer programs, instructions, and data. The memory that is internal to the processor is primary memory (RAM), and the memory that is external to the processor is secondary (**Hard Drive**).\n\nVisit the following resources to learn more:",
"links": [
{
"title": "RAM vs. Registers - What's the Difference?",
"url": "https://thisvsthat.io/ram-vs-registers",
"type": "article"
},
{
"title": "Registers and RAM",
"url": "https://youtu.be/fpnE6UAfbtU",
@@ -3997,6 +4073,11 @@
"title": "Process Forking",
"description": "Process forking is a way to create a new process from an existing process. The new process is a copy of the existing process. The new process is called a child process and the existing process is called a parent process.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Fork System Call in Operating System",
"url": "https://www.geeksforgeeks.org/fork-system-call-in-operating-system/",
"type": "article"
},
{
"title": "Understanding fork() system call for new process creation",
"url": "https://www.youtube.com/watch?v=PwxTbksJ2fo",
@@ -4069,13 +4150,24 @@
},
"Ge2nagN86ofa2y-yYR1lv": {
"title": "Scheduling Algorithms",
"description": "CPU Scheduling is the process of selecting a process from the ready queue and allocating the CPU to it. The selection of a process is based on a particular scheduling algorithm. The scheduling algorithm is chosen depending on the type of system and the requirements of the processes.\n\nHere is the list of some of the most commonly used scheduling algorithms:\n\n* **First Come First Serve (FCFS):** The process that arrives first is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Job First (SJF):** The process with the smallest execution time is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Remaining Time First (SRTF):** The process with the smallest remaining execution time is allocated the CPU first. It is a preemptive algorithm.\n* **Round Robin (RR):** The process is allocated the CPU for a fixed time slice. The time slice is usually 10 milliseconds. It is a preemptive algorithm.\n* **Priority Scheduling:** The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Feedback Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. If a process is preempted, it is moved to the next queue. It is a preemptive algorithm.\n* **Highest Response Ratio Next(HRRN):** CPU is allotted to the next process which has the highest response ratio and not to the process having less burst time. It is a Non-Preemptive algorithm.\n* **Lottery Scheduling:** The process is allocated the CPU based on a lottery system. It is a preemptive algorithm.",
"links": []
"description": "CPU Scheduling is the process of selecting a process from the ready queue and allocating the CPU to it. The selection of a process is based on a particular scheduling algorithm. The scheduling algorithm is chosen depending on the type of system and the requirements of the processes.\n\nHere is the list of some of the most commonly used scheduling algorithms:\n\n* **First Come First Serve (FCFS):** The process that arrives first is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Job First (SJF):** The process with the smallest execution time is allocated the CPU first. It is a non-preemptive algorithm.\n* **Shortest Remaining Time First (SRTF):** The process with the smallest remaining execution time is allocated the CPU first. It is a preemptive algorithm.\n* **Round Robin (RR):** The process is allocated the CPU for a fixed time slice. The time slice is usually 10 milliseconds. It is a preemptive algorithm.\n* **Priority Scheduling:** The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. It is a preemptive algorithm.\n* **Multi-level Feedback Queue Scheduling:** The processes are divided into different queues based on their priority. The process with the highest priority is allocated the CPU first. If a process is preempted, it is moved to the next queue. It is a preemptive algorithm.\n* **Highest Response Ratio Next(HRRN):** CPU is allotted to the next process which has the highest response ratio and not to the process having less burst time. It is a Non-Preemptive algorithm.\n* **Lottery Scheduling:** The process is allocated the CPU based on a lottery system. It is a preemptive algorithm.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "CPU Scheduling in Operating System",
"url": "https://www.scaler.com/topics/operating-system/cpu-scheduling/",
"type": "article"
}
]
},
"cpQvB0qMDL3-NWret7oeA": {
"title": "CPU Interrupts",
"description": "CPU Interrupts are a way for the CPU to communicate with the rest of the computer. They are a way for the CPU to tell the rest of the computer that it needs to do something. For example, if the CPU is running a program and it needs to read from the keyboard, it will send an interrupt to the keyboard to tell it to send the data to the CPU. The CPU will then wait for the keyboard to send the data and then continue running the program.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "System Interrupts 100% CPU",
"url": "https://www.wikihow.com/System-Interrupts-100-Cpu",
"type": "article"
},
{
"title": "Explore top posts about Computing",
"url": "https://app.daily.dev/tags/computing?ref=roadmapsh",
@@ -4125,7 +4217,7 @@
"description": "Skip lists are a data structure that allows you to perform operations on a sorted list in O(log n) time. Skip lists are a probabilistic data structure, which means that the probability of a certain operation taking a certain amount of time is a certain value. In the case of skip lists, the probability of an operation taking O(log n) time is 1.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Skip Lists - Wikipedia",
"title": "Skip Lists",
"url": "https://en.wikipedia.org/wiki/Skip_list",
"type": "article"
},
@@ -4169,21 +4261,6 @@
"title": "Greedy Algs. II & Intro to NP Completeness",
"url": "https://youtu.be/qcGnJ47Smlo?list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&t=2939",
"type": "video"
},
{
"title": "NP Completeness II & Reductions",
"url": "https://www.youtube.com/watch?v=e0tGC6ZQdQE&index=16&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
"type": "video"
},
{
"title": "NP Completeness III",
"url": "https://www.youtube.com/watch?v=fCX1BGT3wjE&index=17&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm",
"type": "video"
},
{
"title": "NP Completeness IV",
"url": "https://www.youtube.com/watch?v=NKLDp3Rch3M&list=PLFDnELG9dpVxQCxuD-9BSy2E7BWY3t5Sm&index=18",
"type": "video"
}
]
},

View File

@@ -141,8 +141,24 @@
},
"bjpFWxiCKGz28E-ukhZBp": {
"title": "if else / switch / goto",
"description": "",
"links": []
"description": "C++ provides you with tools which helps you to control the way your program behaves (logic flows) based on how the user interact with your program. Here we will discuss about `if-else`, `switch` and `goto` are three common ways to guide the flow of logic in your code.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "The 'if-else' Statement in C++",
"url": "https://www.youtube.com/watch?v=9-BjXs1vMSc",
"type": "video"
},
{
"title": "Learn C++ With Me - Switch Statement",
"url": "https://www.youtube.com/watch?v=uOlLs1OYSSI",
"type": "video"
},
{
"title": "Why is it illegal to use \"goto\"?",
"url": "https://youtu.be/AKJhThyTmQw?si=gjEqAsDZVMDGVAT2",
"type": "video"
}
]
},
"oYi3YOc1GC2Nfp71VOkJt": {
"title": "Functions",
@@ -325,8 +341,24 @@
},
"hNBErGNiegLsUJn_vgcOR": {
"title": "Virtual Methods",
"description": "",
"links": []
"description": "Virtual functions enable runtime polymorphism in C++, allowing derived classes to override base class behavior. When called via a base pointer/reference, the _actual object's type_ determines which function is executed (dynamic dispatch). Non-virtual functions use compile-time resolution based on the pointer/reference type (static dispatch), which prevents overriding.\n\n // Base class with virtual function\n class Animal {\n public:\n virtual void speak() { std::cout << \"Generic sound\"; }\n };\n \n // Derived class override\n class Dog : public Animal {\n public:\n void speak() override { std::cout << \"Woof!\"; } // Dynamic dispatch\n };\n \n\nVisit the following resources to learn more:",
"links": [
{
"title": "C++ Virtual Functions Documentation",
"url": "https://en.cppreference.com/w/cpp/language/virtual",
"type": "article"
},
{
"title": "GeeksforGeeks Virtual Functions Guide",
"url": "https://www.geeksforgeeks.org/virtual-function-cpp/",
"type": "article"
},
{
"title": "Virtual Functions Explained (YouTube)",
"url": "https://www.youtube.com/watch?v=oIV2KchSyGQ&ab_channel=TheCherno",
"type": "video"
}
]
},
"s99ImazcwCgAESxZd8ksa": {
"title": "Virtual Tables",
@@ -530,7 +562,7 @@
"links": []
},
"vvE1aUsWbF1OFcmMUHbJa": {
"title": "Standardds",
"title": "Standards",
"description": "C++ standards are a set of rules and guidelines that define the language's features, syntax, and semantics. The International Organization for Standardization (ISO) is responsible for maintaining and updating the C++ standards. The main purpose of the standards is to ensure consistency, efficiency, and maintainability across multiple platforms and compilers.\n\nHere's a brief summary of the different C++ standards released to date:\n\n* **C++98/C++03**: The first standardized version of C++, which introduced many features like templates, exceptions, and the Standard Template Library (STL). C++03 is a minor update to C++98 with some bug fixes and performance improvements.\n \n* **C++11**: A major upgrade to the language, which introduced features such as:\n \n * Lambda expressions:\n \n auto sum = [](int a, int b) -> int { return a + b; };\n \n \n * Range-based for loops:\n \n std::vector<int> numbers = {1, 2, 3, 4};\n for (int num : numbers) {\n std::cout << num << '\\n';\n }\n \n \n * Smart pointers like `std::shared_ptr` and `std::unique_ptr`.\n* **C++14**: A minor update to C++11, which added features such as:\n \n * Generic lambda expressions:\n \n auto generic_sum = [](auto a, auto b) { return a + b; };\n \n \n * Binary literals:\n \n int binary_number = 0b1010;\n \n \n* **C++17**: Another major update that introduced features such as:\n \n * `if` and `switch` with initializers:\n \n if (auto it = my_map.find(key); it != my_map.end()) {\n // use 'it' here\n }\n \n \n * Structured bindings:\n \n std::map<std::string, int> my_map = {{\"A\", 1}, {\"B\", 2}};\n for (const auto& [key, value] : my_map) {\n // use 'key' and 'value' here\n }\n \n \n* **C++20**: The latest major update to the language, with features such as:\n \n * Concepts:\n \n template<typename T>\n concept Addable = requires(T a, T b) {\n { a + b } -> std::same_as<T>;\n };\n \n \n * Ranges:\n \n std::vector<int> numbers = {1, 2, 3, 4};\n auto doubled = numbers | std::views::transform([](int n) { return n * 2; });\n \n \n * Coroutines and more.\n\nRemember that to use these language features, you might need to configure your compiler to use the specific C++ standard version. For example, with GCC or Clang, you can use the `-std=c++11`, `-std=c++14`, `-std=c++17`, or `-std=c++20` flags.",
"links": []
},

File diff suppressed because it is too large Load Diff

View File

@@ -201,7 +201,7 @@
},
"LRZ8yxTfEGCXsYp4N1_uD": {
"title": "Public Speaking",
"description": "",
"description": "Public speaking is very important for a Developer Relations (DevRel) role, though its exact importance depends on the specific job and company. DevRel professionals act as a bridge between a company (often its tech or product team) and the developer community, so communication—especially in public settings—is a core part of the gig.",
"links": []
},
"0ntOE6PSdMl_EXB9gdgIv": {

View File

@@ -17,7 +17,7 @@
},
"yCnn-NfSxIybUQ2iTuUGq": {
"title": "How does the internet work?",
"description": "The Internet works through a global network of interconnected computers and servers, communicating via standardized protocols. Data is broken into packets and routed through various network nodes using the Internet Protocol (IP). These packets travel across different physical infrastructures, including fiber optic cables, satellites, and wireless networks. The Transmission Control Protocol (TCP) ensures reliable delivery and reassembly of packets at their destination. Domain Name System (DNS) servers translate human-readable website names into IP addresses. When you access a website, your device sends a request to the appropriate server, which responds with the requested data. This process, facilitated by routers, switches, and other networking equipment, enables the seamless exchange of information across vast distances, forming the backbone of our digital communications.\n\nVisit the following resources to learn more:",
"description": "The internet is a global network that connects computers and devices so they can share information with each other. Its how you browse websites, send emails, watch videos, and use apps. Think of it like a giant web that links everything together.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Introduction to Internet",

View File

@@ -163,8 +163,8 @@
"type": "article"
},
{
"title": "Git & GitHub Crash Course For Beginners",
"url": "https://www.youtube.com/watch?v=SWYqp7iY_Tc",
"title": "Git & GitHub Crash Course For Beginners 2025",
"url": "https://youtu.be/vA5TTz6BXhY?si=GvKMbLL4UBtOq6fh",
"type": "video"
},
{

View File

@@ -1460,7 +1460,7 @@
]
},
"UFDy19TNkykRsKv4vRsVJ": {
"title": "Cucubmber-JVM",
"title": "Cucumber-JVM",
"description": "Cucumber is a testing tool that supports Behavior Driven Development (BDD). It offers a way to write tests that anybody can understand, regardless of their technical knowledge.\n\nVisit the following resources to learn more:",
"links": [
{

View File

@@ -545,7 +545,7 @@
"description": "The stack trace is used to trace the active stack frames at a particular instance during the execution of a program. The stack trace is useful while debugging code as it shows the exact point that has caused an error.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Multiple ways to log the stack trace in node.js",
"title": "Multiple Ways to Log The Stack Trace in Node.js",
"url": "https://www.cloudhadoop.com/nodejs-print-stack-trace-error/",
"type": "article"
}
@@ -803,13 +803,13 @@
"description": "You can programmatically manipulate files in Node.js with the built-in `fs` module. The name is short for “file system,” and the module contains all the functions you need to read, write, and delete files on the local machine.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "How To Work with Files using the fs Module in Node.js",
"url": "https://www.digitalocean.com/community/tutorials/how-to-work-with-files-using-the-fs-module-in-node-js",
"title": "File System Module",
"url": "https://nodejs.org/docs/latest/api/fs.html",
"type": "article"
},
{
"title": "File system",
"url": "https://nodejs.org/docs/latest/api/fs.html",
"title": "How To Work with Files using the fs Module in Node.js",
"url": "https://www.digitalocean.com/community/tutorials/how-to-work-with-files-using-the-fs-module-in-node-js",
"type": "article"
},
{
@@ -861,7 +861,7 @@
"description": "File System or `fs` module is a built in module in Node that enables interacting with the file system using JavaScript. All file system operations have synchronous, callback, and promise-based forms, and are accessible using both CommonJS syntax and ES6 Modules.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "fs",
"title": "fs module",
"url": "https://nodejs.org/api/fs.html",
"type": "article"
},
@@ -952,7 +952,7 @@
"description": "Chokidar is a fast open-source file watcher for node. js. You give it a bunch of files, it watches them for changes and notifies you every time an old file is edited; or a new file is created.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "chokidar package",
"title": "chokidar",
"url": "https://www.npmjs.com/package/chokidar",
"type": "article"
}
@@ -1159,7 +1159,7 @@
"description": "`process.argv` is an array of parameters that are sent when you run a Node.js file or Node.js process.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Node.js Docs on process.argv",
"title": "process.argv",
"url": "https://nodejs.org/docs/latest/api/process.html#processargv",
"type": "article"
},
@@ -1248,7 +1248,7 @@
"type": "article"
},
{
"title": "Fastify Documentations",
"title": "Fastify Documentation",
"url": "https://www.fastify.io/docs/latest/",
"type": "article"
},
@@ -1358,7 +1358,7 @@
"type": "opensource"
},
{
"title": "npmjs.org",
"title": "Ky Package",
"url": "https://www.npmjs.com/package/ky/v/0.9.0",
"type": "article"
}
@@ -1607,6 +1607,11 @@
"title": "What is Database?",
"url": "https://en.wikipedia.org/wiki/Database",
"type": "article"
},
{
"title": "What is Database - AWS",
"url": "https://aws.amazon.com/what-is/database/",
"type": "article"
}
]
},
@@ -1619,6 +1624,11 @@
"url": "https://mongoosejs.com",
"type": "article"
},
{
"title": "Mongoose Documentation",
"url": "https://mongoosejs.com/docs/guide.html",
"type": "article"
},
{
"title": "Getting Started with MongoDB and Mongoose",
"url": "https://www.mongodb.com/developer/languages/javascript/getting-started-with-mongodb-and-mongoose/",
@@ -1654,8 +1664,14 @@
},
"5WqLm53CHDT5uBoMH-iPl": {
"title": "Native Drivers",
"description": "Another way to connect to different databases in Node.js is to use the official native drivers provided by the database.\n\nVisit the following resources to learn more:\n\n[@official@MongoDB Drivers](https://www.mongodb.com/docs/drivers/)",
"links": []
"description": "Another way to connect to different databases in Node.js is to use the official native drivers provided by the database.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "MongoDB Drivers",
"url": "https://www.mongodb.com/docs/drivers/",
"type": "article"
}
]
},
"HDDnt79_PCB5JU-KnHKUh": {
"title": "Knex",
@@ -1972,7 +1988,7 @@
"type": "article"
},
{
"title": "Pm2 Documentations",
"title": "Pm2 Documentation",
"url": "https://pm2.keymetrics.io/docs/usage/quick-start/",
"type": "article"
}
@@ -2015,7 +2031,7 @@
"description": "The Cluster module allows you to easily create child processes that each runs simultaneously on their own single thread, to handle workloads among their application threads.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "Cluster Docs",
"title": "Node.js Cluster",
"url": "https://nodejs.org/api/cluster.html#cluster",
"type": "article"
}

View File

@@ -172,11 +172,6 @@
"title": "Rows",
"description": "A row in PostgreSQL represents a single, uniquely identifiable record with a specific set of fields in a table. Each row in a table is made up of one or more columns, where each column can store a specific type of data (e.g., integer, character, date, etc.). The structure of a table determines the schema of its rows, and each row in a table must adhere to this schema.\n\nLearn more from the following resources:",
"links": [
{
"title": "Concepts",
"url": "https://www.postgresql.org/docs/7.1/query-concepts.html",
"type": "article"
},
{
"title": "PostgreSQL - Rows",
"url": "https://www.postgresql.org/docs/current/functions-comparisons.html",
@@ -233,12 +228,7 @@
"links": [
{
"title": "Managing Databases",
"url": "https://www.postgresql.org/docs/8.1/managing-databases.html",
"type": "article"
},
{
"title": "Managing a Database",
"url": "https://www.postgresql.org/docs/7.1/start-manage-db.html",
"url": "https://www.postgresql.org/docs/current/managing-databases.html",
"type": "article"
}
]
@@ -451,7 +441,7 @@
},
{
"title": "Query Processing in PostgreSQL",
"url": "https://medium.com/agedb/query-processing-in-postgresql-1309fa93f69f",
"url": "https://www.interdb.jp/pg/pgsql03.html",
"type": "article"
}
]

View File

@@ -545,8 +545,24 @@
},
"XCeXiKvBblmDArfbWjDvw": {
"title": "Regression Testing",
"description": "Regression Testing is a type of software testing to confirm that a recent program or code change has not adversely affected existing features. Regression testing is a black box testing technique. Test cases are re-executed to check the previous functionality of the application is working fine and that the new changes have not produced any bugs.",
"links": []
"description": "Regression Testing is a type of software testing to confirm that a recent program or code change has not adversely affected existing features. Regression testing is a black box testing technique. Test cases are re-executed to check the previous functionality of the application is working fine and that the new changes have not produced any bugs.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "What is Regression Testing?",
"url": "https://www.guru99.com/regression-testing.html",
"type": "article"
},
{
"title": "What is Regression Testing? Definition, Tools and Examples",
"url": "https://katalon.com/resources-center/blog/regression-testing",
"type": "article"
},
{
"title": "What is Regression Testing? A Software Testing FAQ - Why? How? When?",
"url": "https://www.youtube.com/watch?v=xmQuLTarGI4",
"type": "video"
}
]
},
"MVShii4LZiWW_gPTJzkty": {
"title": "Smoke Testing",

View File

@@ -674,41 +674,6 @@
"title": "React Router — Official Website",
"url": "https://reactrouter.com/",
"type": "article"
},
{
"title": "Getting Started Guide",
"url": "https://reactrouter.com/en/main/start/tutorial",
"type": "article"
},
{
"title": "How to use React Router v6",
"url": "https://www.robinwieruch.de/react-router/",
"type": "article"
},
{
"title": "React Router Cheat Sheet",
"url": "https://devhints.io/react-router/",
"type": "article"
},
{
"title": "Explore top posts about React",
"url": "https://app.daily.dev/tags/react?ref=roadmapsh",
"type": "article"
},
{
"title": "Tanstack/Router — Official Website",
"url": "https://tanstack.com/router/latest/docs/framework/react/overview",
"type": "article"
},
{
"title": "React Router v6 in 1 hour",
"url": "https://youtu.be/0cSVuySEB0A",
"type": "video"
},
{
"title": "TanStack/Router - Complete Course",
"url": "https://www.youtube.com/watch?v=4sslBg8LprE&list=PLOQjd5dsGSxJilh0lBofeY8Qib98kzmF5",
"type": "video"
}
]
},

View File

@@ -4,9 +4,24 @@
"description": "Redis is an open-source, in-memory data structure store, primarily used as a database, cache, and message broker. It supports various data structures like strings, hashes, lists, sets, and sorted sets, making it highly versatile. Redis operates with extremely low latency due to its in-memory nature, enabling fast access to data. It is often used in real-time applications such as session management, leaderboards, or caching mechanisms, where quick data retrieval is critical. Additionally, Redis supports data persistence by periodically writing the dataset to disk, balancing memory speed with data reliability.\n\nVisit the following resources to learn more:",
"links": [
{
"title": "What is redis?",
"title": "What is Redis?",
"url": "https://redis.io/docs/latest/get-started/",
"type": "article"
},
{
"title": "Introduction to Redis",
"url": "https://redis.io/about/",
"type": "article"
},
{
"title": "Redis - Wikipedia",
"url": "https://en.wikipedia.org/wiki/Redis",
"type": "article"
},
{
"title": "What is Redis Explained? - IBM",
"url": "https://www.ibm.com/think/topics/redis",
"type": "article"
}
]
},
@@ -14,6 +29,11 @@
"title": "In-memory Data Structure Store",
"description": "An in-memory database is a purpose-built database that relies primarily on internal memory for data storage. It enables minimal response times by eliminating the need to access standard disk drives (SSDs). In-memory databases are ideal for applications that require microsecond response times or have large spikes in traffic, such as gaming leaderboards, session stores, and real-time data analytics. The terms main memory database (MMDB), in-memory database system (IMDS), and real-time database system (RTDB) also refer to in-memory databases.\n\nLearn more from the following resources:",
"links": [
{
"title": "Get Started with In-Memory Data Store",
"url": "https://redis.io/docs/latest/develop/get-started/data-store/",
"type": "article"
},
{
"title": "Amazon MemoryDB",
"url": "https://aws.amazon.com/memorydb/",
@@ -46,6 +66,16 @@
"url": "https://redis.io/solutions/caching/",
"type": "article"
},
{
"title": "How to use Redis for Query Caching",
"url": "https://redis.io/learn/howtos/solutions/microservices/caching",
"type": "article"
},
{
"title": "Understanding Redis Caching",
"url": "https://medium.com/@devlexus/understanding-redis-caching-how-it-works-and-why-its-efficient-99afdbf1b8e0",
"type": "article"
},
{
"title": "How to use Redis Caching for incredible performance",
"url": "https://www.youtube.com/watch?v=-5RTyEim384",
@@ -83,6 +113,16 @@
"url": "https://redis.io/solutions/caching/",
"type": "article"
},
{
"title": "How to use Redis for Query Caching",
"url": "https://redis.io/learn/howtos/solutions/microservices/caching",
"type": "article"
},
{
"title": "Understanding Redis Caching",
"url": "https://medium.com/@devlexus/understanding-redis-caching-how-it-works-and-why-its-efficient-99afdbf1b8e0",
"type": "article"
},
{
"title": "How to use Redis Caching for incredible performance",
"url": "https://www.youtube.com/watch?v=-5RTyEim384",
@@ -127,17 +167,17 @@
"type": "article"
},
{
"title": "PUBLISH Documentation",
"title": "PUBLISH Command",
"url": "https://redis.io/docs/latest/commands/publish/",
"type": "article"
},
{
"title": "SUBSCRIBE Documentation",
"title": "SUBSCRIBE Command",
"url": "https://redis.io/docs/latest/commands/subscribe/",
"type": "article"
},
{
"title": "UNSUBSCRIBE Documentation",
"title": "UNSUBSCRIBE Command",
"url": "https://redis.io/docs/latest/commands/unsubscribe/",
"type": "article"
}
@@ -148,12 +188,12 @@
"description": "Leaderboards and counters are common use cases for Redis, leveraging its sorted sets and atomic increment operations. For leaderboards, the `ZADD` command is used to add members with their scores to a sorted set, and `ZRANGE` or `ZREVRANGE` retrieves the top (or bottom) ranked members efficiently. This makes Redis ideal for ranking systems in gaming or tracking top-performing entities. Counters are managed using commands like `INCR` and `DECR`, which atomically increase or decrease integer values. These operations are lightweight and performant, making Redis a go-to solution for tracking metrics, analytics, or rate limiting.\n\nLearn more from the following resources:",
"links": [
{
"title": "ZADD Documentation",
"title": "ZADD",
"url": "https://redis.io/docs/latest/commands/zadd/",
"type": "article"
},
{
"title": "ZRANGE Documentation",
"title": "ZRANGE",
"url": "https://redis.io/docs/latest/commands/zrange/",
"type": "article"
}
@@ -188,6 +228,11 @@
"title": "Optimizing Redis for High Performance",
"url": "https://loadforge.com/guides/optimizing-redis-for-high-performance-essential-configuration-tweaks",
"type": "article"
},
{
"title": "High-Performance and Scalable Architecture with Redis",
"url": "https://medium.com/@emreemenekse/high-performance-and-scalable-architecture-with-redis-and-net-core-abde36074d26",
"type": "article"
}
]
},
@@ -328,7 +373,7 @@
"type": "article"
},
{
"title": "Redis command cheat sheet",
"title": "Redis Cheat Sheet",
"url": "https://redis.io/learn/howtos/quick-start/cheat-sheet",
"type": "article"
}
@@ -344,7 +389,7 @@
"description": "The `DEL` command in Redis is used to delete one or more keys from the database. If the specified key(s) exist, they are removed, and the command returns the number of keys that were deleted. If a key does not exist, it is simply ignored, and no error is returned. This command is useful for managing memory by removing unnecessary or obsolete data and is an atomic operation, ensuring that keys are deleted without interference from other operations.\n\nLearn more from the following resources:",
"links": [
{
"title": "DEL Documentation",
"title": "DEL",
"url": "https://redis.io/docs/latest/commands/del/",
"type": "article"
},
@@ -376,7 +421,7 @@
"description": "The SET command sets the defined key to hold a value, if the key already holds a value then it will be overwritten regardless of its type.\n\nLearn more from the following resources:",
"links": [
{
"title": "SET Command",
"title": "SET",
"url": "https://redis.io/docs/latest/commands/set/",
"type": "article"
}
@@ -387,7 +432,7 @@
"description": "The `GET` command in Redis is used to retrieve the value associated with a specified key. If the key exists, it returns the value as a string; if the key does not exist, it returns a nil response. This command is fundamental for accessing stored data in Redis and is often used in conjunction with other commands to manipulate and manage data within the database. The `GET` command is atomic, meaning it provides a consistent view of the data at the time the command is executed.\n\nLearn more from the following resources:",
"links": [
{
"title": "GET Documentation",
"title": "GET",
"url": "https://redis.io/docs/latest/commands/get/",
"type": "article"
},
@@ -400,10 +445,10 @@
},
"5K9qyC4mrhXYWOC8WSq8C": {
"title": "INCR",
"description": "`INCR` is a Redis command used to increment the value of a string key by 1. If the key does not exist, it initializes the key with a value of 0 before performing the increment operation, resulting in a value of 1. If the key contains a non-integer value, the command will return an error. `INCR` is atomic, meaning it is safe to use in concurrent environments without race conditions, making it ideal for use cases like counters or tracking metrics.\n\nLearn more from the following resources:",
"description": "`INCR` command used to increment the value of a string key by 1. If the key does not exist, it initializes the key with a value of 0 before performing the increment operation, resulting in a value of 1. If the key contains a non-integer value, the command will return an error. `INCR` is atomic, meaning it is safe to use in concurrent environments without race conditions, making it ideal for use cases like counters or tracking metrics.\n\nLearn more from the following resources:",
"links": [
{
"title": "INCR Documentation",
"title": "INCR",
"url": "https://redis.io/docs/latest/commands/incr/",
"type": "article"
}
@@ -430,7 +475,7 @@
"description": "Redis APPEND command is used to add some value in a key. If the key already exists and is a string, this command appends the value at the end of the string. If key does not exist it is created and set as an empty string,\n\nLearn more from the following resources:",
"links": [
{
"title": "APPEND Docs",
"title": "APPEND",
"url": "https://redis.io/docs/latest/commands/append/",
"type": "article"
},
@@ -446,7 +491,7 @@
"description": "The STRLEN command returns the length of a string value that is stored at the defined key, if no string value is help at the key then an error will be returned.\n\nLearn more from the following resources:",
"links": [
{
"title": "STRLEN Documentation",
"title": "STRLEN",
"url": "https://redis.io/docs/latest/commands/strlen/",
"type": "article"
}
@@ -478,7 +523,7 @@
"type": "article"
},
{
"title": "EXPIRE Documentation",
"title": "EXPIRE",
"url": "https://redis.io/docs/latest/commands/expire/",
"type": "article"
}
@@ -489,7 +534,7 @@
"description": "The TTL command returns the remaining time left to live of a key that is specified, this capability allows a Redis client to check how many seconds a given key will continue to be a part of a dataset.\n\nLearn more from the following resources:",
"links": [
{
"title": "TTL Documentation",
"title": "TTL",
"url": "https://redis.io/docs/latest/commands/ttl/",
"type": "article"
}
@@ -500,7 +545,7 @@
"description": "`LPUSH` is a Redis command that inserts one or more elements at the beginning (left side) of a list. If the list does not exist, it creates a new list before performing the insertion. This command returns the length of the list after the operation. `LPUSH` is useful for building stacks or adding items to queues where new elements need to be prioritized, enabling efficient manipulation of ordered data structures in Redis.\n\nLearn more from the following resources:",
"links": [
{
"title": "LPUSH Documentation",
"title": "LPUSH",
"url": "https://redis.io/docs/latest/commands/lpush/",
"type": "article"
}
@@ -511,7 +556,7 @@
"description": "The RPUSH command will insert all the specified values at the tail end o the list that is stored at the defined key, if the key does not exist then it will be created as an empty list before performing the push.\n\nLearn more from the following resources:",
"links": [
{
"title": "RPUSH Documentation",
"title": "RPUSH",
"url": "https://redis.io/docs/latest/commands/rpush/",
"type": "article"
}
@@ -522,7 +567,7 @@
"description": "`LPOP` is a Redis command that removes and returns the first element from the left side of a list. If the list is empty or does not exist, it returns `nil`. This command is commonly used in scenarios like implementing queues or consuming elements in FIFO (First-In-First-Out) order, making it ideal for task processing, message handling, and managing ordered data flows in real-time applications.\n\nLearn more from the following resources:",
"links": [
{
"title": "LPOP Documentation",
"title": "LPOP",
"url": "https://redis.io/docs/latest/commands/lpop/",
"type": "article"
}
@@ -533,7 +578,7 @@
"description": "The RPOP command removes and then returns the last elements of the list stored and the specified key, by default it will pop only a single element from the list.\n\nLearn more from the following resources:",
"links": [
{
"title": "RPOP Documentation",
"title": "RPOP",
"url": "https://redis.io/docs/latest/commands/rpop/",
"type": "article"
}
@@ -544,7 +589,7 @@
"description": "`LRANGE` is a Redis command that retrieves a specified range of elements from a list, defined by a start and stop index. The indices can be positive (starting from 0) or negative (e.g., -1 for the last element). This command is commonly used to fetch subsets of a list without loading the entire list into memory, making it useful for paginating data, viewing portions of a queue, or analyzing a segment of ordered data in an efficient manner.\n\nLearn more from the following resources:",
"links": [
{
"title": "LRANGE Documentation",
"title": "LRANGE",
"url": "https://redis.io/docs/latest/commands/lrange/",
"type": "article"
}
@@ -555,7 +600,7 @@
"description": "`LINDEX` is a Redis command used to retrieve an element from a list by its index. The index can be positive (starting from 0 for the first element) or negative (e.g., -1 for the last element). If the index is out of range, the command returns `nil`. This command is useful for accessing specific elements in a list without needing to fetch the entire list, making it efficient for operations where only certain elements are needed.\n\nLearn more from the following resources:",
"links": [
{
"title": "LINDEX Documentation",
"title": "LINDEX",
"url": "https://redis.io/docs/latest/commands/lindex/",
"type": "article"
}
@@ -566,7 +611,7 @@
"description": "`LLEN` is a Redis command used to return the length of a list stored at a specified key. If the list does not exist, it returns `0`. This command is efficient for quickly checking the number of elements in a list without retrieving its contents, making it useful for monitoring queue sizes, tracking list growth, or validating data presence in real-time applications.\n\nLearn more from the following resources:",
"links": [
{
"title": "LLEN Documentation",
"title": "LLEN",
"url": "https://redis.io/docs/latest/commands/llen/",
"type": "article"
}
@@ -577,7 +622,7 @@
"description": "`LMOVE` is a Redis command used to atomically move an element from one list to another. It pops an element from the source list (either from the left or right end) and pushes it to the destination list (either to the left or right end), based on the specified parameters (`LEFT` or `RIGHT`). This command is useful for implementing queue-like patterns or managing work distribution between different lists without race conditions, as it ensures that the element is safely transferred in a single atomic operation.\n\nLearn more from the following resources:",
"links": [
{
"title": "LMOVE Documentation",
"title": "LMOVE",
"url": "https://redis.io/docs/latest/commands/lmove/",
"type": "article"
}
@@ -615,7 +660,7 @@
"description": "The SADD command will add the specified members to the set which is stored and the defined key, any specified members that are already a member of the set will simply be ignored.\n\nLearn more from the following resources:",
"links": [
{
"title": "SADD Documentation",
"title": "SADD",
"url": "https://redis.io/docs/latest/commands/sadd/",
"type": "article"
}
@@ -626,7 +671,7 @@
"description": "The SMEMBERS command returns all the members of the set that is defined at key.\n\nLearn more from the following resources:",
"links": [
{
"title": "SMEMBERS Documentation",
"title": "SMEMBERS",
"url": "https://redis.io/docs/latest/commands/smembers/",
"type": "article"
}
@@ -637,7 +682,7 @@
"description": "The SREM command will remove the specified members from the set stored at the defined key, specified members that are not a member of the set will be ignored.\n\nLearn more from the following resources:",
"links": [
{
"title": "SREM Documentation",
"title": "SREM",
"url": "https://redis.io/docs/latest/commands/srem/",
"type": "article"
}
@@ -659,7 +704,7 @@
"description": "The SINTER command will return members of the set which will result in the intersection of all the given sets, keys that do not exist are considered to be empty sets.\n\nLearn more from the following resources:",
"links": [
{
"title": "SINTER Documentation",
"title": "SINTER",
"url": "https://redis.io/docs/latest/commands/sinter/",
"type": "article"
}
@@ -670,7 +715,7 @@
"description": "`SCARD` is a Redis command used to get the number of members in a set, it returns the cardinality of the specified set, which is the total count of unique elements it contains. If the set does not exist, `SCARD` returns `0`. This command is useful for quickly determining the size of a set, allowing applications to make decisions based on the number of unique items, such as checking user participation in a campaign or the count of unique tags in a system.\n\nLearn more from the following resources:",
"links": [
{
"title": "SCARD Documentation",
"title": "SCARD",
"url": "https://redis.io/docs/latest/commands/scard/",
"type": "article"
}
@@ -681,7 +726,7 @@
"description": "The SUNION command returns the members of a set resulting in a union of all the given sets.\n\nLearn more from the following resources:",
"links": [
{
"title": "SUNION Documentation",
"title": "SUNION",
"url": "https://redis.io/docs/latest/commands/sunion/",
"type": "article"
}
@@ -692,7 +737,7 @@
"description": "The SDIFF coimmand returns the members of a set resulting from the difference between the first set and all the following sets.\n\nLearn more from the following resources:",
"links": [
{
"title": "SDIFF Documentation",
"title": "SDIFF",
"url": "https://redis.io/docs/latest/commands/sdiff/",
"type": "article"
}
@@ -721,8 +766,14 @@
},
"Wl23Jh-ASJOQ850yjaTIU": {
"title": "Strings",
"description": "Strings in Redis are binary-safe, meaning they can contain any kind of data, including text, integers, floats, or even binary data like images and they can hold up to 512 MB of data per key. Redis strings support a wide range of operations, from basic CRUD (Create, Read, Update, Delete) to more complex manipulations like incrementing/decrementing numeric values, appending data, or extracting substrings.",
"links": []
"description": "Strings in Redis are binary-safe, meaning they can contain any kind of data, including text, integers, floats, or even binary data like images and they can hold up to 512 MB of data per key. Redis strings support a wide range of operations, from basic CRUD (Create, Read, Update, Delete) to more complex manipulations like incrementing/decrementing numeric values, appending data, or extracting substrings.\n\nLearn more from the following resources:",
"links": [
{
"title": "Redis Strings",
"url": "https://redis.io/docs/latest/develop/data-types/strings/",
"type": "article"
}
]
},
"4-C4XqACUp4nvcMIj6djF": {
"title": "Lists",
@@ -751,7 +802,7 @@
"description": "`HSET` is a Redis command used to set the value of a specified field within a hash. If the field already exists, it updates the value; if not, it adds the field to the hash. This command is useful for creating and managing key-value pairs within a hash structure without modifying other fields. It returns `1` if a new field is created and `0` if an existing field is updated, making it efficient for atomic updates in a Redis hash.\n\nLearn more from the following resources:",
"links": [
{
"title": "HSET Documentation",
"title": "HSET",
"url": "https://redis.io/docs/latest/commands/hset/",
"type": "article"
}
@@ -762,7 +813,7 @@
"description": "`HGET` is a Redis command used to retrieve the value of a specified field within a hash. If the field exists, it returns the value; if not, it returns `nil`. This command is efficient for accessing specific fields within a hash without retrieving the entire hash structure, making it ideal for scenarios where only selective data needs to be read from a Redis hash.\n\nLearn more from the following resources:",
"links": [
{
"title": "HGET Documentation",
"title": "HGET",
"url": "https://redis.io/docs/latest/commands/hget/",
"type": "article"
}
@@ -773,7 +824,7 @@
"description": "`HGETALL` is a Redis command that retrieves all the fields and their values from a specified hash. It returns the data as an array of field-value pairs, making it useful for obtaining a complete view of the hash's contents. However, it can be memory-intensive for large hashes, so its recommended to use it cautiously when dealing with high data volumes.\n\nLearn more from the following resources:",
"links": [
{
"title": "HGETALL Documentation",
"title": "HGETALL",
"url": "https://redis.io/docs/latest/commands/hgetall/",
"type": "article"
}
@@ -784,7 +835,7 @@
"description": "`HDEL` is a Redis command used to delete one or more specified fields from a hash. If the fields exist in the hash, they are removed, and the command returns the number of fields that were deleted. If a specified field does not exist, it is ignored. `HDEL` is useful for efficiently managing memory and cleaning up data within a Redis hash without removing the entire hash structure.\n\nLearn more from the following resources:",
"links": [
{
"title": "HDEL Documentation",
"title": "HDEL",
"url": "https://redis.io/docs/latest/commands/hdel/",
"type": "article"
}
@@ -795,7 +846,7 @@
"description": "`HEXISTS` is a Redis command used to check if a specified field exists within a hash. It returns `1` if the field is present and `0` if it is not. This command is useful for verifying the presence of specific fields in a hash before performing operations like updates or deletions. It helps ensure data consistency and avoid unnecessary operations in Redis.\n\nLearn more from the following resources:",
"links": [
{
"title": "HEXISTS Documentation",
"title": "HEXISTS",
"url": "https://redis.io/docs/latest/commands/hexists/",
"type": "article"
}
@@ -808,15 +859,21 @@
},
"QTbkWZ7BpqYmBhUivccPu": {
"title": "Sorted Sets",
"description": "A sorted set in Redis is a collection of unique strings, or members, that are ordered by an associated score. When more than one string has the same score, the strings are ordered lexicographically.",
"links": []
"description": "A sorted set in Redis is a collection of unique strings, or members, that are ordered by an associated score. When more than one string has the same score, the strings are ordered lexicographically.\n\nLearn more from the following resources:",
"links": [
{
"title": "Sorted Sets",
"url": "https://redis.io/docs/latest/develop/data-types/sorted-sets/",
"type": "article"
}
]
},
"0swsBD0sOY-o5lzibT999": {
"title": "ZADD",
"description": "The ZADD command adds all of the specified members with the specified scores to the sorted set defined at `key`.\n\nLearn more from the following resources:",
"links": [
{
"title": "ZADD Documentation",
"title": "ZADD",
"url": "https://redis.io/docs/latest/commands/zadd/",
"type": "article"
}
@@ -827,7 +884,7 @@
"description": "The ZRANGE command can perform multiple range queries including by rank, by score or by lexiographical order. The order of elements returned are always from lowest to highest and any score ties are resolved using reverse lexiographical ordering.\n\nLearn more from the following resources:",
"links": [
{
"title": "ZRANGE Documentation",
"title": "ZRANGE",
"url": "https://redis.io/docs/latest/commands/zrange/",
"type": "article"
}
@@ -838,7 +895,7 @@
"description": "This command retrieves elements from a sorted set stored at the specified key. It returns all elements with scores falling within the given min and max range, inclusive of both boundaries. Elements are ordered from lowest to highest score. For elements sharing the same score, the command returns them in lexicographical order. This ordering is an inherent property of Redis sorted sets and requires no additional computation.\n\nLearn more from the following resources:",
"links": [
{
"title": "ZRANGEBYSCORE Documentation",
"title": "ZRANGEBYSCORE",
"url": "https://redis.io/docs/latest/commands/zrangebyscore/",
"type": "article"
}
@@ -849,7 +906,7 @@
"description": "Removes the specified members from the sorted set stored at key. Non existing members are ignored. An error is returned when key exists and does not hold a sorted set.\n\nLearn more from the following resources:",
"links": [
{
"title": "ZREM Documentation",
"title": "ZREM",
"url": "https://redis.io/docs/latest/commands/zrem/",
"type": "article"
}
@@ -860,7 +917,7 @@
"description": "ZINCRBY increments the score of a member in a sorted set by the defined increment. If the member targeted does not exists in the sorted set then it will be added and will be assigned the value of the increment. If the key does not exists that ZINCRBY will created the set with the targeted member as it's only member.\n\nLearn more from the following resources:",
"links": [
{
"title": "ZINCRBY Documentation",
"title": "ZINCRBY",
"url": "https://redis.io/docs/latest/commands/zincrby/",
"type": "article"
}
@@ -871,7 +928,7 @@
"description": "ZRANK returns the rank of member in the sorted set stored at key, with the scores ordered from low to high. The rank is 0-based, which means that the member with the lowest score has rank 0. The optional WITHSCORE argument supplements the command's reply with the score of the element returned.\n\nLearn more from the following resources:",
"links": [
{
"title": "ZRANK Documentation",
"title": "ZRANK",
"url": "https://redis.io/docs/latest/commands/zrank/",
"type": "article"
}
@@ -882,7 +939,7 @@
"description": "ZCOUNT returns the number of elements in the sorted set at the targetted `key`, with a score between `min` and `max`.\n\nLearn more from the following resources:",
"links": [
{
"title": "ZCOUNT Documentation",
"title": "ZCOUNT",
"url": "https://redis.io/docs/latest/commands/zcount/",
"type": "article"
}
@@ -922,10 +979,10 @@
},
"UlQHqw1dbxZnAKbsWsOgU": {
"title": "Retrieval by Pattern",
"description": "Redis offers powerful pattern-based key retrieval, allowing users to query multiple keys using wildcard patterns. This functionality primarily relies on the KEYS command, which supports glob-style patterns such as \\*, ?, and \\[\\] for flexible matching.\n\nLearn more from the following resources:",
"description": "The SCAN command in Redis is a cursor-based iterator that does not guarantee to return all matching keys in one call, even if COUNT is specified. Instead, SCAN returns a small subset of keys that match the pattern, requiring subsequent calls to complete the iteration. Redis offers powerful pattern-based key retrieval, allowing users to query multiple keys using wildcard patterns. This functionality primarily relies on the KEYS command, which supports glob-style patterns such as \\*, ?, and \\[\\] for flexible matching.\n\nLearn more from the following resources:",
"links": [
{
"title": "SCAN Command",
"title": "SCAN",
"url": "https://redis.io/docs/latest/commands/scan/",
"type": "article"
}
@@ -936,12 +993,12 @@
"description": "Redis key expiration allows you to set a time-to-live (TTL) for keys, automatically deleting them after a specified duration. This can be achieved using commands like `EXPIRE`, which sets the expiration time in seconds, or `PEXPIRE`, which uses milliseconds for finer granularity. You can also use `SET` with the EX argument to set a key with a value and expiration in a single command. Expired keys are removed during normal operations, such as when accessed or during periodic cleanup. This feature is useful for managing memory efficiently and for scenarios like session management or caching where temporary data storage is needed.\n\nLearn more from the following resources:",
"links": [
{
"title": "PEXPIRE Documentation",
"title": "PEXPIRE",
"url": "https://redis.io/docs/latest/commands/pexpire/",
"type": "article"
},
{
"title": "EXPIRE Documentation",
"title": "EXPIRE",
"url": "https://redis.io/docs/latest/commands/expire/",
"type": "article"
}
@@ -979,7 +1036,12 @@
"description": "Batch operations in Redis allow you to execute multiple commands efficiently in a single network round-trip. While Redis does not have true batching like some databases (where a set of operations are sent together and processed atomically), it provides ways to send multiple commands together to reduce the overhead of individual network requests. These include Pipelining, Transactions (MULTI/EXEC), and Lua Scripting.\n\nLearn more from the following resources:",
"links": [
{
"title": "Using pipelining to batch issue commands",
"title": "Batch Operations in Redis",
"url": "https://www.compilenrun.com/docs/middleware/redis/redis-operations/redis-batch-operations/",
"type": "article"
},
{
"title": "Using Pipelining to Batch Issue Commands",
"url": "https://www.alibabacloud.com/help/en/redis/use-cases/use-pipelining-to-batch-issue-commands#:~:text=You%20can%20use%20the%20Redis,network%20latency%20and%20improving%20performance.",
"type": "article"
}
@@ -990,12 +1052,12 @@
"description": "In Redis, Bitmaps are a data structure that allows you to manipulate individual bits within a string value. While Redis doesn't have a native \"bitmap\" data type, it uses strings to represent bitmaps. The power of bitmaps comes from their ability to perform operations on binary data at the bit level, making them extremely memory-efficient for certain types of applications, like tracking the presence/absence of elements (such as daily active users, features, etc.).\n\nLearn more from the following resources:",
"links": [
{
"title": "Redis Bitmap docs",
"title": "Bitmap",
"url": "https://redis.io/docs/latest/develop/data-types/bitmaps/",
"type": "article"
},
{
"title": "Redis bitmap explained",
"title": "Redis Bitmap Explained",
"url": "https://youtu.be/oj8LdJQjhJo?si=jem54LfPbZtrpnEP",
"type": "video"
}
@@ -1006,7 +1068,7 @@
"description": "The SETBIT command sets or clearts the bit at the specified offset in the string value that is stored at the specified key. When the key does not exist, a new string value will be created and the string is grown to make sure it can hold a bit to the same value as the earlier defined offset.\n\nLearn more from the following resources:",
"links": [
{
"title": "SETBIT command",
"title": "SETBIT",
"url": "https://redis.io/docs/latest/commands/setbit/",
"type": "article"
}
@@ -1017,7 +1079,7 @@
"description": "The `GETBIT` command in Redis retrieves the value of a specific bit at a given offset in a string key. It returns either 0 or 1, depending on the state of the bit at that position. If the key does not exist, the command returns 0, as it treats non-existing keys as empty strings. This command is particularly useful for working with bitmap data structures, allowing you to check the status of individual bits in a more efficient manner compared to retrieving the entire string.\n\nLearn more from the following resources:",
"links": [
{
"title": "GETBIT Documentation",
"title": "GETBIT",
"url": "https://redis.io/docs/latest/commands/getbit/",
"type": "article"
}
@@ -1028,7 +1090,7 @@
"description": "The BITCOUNT command in Redis is used to count the number of bits set to 1 (i.e., the number of binary 1s) in the value stored at a specific key. Since Redis allows string values to be stored as binary data, the BITCOUNT command becomes useful for operations involving bits, like efficiently tracking and counting bits in binary-encoded data.\n\nLearn more from the following resources:",
"links": [
{
"title": "BITCOUNT - Docs",
"title": "BITCOUNT",
"url": "https://redis.io/docs/latest/commands/bitcount/",
"type": "article"
},
@@ -1044,7 +1106,7 @@
"description": "The `BITOP` command in Redis performs bitwise operations (AND, OR, XOR, and NOT) across one or more string keys, treating the strings as binary data. The result is stored in a destination key. This command is useful for manipulating and analyzing binary data directly in Redis, such as when aggregating flags or working with bitmap data structures.\n\nLearn more from the following resources:",
"links": [
{
"title": "BITOP Documentation",
"title": "BITOP",
"url": "https://redis.io/docs/latest/commands/bitop/",
"type": "article"
},
@@ -1060,12 +1122,12 @@
"description": "The `BITPOS` command in Redis is used to find the position of the first bit set to 1 or 0 in a string key. You can specify a starting and ending byte range for the search. Its commonly used in scenarios where you need to quickly locate specific bits within a bitmap, such as finding the first occurrence of a flag or status in large datasets.\n\nLearn more from the following resources:",
"links": [
{
"title": "BITPOS Documentation",
"title": "BITPOS",
"url": "https://redis.io/docs/latest/commands/bitpos/",
"type": "article"
},
{
"title": "BITPOS",
"title": "BITPOS Documentation",
"url": "https://upstash.com/docs/redis/sdks/py/commands/bitmap/bitpos",
"type": "article"
}
@@ -1094,10 +1156,10 @@
},
"8a4DmPZrX2xGZ7zdWxS63": {
"title": "PFADD",
"description": "`PFADD` is a Redis command used to add elements to a HyperLogLog data structure, which is designed for estimating the cardinality (number of unique elements) of a dataset. When elements are added using `PFADD`, Redis updates the internal structure without storing the actual elements, ensuring low memory consumption. This command returns `1` if the HyperLogLog was modified (i.e., a new unique element was added) and `0` otherwise. `PFADD` is ideal for use cases like counting unique visits or tracking unique events in a highly memory-efficient manner.\n\nLearn more from the following resources:",
"description": "`PFADD` command used to add elements to a HyperLogLog data structure, which is designed for estimating the cardinality (number of unique elements) of a dataset. When elements are added using `PFADD`, Redis updates the internal structure without storing the actual elements, ensuring low memory consumption. This command returns `1` if the HyperLogLog was modified (i.e., a new unique element was added) and `0` otherwise. `PFADD` is ideal for use cases like counting unique visits or tracking unique events in a highly memory-efficient manner.\n\nLearn more from the following resources:",
"links": [
{
"title": "PFADD Documentation",
"title": "PFADD",
"url": "https://redis.io/docs/latest/commands/pfadd/",
"type": "article"
}
@@ -1108,7 +1170,7 @@
"description": "`PFCOUNT` is a Redis command used to retrieve the estimated number of unique elements in one or more HyperLogLog structures. It provides an approximate cardinality count with a typical error rate of 0.81%, making it highly efficient for large datasets while using minimal memory. When called with multiple HyperLogLog keys, `PFCOUNT` merges the data and returns the approximate count of the union, allowing for quick aggregation of unique elements across multiple sets.\n\nLearn more from the following resources:",
"links": [
{
"title": "PFCOUNT Documentation",
"title": "PFCOUNT",
"url": "https://redis.io/docs/latest/commands/pfcount/",
"type": "article"
}
@@ -1119,7 +1181,7 @@
"description": "`PFMERGE` is a Redis command used to combine multiple HyperLogLog data structures into a single HyperLogLog key, creating a new structure that represents the union of all unique elements. This command is useful when you want to aggregate and estimate the cardinality of distinct elements across multiple datasets. The resulting HyperLogLog can then be queried using `PFCOUNT` to get the approximate count of the merged unique elements.\n\nLearn more from the following resources:",
"links": [
{
"title": "PFMERGE Documentation",
"title": "PFMERGE",
"url": "https://redis.io/docs/latest/commands/pfmerge/",
"type": "article"
}
@@ -1132,15 +1194,26 @@
},
"zXs_9n2yEb_eVi0WuOQKH": {
"title": "Streams",
"description": "A Redis stream is a data structure that acts like an append-only log but can also implement multiple operations to overcome limits seen in typical append-only logs. These include random access in O(1) time and complex consumption strategies, such as consumer groups.",
"links": []
"description": "A Redis stream is a data structure that acts like an append-only log but can also implement multiple operations to overcome limits seen in typical append-only logs. These include random access in O(1) time and complex consumption strategies, such as consumer groups.\n\nLearn more from the following resources:",
"links": [
{
"title": "Redis Streams",
"url": "https://redis.io/docs/latest/develop/data-types/streams/",
"type": "article"
},
{
"title": "Transport Layer Security Documentation",
"url": "https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl/",
"type": "article"
}
]
},
"7isWhgrUA6M5IGM2U2tm4": {
"title": "XADD",
"description": "The XADD command used to append new entries to a stream data structure. It allows you to add one or more field-value pairs as a single entry, automatically assigning a unique ID to each new entry.\n\nLearn more from the following resources:",
"links": [
{
"title": "XADD Documentation",
"title": "XADD",
"url": "https://redis.io/docs/latest/commands/xadd/",
"type": "article"
}
@@ -1151,7 +1224,7 @@
"description": "The XREAD command reads data from one or more streams, only returning entired with an ID greater than the last recieved ID.\n\nLearn more from the following resources:",
"links": [
{
"title": "XREAD Documentation",
"title": "XREAD",
"url": "https://redis.io/docs/latest/commands/xread/",
"type": "article"
}
@@ -1162,7 +1235,7 @@
"description": "The XRANGE command used for retrieving messages from a stream. It allows you to query a range of messages based on their IDs, returning them in chronological order. This command is particularly useful for reading a portion of a stream's history, enabling efficient data retrieval and processing.\n\nLearn more from the following resources:",
"links": [
{
"title": "XRANGE Documentation",
"title": "XRANGE",
"url": "https://redis.io/docs/latest/commands/xrange/",
"type": "article"
}
@@ -1173,7 +1246,7 @@
"description": "The XLEN command used to get the length of a stream, returning the number of entries it contains. This simple yet powerful command provides a quick way to assess the size of a stream without retrieving its contents. Unlike other Redis types, zero-length streams are possible so XLEN should be used in tandem with TYPE or EXISTS.\n\nLearn more from the following resources:",
"links": [
{
"title": "XLEN Documentation",
"title": "XLEN",
"url": "https://redis.io/docs/latest/commands/xlen/",
"type": "article"
}
@@ -1197,15 +1270,26 @@
},
"_NiUdVQ85qnvryI38k_vQ": {
"title": "Geospatial Indexes",
"description": "Geospatial indexes in Redis are used to efficiently store and query location-based data, enabling fast geospatial operations. Redis uses a sorted set data structure to maintain these indexes, where each member represents a geographic location identified by longitude and latitude coordinates. The coordinates are encoded into a single value, allowing Redis to perform operations like adding locations (`GEOADD`), searching for nearby locations (`GEOSEARCH`), and calculating distances (`GEODIST`). This indexing mechanism allows for rapid retrieval of geospatial data, making it suitable for applications such as mapping services, location tracking, and proximity-based searches.",
"links": []
"description": "Geospatial indexes in Redis are used to efficiently store and query location-based data, enabling fast geospatial operations. Redis uses a sorted set data structure to maintain these indexes, where each member represents a geographic location identified by longitude and latitude coordinates. The coordinates are encoded into a single value, allowing Redis to perform operations like adding locations (`GEOADD`), searching for nearby locations (`GEOSEARCH`), and calculating distances (`GEODIST`). This indexing mechanism allows for rapid retrieval of geospatial data, making it suitable for applications such as mapping services, location tracking, and proximity-based searches.\n\nLearn more from the following resources:",
"links": [
{
"title": "Geospatial Indexing",
"url": "https://redis.io/docs/latest/develop/interact/search-and-query/indexing/geoindex/",
"type": "article"
},
{
"title": "Geospatial Indexes in Redis",
"url": "https://codesignal.com/learn/courses/redis-data-structures-beyond-basics/lessons/introduction-to-geospatial-indexes-in-redis-using-java",
"type": "article"
}
]
},
"U3N1EgHFs1-YUaB_VrJfw": {
"title": "GEOADD",
"description": "The `GEOADD` command in Redis is used to add geospatial data to a sorted set, where each entry consists of a member (a unique identifier) and its corresponding longitude and latitude coordinates. This command allows you to store location-based data efficiently, enabling geospatial queries such as finding members within a specified radius or calculating distances between points. The coordinates are stored in a format that allows for quick retrieval and analysis, making `GEOADD` a powerful tool for applications involving mapping, location tracking, and proximity searches.\n\nLearn more from the following resources:",
"links": [
{
"title": "GEOADD Documentation",
"title": "GEOADD",
"url": "https://redis.io/docs/latest/commands/geoadd/",
"type": "article"
}
@@ -1216,7 +1300,7 @@
"description": "The `GEOSEARCH` command in Redis is used to query geospatial data by finding members within a specified geographic area. It allows you to search for entries based on a central point (latitude and longitude) and a defined radius, or by bounding box coordinates. The command returns a sorted set of members that fall within the specified geographical range, making it ideal for applications that require proximity searches, such as locating nearby businesses or services. `GEOSEARCH` can also be combined with various options, such as sorting results by distance or limiting the number of results returned.\n\nLearn more from the following resources:",
"links": [
{
"title": "GEOADD Documentation",
"title": "GEOADD",
"url": "https://redis.io/docs/latest/commands/geoadd/",
"type": "article"
},
@@ -1259,7 +1343,7 @@
"description": "The SUBSCRIBE command subscribes the client to the channels specified, once subscribed the client enters a state where it is not supposed to issue any other commands, except from those in the SUBSCRIBE subset i.e. SSUBSCRIBE, PSUBSCRIBE etc.\n\nLearn more from the following resources:",
"links": [
{
"title": "SUBSCRIBE Documentation",
"title": "SUBSCRIBE",
"url": "https://redis.io/docs/latest/commands/subscribe/",
"type": "article"
}
@@ -1270,7 +1354,7 @@
"description": "The UNSUBSCRIBE command unsubscribes the client for the given channels, or all channels if none are specifically stated.\n\nLearn more from the following resources:",
"links": [
{
"title": "UNSUBSCRIBE Documentation",
"title": "UNSUBSCRIBE",
"url": "https://redis.io/docs/latest/commands/unsubscribe/",
"type": "article"
}
@@ -1281,7 +1365,7 @@
"description": "`PUBLISH` is a Redis command used to send messages to a specified channel in the pub/sub messaging system. When a message is published, all clients that are subscribed to that channel receive the message immediately. This command is useful for implementing real-time communication features, such as chat applications, notifications, or event broadcasting. The `PUBLISH` command does not return any acknowledgment to the sender, as it operates on a fire-and-forget basis, allowing for efficient message distribution without requiring the sender to wait for subscribers to process the message.\n\nLearn more from the following resources:",
"links": [
{
"title": "PUBLISH Documentation",
"title": "PUBLISH",
"url": "https://redis.io/docs/latest/commands/publish/",
"type": "article"
}
@@ -1339,7 +1423,7 @@
"description": "The WATCH command marks the given keys to be watched for conditional execution of a transaction. It's a critical component of Redis's approach to handling race conditions in multi-key transactions.\n\nLearn more from the following resources:",
"links": [
{
"title": "WATCH Documentation",
"title": "WATCH",
"url": "https://redis.io/docs/latest/commands/watch/",
"type": "article"
}
@@ -1350,7 +1434,7 @@
"description": "The `EXEC` command in Redis is used to execute a transaction that has been initiated with the `MULTI` command. When a transaction is started with `MULTI`, subsequent commands are queued but not executed immediately. Calling `EXEC` will execute all the commands in the transaction atomically, ensuring that either all commands succeed or none are applied. If any command in the transaction fails, the entire transaction is aborted. This command is essential for maintaining data integrity when performing a series of operations that should be treated as a single unit of work.\n\nLearn more from the following resources:",
"links": [
{
"title": "EXEC Documentation",
"title": "EXEC",
"url": "https://redis.io/docs/latest/commands/exec/",
"type": "article"
},
@@ -1366,7 +1450,7 @@
"description": "`MULTI` is a Redis command used to start a transaction, allowing a group of commands to be executed sequentially and atomically. After initiating a `MULTI` block, commands are queued instead of being executed immediately. Once all desired commands are added, the `EXEC` command is called to run them as a single atomic operation. If an error occurs in any command during queuing, it can be discarded using `DISCARD`. `MULTI` ensures that no other clients can interfere with the transaction, making it ideal for complex operations that require consistent state updates.\n\nLearn more from the following resources:",
"links": [
{
"title": "MULTI Command",
"title": "MULTI",
"url": "https://redis.io/docs/latest/commands/multi/",
"type": "article"
}
@@ -1419,7 +1503,7 @@
"description": "The `EVAL` command in Redis allows the execution of Lua scripts directly on the server, enabling complex operations that can be atomically executed. This command takes a Lua script as an argument, along with a list of keys and arguments for the script. By executing scripts server-side, `EVAL` reduces the number of round trips between the client and server, enhances performance, and allows for operations that require multiple commands to be executed in a single atomic operation. It is particularly useful for implementing advanced data manipulations, custom logic, or conditional operations within Redis.\n\nLearn more from the following resources:",
"links": [
{
"title": "EVAL Documentation",
"title": "EVAL",
"url": "https://redis.io/docs/latest/commands/eval/",
"type": "article"
},
@@ -1435,7 +1519,7 @@
"description": "The `EVALSHA` command in Redis is used to execute a Lua script that has already been loaded into the server with the `SCRIPT LOAD` command. Instead of sending the entire script each time, you provide the SHA1 hash of the script, which allows for more efficient execution and reduced network overhead. Like `EVAL`, `EVALSHA` can accept keys and arguments, enabling complex, atomic operations to be performed directly on the server. This approach is particularly beneficial in scenarios where the same script is executed multiple times, as it avoids the need to re-transmit the scripts source code.\n\nLearn more from the following resources:",
"links": [
{
"title": "EVALSHA Documentation",
"title": "EVALSHA",
"url": "https://redis.io/docs/latest/commands/evalsha/",
"type": "article"
},
@@ -1471,6 +1555,11 @@
"title": "How RDB Works?",
"description": "The RDB (Redis Database Backup) mechanism in Redis creates snapshots of the dataset at specified intervals and saves them to disk as a compact binary file. This process is triggered manually, via the `SAVE` or `BGSAVE` commands, or automatically based on predefined conditions. During a snapshot, Redis forks a child process to write the in-memory data to the RDB file, ensuring that the main process is not blocked. While RDB offers a lightweight and fast backup option, it may lead to potential data loss if Redis crashes between snapshots, making it ideal for periodic backups rather than real-time persistence.\n\nLearn more from the following resources:",
"links": [
{
"title": "Backup Data",
"url": "https://redis.io/docs/latest/operate/rc/databases/back-up-data/",
"type": "article"
},
{
"title": "About RDB Snapshots",
"url": "https://cloud.google.com/memorystore/docs/redis/about-rdb-snapshots",
@@ -1562,7 +1651,7 @@
"description": "The **No Persistence** option in Redis disables all data persistence mechanisms, meaning that no data will be saved to disk. This can be configured by turning off both RDB snapshots and AOF (Append-Only File) logging. Running Redis without persistence is ideal for use cases where high-speed caching is prioritized over data durability, such as storing ephemeral data or managing sessions that dont need to survive a server restart. While this option reduces disk I/O and maximizes performance, it also means that all data will be lost if the server is shut down or crashes, making it suitable only for scenarios where data loss is acceptable.\n\nLearn more from the following resources:",
"links": [
{
"title": "Redis Persistence Documentation",
"title": "Redis Persistence",
"url": "https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/",
"type": "article"
}
@@ -1616,7 +1705,7 @@
"description": "Redis Sentinel serves as a robust high-availability solution for Redis deployments, offering a comprehensive suite of monitoring, notification, and automatic failover capabilities. By continuously overseeing master and replica Redis servers, Sentinel ensures system integrity and swift response to failures. In the event of a master instance failure, it seamlessly promotes a replica to master status, reconfiguring the system to maintain service continuity with minimal downtime.\n\nLearn more from the following resources:",
"links": [
{
"title": "High Availability with Redis Sentinal",
"title": "High Availability with Redis Sentinel",
"url": "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/",
"type": "article"
}
@@ -1654,12 +1743,12 @@
"description": "Network security in Redis involves implementing measures to protect the server from unauthorized access and data breaches. Best practices include binding Redis to trusted interfaces, using firewalls to restrict access, and configuring `requirepass` for password protection. Redis should run in a secure network environment, ideally with TLS/SSL enabled for encrypted communication. Additionally, the use of `ACL` (Access Control Lists) provides granular permissions for different users. By disabling dangerous commands and using proper authentication and authorization mechanisms, Redis instances can be secured against common threats such as unauthorized data access and denial-of-service attacks.\n\nLearn more from the following resources:",
"links": [
{
"title": "Redis Authentication Documentation",
"title": "Redis Authentication",
"url": "https://redis.io/docs/latest/operate/oss_and_stack/management/security/#authentication",
"type": "article"
},
{
"title": "Redis Network Security Documentation",
"title": "Redis Network Security",
"url": "https://redis.io/docs/latest/operate/rc/security/database-security/network-security/",
"type": "article"
}
@@ -1683,10 +1772,10 @@
},
"q2Jw49QUWCUGIfcEC1bZI": {
"title": "INFO",
"description": "`INFO` is a Redis command that provides detailed information and statistics about the server, including memory usage, CPU load, connected clients, replication status, and more. It can be called without arguments to get a full overview or with specific sections (e.g., `INFO memory`) to retrieve targeted data. This command is useful for monitoring and debugging Redis instances, helping administrators understand the server's current state and performance metrics.\n\nLearn more from the following resources:",
"description": "`INFO` command that provides detailed information and statistics about the server, including memory usage, CPU load, connected clients, replication status, and more. It can be called without arguments to get a full overview or with specific sections (e.g., `INFO memory`) to retrieve targeted data. This command is useful for monitoring and debugging Redis instances, helping administrators understand the server's current state and performance metrics.\n\nLearn more from the following resources:",
"links": [
{
"title": "INFO Documentation",
"title": "INFO",
"url": "https://redis.io/docs/latest/commands/info/",
"type": "article"
}
@@ -1697,12 +1786,12 @@
"description": "`MONITOR` is a Redis command that provides a real-time feed of all commands executed on the server, displaying each command along with its arguments as they are processed. It is primarily used for debugging, monitoring, or analyzing the behavior of a Redis instance. Since `MONITOR` can impact performance by streaming every command in real-time, it should be used cautiously in production environments. It is a useful tool for understanding command patterns, tracking down issues, and gaining insights into how clients interact with the Redis server.\n\nLearn more from the following resources:",
"links": [
{
"title": "MONITOR Documentation",
"title": "MONITOR",
"url": "https://redis.io/docs/latest/commands/monitor/",
"type": "article"
},
{
"title": "SLOWLOG Documentation",
"title": "SLOWLOG",
"url": "https://redis.io/docs/latest/commands/slowlog/",
"type": "article"
}
@@ -1729,7 +1818,7 @@
"description": "RedisCommander is a web-based GUI management tool for Redis that simplifies the interaction with Redis databases through a user-friendly interface. It allows users to browse, edit, and manage Redis keys and data structures easily, providing visual representations of data types such as strings, hashes, lists, sets, and sorted sets. With RedisCommander, users can perform common operations like adding, modifying, and deleting keys, as well as executing commands directly from the interface. The tool also supports features like searching for keys, viewing key details, and monitoring server performance metrics. RedisCommander is particularly useful for developers and administrators who prefer a graphical interface over command-line interaction, making it easier to manage Redis instances, troubleshoot issues, and explore data in real-time. Overall, it enhances productivity and streamlines Redis database management tasks.\n\nLearn more from the following resources:",
"links": [
{
"title": "joeferner/redis-commander - RedisCommander Module on GitHub",
"title": "joeferner/redis-commander - GitHub",
"url": "https://github.com/joeferner/redis-commander",
"type": "opensource"
}
@@ -1750,7 +1839,7 @@
"type": "article"
},
{
"title": "Memory Management best practices",
"title": "Memory Management Best Practices",
"url": "https://cloud.google.com/memorystore/docs/redis/memory-management-best-practices",
"type": "article"
}
@@ -1759,7 +1848,18 @@
"Sd1ENOXSFCz1YqccXjr2A": {
"title": "Max Memory Policy",
"description": "The Max Memory Policy in Redis determines how the server handles data when it reaches the configured maximum memory limit. Redis offers several eviction policies, such as `noeviction` (return an error on writes), `allkeys-lru` (evict the least recently used keys), `volatile-lru` (evict the least recently used keys with an expiration set), `allkeys-random` (evict random keys), and others. These policies allow Redis to optimize memory usage based on the use case, balancing between maintaining data availability and minimizing the risk of data loss when memory constraints are reached.\n\nLearn more from the following resources:",
"links": []
"links": [
{
"title": "Database Memory Limits",
"url": "https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit/",
"type": "article"
},
{
"title": "Eviction Policy",
"url": "https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy/",
"type": "article"
}
]
},
"yaCWw2KjX58SaPajUAb0d": {
"title": "Slow Log Analysis",
@@ -1782,7 +1882,7 @@
"description": "`redis-benchmark` is a utility provided with Redis that measures the performance of the Redis server by simulating various types of workloads. It allows users to test the speed and responsiveness of Redis commands under different conditions, providing metrics such as requests per second and latency. The tool can simulate multiple clients and different command types, such as `GET`, `SET`, and `INCR`, enabling users to evaluate the performance of their Redis configuration and hardware. By adjusting parameters like the number of parallel connections and the number of requests to be sent, `redis-benchmark` helps identify performance bottlenecks, optimize configurations, and assess the impact of changes to the Redis environment, making it a valuable tool for capacity planning and performance tuning.\n\nLearn more from the following resources:",
"links": [
{
"title": "Redis Benchmark Documentation",
"title": "Redis Benchmark",
"url": "https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/benchmarks/",
"type": "article"
},
@@ -1798,12 +1898,12 @@
"description": "Monitoring in Redis involves tracking the health, performance, and resource usage of the server to ensure optimal operation and early detection of issues. Tools and commands like `INFO` (providing statistics on memory, CPU, and clients), `MONITOR` (real-time command tracking), and `SLOWLOG` (logging slow queries) offer insights into server activity. Additionally, external tools like Redis Sentinel, Prometheus, and Grafana are often integrated for more comprehensive monitoring, alerting, and visualization. Effective monitoring helps maintain stability, optimize performance, and troubleshoot potential bottlenecks, making it crucial for managing Redis deployments at scale.\n\nLearn more from the following resources:",
"links": [
{
"title": "Monitoring with metrics and alerts",
"title": "Monitoring with Metrics and Alerts",
"url": "https://redis.io/docs/latest/operate/rs/clusters/monitoring/",
"type": "article"
},
{
"title": "MONITOR Documentation",
"title": "MONITOR",
"url": "https://redis.io/docs/latest/commands/monitor/",
"type": "article"
}
@@ -1819,7 +1919,7 @@
"description": "RedisJSON is a Redis module that enables the storage, retrieval, and manipulation of JSON documents directly within Redis. It provides a rich set of commands for working with JSON data structures, allowing users to perform operations such as adding, updating, and querying JSON documents efficiently. With RedisJSON, users can store complex nested data and perform queries on specific fields, making it ideal for applications that require handling structured data without the need for additional data transformation or processing layers. Key features include support for JSONPath querying, atomic updates, and the ability to index JSON fields for faster retrieval. This module enhances Redis's capabilities, making it a suitable choice for use cases like real-time analytics, configuration management, and any application that benefits from the flexibility and performance of JSON data structures. Overall, RedisJSON allows developers to leverage the speed of Redis while working with JSON natively, streamlining data management in modern applications.\n\nLearn more from the following resources:",
"links": [
{
"title": "RedisJSON/RedisJSON - RedisJSON Module on GitHub",
"title": "RedisJSON/RedisJSON - GitHub",
"url": "https://github.com/RedisJSON/RedisJSON",
"type": "opensource"
},
@@ -1867,7 +1967,7 @@
"description": "RedisBloom is a Redis module that extends the capabilities of Redis by introducing probabilistic data structures, allowing for efficient membership testing and counting while minimizing memory usage. It provides tools such as Bloom Filters, Cuckoo Filters, Count-Min Sketches, and HyperLogLogs, enabling developers to manage large datasets with high performance and low memory overhead. With Bloom Filters, for instance, users can quickly determine if an element is possibly in a set or definitely not, making it useful for applications like web caching, spam filtering, and network security. Cuckoo Filters offer similar functionality but allow for the deletion of items. Count-Min Sketches enable approximate counting of elements in a dataset, while HyperLogLogs provide efficient cardinality estimation. RedisBloom is particularly beneficial for use cases involving big data analytics, real-time monitoring, and applications requiring high throughput with limited memory resources.\n\nLearn more from the following resources:",
"links": [
{
"title": "RedisBloom/RedisBloom - RedisBloom on GitHub",
"title": "RedisBloom/RedisBloom - GitHub",
"url": "https://github.com/RedisBloom/RedisBloom",
"type": "opensource"
},
@@ -1899,7 +1999,7 @@
"description": "Backing up and recovering Redis data is crucial for ensuring data persistence and reliability. Redis, by default, stores its data in memory for fast access, but it provides mechanisms to persist data to disk to allow for recovery in case of failure or system restarts. The primary methods for backup and recovery in Redis are RDB snapshots and AOF (Append-Only File). These methods can be used individually or in combination, depending on the specific use case.\n\nLearn more from the following resources:",
"links": [
{
"title": "Backup and recovery",
"title": "Backup and Recovery",
"url": "https://redis.io/redis-enterprise/technology/backup-disaster-recovery/",
"type": "article"
},
@@ -1944,8 +2044,24 @@
},
"8lyXDuZJ-KHl4v2_8Ew1h": {
"title": "Redis Enterprise",
"description": "Redis Enterprise is a commercial offering that extends the capabilities of open-source Redis with advanced features designed for high availability, scalability, and performance in enterprise environments. It provides automatic sharding and replication, allowing for seamless horizontal scaling across multiple nodes and data centers. Redis Enterprise supports various deployment options, including on-premises, cloud, and hybrid environments, and offers enhanced data persistence options like active-active geo-distribution for global applications. Additionally, it includes advanced security features, such as role-based access control (RBAC), encryption, and audit logging, along with built-in monitoring and management tools. Redis Enterprise is particularly suited for mission-critical applications that require low-latency access to data and robust data management capabilities, making it ideal for use cases like real-time analytics, session management, and caching.",
"links": []
"description": "Redis Enterprise is a commercial offering that extends the capabilities of open-source Redis with advanced features designed for high availability, scalability, and performance in enterprise environments. It provides automatic sharding and replication, allowing for seamless horizontal scaling across multiple nodes and data centers. Redis Enterprise supports various deployment options, including on-premises, cloud, and hybrid environments, and offers enhanced data persistence options like active-active geo-distribution for global applications. Additionally, it includes advanced security features, such as role-based access control (RBAC), encryption, and audit logging, along with built-in monitoring and management tools. Redis Enterprise is particularly suited for mission-critical applications that require low-latency access to data and robust data management capabilities, making it ideal for use cases like real-time analytics, session management, and caching.\n\nLearn more from the following resources:",
"links": [
{
"title": "Redis Enterprise",
"url": "https://redis.io/about/redis-enterprise/",
"type": "article"
},
{
"title": "Redis Enterprise Software",
"url": "https://redis.io/docs/latest/operate/rs/",
"type": "article"
},
{
"title": "Redis Open Source vs. Enterprise",
"url": "https://www.metricfire.com/blog/redis-open-source-vs-enterprise/",
"type": "article"
}
]
},
"cybF72wlJyJbHLUjitLvn": {
"title": "Active-Active geo Distribution",
@@ -2010,7 +2126,7 @@
"type": "article"
},
{
"title": "Redis Enterprise Website",
"title": "Redis Enterprise",
"url": "https://redis.io/enterprise/",
"type": "article"
}

View File

@@ -1260,7 +1260,7 @@
},
"Ps9Yv2s-bKvEegGAbPsiA": {
"title": "Query Optimization",
"description": "Query optimization in SQL involves refining queries to enhance their execution speed and reduce resource consumption. Key strategies include indexing columns used in `WHERE`, `JOIN`, and `ORDER BY` clauses to accelerate data retrieval, minimizing data processed by limiting the number of columns selected and filtering rows early in the query. Using appropriate join types and arranging joins in the most efficient order are crucial. Avoiding inefficient patterns like `SELECT`, replacing subqueries with joins or common table expressions (CTEs), and leveraging query hints or execution plan analysis can also improve performance. Regularly updating statistics and ensuring that queries are structured to take advantage of database-specific optimizations are essential practices for maintaining optimal performance.\n\nLearn more from the following resources:",
"description": "Query optimization in SQL involves refining queries to enhance their execution speed and reduce resource consumption. Key strategies include indexing columns used in `WHERE`, `JOIN`, and `ORDER BY` clauses to accelerate data retrieval, minimizing data processed by limiting the number of columns selected and filtering rows early in the query. Using appropriate join types and arranging joins in the most efficient order are crucial. Avoiding inefficient patterns like `SELECT *`, replacing subqueries with joins or common table expressions (CTEs), and leveraging query hints or execution plan analysis can also improve performance. Regularly updating statistics and ensuring that queries are structured to take advantage of database-specific optimizations are essential practices for maintaining optimal performance.\n\nLearn more from the following resources:",
"links": [
{
"title": "12 Ways to Optimize SQL Queries",
@@ -1495,7 +1495,7 @@
},
"UVTgbZrqpbYl1bQvQejcF": {
"title": "Reducing Subqueries",
"description": "Recursive queries in SQL allow for iterative processing of hierarchical or tree-structured data within a single query. They consist of an anchor member (the base case) and a recursive member that references the query itself, enabling the exploration of parent-child relationships, traversal of graphs, or generation of series data. This powerful feature is particularly useful for tasks like querying organizational hierarchies, bill of materials structures, or navigating complex relationships in data that would otherwise require multiple separate queries or procedural code.\n\nLearn more from the following resources:",
"description": "Reducing subqueries is a common SQL optimization technique, especially when dealing with complex logic or large datasets. Correlated subqueries, which are evaluated once for each row in the outer query, can degrade the performance. Subqueries can often be replaced with JOIN operations. In cases where subqueries are reused, consider replacing them with Common Table Expressions (CTEs), which offer modularity and avoid repeated executions of the same logic. Limiting the result set returned by subqueries and storing the results of expensive subqueries in temporary tables for reuse can also improve performance.\n\nLearn more from the following resources:",
"links": []
},
"w53CSY53nAAN0ux-XeJ4c": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

View File

@@ -87,6 +87,8 @@ Here is the list of available roadmaps with more being actively worked upon.
- [Prompt Engineering Roadmap](https://roadmap.sh/prompt-engineering)
- [Technical Writer Roadmap](https://roadmap.sh/technical-writer)
- [DevRel Engineer Roadmap](https://roadmap.sh/devrel)
- [AI Red Teaming Roadmap](https://roadmap.sh/ai-red-teaming)
- [AI Agents Roadmap](https://roadmap.sh/ai-agents)
There are also interactive best practices:

View File

@@ -1,14 +0,0 @@
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

@@ -1,5 +0,0 @@
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

@@ -1,7 +1,7 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Node } from 'reactflow';
import type { Node } from '@roadmapsh/editor';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
import { slugify } from '../src/lib/slugger';

View File

@@ -1,7 +1,7 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Edge, Node } from 'reactflow';
import type { Edge, Node } from '@roadmapsh/editor';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
import { slugify } from '../src/lib/slugger';

View File

@@ -1,7 +1,7 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Node } from 'reactflow';
import type { Node } from '@roadmapsh/editor';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
import { slugify } from '../src/lib/slugger';

View File

@@ -2,33 +2,24 @@
set -e
# ignore cloning if .temp/web-draw already exists
# Remove old editor
rm -rf editor
if [ ! -d ".temp/web-draw" ]; then
mkdir -p .temp
git clone git@github.com:roadmapsh/web-draw.git .temp/web-draw
git clone ssh://git@github.com/roadmapsh/web-draw.git .temp/web-draw --depth 1
fi
rm -rf editor
mkdir editor
# Make dir
mkdir -p packages/editor
mkdir -p packages/editor/dist
# copy the files at /src/editor/* to /editor
# while replacing any existing files
cp -rf .temp/web-draw/src/editor/* editor
# Copy the editor dist, package.json
cp -rf .temp/web-draw/packages/editor/dist packages/editor
cp -rf .temp/web-draw/packages/editor/package.json packages/editor
# Add @ts-nocheck to the top of each ts and tsx file
# so that the typescript compiler doesn't complain
# about the missing types
find editor -type f \( -name "*.ts" -o -name "*.tsx" \) -print0 | while IFS= read -r -d '' file; do
if [ -f "$file" ]; then
echo "// @ts-nocheck" > temp
cat "$file" >> temp
mv temp "$file"
echo "Added @ts-nocheck to $file"
fi
done
# Remove temp directory
rm -rf .temp
# ignore the worktree changes for the editor directory
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
# Reinstall so that the editor which was setup gets used
rm -rf node_modules
pnpm install

View File

@@ -0,0 +1,76 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Node } from '@roadmapsh/editor';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Directory containing the roadmaps
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/data/roadmaps');
const allRoadmaps = await fs.readdir(ROADMAP_CONTENT_DIR);
const editorRoadmapIds = new Set<string>();
for (const roadmapId of allRoadmaps) {
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.renderer === 'editor') {
editorRoadmapIds.add(roadmapId);
}
}
for (const roadmapId of editorRoadmapIds) {
const roadmapJSONDir = path.join(
ROADMAP_CONTENT_DIR,
roadmapId,
`${roadmapId}.json`,
);
const roadmapJSONRaw = await fs.readFile(roadmapJSONDir, 'utf-8');
const roadmapJSON = JSON.parse(roadmapJSONRaw);
const roadmapNodes = roadmapJSON.nodes as Node[];
const updatedNodes = roadmapNodes.map((node) => {
const width = +(node?.width || node?.style?.width || 0);
const height = +(node?.height || node?.style?.height || 0);
const ADDITIONAL_WIDTH = 1;
// adding one `1px` in width to avoid the node to be cut in half
// this is a quick fix to avoid the issue
if (node?.style?.width) {
node.style.width = width + ADDITIONAL_WIDTH;
}
if (node?.width) {
node.width = width + ADDITIONAL_WIDTH;
}
return {
...node,
measured: {
width: width + ADDITIONAL_WIDTH,
height,
},
};
});
const updatedRoadmapJSON = {
...roadmapJSON,
nodes: updatedNodes,
};
const updatedRoadmapJSONString = JSON.stringify(updatedRoadmapJSON, null, 2);
await fs.writeFile(roadmapJSONDir, updatedRoadmapJSONString, 'utf-8');
}

58
scripts/rename-content.ts Normal file
View File

@@ -0,0 +1,58 @@
import fs from 'fs';
import path from 'path';
const roadmapDirs = fs.readdirSync(
path.join(__dirname, '..', 'src', 'data', 'roadmaps'),
);
roadmapDirs.forEach((roadmapDir) => {
const roadmapDirPath = path.join(
__dirname,
'..',
'src',
'data',
'roadmaps',
roadmapDir,
'content',
);
const roadmapDirContent = fs.readdirSync(roadmapDirPath);
roadmapDirContent.forEach((content) => {
const contentPath = path.join(roadmapDirPath, content);
const contentStats = fs.statSync(contentPath);
const oldName = path.basename(contentPath);
const newName = oldName.replace(/^(\d+)-/, '');
fs.renameSync(contentPath, path.join(roadmapDirPath, newName));
if (contentStats.isDirectory()) {
const contentDirContent = fs.readdirSync(contentPath);
contentDirContent.forEach((contentDir) => {
const contentDirPath = path.join(contentPath, contentDir);
const contentDirStats = fs.statSync(contentDirPath);
const oldName = path.basename(contentDirPath);
const newName = oldName.replace(/^(\d+)-/, '');
fs.renameSync(contentDirPath, path.join(contentPath, newName));
if (contentDirStats.isDirectory()) {
const contentDirContent = fs.readdirSync(contentDirPath);
contentDirContent.forEach((contentDir) => {
const contentDirPath2 = path.join(contentDirPath, contentDir);
const contentDirStats2 = fs.statSync(contentDirPath2);
const oldName2 = path.basename(contentDirPath2);
const newName2 = oldName2.replace(/^(\d+)-/, '');
fs.renameSync(contentDirPath2, path.join(contentDirPath, newName2));
});
}
});
}
});
});

View File

@@ -13,494 +13,394 @@ const __dirname = dirname(__filename);
* @property {string} text - The text content of the node
*/
const roadmapId = 'php';
const roadmapId = 'ai-agents';
/** @type {Node[]} */
const nodes = [
{
"id": "_hYN0gEi9BL24nptEtXWU",
"text": "PHP > Introduction to PHP"
id: 'ZF5_5Y5zqa75Ov22JACX6',
text: 'AI Agents > Transformer Models and LLMs',
},
{
"id": "_LhLDVZjLt1DoAP1NuUES",
"text": "PHP > Introduction to PHP > What is PHP?"
id: 'GAjuWyJl9CI1nqXBp6XCf',
text: 'AI Agents > LLM Fundamentals > Model Mechanis > Tokenization',
},
{
"id": "b2CuLrhsUNnb4OxI6RRAS",
"text": "PHP > Introduction to PHP > Evolution and History"
id: 'dyn1LSioema-Bf9lLTgUZ',
text: 'AI Agents > LLM Fundamentals > Model Mechanis > Context Windows',
},
{
"id": "6sHRQTcoKL3TlgNJlwyx8",
"text": "PHP > Introduction to PHP > PHP Versions and Features"
id: '1fiWPBV99E2YncqdCgUw2',
text: 'AI Agents > LLM Fundamentals > Model Mechanis > Token Based Pricing',
},
{
"id": "3_TuxOSzBuktBlBF05r_z",
"text": "PHP > Installing PHP"
id: 'L1zL1GzqjSAjF06pIIXhy',
text: 'AI Agents > LLM Fundamentals > Generation Controls > Temperature',
},
{
"id": "36Y1HkHxhuxh2qVQB8NVE",
"text": "PHP > WAMP"
id: 'icbp1NjurQfdM0dHnz6v2',
text: 'AI Agents > LLM Fundamentals > Generation Controls > Top-p',
},
{
"id": "-wniKEBwbF0Fi1fHpF-Gc",
"text": "PHP > XAMPP"
id: 'z_N-Y0zGkv8_qHPuVtimL',
text: 'AI Agents > LLM Fundamentals > Generation Controls > Frequency Penalty',
},
{
"id": "t7p7TU2khaxsZPYAdwFAA",
"text": "PHP > MAMP"
id: 'Vd8ycw8pW-ZKvg5WYFtoh',
text: 'AI Agents > LLM Fundamentals > Generation Controls > Presence Penalty',
},
{
"id": "7LjxtrmgJtTJc0_kP83Tr",
"text": "PHP > LAMP"
id: 'K0G-Lw069jXUJwZqHtybd',
text: 'AI Agents > LLM Fundamentals > Generation Controls > Stopping Criteria',
},
{
"id": "hzBUHSuFwLYNooF_vEmrs",
"text": "PHP > Basic PHP Syntax"
id: 'Bn_BkthrVX_vOuwQzvPZa',
text: 'AI Agents > LLM Fundamentals > Generation Controls > Max Length',
},
{
"id": "D0BtyxyjIBcpfn5wP23WC",
"text": "PHP > Variables and Scope"
id: 'DSJAhQhc1dQmBHQ8ZkTau',
text: 'AI Agents > Model Families and Licences > Open Weight Models',
},
{
"id": "srIHPZabaCGdB5VvUXaMa",
"text": "PHP > Data Types"
id: 'tJYmEDDwK0LtEux-kwp9B',
text: 'AI Agents > Model Families and Licences > Closed Weight Models',
},
{
"id": "pzReF4C0mcCWAnpfIJbwl",
"text": "PHP > Casting Data Types"
id: 'i2NE6haX9-7mdoV5LQ3Ah',
text: 'AI Agents > Understand the Basics > Streamed vs Unstreamed Responses',
},
{
"id": "2ykzBBdYhWuM-neGf0AWP",
"text": "PHP > echo"
id: 'N3yZfUxphxjiupqGpyaS9',
text: 'AI Agents > Understand the Basics > Reasoning vs Standard Models',
},
{
"id": "NQUmO90sqe7fnzod3Ia8H",
"text": "PHP > print"
id: '5OW_6o286mj470ElFyJ_5',
text: 'AI Agents > Understand the Basics > Fine-tuning vs Prompt Engineering',
},
{
"id": "wsC7OGXOyfCY4pLLNrR2v",
"text": "PHP > print_r"
id: 'UIm54UmICKgep6s8Itcyv',
text: 'AI Agents > Understand the Basics > Embeddings and Vector Search',
},
{
"id": "JCCeVC0hOrvIeyfg1ScKA",
"text": "PHP > var_dump"
id: 'qwVQOwBTLA2yUgRISzC8k',
text: 'AI Agents > Understand the Basics > Understand the Basics of RAG',
},
{
"id": "VLRLymQmLfscrBfzXKvHi",
"text": "PHP > Constants"
id: 'B8dzg61TGaknuruBgkEJd',
text: 'AI Agents > Understand the Basics > Pricing of Common Models',
},
{
"id": "IhKjvT6CjRz4dsSU7SNQo",
"text": "PHP > Arrays"
id: 'aFZAm44nP5NefX_9TpT0A',
text: 'AI Agents > AI Agents 101 > What are AI Agents?',
},
{
"id": "j2S8dP3HlAOOoZdpj-7Dx",
"text": "PHP > Arrays > Indexed Arrays"
id: '2zsOUWJQ8e7wnoHmq1icG',
text: 'AI Agents > AI Agents 101 > What are Tools?',
},
{
"id": "i_NRsOJNNp7AOqMgu5Jg8",
"text": "PHP > Arrays > Associative Arrays"
id: 'Eih4eybuYB3C2So8K0AT3',
text: 'AI Agents > AI Agents 101 > Agent Loop',
},
{
"id": "uARTOZ-ZwugSmbCJoRS5Y",
"text": "PHP > Arrays > Multi-dimensional Arrays"
id: 'LU76AhCYDjxdBhpMQ4eMU',
text: 'AI Agents > AI Agents 101 > Agent Loop > Perception / User Input',
},
{
"id": "38YksjvhXCbgnHqkl57Cz",
"text": "PHP > Conditionals"
id: 'ycPRgRYR4lEBQr_xxHKnM',
text: 'AI Agents > AI Agents 101 > Agent Loop > Reason and Plan',
},
{
"id": "-McOv-ZPTGayX7Mx2Thw1",
"text": "PHP > Conditionals > if/else"
id: 'sHYd4KsKlmw5Im3nQ19W8',
text: 'AI Agents > AI Agents 101 > Agent Loop > Acting / Tool Invocation',
},
{
"id": "bgJ9-m6Fiu3VCc-NZlbpn",
"text": "PHP > Conditionals > switch"
id: 'ZJTrun3jK3zBGOTm1jdMI',
text: 'AI Agents > AI Agents 101 > Agent Loop > Observation & Reflection',
},
{
"id": "3gNzX-bw2iqur7U7-_W38",
"text": "PHP > Conditionals > match"
id: 'PPdAutqJF5G60Eg9lYBND',
text: 'AI Agents > AI Agents 101 > Example Usecases > Personal assistant',
},
{
"id": "w0ntgFBhgGd5RUFd-qlPK",
"text": "PHP > Conditionals > Null Coalescing Operator"
id: 'PK8w31GlvtmAuU92sHaqr',
text: 'AI Agents > AI Agents 101 > Example Usecases > Code generation',
},
{
"id": "1NXSk8VZDr89jQTTkOL7x",
"text": "PHP > Conditionals > Null Safe Operator"
id: 'wKYEaPWNsR30TIpHaxSsq',
text: 'AI Agents > AI Agents 101 > Example Usecases > Data analysis',
},
{
"id": "qwt8xN4vuTrY-D0czYITI",
"text": "PHP > Loops"
id: '5oLc-235bvKhApxzYFkEc',
text: 'AI Agents > AI Agents 101 > Example Usecases > Web Scraping / Crawling',
},
{
"id": "WiGv7vi7Mtw-YqPMcnnyw",
"text": "PHP > Functions"
id: 'ok8vN7VtCgyef5x6aoQaL',
text: 'AI Agents > AI Agents 101 > Example Usecases > NPC / Game AI',
},
{
"id": "1nODJchgSuWbcvSlxnWeE",
"text": "PHP > Functions > Function Declaration"
id: 'Y8EqzFx3qxtrSh7bWbbV8',
text: 'AI Agents > Prompt Engineering > What is Prompt Engineering',
},
{
"id": "mpQKoBzsOa-5iWo08sOhQ",
"text": "PHP > Functions > Parameters / Return Values"
id: 'qFKFM2qNPEN7EoD0V-1SM',
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Be specific in what you want',
},
{
"id": "RgVP99rJJ8FVecIA45w20",
"text": "PHP > Functions > Default / Optional Params"
id: '6I42CoeWX-kkFXTKAY7rw',
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Provide additional context',
},
{
"id": "RkNjYva8o_jXp9suz5YdG",
"text": "PHP > Functions > Named Arguments"
id: 'sUwdtOX550tSdceaeFPmF',
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Use relevant technical terms',
},
{
"id": "Nr5m6wQLp7VyG3AucrSc8",
"text": "PHP > Functions > Anonymous Functions"
id: 'yulzE4ZNLhXOgHhG7BtZQ',
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Use Examples in your Prompt',
},
{
"id": "x7hA2KAzJIjc-prgCEw6V",
"text": "PHP > Functions > Callback Functions"
id: 'noTuUFnHSBzn7GKG9UZEi',
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Iterate and Test your Prompts',
},
{
"id": "mP1BIkqbWVVTU-zZv1ZL6",
"text": "PHP > Functions > Arrow Functions"
id: 'wwHHlEoPAx0TLxbtY6nMA',
text: 'AI Agents > Prompt Engineering > Writing Good Prompts > Specify Length, format etc',
},
{
"id": "D9ybK5INH5zSOcYMb5ZPi",
"text": "PHP > Functions > Recursion"
id: 'qakbxB8xe7Y8gejC5cZnK',
text: 'AI Agents > AI Agents 101 > Tools / Actions > Tool Definition',
},
{
"id": "rtmytETfyyLdcXUC0QyzL",
"text": "PHP > Functions > Variadic Functions"
id: 'kBtqT8AduLoYDWopj-V9_',
text: 'AI Agents > Tools / Actions > Examples of Tools > Web Search',
},
{
"id": "Kaaqu-mN7xvHN6CbIn616",
"text": "PHP > File Handling > require"
id: 'mS0EVCkWuPN_GkVPng4A2',
text: 'AI Agents > Tools / Actions > Examples of Tools > Code Execution / REPL',
},
{
"id": "-CyJbsg2ho3RvfzKnJj5C",
"text": "PHP > File Handling > require_once"
id: 'sV1BnA2-qBnXoKpUn-8Ub',
text: 'AI Agents > Tools / Actions > Examples of Tools > Database Queries',
},
{
"id": "hKfv7V6bl2LXssq9Ffi7C",
"text": "PHP > File Handling > include"
id: '52qxjZILV-X1isup6dazC',
text: 'AI Agents > Tools / Actions > Examples of Tools > API Requests > Tools / Actions > Examples of Tools > API Requests',
},
{
"id": "SwtLDgyPmDry20qS4FBfH",
"text": "PHP > File Handling > include_once"
id: 'qaNr5I-NQPnfrRH7ynGTl',
text: 'AI Agents > Tools / Actions > Examples of Tools > Email / Slack / SMS',
},
{
"id": "S9wTlkbv9-R6dohhZ47hs",
"text": "PHP > File Operations > Reading Files"
id: 'BoJqZvdGam4cd6G6yK2IV',
text: 'AI Agents > Tools / Actions > Examples of Tools > File System Access',
},
{
"id": "two4UycJaCfSp6jQqtTAb",
"text": "PHP > File Operations > Writing Files"
id: '1B0IqRNYdtbHDi1jHSXuI',
text: 'AI Agents > Model Context Protocol (MCP)',
},
{
"id": "tgIyG6vHWpe9sz6lHmj5a",
"text": "PHP > File Operations > File Permissions"
id: '9FryAIrWRHh8YlzKX3et5',
text: 'AI Agents > Model Context Protocol (MCP) > Core Components > MCP Hosts',
},
{
"id": "MRDjEjbkMpk7shcWAoPOF",
"text": "PHP > File Operations > CSV Processing"
id: 'CGVstUxVXLJcYZrwk3iNQ',
text: 'AI Agents > Model Context Protocol (MCP) > Core Components > MCP Client',
},
{
"id": "DB2cxZE58WCCavW2PNwmf",
"text": "PHP > File Operations > JSON Processing"
id: 'yv_-87FVM7WKn5iv6LW9q',
text: 'AI Agents > Model Context Protocol (MCP) > Core Components > MCP Servers',
},
{
"id": "ggkWo0DRSSDDkHpbiyUyf",
"text": "PHP > File Operations > XML Processing"
id: '1NXIN-Hbjl5rPy_mqxQYW',
text: 'AI Agents > Model Context Protocol (MCP) > Creating MCP Servers',
},
{
"id": "tn_iIfaJZVtPK6vFds7FH",
"text": "PHP > HTTP / Request Handling > HTTP Methods"
id: 'iBtJp24F_kJE3YlBsW60s',
text: 'AI Agents > Model Context Protocol (MCP) > Creating MCP Servers > Deployment Modes > Local Desktop',
},
{
"id": "GFYGFVfxkOoPI5mI4zSt1",
"text": "PHP > HTTP / Request Handling > $_GET"
id: 'dHNMX3_t1KSDdAWqgdJXv',
text: 'AI Agents > Model Context Protocol (MCP) > Creating MCP Servers > Deployment Modes > Remote / Cloud',
},
{
"id": "qNG-a4iIO-puZsMwAMzYC",
"text": "PHP > HTTP / Request Handling > $_POST"
id: 'TBH_DZTAfR8Daoh-njNFC',
text: 'AI Agents > Agent Memory > What is Agent Memory?',
},
{
"id": "A6rfW4uJhyfAX2b18_EEC",
"text": "PHP > HTTP / Request Handling > $_REQUEST"
id: 'M3U6RfIqaiut2nuOibY8W',
text: 'AI Agents > Agent Memory > Short Term Memory',
},
{
"id": "7Ja2at_N9tRTlvSGahrqn",
"text": "PHP > HTTP / Request Handling > $_SERVER"
id: 'Ue633fz6Xu2wa2-KOAtdP',
text: 'AI Agents > Agent Memory > What is Agent Memory? > Long Term Memory',
},
{
"id": "sYI7f1PYP7G30_Uj2mZRv",
"text": "PHP > Form Processing"
id: 'EfCCNqLMJpWKKtamUa5gK',
text: 'AI Agents > Agent Memory > What is Agent Memory? > Episodic vs Semantic Memory',
},
{
"id": "HNo8QO4aPbvgePiA4l6tq",
"text": "PHP > File Uploads"
id: 'wkS4yOJ3JdZQE_yBID8K7',
text: 'AI Agents > Agent Memory > What is Agent Memory? > Maintaining Memory > RAG and Vector Databases',
},
{
"id": "CGehmZjcgTWC7fQAvxmNW",
"text": "PHP > State Management"
id: 'QJqXHV8VHPTnfYfmKPzW7',
text: 'AI Agents > Agent Memory > What is Agent Memory? > Maintaining Memory > User Profile Storage',
},
{
"id": "so03-fK7E2WvTm6XsPq4i",
"text": "PHP > State Management > Cookies"
id: 'jTDC19BTWCqxqMizrIJHr',
text: 'AI Agents > Agent Memory > Maintaining Memory > Summarization / Compression',
},
{
"id": "qobzzgzArNHLLn9Oiqc6G",
"text": "PHP > State Management > Sessions"
id: 'm-97m7SI0XpBnhEE8-_1S',
text: 'AI Agents > Agent Memory > Maintaining Memory > Forgetting / Aging Strategies',
},
{
"id": "93oEIZttb85S23C1fLraP",
"text": "PHP > Basics of Security > Input Validation"
id: 'cW8O4vLLKEG-Q0dE8E5Zp',
text: 'AI Agents > Agent Architectures > Common Architectures > RAG Agent',
},
{
"id": "801vB_JMas4ucriUmfrLg",
"text": "PHP > Basics of Security > SQL Injection"
id: '53xDks6JQ33fHMa3XcuCd',
text: 'AI Agents > Agent Architectures > Common Architectures > ReAct (Reason + Act)',
},
{
"id": "DxqQrToZSayWplKdCkTgT",
"text": "PHP > Basics of Security > XSS Prevention"
id: 'qwdh5pkBbrF8LKPxbZp4F',
text: 'AI Agents > Agent Architectures > Common Architectures > Chain of Thought (CoT)',
},
{
"id": "J9yIXZTtwbFzH2u4dI1ep",
"text": "PHP > Basics of Security > CSRF Protection"
id: '6YLCMWzystao6byCYCTPO',
text: 'AI Agents > Agent Architectures > Common Architectures > Planner Executor',
},
{
"id": "JbWFfJiCRrXDhnuIx_lqx",
"text": "PHP > Basics of Security > Password Hashing"
id: 'Ep8RoZSy_Iq_zWXlGQLZo',
text: 'AI Agents > Agent Architectures > Common Architectures > DAG Agents',
},
{
"id": "HJJzKYXdK4BWITLP4APLZ",
"text": "PHP > Basics of Security > Auth Mechanisms"
id: 'Nmy1PoB32DcWZnPM8l8jT',
text: 'AI Agents > Agent Architectures > Common Architectures > Tree-of-Thought',
},
{
"id": "tfC1tCrbvH5J43WUpG9Yb",
"text": "PHP > Basics of Security > Sanitization Techniques"
id: 'hj1adjkG9nalXKZ-Youn0',
text: 'AI Agents > Agent Architectures > Common Architectures > Tree-of-Thought',
},
{
"id": "cJtPz1RMN1qDE4eRdv4N_",
"text": "PHP > Database Connectivity > PDO"
id: 'US6T5dXM8IY9V2qZnTOFW',
text: 'AI Agents > Building Agents > Manual (from scratch)',
},
{
"id": "YLuo0oZJzTCoiZoOSG57z",
"text": "PHP > Database Connectivity > MySQLi"
id: 'aafZxtjxiwzJH1lwHBODi',
text: 'AI Agents > Building Agents > LLM Native "Function Calling"',
},
{
"id": "SeqGIfcLuveZ2z5ZSXcOd",
"text": "PHP > Advanced Database Techniques > Object-Relational Mapping (ORM)"
id: 'AQtxTTxmBpfl8BMgJbGzc',
text: 'AI Agents > Building Agents > LLM Native "Function Calling" > OpenAI Functions Calling',
},
{
"id": "FY-F6n9j29hQrnFry3VGb",
"text": "PHP > Advanced Database Techniques > Database Transactions"
id: '37GBFVZ2J2d5r8bd1ViHq',
text: 'AI Agents > Building Agents > LLM Native "Function Calling" > OpenAI Assistant API',
},
{
"id": "txUyPR_tdC8iTJV3RtvBz",
"text": "PHP > Advanced Database Techniques > Connection Pooling"
id: '_iIsBJTVS6OBf_dsdmbVO',
text: 'AI Agents > Building Agents > LLM Native "Function Calling" > Gemini Function Calling',
},
{
"id": "M1nVsh_sCSFJRf6-7Ttsj",
"text": "PHP > Advanced Database Techniques > Performance Optimization"
id: '1EZFbDHA5J5_5BPMLMxXb',
text: 'AI Agents > Building Agents > LLM Native "Function Calling" > Anthropic Tool Use',
},
{
"id": "meplwvmHMtI3Sb_fyodzZ",
"text": "PHP > Advanced Database Techniques > Database Migrations"
id: 'Ka6VpCEnqABvwiF9vba7t',
text: 'AI Agents > Building Agents > Building Using Frameworks > Langchain',
},
{
"id": "yTviiPFR5b_dr3WyxdxxQ",
"text": "PHP > OOP Fundamentals"
id: 'iEHF-Jm3ck-Iu85EbCoDi',
text: 'AI Agents > Building Agents > Building Using Frameworks > LlamaIndex',
},
{
"id": "PIuplWreo7PFG3Mdn2t6W",
"text": "PHP > OOP Fundamentals > Classes and Objects"
id: 'XS-FsvtrXGZ8DPrwOsnlI',
text: 'AI Agents > Building Agents > Building Using Frameworks > Haystack',
},
{
"id": "oNUt1oT8pYBVvH0S2P6cb",
"text": "PHP > OOP Fundamentals > Constructor / Destructor"
id: '7YtnQ9-KIvGPSpDzEDexl',
text: 'AI Agents > Building Agents > Building Using Frameworks > AutoGen',
},
{
"id": "MRAPXshy9RoYdReY6grf_",
"text": "PHP > OOP Fundamentals > Properties and Methods"
id: 'uFPJqgU4qGvZyxTv-osZA',
text: 'AI Agents > Building Agents > Building Using Frameworks > CrewAI',
},
{
"id": "RD2RaBmA2XWkEa13PTCTX",
"text": "PHP > OOP Fundamentals > Access Specifiers"
id: 'eWxQiBrxIUG2JNcrdfIHS',
text: 'AI Agents > Building Agents > Building Using Frameworks > Smol Depot',
},
{
"id": "qlkpwXfOc1p7j37hrzffI",
"text": "PHP > OOP Fundamentals > Static Methods and Properties"
id: 'v8qLnyFRnEumodBYxQSXQ',
text: 'AI Agents > Building Agents > Evaluation and Testing > Metrics to Track',
},
{
"id": "c5q2e_jyMt8Pir5Od3lRi",
"text": "PHP > OOP Fundamentals > Inheritance"
id: 'qo_O4YAe4-MTP_ZJoXJHR',
text: 'AI Agents > Evaluation and Testing > Unit Testing for Individual Tools',
},
{
"id": "gtq5KrghF28f5G8nuDcYQ",
"text": "PHP > Polymorphism"
id: 'P9-SiIda3TSjHsfkI5OUV',
text: 'AI Agents > Evaluation and Testing > Integration Testing for Flows',
},
{
"id": "ub79EkMiOmPBwXLRuYFL8",
"text": "PHP > Abstract classes"
id: 'rHxdxN97ZcU7MPl8L1jzN',
text: 'AI Agents > Evaluation and Testing > Human in the Loop Evaluation',
},
{
"id": "vu0H-TsD7hkJgOQbSRj92",
"text": "PHP > Interfaces"
id: 'xp7TCTRE9HP60_rGzTUF6',
text: 'AI Agents > Evaluation and Testing > Frameworks > LangSmith',
},
{
"id": "GR09ns9B-0cONQaQ_uj-7",
"text": "PHP > Traits"
id: '0924QUH1wV7Mp-Xu0FAhF',
text: 'AI Agents > Evaluation and Testing > Frameworks > DeepEval',
},
{
"id": "9raJ06lKRZITbjWeLil-F",
"text": "PHP > Namespaces"
id: 'YzEDtGEaMaMWVt0W03HRt',
text: 'AI Agents > Evaluation and Testing > Frameworks > Ragas',
},
{
"id": "rSXsPWto7Jeyw3Szl9pvf",
"text": "PHP > Magic methods"
id: 'zs6LM8WEnb0ERWpiaQCgc',
text: 'AI Agents > Debugging and Monitoring > Structured logging & tracing',
},
{
"id": "sPW-Ti2VyNYzxq6EYkbn7",
"text": "PHP > Type Declarations"
id: 'zs6LM8WEnb0ERWpiaQCgc',
text: 'AI Agents > Debugging and Monitoring > Structured logging & tracing',
},
{
"id": "KEE50C6lOS4eX8sAbfhYe",
"text": "PHP > Dependency injection"
id: 'SS8mGqf9wfrNqenIWvN8Z',
text: 'AI Agents > Observability Tools > LangSmith',
},
{
"id": "zsscRQZIq5o0JZir9hlz-",
"text": "PHP > Laravel"
id: 'MLxP5N0Vrmwh-kyvNeGXn',
text: 'AI Agents > Observability Tools > Helicone',
},
{
"id": "57VSMVePOr9qUD5x_LNdf",
"text": "PHP > Symfony"
id: 'UoIheaJlShiceafrWALEH',
text: 'AI Agents > Observability Tools > LangFuse',
},
{
"id": "yVFDu2aTiEZ4PWMdKdW2P",
"text": "PHP > Composer"
id: '7UqPXUzqKYXklnB3x-tsv',
text: 'AI Agents > Observability Tools > openllmetry',
},
{
"id": "xZf2jjnCVHwYfDH2hs9kR",
"text": "PHP > Packagist"
id: 'SU2RuicMUo8tiAsQtDI1k',
text: 'AI Agents > Security & Ethics > Prompt Injection / Jailbreaks',
},
{
"id": "qFiTsf6Es-gwqe6J6bdL1",
"text": "PHP > Autoloading"
id: 'UVzLGXG6K7HQVHmw8ZAv2',
text: 'AI Agents > Security & Ethics > Tool sandboxing / Permissioning',
},
{
"id": "NfBKKwG2GGBPppOjoLLBg",
"text": "PHP > PHPUnit"
id: 'rdlYBJNNyZUshzsJawME4',
text: 'AI Agents > Security & Ethics > Data Privacy + PII Redaction',
},
{
"id": "d6MydchA52HIxfAUjmZui",
"text": "PHP > Pest"
id: 'EyLo2j8IQsIK91SKaXkmK',
text: 'AI Agents > Security & Ethics > Bias & Toxicity Guardrails',
},
{
"id": "6eWgZVLV479oQzl0fu-Od",
"text": "PHP > Style Tools"
id: '63nsfJFO1BwjLX_ZVaPFC',
text: 'AI Agents > Security & Ethics > Safety + Red Team Testing',
},
{
"id": "fSpvZ_4kGFMbFVCWhA8vn",
"text": "PHP > Style Tools > PHPCodeSniffer"
},
{
"id": "r07k_hT2z2EiIBH4q3F7-",
"text": "PHP > Style Tools > PHP CS Fixer"
},
{
"id": "PrG_5dyBblXsWYYRcOJMa",
"text": "PHP > Static Analysis"
},
{
"id": "12k71gNfwAcT9K5aLWgbZ",
"text": "PHP > Static Analysis > PHPStan"
},
{
"id": "T1XD93j6Lkpl88JSmys9b",
"text": "PHP > Static Analysis > Psalm"
},
{
"id": "B45YVzov8X_iOtneiFEqa",
"text": "PHP > Static Analysis > Phan"
},
{
"id": "KC6D81-T-FwQc7Osw1rlY",
"text": "PHP > External Integrations > cURL"
},
{
"id": "_Al4NXKVQAnk8OikwvXCL",
"text": "PHP > External Integrations > Guzzle"
},
{
"id": "SD9k16UlVve9WtNMDA5Za",
"text": "PHP > PHP-FIG"
},
{
"id": "3tONibbRgK7HCwGTE2Gqw",
"text": "PHP > PHP-FIG > PSR Standards"
},
{
"id": "_Dh78x_tPLqZweg--qZFQ",
"text": "PHP > Performance Optimization > Profiling Techniques"
},
{
"id": "Av-BMa57RvrLlAXLffOH0",
"text": "PHP > Performance Optimization > Caching Strategies"
},
{
"id": "bt7dK2PcOZ72B9HXPyMwL",
"text": "PHP > Performance Optimization > Memory Management"
},
{
"id": "VpwwF8j5ZtXVSbzNfE7Sx",
"text": "PHP > Performance Optimization > Configuration Tuning"
},
{
"id": "NieqZd1juaNYoZOrB7e31",
"text": "PHP > Performance Optimization > Opcode Caching"
},
{
"id": "AoGS-5MSkp8gtJFQVPSBE",
"text": "PHP > Performance Optimization > PHP-FPM"
},
{
"id": "qp5Xi12c0qcSzTanzJq0Z",
"text": "PHP > System Interactions"
},
{
"id": "VhyYNGhOdKKrz_-uTkrjD",
"text": "PHP > System Interactions > Executing System Commands"
},
{
"id": "NTKUMgsKGYISIyhgOJPQn",
"text": "PHP > System Interactions > Process Control"
},
{
"id": "fitjnLYKLHJ2P5G7JAvzm",
"text": "PHP > System Interactions > Environment Variables"
},
{
"id": "DTaAZaU1CwzW7esoDhj85",
"text": "PHP > System Interactions > Configuration Files"
},
{
"id": "lFoHoMRywCWa056ii5cKQ",
"text": "PHP > Debugging Tools > Xdebug"
},
{
"id": "KpQb5Zh3GUcbYUyXHvyu2",
"text": "PHP > Debugging Tools > Zend Debugger"
},
{
"id": "KMQqePqAjQ-ReDwHqeofx",
"text": "PHP > Web Servers > Apache"
},
{
"id": "aspZpACHEKOsi_Er5FYPY",
"text": "PHP > Web Servers > Nginx"
}
];
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
@@ -516,19 +416,11 @@ const openai = new OpenAI({
const prompt = `
You are a helpful assistant that can help me generate content for a roadmap tree.
User will give you roadmap topics in the form of "Parent > Child > Leaf". You need
to generate content for the last node in the hierarchy in relation to the parents.
Remember that you are explaining the topics for PHP showing what the given topic is
with respect to PHP and giving a short code sample ONLY when required.
You will be given a roadmap topic in the form of "Parent > Child > Leaf". You need
to generate a single paragraph explaining the topic.
Also, I hate it when you say "In the realm of..."
or "In the context of..." or "..in the context of..." or "when we talk about..." or something
similar.
Content should be helpful and engaging for a technical audience.
It can include things like (you can include more or less, depending on the topic):
- Briefly explain the given topic in relation to PHP.
- Code sample if applicable.
- Add a link to PHP documentation
The content should be a a single textual paragraph.
IMPORTANT: Use simple and clear English. Avoid complex words and jargon when possible.
Write in a way that is easy to understand. Use short sentences and common words.
`;
@@ -541,7 +433,7 @@ const prompt = `
const generateContent = async (node) => {
try {
const content = await openai.chat.completions.create({
model: 'gpt-4',
model: 'o3',
messages: [
{ role: 'system', content: prompt },
{

View File

@@ -0,0 +1,128 @@
import { useQuery } from '@tanstack/react-query';
import { useEffect, useState } from 'react';
import { AICourseCard } from '../GenerateCourse/AICourseCard';
import { AILoadingState } from './AILoadingState';
import { AITutorHeader } from './AITutorHeader';
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
import {
listExploreAiCoursesOptions,
type ListExploreAiCoursesQuery,
} from '../../queries/ai-course';
import { queryClient } from '../../stores/query-client';
import { deleteUrlParam, getUrlParams, setUrlParams } from '../../lib/browser';
import { Pagination } from '../Pagination/Pagination';
import { AICourseSearch } from '../GenerateCourse/AICourseSearch';
import { AITutorTallMessage } from './AITutorTallMessage';
import { BookOpen } from 'lucide-react';
export function AIExploreCourseListing() {
const [isInitialLoading, setIsInitialLoading] = useState(true);
const [showUpgradePopup, setShowUpgradePopup] = useState(false);
const [pageState, setPageState] = useState<ListExploreAiCoursesQuery>({
perPage: '21',
currPage: '1',
query: '',
});
const {
data: exploreAiCourses,
isFetching: isExploreAiCoursesLoading,
isRefetching: isExploreAiCoursesRefetching,
} = useQuery(listExploreAiCoursesOptions(pageState), queryClient);
useEffect(() => {
setIsInitialLoading(false);
}, [exploreAiCourses]);
const courses = exploreAiCourses?.data ?? [];
useEffect(() => {
const queryParams = getUrlParams();
setPageState({
...pageState,
currPage: queryParams?.p || '1',
});
}, []);
useEffect(() => {
if (pageState?.currPage !== '1') {
setUrlParams({
p: pageState?.currPage || '1',
});
} else {
deleteUrlParam('p');
}
}, [pageState]);
return (
<>
{showUpgradePopup && (
<UpgradeAccountModal onClose={() => setShowUpgradePopup(false)} />
)}
<AITutorHeader
title="Explore Courses"
onUpgradeClick={() => setShowUpgradePopup(true)}
>
<AICourseSearch
value={pageState?.query || ''}
onChange={(value) => {
setPageState({
...pageState,
query: value,
currPage: '1',
});
}}
/>
</AITutorHeader>
{(isInitialLoading || isExploreAiCoursesLoading) && (
<AILoadingState
title="Loading courses"
subtitle="This may take a moment..."
/>
)}
{!isExploreAiCoursesLoading && courses && courses.length > 0 && (
<div className="flex flex-col gap-2">
<div className="grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-3">
{courses.map((course) => (
<AICourseCard
key={course._id}
course={course}
showActions={false}
showProgress={false}
/>
))}
</div>
<Pagination
totalCount={exploreAiCourses?.totalCount || 0}
totalPages={exploreAiCourses?.totalPages || 0}
currPage={Number(exploreAiCourses?.currPage || 1)}
perPage={Number(exploreAiCourses?.perPage || 21)}
onPageChange={(page) => {
setPageState({ ...pageState, currPage: String(page) });
}}
className="rounded-lg border border-gray-200 bg-white p-4"
/>
</div>
)}
{!isInitialLoading &&
!isExploreAiCoursesLoading &&
courses.length === 0 && (
<AITutorTallMessage
title="No courses found"
subtitle="Try a different search or check back later."
icon={BookOpen}
buttonText="Create your first course"
onButtonClick={() => {
window.location.href = '/ai';
}}
/>
)}
</>
);
}

View File

@@ -0,0 +1,131 @@
import { useQuery } from '@tanstack/react-query';
import {
listFeaturedAiCoursesOptions,
type ListUserAiCoursesQuery,
} from '../../queries/ai-course';
import { queryClient } from '../../stores/query-client';
import { useEffect, useState } from 'react';
import { getUrlParams, setUrlParams, deleteUrlParam } from '../../lib/browser';
import { AICourseCard } from '../GenerateCourse/AICourseCard';
import { Pagination } from '../Pagination/Pagination';
import { AITutorHeader } from './AITutorHeader';
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
import { AITutorTallMessage } from './AITutorTallMessage';
import { BookOpen } from 'lucide-react';
import { AILoadingState } from './AILoadingState';
import { AICourseSearch } from '../GenerateCourse/AICourseSearch';
export function AIFeaturedCoursesListing() {
const [isInitialLoading, setIsInitialLoading] = useState(true);
const [showUpgradePopup, setShowUpgradePopup] = useState(false);
const [pageState, setPageState] = useState<ListUserAiCoursesQuery>({
perPage: '21',
currPage: '1',
query: '',
});
const { data: featuredAiCourses, isFetching: isFeaturedAiCoursesLoading } =
useQuery(listFeaturedAiCoursesOptions(pageState), queryClient);
useEffect(() => {
setIsInitialLoading(false);
}, [featuredAiCourses]);
const courses = featuredAiCourses?.data ?? [];
useEffect(() => {
const queryParams = getUrlParams();
setPageState({
...pageState,
currPage: queryParams?.p || '1',
query: queryParams?.q || '',
});
}, []);
useEffect(() => {
if (pageState?.currPage !== '1' || pageState?.query !== '') {
setUrlParams({
p: pageState?.currPage || '1',
q: pageState?.query || '',
});
} else {
deleteUrlParam('p');
deleteUrlParam('q');
}
}, [pageState]);
return (
<>
{showUpgradePopup && (
<UpgradeAccountModal onClose={() => setShowUpgradePopup(false)} />
)}
<AITutorHeader
title="Featured Courses"
onUpgradeClick={() => setShowUpgradePopup(true)}
>
<AICourseSearch
value={pageState?.query || ''}
onChange={(value) => {
setPageState({
...pageState,
query: value,
currPage: '1',
});
}}
/>
</AITutorHeader>
{(isFeaturedAiCoursesLoading || isInitialLoading) && (
<AILoadingState
title="Loading featured courses"
subtitle="This may take a moment..."
/>
)}
{!isFeaturedAiCoursesLoading &&
!isInitialLoading &&
courses.length > 0 && (
<div className="flex flex-col gap-2">
<div className="grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-3">
{courses.map((course) => (
<AICourseCard
key={course._id}
course={course}
showActions={false}
showProgress={false}
/>
))}
</div>
<Pagination
totalCount={featuredAiCourses?.totalCount || 0}
totalPages={featuredAiCourses?.totalPages || 0}
currPage={Number(featuredAiCourses?.currPage || 1)}
perPage={Number(featuredAiCourses?.perPage || 10)}
onPageChange={(page) => {
setPageState({ ...pageState, currPage: String(page) });
}}
className="rounded-lg border border-gray-200 bg-white p-4"
/>
</div>
)}
{!isFeaturedAiCoursesLoading &&
!isInitialLoading &&
courses.length === 0 && (
<AITutorTallMessage
title="No featured courses"
subtitle="There are no featured courses available at the moment."
icon={BookOpen}
buttonText="Browse all courses"
onButtonClick={() => {
window.location.href = '/ai';
}}
/>
)}
</>
);
}

View File

@@ -0,0 +1,27 @@
import { Loader2 } from 'lucide-react';
type AILoadingStateProps = {
title: string;
subtitle?: string;
};
export function AILoadingState(props: AILoadingStateProps) {
const { title, subtitle } = props;
return (
<div className="flex flex-grow w-full flex-col items-center justify-center gap-4 rounded-lg border border-gray-200 bg-white p-8">
<div className="relative">
<Loader2 className="size-12 animate-spin text-gray-300" />
<div className="absolute inset-0 flex items-center justify-center">
<div className="size-4 rounded-full bg-white"></div>
</div>
</div>
<div className="text-center">
<p className="text-lg font-medium text-gray-900">{title}</p>
{subtitle && (
<p className="mt-1 text-sm text-gray-500">{subtitle}</p>
)}
</div>
</div>
);
}

View File

@@ -0,0 +1,40 @@
import { useQuery } from '@tanstack/react-query';
import { AITutorLimits } from './AITutorLimits';
import { getAiCourseLimitOptions } from '../../queries/ai-course';
import { queryClient } from '../../stores/query-client';
import { useIsPaidUser } from '../../queries/billing';
type AITutorHeaderProps = {
title: string;
onUpgradeClick: () => void;
children?: React.ReactNode;
};
export function AITutorHeader(props: AITutorHeaderProps) {
const { title, onUpgradeClick, children } = props;
const { data: limits } = useQuery(getAiCourseLimitOptions(), queryClient);
const { isPaidUser, isLoading: isPaidUserLoading } = useIsPaidUser();
const { used, limit } = limits ?? { used: 0, limit: 0 };
return (
<div className="mb-3 flex min-h-[35px] items-center justify-between max-sm:mb-1">
<div className="flex items-center gap-2">
<h2 className="relative flex-shrink-0 top-0 lg:top-1 text-lg font-semibold">{title}</h2>
</div>
<div className="flex items-center gap-2">
<AITutorLimits
used={used}
limit={limit}
isPaidUser={isPaidUser}
isPaidUserLoading={isPaidUserLoading}
onUpgradeClick={onUpgradeClick}
/>
{children}
</div>
</div>
);
}

View File

@@ -0,0 +1,42 @@
import { Menu } from 'lucide-react';
import { useState } from 'react';
import { AITutorSidebar, type AITutorTab } from './AITutorSidebar';
import { RoadmapLogoIcon } from '../ReactIcons/RoadmapLogo';
type AITutorLayoutProps = {
children: React.ReactNode;
activeTab: AITutorTab;
};
export function AITutorLayout(props: AITutorLayoutProps) {
const { children, activeTab } = props;
const [isSidebarFloating, setIsSidebarFloating] = useState(false);
return (
<>
<div className="flex flex-row items-center justify-between border-b border-slate-200 px-4 py-3 lg:hidden sticky top-0 bg-white z-10">
<a href="/" className="flex flex-row items-center gap-1.5">
<RoadmapLogoIcon className="size-6 text-gray-500" color="black" />
</a>
<button
className="flex flex-row items-center gap-1"
onClick={() => setIsSidebarFloating(!isSidebarFloating)}
>
<Menu className="size-5 text-gray-500" />
</button>
</div>
<div className="flex flex-grow lg:h-screen flex-row">
<AITutorSidebar
onClose={() => setIsSidebarFloating(false)}
isFloating={isSidebarFloating}
activeTab={activeTab}
/>
<div className="flex flex-grow flex-col overflow-y-scroll bg-gray-100 p-3 lg:px-4 lg:py-4">
{children}
</div>
</div>
</>
);
}

View File

@@ -0,0 +1,43 @@
import { Gift } from 'lucide-react';
import { cn } from '../../lib/classname';
type AITutorLimitsProps = {
used: number;
limit: number;
isPaidUser: boolean;
isPaidUserLoading: boolean;
onUpgradeClick: () => void;
};
export function AITutorLimits(props: AITutorLimitsProps) {
const limitUsedPercentage = Math.round((props.used / props.limit) * 100);
if (props.limit <= 0 || props.isPaidUserLoading) {
return null;
}
return (
<div
className={cn(
'pointer-events-none flex items-center gap-2 opacity-0 transition-opacity',
{
'pointer-events-auto opacity-100': !props.isPaidUser,
},
)}
>
<p className="flex items-center text-sm text-yellow-600">
<span className="max-md:hidden">
{limitUsedPercentage}% of daily limit used{' '}
</span>
<span className="inline md:hidden">{limitUsedPercentage}% used</span>
<button
onClick={props.onUpgradeClick}
className="ml-1.5 flex items-center gap-1 rounded-full bg-yellow-600 py-0.5 pr-2 pl-1.5 text-xs text-white"
>
<Gift className="size-4" />
Upgrade
</button>
</p>
</div>
);
}

View File

@@ -0,0 +1,143 @@
import { useEffect, useState } from 'react';
import { BookOpen, Compass, Plus, Star, X, Zap } from 'lucide-react';
import { AITutorLogo } from '../ReactIcons/AITutorLogo';
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
import { useIsPaidUser } from '../../queries/billing';
import { isLoggedIn } from '../../lib/jwt';
type AITutorSidebarProps = {
isFloating: boolean;
activeTab: AITutorTab;
onClose: () => void;
};
const sidebarItems = [
{
key: 'new',
label: 'New Course',
href: '/ai',
icon: Plus,
},
{
key: 'courses',
label: 'My Courses',
href: '/ai/courses',
icon: BookOpen,
},
{
key: 'staff-picks',
label: 'Staff Picks',
href: '/ai/staff-picks',
icon: Star,
},
{
key: 'community',
label: 'Community',
href: '/ai/community',
icon: Compass,
},
];
export type AITutorTab = (typeof sidebarItems)[number]['key'];
export function AITutorSidebar(props: AITutorSidebarProps) {
const { activeTab, isFloating, onClose } = props;
const [isInitialLoad, setIsInitialLoad] = useState(true);
const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false);
const { isPaidUser, isLoading: isPaidUserLoading } = useIsPaidUser();
useEffect(() => {
setIsInitialLoad(false);
}, []);
return (
<>
{isUpgradeModalOpen && (
<UpgradeAccountModal onClose={() => setIsUpgradeModalOpen(false)} />
)}
<aside
className={`w-[255px] shrink-0 border-r border-slate-200 ${
isFloating
? 'fixed top-0 bottom-0 left-0 z-50 block border-r-0 bg-white shadow-xl'
: 'hidden lg:block'
}`}
>
{isFloating && (
<button className="absolute top-3 right-3" onClick={onClose}>
<X
strokeWidth={3}
className="size-3.5 text-gray-400 hover:text-black"
/>
</button>
)}
<div className="flex flex-col items-start justify-center px-6 py-5">
<div className="flex flex-row items-center gap-1">
<AITutorLogo className="size-11 text-gray-500" color="black" />
</div>
<div className="my-3 flex flex-col">
<h2 className="-mb-px text-base font-semibold text-black">
AI Tutor
</h2>
<span className="text-xs text-gray-500">
by{' '}
<a href="/" className="underline-offset-2 hover:underline">
roadmap.sh
</a>
</span>
</div>
<p className="max-w-[150px] text-xs text-gray-500">
Your personalized learning companion for any topic
</p>
</div>
<ul className="space-y-1">
{sidebarItems.map((item) => (
<li key={item.key}>
<a
href={item.href}
className={`font-regular flex w-full items-center border-r-2 px-5 py-2 text-sm transition-all ${
activeTab === item.key
? 'border-r-black bg-gray-100 text-black'
: 'border-r-transparent text-gray-500 hover:border-r-gray-300'
}`}
>
<span className="flex grow items-center">
<item.icon className="mr-2 size-4" />
{item.label}
</span>
</a>
</li>
))}
{!isInitialLoad &&
isLoggedIn() &&
!isPaidUser &&
!isPaidUserLoading && (
<li>
<button
onClick={() => {
setIsUpgradeModalOpen(true);
}}
className="mx-4 mt-4 rounded-xl bg-amber-100 p-4 text-left transition-colors hover:bg-amber-200/80"
>
<span className="mb-2 flex items-center gap-2">
<Zap className="size-4 text-amber-600" />
<span className="font-medium text-amber-900">Upgrade</span>
</span>
<span className="mt-1 block text-left text-xs leading-4 text-amber-700">
Get access to all features and benefits of the AI Tutor.
</span>
</button>
</li>
)}
</ul>
</aside>
{isFloating && (
<div className="fixed inset-0 z-40 bg-black/50" onClick={onClose} />
)}
</>
);
}

View File

@@ -0,0 +1,13 @@
import { Zap } from 'lucide-react';
<li>
<div className="mx-4 mt-4 rounded-lg bg-amber-50 p-3">
<div className="flex items-center gap-2">
<Zap className="size-4 text-amber-600" />
<span className="font-medium text-amber-900">Free Tier</span>
</div>
<p className="mt-1 text-xs text-amber-700">
Upgrade to Pro to unlock unlimited AI tutoring sessions
</p>
</div>
</li>

View File

@@ -0,0 +1,31 @@
import { type LucideIcon } from 'lucide-react';
type AITutorTallMessageProps = {
title: string;
subtitle?: string;
icon: LucideIcon;
buttonText?: string;
onButtonClick?: () => void;
};
export function AITutorTallMessage(props: AITutorTallMessageProps) {
const { title, subtitle, icon: Icon, buttonText, onButtonClick } = props;
return (
<div className="flex flex-grow flex-col items-center justify-center rounded-lg border border-gray-200 bg-white p-8">
<Icon className="size-12 text-gray-300" />
<div className="my-4 text-center">
<h2 className="mb-2 text-xl font-semibold">{title}</h2>
{subtitle && <p className="text-base text-gray-600">{subtitle}</p>}
</div>
{buttonText && onButtonClick && (
<button
onClick={onButtonClick}
className="rounded-lg bg-black px-4 py-2 text-sm text-white hover:opacity-80"
>
{buttonText}
</button>
)}
</div>
);
}

View File

@@ -0,0 +1,69 @@
import { ChevronDown } from 'lucide-react';
import { useState, useRef, useEffect } from 'react';
import { cn } from '../../lib/classname';
import {
difficultyLevels,
type DifficultyLevel,
} from '../GenerateCourse/AICourse';
type DifficultyDropdownProps = {
value: DifficultyLevel;
onChange: (value: DifficultyLevel) => void;
};
export function DifficultyDropdown(props: DifficultyDropdownProps) {
const { value, onChange } = props;
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
return (
<div className="relative" ref={dropdownRef}>
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
className={cn(
'flex items-center gap-2 rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700 hover:bg-gray-200 hover:text-black',
)}
>
<span className="capitalize">{value}</span>
<ChevronDown size={16} className={cn(isOpen && 'rotate-180')} />
</button>
{isOpen && (
<div className="absolute z-10 mt-1 flex flex-col overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg">
{difficultyLevels.map((level) => (
<button
key={level}
type="button"
onClick={() => {
onChange(level);
setIsOpen(false);
}}
className={cn(
'px-5 py-2 text-left text-sm capitalize hover:bg-gray-100',
value === level && 'bg-gray-200 font-medium hover:bg-gray-200',
)}
>
{level}
</button>
))}
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,38 @@
import { LockIcon } from 'lucide-react';
import { showLoginPopup } from '../../lib/popup';
import { cn } from '../../lib/classname';
type LoginToViewProps = {
className?: string;
};
export function LoginToView(props: LoginToViewProps) {
const { className } = props;
return (
<div
className={cn(
'mt-8 min-h-[402px] rounded-xl border border-gray-200/50 bg-gradient-to-br from-gray-50 to-gray-100/50 p-12 backdrop-blur-sm',
'flex flex-col items-center justify-center',
className,
)}
>
<LockIcon className="size-8 stroke-[1.5] text-gray-600" />
<div className="mt-5 mb-4 flex flex-col items-center gap-0.5 text-center">
<h3 className="text-xl font-semibold text-gray-700">Login Required</h3>
<p className="text-sm text-balance leading-relaxed text-gray-500">
Please login to access the content and all the features of the AI Tutor.
</p>
</div>
<button
onClick={() => showLoginPopup()}
className="rounded-full bg-black px-6 py-2 text-sm font-medium text-white transition-all duration-300 hover:opacity-80 hover:shadow-md active:scale-[0.98] active:transform"
>
Login to Continue
</button>
</div>
);
}

View File

@@ -163,7 +163,7 @@ const sidebarLinks = [
: 'border-r-transparent text-gray-500 hover:border-r-gray-300'
}`}
>
<span class='flex flex-grow items-center'>
<span class='flex grow items-center'>
{sidebarLink.icon.component ? (
<sidebarLink.icon.component
className={`${sidebarLink.icon.classes} mr-2`}

View File

@@ -83,10 +83,10 @@ export function AccountStreak(props: AccountStreakProps) {
const totalCircles = leftCircleCount + currentCircleCount + remainingCount;
return (
<div className="relative z-[90] animate-fade-in">
<div className="relative z-90 animate-fade-in">
<button
className={cn(
'flex items-center justify-center rounded-lg p-1.5 px-2 text-purple-400 hover:bg-purple-100/10 focus:outline-none',
'flex items-center justify-center rounded-lg p-1.5 px-2 text-purple-400 hover:bg-purple-100/10 focus:outline-hidden',
{
'bg-purple-100/10': showDropdown,
},

View File

@@ -128,20 +128,20 @@ export function AccountStreakHeatmap(props: AccountStreakHeatmapProps) {
]}
classForValue={(value) => {
if (!value) {
return 'fill-slate-700 rounded-md [rx:2px] focus:outline-none';
return 'fill-slate-700 rounded-md [rx:2px] focus:outline-hidden';
}
const { count } = value;
if (count >= 20) {
return 'fill-slate-200 rounded-md [rx:2px] focus:outline-none';
return 'fill-slate-200 rounded-md [rx:2px] focus:outline-hidden';
} else if (count >= 10) {
return 'fill-slate-300 rounded-md [rx:2px] focus:outline-none';
return 'fill-slate-300 rounded-md [rx:2px] focus:outline-hidden';
} else if (count >= 5) {
return 'fill-slate-400 rounded-md [rx:2px] focus:outline-none';
return 'fill-slate-400 rounded-md [rx:2px] focus:outline-hidden';
} else if (count >= 3) {
return 'fill-slate-500 rounded-md [rx:2px] focus:outline-none';
return 'fill-slate-500 rounded-md [rx:2px] focus:outline-hidden';
} else {
return 'fill-slate-600 rounded-md [rx:2px] focus:outline-none';
return 'fill-slate-600 rounded-md [rx:2px] focus:outline-hidden';
}
}}
tooltipDataAttrs={(value: any) => {
@@ -159,7 +159,7 @@ export function AccountStreakHeatmap(props: AccountStreakHeatmapProps) {
<ReactTooltip
id="user-activity-tip"
className="!rounded-lg !bg-slate-900 !p-1 !px-2 !text-xs"
className="rounded-lg! bg-slate-900! p-1! px-2! text-xs!"
/>
<div className="mt-2 flex items-center justify-end">
@@ -173,14 +173,14 @@ export function AccountStreakHeatmap(props: AccountStreakHeatmapProps) {
data-tooltip-content={`${legend.count} Updates`}
>
<div
className={`h-2.5 w-2.5 ${legend.color} mr-1 rounded-sm`}
className={`h-2.5 w-2.5 ${legend.color} mr-1 rounded-xs`}
></div>
</div>
))}
<span className="ml-2 text-xs text-slate-500">More</span>
<ReactTooltip
id="user-activity-tip"
className="!rounded-lg !bg-slate-900 !p-1 !px-2 !text-sm"
className="rounded-lg! bg-slate-900! p-1! px-2! text-sm!"
/>
</div>
</div>

View File

@@ -43,7 +43,7 @@ export function ActivityTopicsModal(props: ActivityTopicDetailsProps) {
onClose();
}}
>
<div className={`popup-body relative rounded-lg bg-white p-4 shadow`}>
<div className={`popup-body relative rounded-lg bg-white p-4 shadow-sm`}>
<span className="mb-2 flex items-center justify-between text-lg font-semibold capitalize">
<span className="flex items-center gap-2">
{actionType.replace('_', ' ')}

View File

@@ -36,7 +36,7 @@ export function ProjectProgress(props: ProjectProgressType) {
target="_blank"
>
<ProjectStatus projectStatus={projectStatus} />
<span className="ml-2 flex-grow truncate">{projectStatus?.title}</span>
<span className="ml-2 grow truncate">{projectStatus?.title}</span>
<span className="inline-flex items-center gap-1 text-xs text-gray-400">
{projectStatus.upvotes}
<ThumbsUp className="size-2.5 stroke-[2.5px]" />

View File

@@ -73,7 +73,7 @@ export function ResourceProgress(props: ResourceProgressType) {
showActions ? 'pr-7' : '',
)}
>
<span className="flex-grow truncate">{title}</span>
<span className="grow truncate">{title}</span>
<span className="text-xs text-gray-400">
{parseInt(progressPercentage, 10)}%
</span>

View File

@@ -69,7 +69,7 @@ export function AddTeamRoadmap(props: AddTeamRoadmapProps) {
<div className="relative h-full w-full max-w-md p-4 md:h-auto">
<div
ref={popupBodyEl}
className="popup-body relative rounded-lg bg-white p-4 shadow"
className="popup-body relative rounded-lg bg-white p-4 shadow-sm"
>
{isLoading && (
<>
@@ -99,7 +99,7 @@ export function AddTeamRoadmap(props: AddTeamRoadmapProps) {
<button
onClick={onClose}
type="button"
className="flex-grow cursor-pointer rounded-lg bg-gray-200 py-2 text-center hover:bg-gray-300"
className="grow cursor-pointer rounded-lg bg-gray-200 py-2 text-center hover:bg-gray-300"
>
Done
</button>
@@ -110,7 +110,7 @@ export function AddTeamRoadmap(props: AddTeamRoadmapProps) {
setIsLoading(false);
}}
type="button"
className="flex-grow cursor-pointer rounded-lg bg-black py-2 text-center text-white"
className="grow cursor-pointer rounded-lg bg-black py-2 text-center text-white"
>
+ Add More
</button>
@@ -126,7 +126,7 @@ export function AddTeamRoadmap(props: AddTeamRoadmapProps) {
<button
onClick={onClose}
type="button"
className="flex-grow cursor-pointer rounded-lg bg-gray-200 py-2 text-center hover:bg-gray-300"
className="grow cursor-pointer rounded-lg bg-gray-200 py-2 text-center hover:bg-gray-300"
>
Cancel
</button>
@@ -152,7 +152,7 @@ export function AddTeamRoadmap(props: AddTeamRoadmapProps) {
setSelectedRoadmap(roadmapId);
});
}}
inputClassName="mt-2 mb-2 block w-full rounded-md border border-gray-300 px-3 py-2 outline-none placeholder:text-gray-400 focus:border-gray-400"
inputClassName="mt-2 mb-2 block w-full rounded-md border border-gray-300 px-3 py-2 outline-hidden placeholder:text-gray-400 focus:border-gray-400"
placeholder={'Search for roadmap'}
/>
@@ -160,7 +160,7 @@ export function AddTeamRoadmap(props: AddTeamRoadmapProps) {
<button
onClick={onClose}
type="button"
className="flex-grow cursor-pointer rounded-lg bg-gray-200 py-2 text-center hover:bg-gray-300"
className="grow cursor-pointer rounded-lg bg-gray-200 py-2 text-center hover:bg-gray-300"
>
Cancel
</button>

View File

@@ -30,7 +30,7 @@ function Input(props: InputProps) {
value={value}
onChange={onChange}
rows={rows}
className="mt-1 block w-full rounded-md border border-gray-300 p-2 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
className="mt-1 block w-full rounded-md border border-gray-300 p-2 shadow-xs focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
autoComplete="off"
data-1p-ignore=""
data-form-type="other"
@@ -45,7 +45,7 @@ function Input(props: InputProps) {
value={value}
onChange={onChange}
required={required}
className="mt-1 block w-full rounded-md border border-gray-300 p-2 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
className="mt-1 block w-full rounded-md border border-gray-300 p-2 shadow-xs focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
autoComplete="off"
data-1p-ignore=""
data-form-type="other"
@@ -120,7 +120,7 @@ export function AdvertiseForm() {
Ready to learn more? Fill out the form below to get started!
</h2>
{error && (
<div className="relative mb-4 rounded border border-red-400 bg-red-100 px-4 py-3 text-red-700">
<div className="relative mb-4 rounded-sm border border-red-400 bg-red-100 px-4 py-3 text-red-700">
{error}
</div>
)}
@@ -199,7 +199,7 @@ export function AdvertiseForm() {
type="checkbox"
checked={formData.updates}
onChange={handleInputChange}
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
className="h-4 w-4 rounded-sm border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
</div>
<div className="ml-3 text-sm">
@@ -213,7 +213,7 @@ export function AdvertiseForm() {
<div>
<button
type="submit"
className="flex justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
className="flex justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-xs hover:bg-indigo-700 focus:outline-hidden focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Send
</button>

View File

@@ -1,4 +1,4 @@
<script type='text/javascript'>
<script type='text/javascript' is:inline>
(function (c, l, a, r, i, t, y) {
c[a] =
c[a] ||

View File

@@ -10,8 +10,8 @@
'roadmaps',
'community',
'start-here',
'ai-roadmaps',
'ai',
'ai-tutor',
'teams',
'about',
'account',

View File

@@ -0,0 +1,6 @@
<script
type='text/javascript'
id='hs-script-loader'
async
defer
src='//js.hs-scripts.com/46095657.js?businessUnitId=2306992'></script>

View File

@@ -1,4 +1,4 @@
<script>
<script is:inline>
// @ts-nocheck
!(function (w, d) {
if (!w.rdt) {

View File

@@ -1,6 +1,7 @@
declare global {
interface Window {
gtag: any;
varify: any;
fireEvent: (props: {
action: string;
category: string;

View File

@@ -12,6 +12,7 @@ type CourseLoginPopupProps = {
};
export const CHECKOUT_AFTER_LOGIN_KEY = 'checkoutAfterLogin';
export const SAMPLE_AFTER_LOGIN_KEY = 'sampleAfterLogin';
export function CourseLoginPopup(props: CourseLoginPopupProps) {
const { onClose: parentOnClose, checkoutAfterLogin = true } = props;
@@ -27,6 +28,7 @@ export function CourseLoginPopup(props: CourseLoginPopupProps) {
// if user didn't login and closed the popup, we remove the checkoutAfterLogin flag
// so that login from other buttons on course page will trigger purchase
localStorage.removeItem(CHECKOUT_AFTER_LOGIN_KEY);
localStorage.removeItem(SAMPLE_AFTER_LOGIN_KEY);
parentOnClose();
}
@@ -40,7 +42,7 @@ export function CourseLoginPopup(props: CourseLoginPopupProps) {
if (emailNature) {
const emailHeader = (
<div className="mb-7 text-center">
<p className="mb-3.5 pt-2 text-2xl font-semibold leading-5 text-slate-900">
<p className="mb-3.5 pt-2 text-2xl leading-5 font-semibold text-slate-900">
{emailNature === 'login'
? 'Login to your account'
: 'Create an account'}
@@ -80,8 +82,8 @@ export function CourseLoginPopup(props: CourseLoginPopupProps) {
return (
<Modal onClose={onClose} bodyClassName="p-5 h-auto">
<div className="mb-7 text-center">
<p className="mb-3.5 pt-2 text-2xl font-semibold leading-5 text-slate-900">
Create or login to your account
<p className="mb-3.5 pt-2 text-2xl leading-5 font-semibold text-slate-900">
Create or login to Enroll
</p>
<p className="mt-2 text-sm leading-4 text-slate-600">
Login or sign up for an account to start learning
@@ -115,7 +117,7 @@ export function CourseLoginPopup(props: CourseLoginPopupProps) {
<div className="flex flex-row gap-2">
{!isUsingEmail && (
<button
className="flex-grow rounded-md border border-gray-400 px-4 py-2 text-sm text-gray-600 hover:bg-gray-100"
className="grow rounded-md border border-gray-400 px-4 py-2 text-sm text-gray-600 hover:bg-gray-100"
onClick={() => setIsUsingEmail(true)}
>
Use your email address
@@ -124,13 +126,13 @@ export function CourseLoginPopup(props: CourseLoginPopupProps) {
{isUsingEmail && (
<>
<button
className="flex-grow rounded-md border border-gray-400 px-4 py-2 text-sm text-gray-600 hover:bg-gray-100"
className="grow rounded-md border border-gray-400 px-4 py-2 text-sm text-gray-600 hover:bg-gray-100"
onClick={() => setEmailNature('login')}
>
Already have an account
</button>
<button
className="flex-grow rounded-md border border-gray-400 px-4 py-2 text-sm text-gray-600 hover:bg-gray-100"
className="grow rounded-md border border-gray-400 px-4 py-2 text-sm text-gray-600 hover:bg-gray-100"
onClick={() => setEmailNature('signup')}
>
Create an account

View File

@@ -1,8 +1,10 @@
import Cookies from 'js-cookie';
import type { FormEvent } from 'react';
import { useId, useState } from 'react';
import { httpPost } from '../../lib/http';
import { FIRST_LOGIN_PARAM, setAuthToken } from '../../lib/jwt';
import {
COURSE_PURCHASE_PARAM, FIRST_LOGIN_PARAM,
setAuthToken
} from '../../lib/jwt';
type EmailLoginFormProps = {
isDisabled?: boolean;
@@ -38,7 +40,10 @@ export function EmailLoginForm(props: EmailLoginFormProps) {
const currentLocation = window.location.href;
const url = new URL(currentLocation, window.location.origin);
url.searchParams.set(FIRST_LOGIN_PARAM, response?.isNewUser ? '1' : '0');
url.searchParams.set(COURSE_PURCHASE_PARAM, '1');
window.location.href = url.toString();
return;
}
@@ -70,7 +75,7 @@ export function EmailLoginForm(props: EmailLoginFormProps) {
type="email"
autoComplete="email"
required
className="block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-xs outline-hidden placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="Email Address"
value={email}
onInput={(e) => setEmail(String((e.target as any).value))}
@@ -84,13 +89,13 @@ export function EmailLoginForm(props: EmailLoginFormProps) {
type="password"
autoComplete="current-password"
required
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-xs outline-hidden placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="Password"
value={password}
onInput={(e) => setPassword(String((e.target as any).value))}
/>
<p className="mb-3 mt-2 text-sm text-gray-500">
<p className="mt-2 mb-3 text-sm text-gray-500">
<a
href="/forgot-password"
className="text-blue-800 hover:text-blue-600"
@@ -106,7 +111,7 @@ export function EmailLoginForm(props: EmailLoginFormProps) {
<button
type="submit"
disabled={isLoading || isDisabled}
className="inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-none focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
className="inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-hidden focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
>
{isLoading ? 'Please wait...' : 'Continue'}
</button>

View File

@@ -1,6 +1,11 @@
import { type FormEvent, useEffect, useState } from 'react';
import { httpPost } from '../../lib/http';
import { deleteUrlParam, getUrlParams } from '../../lib/browser';
import {
deleteUrlParam,
getLastPath,
getUrlParams,
urlToId,
} from '../../lib/browser';
import { isLoggedIn, setAIReferralCode } from '../../lib/jwt';
type EmailSignupFormProps = {
@@ -34,6 +39,7 @@ export function EmailSignupForm(props: EmailSignupFormProps) {
email,
password,
name,
src: urlToId(getLastPath() || window.location.pathname),
},
);
@@ -74,7 +80,7 @@ export function EmailSignupForm(props: EmailSignupFormProps) {
min={3}
max={50}
required
className="block w-full rounded-lg border border-gray-300 px-3 py-2 outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="block w-full rounded-lg border border-gray-300 px-3 py-2 outline-hidden placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="Full Name"
value={name}
onInput={(e) => setName(String((e.target as any).value))}
@@ -87,7 +93,7 @@ export function EmailSignupForm(props: EmailSignupFormProps) {
type="email"
autoComplete="email"
required
className="block w-full rounded-lg border border-gray-300 px-3 py-2 outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="block w-full rounded-lg border border-gray-300 px-3 py-2 outline-hidden placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="Email Address"
value={email}
onInput={(e) => setEmail(String((e.target as any).value))}
@@ -102,7 +108,7 @@ export function EmailSignupForm(props: EmailSignupFormProps) {
min={6}
max={50}
required
className="block w-full rounded-lg border border-gray-300 px-3 py-2 outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="block w-full rounded-lg border border-gray-300 px-3 py-2 outline-hidden placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="Password"
value={password}
onInput={(e) => setPassword(String((e.target as any).value))}
@@ -115,7 +121,7 @@ export function EmailSignupForm(props: EmailSignupFormProps) {
<button
type="submit"
disabled={isLoading || isDisabled}
className="inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-none focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
className="inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-hidden focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
>
{isLoading ? 'Please wait...' : 'Continue to Verify Email'}
</button>

View File

@@ -33,7 +33,7 @@ export function ForgotPasswordForm() {
<input
type="email"
name="email"
className="mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-xs outline-hidden transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
required
placeholder="Email Address"
value={email}
@@ -55,7 +55,7 @@ export function ForgotPasswordForm() {
<button
type="submit"
disabled={isLoading}
className="mt-3 inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-none focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
className="mt-3 inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-hidden focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
>
{isLoading ? 'Please wait...' : 'Continue'}
</button>

View File

@@ -9,7 +9,7 @@ import { cn } from '../../lib/classname.ts';
import { httpGet } from '../../lib/http';
import { Spinner } from '../ReactIcons/Spinner.tsx';
import { CHECKOUT_AFTER_LOGIN_KEY } from './CourseLoginPopup.tsx';
import { triggerUtmRegistration } from '../../lib/browser.ts';
import { getLastPath, triggerUtmRegistration, urlToId } from '../../lib/browser.ts';
type GitHubButtonProps = {
isDisabled?: boolean;
@@ -38,10 +38,12 @@ export function GitHubButton(props: GitHubButtonProps) {
setIsLoading(true);
setIsDisabled?.(true);
const lastPageBeforeGithub = localStorage.getItem(GITHUB_LAST_PAGE);
httpGet<{ token: string; isNewUser: boolean }>(
`${import.meta.env.PUBLIC_API_URL}/v1-github-callback${
window.location.search
}`,
}&src=${urlToId(lastPageBeforeGithub || getLastPath() || window.location.pathname)}`,
)
.then(({ response, error }) => {
if (!response?.token) {
@@ -57,7 +59,6 @@ export function GitHubButton(props: GitHubButtonProps) {
let redirectUrl = new URL('/', window.location.origin);
const gitHubRedirectAt = localStorage.getItem(GITHUB_REDIRECT_AT);
const lastPageBeforeGithub = localStorage.getItem(GITHUB_LAST_PAGE);
// If the social redirect is there and less than 30 seconds old
// redirect to the page that user was on before they clicked the github login button
@@ -127,9 +128,12 @@ export function GitHubButton(props: GitHubButtonProps) {
// For non authentication pages, we want to redirect back to the page
// the user was on before they clicked the social login button
if (!['/login', '/signup'].includes(window.location.pathname)) {
const pagePath = ['/respond-invite', '/befriend', '/r', '/ai'].includes(
window.location.pathname,
)
const pagePath = [
'/respond-invite',
'/befriend',
'/r',
'/ai-roadmaps',
].includes(window.location.pathname)
? window.location.pathname + window.location.search
: window.location.pathname;
@@ -144,7 +148,7 @@ export function GitHubButton(props: GitHubButtonProps) {
<>
<button
className={cn(
'inline-flex h-10 w-full items-center justify-center gap-2 rounded border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-none hover:border-gray-400 hover:bg-gray-50 focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60',
'inline-flex h-10 w-full items-center justify-center gap-2 rounded-sm border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-hidden hover:border-gray-400 hover:bg-gray-50 focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60',
className,
)}
disabled={isLoading || isDisabled}

View File

@@ -1,19 +1,11 @@
import { useEffect, useState } from 'react';
import Cookies from 'js-cookie';
import {
FIRST_LOGIN_PARAM,
TOKEN_COOKIE_NAME,
setAuthToken,
} from '../../lib/jwt';
import { FIRST_LOGIN_PARAM, setAuthToken } from '../../lib/jwt';
import { httpGet } from '../../lib/http';
import { COURSE_PURCHASE_PARAM } from '../../lib/jwt';
import { GoogleIcon } from '../ReactIcons/GoogleIcon.tsx';
import { Spinner } from '../ReactIcons/Spinner.tsx';
import { CHECKOUT_AFTER_LOGIN_KEY } from './CourseLoginPopup.tsx';
import {
getStoredUtmParams,
triggerUtmRegistration,
} from '../../lib/browser.ts';
import { triggerUtmRegistration, urlToId, getLastPath } from '../../lib/browser.ts';
import { cn } from '../../lib/classname.ts';
type GoogleButtonProps = {
@@ -43,10 +35,12 @@ export function GoogleButton(props: GoogleButtonProps) {
setIsLoading(true);
setIsDisabled?.(true);
const lastPageBeforeGoogle = localStorage.getItem(GOOGLE_LAST_PAGE);
httpGet<{ token: string; isNewUser: boolean }>(
`${import.meta.env.PUBLIC_API_URL}/v1-google-callback${
window.location.search
}`,
}&src=${urlToId(lastPageBeforeGoogle || getLastPath() || window.location.pathname)}`,
)
.then(({ response, error }) => {
if (!response?.token) {
@@ -61,7 +55,6 @@ export function GoogleButton(props: GoogleButtonProps) {
let redirectUrl = new URL('/', window.location.origin);
const googleRedirectAt = localStorage.getItem(GOOGLE_REDIRECT_AT);
const lastPageBeforeGoogle = localStorage.getItem(GOOGLE_LAST_PAGE);
// If the social redirect is there and less than 30 seconds old
// redirect to the page that user was on before they clicked the github login button
@@ -132,7 +125,7 @@ export function GoogleButton(props: GoogleButtonProps) {
'/respond-invite',
'/befriend',
'/r',
'/ai',
'/ai-roadmaps',
].includes(window.location.pathname)
? window.location.pathname + window.location.search
: window.location.pathname;
@@ -154,7 +147,7 @@ export function GoogleButton(props: GoogleButtonProps) {
<>
<button
className={cn(
'inline-flex h-10 w-full items-center justify-center gap-2 rounded border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-none hover:border-gray-400 hover:bg-gray-50 focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60',
'inline-flex h-10 w-full items-center justify-center gap-2 rounded-sm border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-hidden hover:border-gray-400 hover:bg-gray-50 focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60',
className,
)}
disabled={isLoading || isDisabled}

View File

@@ -1,9 +1,7 @@
import { useEffect, useState } from 'react';
import Cookies from 'js-cookie';
import {
FIRST_LOGIN_PARAM,
COURSE_PURCHASE_PARAM,
TOKEN_COOKIE_NAME,
setAuthToken,
} from '../../lib/jwt';
import { cn } from '../../lib/classname.ts';
@@ -11,7 +9,7 @@ import { httpGet } from '../../lib/http';
import { LinkedInIcon } from '../ReactIcons/LinkedInIcon.tsx';
import { Spinner } from '../ReactIcons/Spinner.tsx';
import { CHECKOUT_AFTER_LOGIN_KEY } from './CourseLoginPopup.tsx';
import { triggerUtmRegistration } from '../../lib/browser.ts';
import { getLastPath, triggerUtmRegistration, urlToId } from '../../lib/browser.ts';
type LinkedInButtonProps = {
isDisabled?: boolean;
@@ -40,10 +38,12 @@ export function LinkedInButton(props: LinkedInButtonProps) {
setIsLoading(true);
setIsDisabled?.(true);
const lastPageBeforeLinkedIn = localStorage.getItem(LINKEDIN_LAST_PAGE);
httpGet<{ token: string; isNewUser: boolean }>(
`${import.meta.env.PUBLIC_API_URL}/v1-linkedin-callback${
window.location.search
}`,
}&src=${urlToId(lastPageBeforeLinkedIn || getLastPath() || window.location.pathname)}`,
)
.then(({ response, error }) => {
if (!response?.token) {
@@ -58,7 +58,6 @@ export function LinkedInButton(props: LinkedInButtonProps) {
let redirectUrl = new URL('/', window.location.origin);
const linkedInRedirectAt = localStorage.getItem(LINKEDIN_REDIRECT_AT);
const lastPageBeforeLinkedIn = localStorage.getItem(LINKEDIN_LAST_PAGE);
// If the social redirect is there and less than 30 seconds old
// redirect to the page that user was on before they clicked the github login button
@@ -131,7 +130,7 @@ export function LinkedInButton(props: LinkedInButtonProps) {
'/respond-invite',
'/befriend',
'/r',
'/ai',
'/ai-roadmaps',
].includes(window.location.pathname)
? window.location.pathname + window.location.search
: window.location.pathname;
@@ -153,7 +152,7 @@ export function LinkedInButton(props: LinkedInButtonProps) {
<>
<button
className={cn(
'inline-flex h-10 w-full items-center justify-center gap-2 rounded border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-none hover:border-gray-400 focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60',
'inline-flex h-10 w-full items-center justify-center gap-2 rounded-sm border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-hidden hover:border-gray-400 focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60',
className,
)}
disabled={isLoading || isDisabled}

View File

@@ -61,7 +61,7 @@ export function ResetPasswordForm() {
<form className="mx-auto w-full" onSubmit={handleSubmit}>
<input
type="password"
className="mb-2 mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="mb-2 mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-xs outline-hidden transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
required
minLength={6}
placeholder="New Password"
@@ -71,7 +71,7 @@ export function ResetPasswordForm() {
<input
type="password"
className="mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="mt-2 block w-full appearance-none rounded-lg border border-gray-300 px-3 py-2 shadow-xs outline-hidden transition duration-150 ease-in-out placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
required
minLength={6}
placeholder="Confirm New Password"
@@ -88,7 +88,7 @@ export function ResetPasswordForm() {
<button
type="submit"
disabled={isLoading}
className="mt-2 inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-none focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
className="mt-2 inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-hidden focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
>
{isLoading ? 'Please wait...' : 'Reset Password'}
</button>

View File

@@ -125,7 +125,7 @@ export function Befriend() {
<div>
<a
href="/"
className="flex-grow cursor-pointer rounded-lg bg-gray-200 px-3 py-2 text-center"
className="grow cursor-pointer rounded-lg bg-gray-200 px-3 py-2 text-center"
>
Back to home
</a>
@@ -141,7 +141,7 @@ export function Befriend() {
const isMe = currentUser?.id === user.id;
return (
<div className="container !max-w-[400px] text-center">
<div className="container max-w-[400px]! text-center">
<img
alt={'join team'}
src={userAvatar}
@@ -169,7 +169,7 @@ export function Befriend() {
});
}}
type="button"
className="w-full flex-grow cursor-pointer rounded-lg bg-black px-3 py-2 text-center text-white disabled:cursor-not-allowed disabled:opacity-40"
className="w-full grow cursor-pointer rounded-lg bg-black px-3 py-2 text-center text-white disabled:cursor-not-allowed disabled:opacity-40"
>
{isMe ? "You can't add yourself" : 'Add Friend'}
</button>
@@ -177,7 +177,7 @@ export function Befriend() {
{user.status === 'sent' && (
<>
<span className="flex w-full flex-grow cursor-default items-center justify-center rounded-lg border border-gray-300 px-3 py-2 text-center text-black">
<span className="flex w-full grow cursor-default items-center justify-center rounded-lg border border-gray-300 px-3 py-2 text-center text-black">
<CheckIcon additionalClasses="mr-2 h-4 w-4" />
Request Sent
</span>
@@ -188,7 +188,7 @@ export function Befriend() {
setIsConfirming(true);
}}
type="button"
className="flex w-full flex-grow cursor-pointer items-center justify-center rounded-lg border border-red-600 bg-red-600 px-3 py-2 text-center text-white hover:bg-red-700"
className="flex w-full grow cursor-pointer items-center justify-center rounded-lg border border-red-600 bg-red-600 px-3 py-2 text-center text-white hover:bg-red-700"
>
<DeleteUserIcon additionalClasses="mr-2 h-[19px] w-[19px]" />
Withdraw Request
@@ -196,7 +196,7 @@ export function Befriend() {
)}
{isConfirming && (
<span className="flex w-full flex-grow cursor-default items-center justify-center rounded-lg border border-red-600 px-3 py-2.5 text-center text-sm text-red-600">
<span className="flex w-full grow cursor-default items-center justify-center rounded-lg border border-red-600 px-3 py-2.5 text-center text-sm text-red-600">
Are you sure?{' '}
<button
className="ml-2 text-red-700 underline"
@@ -225,7 +225,7 @@ export function Befriend() {
{user.status === 'accepted' && (
<>
<span className="flex w-full flex-grow cursor-default items-center justify-center rounded-lg border border-gray-300 px-3 py-2 text-center text-black">
<span className="flex w-full grow cursor-default items-center justify-center rounded-lg border border-gray-300 px-3 py-2 text-center text-black">
<AddedUserIcon additionalClasses="mr-2 h-5 w-5" />
You are friends
</span>
@@ -236,7 +236,7 @@ export function Befriend() {
setIsConfirming(true);
}}
type="button"
className="flex w-full flex-grow cursor-pointer items-center justify-center rounded-lg border border-red-600 bg-red-600 px-3 py-2 text-center text-white hover:bg-red-700"
className="flex w-full grow cursor-pointer items-center justify-center rounded-lg border border-red-600 bg-red-600 px-3 py-2 text-center text-white hover:bg-red-700"
>
<DeleteUserIcon additionalClasses="mr-2 h-[19px] w-[19px]" />
Remove Friend
@@ -244,7 +244,7 @@ export function Befriend() {
)}
{isConfirming && (
<span className="flex w-full flex-grow cursor-default items-center justify-center rounded-lg border border-red-600 px-3 py-2.5 text-center text-sm text-red-600">
<span className="flex w-full grow cursor-default items-center justify-center rounded-lg border border-red-600 px-3 py-2.5 text-center text-sm text-red-600">
Are you sure?{' '}
<button
className="ml-2 text-red-700 underline"
@@ -271,12 +271,12 @@ export function Befriend() {
{user.status === 'rejected' && (
<>
<span className="flex w-full flex-grow cursor-default items-center justify-center rounded-lg border border-gray-300 px-3 py-2 text-center text-black">
<span className="flex w-full grow cursor-default items-center justify-center rounded-lg border border-gray-300 px-3 py-2 text-center text-black">
<DeleteUserIcon additionalClasses="mr-2 h-4 w-4" />
Request Rejected
</span>
<span className="flex w-full flex-grow cursor-default items-center justify-center rounded-lg border border-red-600 px-3 py-2.5 text-center text-sm text-red-600">
<span className="flex w-full grow cursor-default items-center justify-center rounded-lg border border-red-600 px-3 py-2.5 text-center text-sm text-red-600">
Changed your mind?{' '}
<button
className="ml-2 text-red-700 underline"
@@ -296,7 +296,7 @@ export function Befriend() {
{user.status === 'got_rejected' && (
<>
<span className="flex w-full flex-grow cursor-default items-center justify-center rounded-lg border border-red-500 px-3 py-2 text-center text-red-500">
<span className="flex w-full grow cursor-default items-center justify-center rounded-lg border border-red-500 px-3 py-2 text-center text-red-500">
<StopIcon additionalClasses="mr-2 h-4 w-4" />
Request Rejected
</span>
@@ -311,7 +311,7 @@ export function Befriend() {
pageProgressMessage.set('');
});
}}
className="flex w-full flex-grow cursor-pointer items-center justify-center rounded-lg border border-gray-800 bg-gray-800 px-3 py-2 text-center text-white hover:bg-black"
className="flex w-full grow cursor-pointer items-center justify-center rounded-lg border border-gray-800 bg-gray-800 px-3 py-2 text-center text-white hover:bg-black"
>
<CheckIcon additionalClasses="mr-2 h-4 w-4" />
Accept Request
@@ -323,7 +323,7 @@ export function Befriend() {
setIsConfirming(true);
}}
type="button"
className="flex w-full flex-grow cursor-pointer items-center justify-center rounded-lg border border-red-600 bg-white px-3 py-2 text-center text-red-600 hover:bg-red-100"
className="flex w-full grow cursor-pointer items-center justify-center rounded-lg border border-red-600 bg-white px-3 py-2 text-center text-red-600 hover:bg-red-100"
>
<DeleteUserIcon additionalClasses="mr-2 h-[19px] w-[19px]" />
Reject Request
@@ -331,7 +331,7 @@ export function Befriend() {
)}
{isConfirming && (
<span className="flex w-full flex-grow cursor-default items-center justify-center rounded-lg border border-red-600 px-3 py-2.5 text-center text-sm text-red-600">
<span className="flex w-full grow cursor-default items-center justify-center rounded-lg border border-red-600 px-3 py-2.5 text-center text-sm text-red-600">
Are you sure?{' '}
<button
className="ml-2 text-red-700 underline"

View File

@@ -27,7 +27,7 @@ const isBestPracticeReady = !isUpcoming;
<MarkFavorite
resourceId={bestPracticeId}
resourceType="best-practice"
className="text-gray-500 !opacity-100 hover:text-gray-600 [&>svg]:stroke-[0.4] [&>svg]:stroke-gray-400 hover:[&>svg]:stroke-gray-600 [&>svg]:h-4 [&>svg]:w-4 sm:[&>svg]:h-5 sm:[&>svg]:w-5 ml-1.5 relative focus:outline-0"
className="text-gray-500 opacity-100! hover:text-gray-600 [&>svg]:stroke-[0.4] [&>svg]:stroke-gray-400 [&>svg]:hover:stroke-gray-600 [&>svg]:h-4 [&>svg]:w-4 sm:[&>svg]:h-5 sm:[&>svg]:w-5 ml-1.5 relative focus:outline-0"
client:load
/>
</h1>

View File

@@ -19,8 +19,10 @@ import {
CreditCard,
ArrowRightLeft,
CircleX,
AlertCircle,
} from 'lucide-react';
import { BillingWarning } from './BillingWarning';
import { cn } from '../../lib/classname';
export type CreateCustomerPortalBody = {};
@@ -40,8 +42,12 @@ export function BillingPage() {
);
const isCanceled =
billingDetails?.status === 'canceled' || billingDetails?.cancelAtPeriodEnd;
billingDetails?.status === 'canceled' ||
billingDetails?.status === 'incomplete_expired' ||
billingDetails?.cancelAtPeriodEnd;
const isPastDue = billingDetails?.status === 'past_due';
const isIncomplete = billingDetails?.status === 'incomplete';
const {
mutate: createCustomerPortal,
@@ -117,6 +123,19 @@ export function BillingPage() {
!isLoadingBillingDetails &&
priceDetails && (
<div className="mt-1">
{isIncomplete && (
<BillingWarning
icon={AlertCircle}
message="Your subscription is incomplete "
buttonText="please pay invoice on Stripe."
onButtonClick={() => {
createCustomerPortal({});
}}
isLoading={
isCreatingCustomerPortal || isCreatingCustomerPortalSuccess
}
/>
)}
{isCanceled && (
<BillingWarning
icon={CircleX}
@@ -157,7 +176,7 @@ export function BillingPage() {
<RefreshCw className="size-5 text-gray-600" />
</div>
<div>
<span className="text-xs uppercase tracking-wider text-gray-400">
<span className="text-xs tracking-wider text-gray-400 uppercase">
Payment
</span>
<h3 className="flex items-baseline text-lg font-semibold text-black">
@@ -170,27 +189,35 @@ export function BillingPage() {
</div>
</div>
<div className="mt-6 border-t border-gray-100 pt-6">
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-gray-100">
<Calendar className="size-5 text-gray-600" />
<div
className={cn(
'mt-6 pt-6',
!isIncomplete && 'border-t border-gray-100',
isIncomplete && '-mt-6',
)}
>
{!isIncomplete && (
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-gray-100">
<Calendar className="size-5 text-gray-600" />
</div>
<div>
<span className="text-xs tracking-wider text-gray-400 uppercase">
{billingDetails?.cancelAtPeriodEnd
? 'Expires On'
: 'Renews On'}
</span>
<h3 className="text-lg font-semibold text-black">
{formattedNextBillDate}
</h3>
</div>
</div>
<div>
<span className="text-xs uppercase tracking-wider text-gray-400">
{billingDetails?.cancelAtPeriodEnd
? 'Expires On'
: 'Renews On'}
</span>
<h3 className="text-lg font-semibold text-black">
{formattedNextBillDate}
</h3>
</div>
</div>
)}
<div className="mt-8 flex gap-3 max-sm:flex-col">
{!isCanceled && (
{!isCanceled && !isIncomplete && (
<button
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm transition-colors hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 max-sm:flex-grow"
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-xs transition-colors hover:bg-gray-50 focus:ring-2 focus:ring-black focus:ring-offset-2 focus:outline-hidden max-sm:grow"
onClick={() => {
setShowUpgradeModal(true);
}}
@@ -201,7 +228,7 @@ export function BillingPage() {
)}
<button
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm transition-colors hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-xs transition-colors hover:bg-gray-50 focus:ring-2 focus:ring-black focus:ring-offset-2 focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-50"
onClick={() => {
createCustomerPortal({});
}}

View File

@@ -59,7 +59,7 @@ export function EmptyBillingScreen(props: EmptyBillingScreenProps) {
<button
onClick={onUpgrade}
className="inline-flex items-center justify-center rounded-lg bg-black px-6 py-2.5 text-sm font-medium text-white transition-colors hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2"
className="inline-flex items-center justify-center rounded-lg bg-black px-6 py-2.5 text-sm font-medium text-white transition-colors hover:opacity-80 focus:outline-hidden focus:ring-2 focus:ring-black focus:ring-offset-2"
>
Upgrade Account
</button>

View File

@@ -5,6 +5,7 @@ import {
MessageSquare,
Sparkles,
Heart,
MapIcon,
} from 'lucide-react';
import type { LucideIcon } from 'lucide-react';
import { useEffect, useState } from 'react';
@@ -33,17 +34,22 @@ type Perk = {
const PREMIUM_PERKS: Perk[] = [
{
icon: Zap,
title: 'Unlimited AI Course Generations',
description: 'Generate as many custom courses as you need',
title: 'AI Course Generations',
description: 'No limits on the number of AI courses',
},
{
icon: MapIcon,
title: 'AI Roadmaps',
description: 'No limits on the number of AI roadmaps',
},
{
icon: Infinity,
title: 'No Daily Limits on course features',
description: 'Use all features without restrictions',
title: 'Extended Daily Limits',
description: 'Generate more content in a day',
},
{
icon: MessageSquare,
title: 'Unlimited Course Follow-ups',
title: 'Course Follow-ups',
description: 'Ask as many questions as you need',
},
{
@@ -228,7 +234,14 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
)}
</p>
)}
<p className="text-2xl font-bold text-black sm:text-3xl">
<p
className={cn(
'text-2xl font-bold text-black sm:text-3xl',
{
'mt-0 md:mt-6': !isYearly,
},
)}
>
${plan.amount}{' '}
<span className="text-xs font-normal text-gray-500 sm:text-sm">
/ {isYearly ? 'year' : 'month'}
@@ -236,12 +249,12 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
</p>
</div>
<div className="flex-grow"></div>
<div className="grow"></div>
<div>
<button
className={cn(
'flex min-h-9 w-full items-center justify-center rounded-md py-2 text-sm font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-yellow-400 disabled:cursor-not-allowed disabled:opacity-60 sm:min-h-11 sm:py-2.5 sm:text-base',
'flex min-h-9 w-full items-center justify-center rounded-md py-2 text-sm font-medium transition-colors focus:outline-hidden focus-visible:ring-2 focus-visible:ring-yellow-400 disabled:cursor-not-allowed disabled:opacity-60 sm:min-h-11 sm:py-2.5 sm:text-base',
'bg-yellow-400 text-black hover:bg-yellow-500',
)}
disabled={
@@ -284,7 +297,10 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
{PREMIUM_PERKS.map((perk, index) => {
const Icon = perk.icon;
return (
<div key={index} className="flex items-start space-x-2 sm:space-x-3">
<div
key={index}
className="flex items-start space-x-2 sm:space-x-3"
>
<Icon className="mt-0.5 h-4 w-4 text-yellow-500 sm:h-5 sm:w-5" />
<div>
<h4 className="text-sm font-medium text-black sm:text-base">

View File

@@ -17,11 +17,11 @@ const formattedDate = DateTime.fromISO(frontmatter.date).toFormat(
<div class='relative mb-6' id={changelog.id}>
<span
class='absolute -left-6 top-2 h-2 w-2 flex-shrink-0 rounded-full bg-gray-300'
class='absolute -left-6 top-2 h-2 w-2 shrink-0 rounded-full bg-gray-300'
></span>
<div class='mb-3 flex flex-col sm:flex-row items-start sm:items-center gap-0.5 sm:gap-2'>
<span class='flex-shrink-0 text-xs tracking-wide text-gray-400'>
<span class='shrink-0 text-xs tracking-wide text-gray-400'>
{formattedDate}
</span>
<span class='truncate text-base font-medium text-balance'>

View File

@@ -6,13 +6,13 @@ const formattedDate = DateTime.fromISO('2024-09-13').toFormat('dd LLL, yyyy');
<div class='relative mb-6'>
<span
class='absolute -left-6 top-2 h-2 w-2 flex-shrink-0 rounded-full bg-gray-300'
class='absolute -left-6 top-2 h-2 w-2 shrink-0 rounded-full bg-gray-300'
></span>
<div
class='mb-3 flex flex-col items-start gap-0.5 sm:flex-row sm:items-center sm:gap-2'
>
<span class='flex-shrink-0 text-xs tracking-wide text-gray-400'>
<span class='shrink-0 text-xs tracking-wide text-gray-400'>
{formattedDate}
</span>
<span class='truncate text-balance text-base font-medium'>

View File

@@ -7,7 +7,7 @@ const top10Changelogs = allChangelogs.slice(0, 10);
---
<div class='border-t bg-white py-6 text-left sm:py-16 sm:text-center'>
<div class='container !max-w-[650px]'>
<div class='container max-w-[650px]!'>
<p class='text-2xl font-bold sm:text-5xl'>
<img
src='/images/gifs/rocket.gif'
@@ -40,10 +40,10 @@ const top10Changelogs = allChangelogs.slice(0, 10);
href={`/changelog#${changelog.id}`}
class='flex flex-col items-start sm:flex-row sm:items-center'
>
<span class='flex-shrink-0 pr-0 text-right text-sm tracking-wide text-gray-400 sm:w-[120px] sm:pr-4'>
<span class='shrink-0 pr-0 text-right text-sm tracking-wide text-gray-400 sm:w-[120px] sm:pr-4'>
{formattedDate}
</span>
<span class='hidden h-3 w-3 flex-shrink-0 rounded-full bg-gray-300 sm:block' />
<span class='hidden h-3 w-3 shrink-0 rounded-full bg-gray-300 sm:block' />
<span class='text-balance text-base font-medium text-gray-900 sm:pl-8'>
{changelog.frontmatter.title}
</span>

View File

@@ -1,4 +1,4 @@
import { ChevronLeft, ChevronRight, MoveRight } from 'lucide-react';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import React, { useState, useEffect, useCallback } from 'react';
interface ChangelogImagesProps {
@@ -63,17 +63,17 @@ const ChangelogImages: React.FC<ChangelogImagesProps> = ({ images }) => {
alt={title}
className="h-[120px] w-full object-cover object-left-top"
/>
<span className="absolute group-hover:opacity-0 inset-0 bg-gradient-to-b from-transparent to-black/40" />
<span className="absolute group-hover:opacity-0 inset-0 bg-linear-to-b from-transparent to-black/40" />
<div className="absolute font-medium inset-x-0 top-full group-hover:inset-y-0 flex items-center justify-center px-2 text-center text-xs bg-black/50 text-white py-0.5 opacity-0 group-hover:opacity-100 cursor-pointer">
<span className='bg-black py-0.5 rounded px-1'>{title}</span>
<span className='bg-black py-0.5 rounded-sm px-1'>{title}</span>
</div>
</div>
))}
</div>
{enlargedImage && (
<div
className="fixed inset-0 z-[999] flex items-center justify-center bg-black bg-opacity-75"
className="fixed inset-0 z-999 flex items-center justify-center bg-black/75"
onClick={handleCloseEnlarged}
>
<img
@@ -82,7 +82,7 @@ const ChangelogImages: React.FC<ChangelogImagesProps> = ({ images }) => {
className="max-h-[90%] max-w-[90%] rounded-xl object-contain"
/>
<button
className="absolute left-4 top-1/2 -translate-y-1/2 transform rounded-full bg-white hover:bg-opacity-100 bg-opacity-50 p-2"
className="absolute left-4 top-1/2 -translate-y-1/2 transform rounded-full bg-white/50 hover:bg-white/100 p-2"
onClick={(e) => {
e.stopPropagation();
handleNavigation('prev');
@@ -91,7 +91,7 @@ const ChangelogImages: React.FC<ChangelogImagesProps> = ({ images }) => {
<ChevronLeft size={24} />
</button>
<button
className="absolute right-4 top-1/2 -translate-y-1/2 transform rounded-full bg-white hover:bg-opacity-100 bg-opacity-50 p-2"
className="absolute right-4 top-1/2 -translate-y-1/2 transform rounded-full bg-white/50 hover:bg-white/100 p-2"
onClick={(e) => {
e.stopPropagation();
handleNavigation('next');

View File

@@ -194,13 +194,13 @@ 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:mt-20 md:h-auto">
<div className="relative rounded-lg bg-white shadow" ref={modalRef}>
<div className="relative rounded-lg bg-white shadow-sm" ref={modalRef}>
<input
ref={inputRef}
autoFocus={true}
type="text"
value={searchedText}
className="w-full rounded-t-md border-b p-4 text-sm focus:bg-gray-50 focus:outline-none"
className="w-full rounded-t-md border-b p-4 text-sm focus:bg-gray-50 focus:outline-hidden"
placeholder="Search roadmaps, guides or pages .."
autoComplete="off"
onInput={(e) => {
@@ -249,7 +249,7 @@ export function CommandMenu() {
)}
<a
className={cn(
'flex w-full items-center rounded p-2 text-sm',
'flex w-full items-center rounded-sm p-2 text-sm',
counter === activeCounter ? 'bg-gray-100' : '',
)}
onMouseOver={() => setActiveCounter(counter)}

View File

@@ -286,7 +286,7 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
className="relative flex flex-col items-start overflow-hidden rounded-md border border-gray-300"
key={resourceId}
>
<div className={'w-full flex-grow px-3 pb-2 pt-4'}>
<div className={'w-full grow px-3 pb-2 pt-4'}>
<span className="mb-0.5 block text-base font-medium leading-snug text-black">
{roadmapTitle}
</span>
@@ -341,7 +341,7 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
<button
type="button"
className={
'text-xs text-gray-500 underline hover:text-black focus:outline-none'
'text-xs text-gray-500 underline hover:text-black focus:outline-hidden'
}
onClick={() => {
if (isCustomResource) {

View File

@@ -68,11 +68,11 @@ export function SelectRoadmapModal(props: SelectRoadmapModalProps) {
);
return (
<div className="fixed left-0 right-0 top-0 z-[100] h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div className="fixed 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}
className="popup-body relative mt-4 overflow-hidden rounded-lg bg-white shadow"
className="popup-body relative mt-4 overflow-hidden rounded-lg bg-white shadow-sm"
>
<button
type="button"
@@ -86,7 +86,7 @@ export function SelectRoadmapModal(props: SelectRoadmapModalProps) {
ref={searchInputEl}
type="text"
placeholder="Search roadmaps"
className="block w-full border-b px-5 pb-3.5 pt-4 outline-none placeholder:text-gray-400"
className="block w-full border-b px-5 pb-3.5 pt-4 outline-hidden placeholder:text-gray-400"
value={searchText}
onInput={(e) => setSearchText((e.target as HTMLInputElement).value)}
/>

View File

@@ -76,7 +76,7 @@ export function Step0(props: Step0Props) {
{validTeamTypes.map((validTeamType) => (
<button
key={validTeamType.value}
className={`flex flex-grow flex-col items-center rounded-lg border px-5 pb-10 pt-12 ${
className={`flex grow flex-col items-center rounded-lg border px-5 pb-10 pt-12 ${
validTeamType.value == selectedTeamType
? 'border-gray-400 bg-gray-100'
: 'border-gray-300 hover:border-gray-400 hover:bg-gray-50'

View File

@@ -135,7 +135,7 @@ export function Step1(props: Step1Props) {
ref={nameRef as any}
autoFocus={true}
id="name"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-xs outline-hidden placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="Roadmap Inc."
disabled={isLoading}
required
@@ -157,7 +157,7 @@ export function Step1(props: Step1Props) {
name="website"
required
id="website"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-xs outline-hidden placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="https://roadmap.sh"
disabled={isLoading}
value={website}
@@ -178,7 +178,7 @@ export function Step1(props: Step1Props) {
type="url"
name="website"
id="website"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-xs outline-hidden placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="https://www.linkedin.com/company/roadmapsh"
disabled={isLoading}
value={linkedInUrl}
@@ -200,7 +200,7 @@ export function Step1(props: Step1Props) {
type="url"
name="website"
id="website"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-xs outline-hidden placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="https://github.com/roadmapsh"
disabled={isLoading}
value={gitHubUrl}
@@ -219,7 +219,7 @@ export function Step1(props: Step1Props) {
<select
name="team-size"
id="team-size"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-xs outline-hidden placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
required={selectedTeamType === 'company'}
disabled={isLoading}
value={teamSize}

View File

@@ -50,7 +50,7 @@ export function Step2(props: Step2Props) {
onClick={onNext}
disabled={teamResourceConfig.length !== 0}
className={
'flex-grow rounded-md border border-gray-300 bg-white px-4 py-2 text-gray-500 hover:border-gray-400 hover:text-black md:flex-auto disabled:opacity-50 disabled:pointer-events-none'
'grow rounded-md border border-gray-300 bg-white px-4 py-2 text-gray-500 hover:border-gray-400 hover:text-black md:flex-auto disabled:opacity-50 disabled:pointer-events-none'
}
>
Skip for Now

View File

@@ -109,7 +109,7 @@ export function Step3(props: Step3Props) {
setUsers(newUsers);
}}
className="flex-grow rounded-md border border-gray-200 bg-white px-4 py-2 text-gray-900"
className="grow rounded-md border border-gray-200 bg-white px-4 py-2 text-gray-900"
/>
<RoleDropdown
selectedRole={user.role}
@@ -180,7 +180,7 @@ export function Step3(props: Step3Props) {
onClick={onNext}
disabled={users.filter((u) => u.email).length !== 0}
className={
'rounded-md flex-grow md:flex-auto border border-gray-300 bg-white px-4 py-2 text-gray-500 hover:border-gray-400 hover:text-black disabled:opacity-50 disabled:pointer-events-none'
'rounded-md grow md:flex-auto border border-gray-300 bg-white px-4 py-2 text-gray-500 hover:border-gray-400 hover:text-black disabled:opacity-50 disabled:pointer-events-none'
}
>
Skip for Now

View File

@@ -148,12 +148,12 @@ export function UpdateTeamResourceModal(props: ProgressMapProps) {
}, []);
return (
<div className="fixed left-0 right-0 top-0 z-[100] h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div className="fixed 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'}
ref={popupBodyEl}
className="popup-body relative rounded-lg bg-white shadow"
className="popup-body relative rounded-lg bg-white shadow-sm"
>
<div
className={

View File

@@ -179,7 +179,7 @@ export function CreateRoadmapModal(props: CreateRoadmapModalProps) {
name="title"
id="title"
required
className="block w-full rounded-md border border-gray-300 px-2.5 py-2 text-black outline-none focus:border-black sm:text-sm"
className="block w-full rounded-md border border-gray-300 px-2.5 py-2 text-black outline-hidden focus:border-black sm:text-sm"
placeholder="Enter Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
@@ -199,7 +199,7 @@ export function CreateRoadmapModal(props: CreateRoadmapModalProps) {
name="description"
required
className={cn(
'block h-24 w-full resize-none rounded-md border border-gray-300 px-2.5 py-2 text-black outline-none focus:border-black sm:text-sm',
'block h-24 w-full resize-none rounded-md border border-gray-300 px-2.5 py-2 text-black outline-hidden focus:border-black sm:text-sm',
isInvalidDescription && 'border-red-300 bg-red-100',
)}
placeholder="Enter Description"
@@ -219,7 +219,7 @@ export function CreateRoadmapModal(props: CreateRoadmapModalProps) {
onClick={onClose}
type="button"
className={cn(
'block h-9 rounded-md border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-black outline-none hover:border-gray-300 hover:bg-gray-50 focus:border-gray-300 focus:bg-gray-100',
'block h-9 rounded-md border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-black outline-hidden hover:border-gray-300 hover:bg-gray-50 focus:border-gray-300 focus:bg-gray-100',
!teamId && 'w-full',
)}
>
@@ -232,7 +232,7 @@ export function CreateRoadmapModal(props: CreateRoadmapModalProps) {
disabled={isLoading}
type="button"
onClick={(e) => handleSubmit(e, false)}
className="flex h-9 items-center justify-center rounded-md border border-black bg-white px-4 py-2 text-sm font-medium text-black outline-none hover:bg-black hover:text-white focus:bg-black focus:text-white"
className="flex h-9 items-center justify-center rounded-md border border-black bg-white px-4 py-2 text-sm font-medium text-black outline-hidden hover:bg-black hover:text-white focus:bg-black focus:text-white"
>
{isLoading ? (
<Loader2 size={16} className="animate-spin" />
@@ -246,7 +246,7 @@ export function CreateRoadmapModal(props: CreateRoadmapModalProps) {
disabled={isLoading}
type="submit"
className={cn(
'flex h-9 items-center justify-center rounded-md border border-transparent bg-black px-4 py-2 text-sm font-medium text-white outline-none hover:bg-gray-800 focus:bg-gray-800',
'flex h-9 items-center justify-center rounded-md border border-transparent bg-black px-4 py-2 text-sm font-medium text-white outline-hidden hover:bg-gray-800 focus:bg-gray-800',
teamId ? 'hidden sm:flex' : 'w-full',
)}
>

View File

@@ -118,6 +118,7 @@ export function CustomRoadmap(props: CustomRoadmapProps) {
resourceId={roadmap!._id}
resourceTitle={roadmap!.title}
resourceType="roadmap"
renderer='editor'
isEmbed={isEmbed}
canSubmitContribution={false}
/>

View File

@@ -53,7 +53,7 @@ export function EmbedRoadmapModal(props: ShareRoadmapModalProps) {
<div className="flex items-center justify-between px-4 pb-4 pt-2">
<button
className={cn(
'flex h-9 w-full items-center justify-center rounded-md border border-transparent px-4 py-2 text-sm font-medium text-white outline-none',
'flex h-9 w-full items-center justify-center rounded-md border border-transparent px-4 py-2 text-sm font-medium text-white outline-hidden',
{
'bg-green-500 hover:bg-green-600 focus:bg-green-600': isCopied,
'bg-gray-900 hover:bg-gray-800 focus:bg-gray-800': !isCopied,

View File

@@ -1,4 +1,4 @@
import { ReadonlyEditor } from '../../../editor/readonly-editor';
import { ReadonlyEditor } from '@roadmapsh/editor';
import type { RoadmapDocument } from './CreateRoadmap/CreateRoadmapModal';
import {
refreshProgressCounters,
@@ -9,7 +9,7 @@ import {
} from '../../lib/resource-progress';
import { pageProgressMessage } from '../../stores/page';
import { useToast } from '../../hooks/use-toast';
import type { Node } from 'reactflow';
import type { Node } from '@roadmapsh/editor';
import { type MouseEvent, useCallback, useRef, useState } from 'react';
import { EmptyRoadmap } from './EmptyRoadmap';
import { cn } from '../../lib/classname';

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