Add courses functionality

pull/8467/head
Kamran Ahmed 2 weeks ago
parent 7a00234f9a
commit 0d62847053
  1. 12
      .astro/settings.json
  2. 10
      package.json
  3. 321
      pnpm-lock.yaml
  4. 88
      src/components/RoadmapHeader.astro
  5. 31
      src/pages/[roadmapId]/courses.astro
  6. 2
      src/pages/[roadmapId]/index.astro
  7. 1
      src/pages/[roadmapId]/projects.astro

@ -1,8 +1,8 @@
{ {
"devToolbar": { "devToolbar": {
"enabled": false "enabled": false
}, },
"_variables": { "_variables": {
"lastUpdateCheck": 1743851801172 "lastUpdateCheck": 1743851801172
} }
} }

@ -32,7 +32,7 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/node": "^8.3.4", "@astrojs/node": "^8.3.4",
"@astrojs/react": "^3.6.2", "@astrojs/react": "^4.2.3",
"@astrojs/sitemap": "^3.2.0", "@astrojs/sitemap": "^3.2.0",
"@fingerprintjs/fingerprintjs": "^4.5.0", "@fingerprintjs/fingerprintjs": "^4.5.0",
"@microsoft/clarity": "^1.0.0", "@microsoft/clarity": "^1.0.0",
@ -42,8 +42,8 @@
"@roadmapsh/editor": "workspace:*", "@roadmapsh/editor": "workspace:*",
"@tailwindcss/vite": "^4.1.3", "@tailwindcss/vite": "^4.1.3",
"@tanstack/react-query": "^5.59.16", "@tanstack/react-query": "^5.59.16",
"@types/react": "^18.3.11", "@types/react": "^19.0.0",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^19.0.0",
"astro": "^4.16.1", "astro": "^4.16.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
@ -63,10 +63,10 @@
"npm-check-updates": "^17.1.3", "npm-check-updates": "^17.1.3",
"playwright": "^1.48.0", "playwright": "^1.48.0",
"prismjs": "^1.29.0", "prismjs": "^1.29.0",
"react": "^18.3.1", "react": "^19.0.0",
"react-calendar-heatmap": "^1.9.0", "react-calendar-heatmap": "^1.9.0",
"react-confetti": "^6.1.0", "react-confetti": "^6.1.0",
"react-dom": "^18.3.1", "react-dom": "^19.0.0",
"react-resizable-panels": "^2.1.7", "react-resizable-panels": "^2.1.7",
"react-textarea-autosize": "^8.5.7", "react-textarea-autosize": "^8.5.7",
"react-tooltip": "^5.28.0", "react-tooltip": "^5.28.0",

@ -12,8 +12,8 @@ importers:
specifier: ^8.3.4 specifier: ^8.3.4
version: 8.3.4(astro@4.16.18(@types/node@18.19.86)(lightningcss@1.29.2)(rollup@4.39.0)(typescript@5.8.3)) version: 8.3.4(astro@4.16.18(@types/node@18.19.86)(lightningcss@1.29.2)(rollup@4.39.0)(typescript@5.8.3))
'@astrojs/react': '@astrojs/react':
specifier: ^3.6.2 specifier: ^4.2.3
version: 3.6.3(@types/node@18.19.86)(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(lightningcss@1.29.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 4.2.3(@types/node@18.19.86)(@types/react-dom@19.1.1(@types/react@19.1.0))(@types/react@19.1.0)(jiti@2.4.2)(lightningcss@1.29.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tsx@4.19.3)
'@astrojs/sitemap': '@astrojs/sitemap':
specifier: ^3.2.0 specifier: ^3.2.0
version: 3.3.0 version: 3.3.0
@ -25,7 +25,7 @@ importers:
version: 1.0.0 version: 1.0.0
'@nanostores/react': '@nanostores/react':
specifier: ^0.8.0 specifier: ^0.8.0
version: 0.8.4(nanostores@0.11.4)(react@18.3.1) version: 0.8.4(nanostores@0.11.4)(react@19.1.0)
'@napi-rs/image': '@napi-rs/image':
specifier: ^1.9.2 specifier: ^1.9.2
version: 1.9.2 version: 1.9.2
@ -40,13 +40,13 @@ importers:
version: 4.1.3(vite@5.4.17(@types/node@18.19.86)(lightningcss@1.29.2)) version: 4.1.3(vite@5.4.17(@types/node@18.19.86)(lightningcss@1.29.2))
'@tanstack/react-query': '@tanstack/react-query':
specifier: ^5.59.16 specifier: ^5.59.16
version: 5.71.10(react@18.3.1) version: 5.72.0(react@19.1.0)
'@types/react': '@types/react':
specifier: ^18.3.11 specifier: ^19.0.0
version: 18.3.20 version: 19.1.0
'@types/react-dom': '@types/react-dom':
specifier: ^18.3.1 specifier: ^19.0.0
version: 18.3.6(@types/react@18.3.20) version: 19.1.1(@types/react@19.1.0)
astro: astro:
specifier: ^4.16.1 specifier: ^4.16.1
version: 4.16.18(@types/node@18.19.86)(lightningcss@1.29.2)(rollup@4.39.0)(typescript@5.8.3) version: 4.16.18(@types/node@18.19.86)(lightningcss@1.29.2)(rollup@4.39.0)(typescript@5.8.3)
@ -79,7 +79,7 @@ importers:
version: 3.0.5 version: 3.0.5
lucide-react: lucide-react:
specifier: ^0.452.0 specifier: ^0.452.0
version: 0.452.0(react@18.3.1) version: 0.452.0(react@19.1.0)
luxon: luxon:
specifier: ^3.5.0 specifier: ^3.5.0
version: 3.6.1 version: 3.6.1
@ -105,26 +105,26 @@ importers:
specifier: ^1.29.0 specifier: ^1.29.0
version: 1.30.0 version: 1.30.0
react: react:
specifier: ^18.3.1 specifier: ^19.0.0
version: 18.3.1 version: 19.1.0
react-calendar-heatmap: react-calendar-heatmap:
specifier: ^1.9.0 specifier: ^1.9.0
version: 1.10.0(react@18.3.1) version: 1.10.0(react@19.1.0)
react-confetti: react-confetti:
specifier: ^6.1.0 specifier: ^6.1.0
version: 6.4.0(react@18.3.1) version: 6.4.0(react@19.1.0)
react-dom: react-dom:
specifier: ^18.3.1 specifier: ^19.0.0
version: 18.3.1(react@18.3.1) version: 19.1.0(react@19.1.0)
react-resizable-panels: react-resizable-panels:
specifier: ^2.1.7 specifier: ^2.1.7
version: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-textarea-autosize: react-textarea-autosize:
specifier: ^8.5.7 specifier: ^8.5.7
version: 8.5.9(@types/react@18.3.20)(react@18.3.1) version: 8.5.9(@types/react@19.1.0)(react@19.1.0)
react-tooltip: react-tooltip:
specifier: ^5.28.0 specifier: ^5.28.0
version: 5.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 5.28.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
rehype-external-links: rehype-external-links:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.0.0 version: 3.0.0
@ -169,7 +169,7 @@ importers:
version: 11.0.5 version: 11.0.5
zustand: zustand:
specifier: ^5.0.1 specifier: ^5.0.1
version: 5.0.3(@types/react@18.3.20)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)) version: 5.0.3(@types/react@19.1.0)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0))
devDependencies: devDependencies:
'@ai-sdk/google': '@ai-sdk/google':
specifier: ^1.1.19 specifier: ^1.1.19
@ -206,7 +206,7 @@ importers:
version: 5.0.5 version: 5.0.5
ai: ai:
specifier: ^4.1.51 specifier: ^4.1.51
version: 4.3.2(react@18.3.1)(zod@3.24.2) version: 4.3.2(react@19.1.0)(zod@3.24.2)
csv-parser: csv-parser:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.0 version: 3.2.0
@ -221,7 +221,7 @@ importers:
version: 14.1.0 version: 14.1.0
openai: openai:
specifier: ^4.67.3 specifier: ^4.67.3
version: 4.91.1(zod@3.24.2) version: 4.92.0(zod@3.24.2)
prettier: prettier:
specifier: ^3.3.3 specifier: ^3.3.3
version: 3.5.3 version: 3.5.3
@ -239,7 +239,7 @@ importers:
dependencies: dependencies:
'@xyflow/react': '@xyflow/react':
specifier: ^12.4.2 specifier: ^12.4.2
version: 12.5.4(@types/react@19.1.0)(react-dom@18.3.1(react@19.1.0))(react@19.1.0) version: 12.5.5(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
clsx: clsx:
specifier: ^2.1.1 specifier: ^2.1.1
version: 2.1.1 version: 2.1.1
@ -257,7 +257,7 @@ importers:
version: 11.0.0 version: 11.0.0
tailwind-merge: tailwind-merge:
specifier: ^3.0.1 specifier: ^3.0.1
version: 3.1.0 version: 3.2.0
unified: unified:
specifier: ^11.0.5 specifier: ^11.0.5
version: 11.0.5 version: 11.0.5
@ -347,14 +347,14 @@ packages:
resolution: {integrity: sha512-Z9IYjuXSArkAUx3N6xj6+Bnvx8OdUSHA8YoOgyepp3+zJmtVYJIl/I18GozdJVW1p5u/CNpl3Km7/gwTJK85cw==} resolution: {integrity: sha512-Z9IYjuXSArkAUx3N6xj6+Bnvx8OdUSHA8YoOgyepp3+zJmtVYJIl/I18GozdJVW1p5u/CNpl3Km7/gwTJK85cw==}
engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0}
'@astrojs/react@3.6.3': '@astrojs/react@4.2.3':
resolution: {integrity: sha512-5ihLQDH5Runddug5AZYlnp/Q5T81QxhwnWJXA9rchBAdh11c6UhBbv9Kdk7b2PkXoEU70CGWBP9hSh0VCR58eA==} resolution: {integrity: sha512-icL1hCnW1v+w+NCAz8REfsh9R1aGMW75fYBoeLjyhrVDxXQHiFbTfyBIHkgH79qqID7SM81+hPxHlqcgCuBP8w==}
engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.1 || ^20.3.0 || >=22.0.0}
peerDependencies: peerDependencies:
'@types/react': ^17.0.50 || ^18.0.21 '@types/react': ^17.0.50 || ^18.0.21 || ^19.0.0
'@types/react-dom': ^17.0.17 || ^18.0.6 '@types/react-dom': ^17.0.17 || ^18.0.6 || ^19.0.0
react: ^17.0.2 || ^18.0.0 || ^19.0.0-beta react: ^17.0.2 || ^18.0.0 || ^19.0.0
react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0-beta react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0
'@astrojs/sitemap@3.3.0': '@astrojs/sitemap@3.3.0':
resolution: {integrity: sha512-nYE4lKQtk+Kbrw/w0G0TTgT724co0jUsU4tPlHY9au5HmTBKbwiCLwO/15b1/y13aZ4Kr9ZbMeMHlXuwn0ty4Q==} resolution: {integrity: sha512-nYE4lKQtk+Kbrw/w0G0TTgT724co0jUsU4tPlHY9au5HmTBKbwiCLwO/15b1/y13aZ4Kr9ZbMeMHlXuwn0ty4Q==}
@ -1335,11 +1335,11 @@ packages:
peerDependencies: peerDependencies:
vite: ^5.2.0 || ^6 vite: ^5.2.0 || ^6
'@tanstack/query-core@5.71.10': '@tanstack/query-core@5.72.0':
resolution: {integrity: sha512-/fKEY8fO1nbszfrBatzmhJa1nEwIKn0c6Tv2A1ocSA5OiD2GukOIV8nnBbvJRgZb/VIoBy9/N4PVbABI8YQLow==} resolution: {integrity: sha512-aa3p6Mou++JLLxxxVX9AB9uGeRIGc0JWkw96GASXuMG8K3D+JpYbSFcqXbkGFJ1eX2jKHPurmCBoO43RjjXJCA==}
'@tanstack/react-query@5.71.10': '@tanstack/react-query@5.72.0':
resolution: {integrity: sha512-mQYM/ObpL8YMDz8vCoUuHkbe8Yu7NnVRH8aBaBa/3zlufjp1f1VuWjeO3TcumNHfuVMDwEAGinsgwrB7OKADiQ==} resolution: {integrity: sha512-4Dejq/IiXrPlr/0xxj4H2GbC6KckwfTCoHWbd02+UoIV0laC9yke0d0KegmFdXJA712I6UCuy8WpPM76uuPJ+w==}
peerDependencies: peerDependencies:
react: ^18 || ^19 react: ^18 || ^19
@ -1447,23 +1447,17 @@ packages:
'@types/prismjs@1.26.5': '@types/prismjs@1.26.5':
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
'@types/prop-types@15.7.14':
resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
'@types/react-calendar-heatmap@1.9.0': '@types/react-calendar-heatmap@1.9.0':
resolution: {integrity: sha512-BH8M/nsXoLGa3hxWbrq3guPwlK0cV+w1i4c/ktrTxTzN5fBths6WbeUZ4dK0+tE76qiGoVSo9Tse8WVVuMIV+w==} resolution: {integrity: sha512-BH8M/nsXoLGa3hxWbrq3guPwlK0cV+w1i4c/ktrTxTzN5fBths6WbeUZ4dK0+tE76qiGoVSo9Tse8WVVuMIV+w==}
'@types/react-dom@18.3.6': '@types/react-dom@19.1.1':
resolution: {integrity: sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==} resolution: {integrity: sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==}
peerDependencies: peerDependencies:
'@types/react': ^18.0.0 '@types/react': ^19.0.0
'@types/react-slick@0.23.13': '@types/react-slick@0.23.13':
resolution: {integrity: sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==} resolution: {integrity: sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==}
'@types/react@18.3.20':
resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==}
'@types/react@19.1.0': '@types/react@19.1.0':
resolution: {integrity: sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==} resolution: {integrity: sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==}
@ -1488,14 +1482,14 @@ packages:
peerDependencies: peerDependencies:
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 vite: ^4.2.0 || ^5.0.0 || ^6.0.0
'@xyflow/react@12.5.4': '@xyflow/react@12.5.5':
resolution: {integrity: sha512-ITCW3h/pXVKwb0j3w8CxPyfmmxIqToctE55R+BcfTAe9j+qr/6Uc+NUsbIgnvEyiziJKq/sS0ovVfZ4482txww==} resolution: {integrity: sha512-mAtHuS4ktYBL1ph5AJt7X/VmpzzlmQBN3+OXxyT/1PzxwrVto6AKc3caerfxzwBsg3cA4J8lB63F3WLAuPMmHw==}
peerDependencies: peerDependencies:
react: '>=17' react: '>=17'
react-dom: '>=17' react-dom: '>=17'
'@xyflow/system@0.0.54': '@xyflow/system@0.0.55':
resolution: {integrity: sha512-DBoQTcSQ2620WMfakCcjRLrlqalWcZBPgMNrfSAybnVeyZm73rT1592GAXPcC3eoVmWcvGfBgqwAmmNtlrowdw==} resolution: {integrity: sha512-6cngWlE4oMXm+zrsbJxerP3wUNUFJcv/cE5kDfu0qO55OWK3fAeSOLW9td3xEVQlomjIW5knds1MzeMnBeCfqw==}
abort-controller@3.0.0: abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
@ -2777,8 +2771,8 @@ packages:
oniguruma-to-es@4.1.0: oniguruma-to-es@4.1.0:
resolution: {integrity: sha512-SNwG909cSLo4vPyyPbU/VJkEc9WOXqu2ycBlfd1UCXLqk1IijcQktSBb2yRQ2UFPsDhpkaf+C1dtT3PkLK/yWA==} resolution: {integrity: sha512-SNwG909cSLo4vPyyPbU/VJkEc9WOXqu2ycBlfd1UCXLqk1IijcQktSBb2yRQ2UFPsDhpkaf+C1dtT3PkLK/yWA==}
openai@4.91.1: openai@4.92.0:
resolution: {integrity: sha512-DbjrR0hIMQFbxz8+3qBsfPJnh3+I/skPgoSlT7f9eiZuhGBUissPQULNgx6gHNkLoZ3uS0uYS6eXPUdtg4nHzw==} resolution: {integrity: sha512-vLIBP8gygD5M7XIrdBkUFKnfEq3EmaI+lmGjDDAmjahzmdhwdpzDA+GBA4ZZwj7rgu1WMNh9/SqyTysxMulC2g==}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
ws: ^8.18.0 ws: ^8.18.0
@ -3094,10 +3088,10 @@ packages:
peerDependencies: peerDependencies:
react: ^16.3.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 react: ^16.3.0 || ^17.0.1 || ^18.0.0 || ^19.0.0
react-dom@18.3.1: react-dom@19.1.0:
resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
peerDependencies: peerDependencies:
react: ^18.3.1 react: ^19.1.0
react-is@16.13.1: react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
@ -3124,10 +3118,6 @@ packages:
react: '>=16.14.0' react: '>=16.14.0'
react-dom: '>=16.14.0' react-dom: '>=16.14.0'
react@18.3.1:
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
engines: {node: '>=0.10.0'}
react@19.1.0: react@19.1.0:
resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -3245,8 +3235,8 @@ packages:
sax@1.4.1: sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
scheduler@0.23.2: scheduler@0.26.0:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
section-matter@1.0.0: section-matter@1.0.0:
resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
@ -3394,8 +3384,8 @@ packages:
tailwind-merge@2.6.0: tailwind-merge@2.6.0:
resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==}
tailwind-merge@3.1.0: tailwind-merge@3.2.0:
resolution: {integrity: sha512-aV27Oj8B7U/tAOMhJsSGdWqelfmudnGMdXIlMnk1JfsjwSjts6o8HyfN7SFH3EztzH4YH8kk6GbLTHzITJO39Q==} resolution: {integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==}
tailwindcss@4.1.3: tailwindcss@4.1.3:
resolution: {integrity: sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==} resolution: {integrity: sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==}
@ -3640,6 +3630,46 @@ packages:
terser: terser:
optional: true optional: true
vite@6.2.5:
resolution: {integrity: sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
jiti: '>=1.21.0'
less: '*'
lightningcss: ^1.21.0
sass: '*'
sass-embedded: '*'
stylus: '*'
sugarss: '*'
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
peerDependenciesMeta:
'@types/node':
optional: true
jiti:
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
vitefu@1.0.6: vitefu@1.0.6:
resolution: {integrity: sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==} resolution: {integrity: sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==}
peerDependencies: peerDependencies:
@ -3785,12 +3815,12 @@ snapshots:
dependencies: dependencies:
json-schema: 0.4.0 json-schema: 0.4.0
'@ai-sdk/react@1.2.6(react@18.3.1)(zod@3.24.2)': '@ai-sdk/react@1.2.6(react@19.1.0)(zod@3.24.2)':
dependencies: dependencies:
'@ai-sdk/provider-utils': 2.2.4(zod@3.24.2) '@ai-sdk/provider-utils': 2.2.4(zod@3.24.2)
'@ai-sdk/ui-utils': 1.2.5(zod@3.24.2) '@ai-sdk/ui-utils': 1.2.5(zod@3.24.2)
react: 18.3.1 react: 19.1.0
swr: 2.3.3(react@18.3.1) swr: 2.3.3(react@19.1.0)
throttleit: 2.1.0 throttleit: 2.1.0
optionalDependencies: optionalDependencies:
zod: 3.24.2 zod: 3.24.2
@ -3848,17 +3878,18 @@ snapshots:
dependencies: dependencies:
prismjs: 1.30.0 prismjs: 1.30.0
'@astrojs/react@3.6.3(@types/node@18.19.86)(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(lightningcss@1.29.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@astrojs/react@4.2.3(@types/node@18.19.86)(@types/react-dom@19.1.1(@types/react@19.1.0))(@types/react@19.1.0)(jiti@2.4.2)(lightningcss@1.29.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tsx@4.19.3)':
dependencies: dependencies:
'@types/react': 18.3.20 '@types/react': 19.1.0
'@types/react-dom': 18.3.6(@types/react@18.3.20) '@types/react-dom': 19.1.1(@types/react@19.1.0)
'@vitejs/plugin-react': 4.3.4(vite@5.4.17(@types/node@18.19.86)(lightningcss@1.29.2)) '@vitejs/plugin-react': 4.3.4(vite@6.2.5(@types/node@18.19.86)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3))
react: 18.3.1 react: 19.1.0
react-dom: 18.3.1(react@18.3.1) react-dom: 19.1.0(react@19.1.0)
ultrahtml: 1.6.0 ultrahtml: 1.6.0
vite: 5.4.17(@types/node@18.19.86)(lightningcss@1.29.2) vite: 6.2.5(@types/node@18.19.86)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti
- less - less
- lightningcss - lightningcss
- sass - sass
@ -3867,6 +3898,8 @@ snapshots:
- sugarss - sugarss
- supports-color - supports-color
- terser - terser
- tsx
- yaml
'@astrojs/sitemap@3.3.0': '@astrojs/sitemap@3.3.0':
dependencies: dependencies:
@ -4300,10 +4333,10 @@ snapshots:
'@mixmark-io/domino@2.2.0': {} '@mixmark-io/domino@2.2.0': {}
'@nanostores/react@0.8.4(nanostores@0.11.4)(react@18.3.1)': '@nanostores/react@0.8.4(nanostores@0.11.4)(react@19.1.0)':
dependencies: dependencies:
nanostores: 0.11.4 nanostores: 0.11.4
react: 18.3.1 react: 19.1.0
'@napi-rs/image-android-arm64@1.9.2': '@napi-rs/image-android-arm64@1.9.2':
optional: true optional: true
@ -4657,12 +4690,12 @@ snapshots:
tailwindcss: 4.1.3 tailwindcss: 4.1.3
vite: 5.4.17(@types/node@18.19.86)(lightningcss@1.29.2) vite: 5.4.17(@types/node@18.19.86)(lightningcss@1.29.2)
'@tanstack/query-core@5.71.10': {} '@tanstack/query-core@5.72.0': {}
'@tanstack/react-query@5.71.10(react@18.3.1)': '@tanstack/react-query@5.72.0(react@19.1.0)':
dependencies: dependencies:
'@tanstack/query-core': 5.71.10 '@tanstack/query-core': 5.72.0
react: 18.3.1 react: 19.1.0
'@tiptap/core@2.11.7(@tiptap/pm@2.11.7)': '@tiptap/core@2.11.7(@tiptap/pm@2.11.7)':
dependencies: dependencies:
@ -4797,24 +4830,17 @@ snapshots:
'@types/prismjs@1.26.5': {} '@types/prismjs@1.26.5': {}
'@types/prop-types@15.7.14': {}
'@types/react-calendar-heatmap@1.9.0': '@types/react-calendar-heatmap@1.9.0':
dependencies: dependencies:
'@types/react': 18.3.20 '@types/react': 19.1.0
'@types/react-dom@18.3.6(@types/react@18.3.20)': '@types/react-dom@19.1.1(@types/react@19.1.0)':
dependencies: dependencies:
'@types/react': 18.3.20 '@types/react': 19.1.0
'@types/react-slick@0.23.13': '@types/react-slick@0.23.13':
dependencies: dependencies:
'@types/react': 18.3.20 '@types/react': 19.1.0
'@types/react@18.3.20':
dependencies:
'@types/prop-types': 15.7.14
csstype: 3.1.3
'@types/react@19.1.0': '@types/react@19.1.0':
dependencies: dependencies:
@ -4834,29 +4860,29 @@ snapshots:
'@ungap/structured-clone@1.3.0': {} '@ungap/structured-clone@1.3.0': {}
'@vitejs/plugin-react@4.3.4(vite@5.4.17(@types/node@18.19.86)(lightningcss@1.29.2))': '@vitejs/plugin-react@4.3.4(vite@6.2.5(@types/node@18.19.86)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3))':
dependencies: dependencies:
'@babel/core': 7.26.10 '@babel/core': 7.26.10
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.10) '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.10)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.10) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.10)
'@types/babel__core': 7.20.5 '@types/babel__core': 7.20.5
react-refresh: 0.14.2 react-refresh: 0.14.2
vite: 5.4.17(@types/node@18.19.86)(lightningcss@1.29.2) vite: 6.2.5(@types/node@18.19.86)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@xyflow/react@12.5.4(@types/react@19.1.0)(react-dom@18.3.1(react@19.1.0))(react@19.1.0)': '@xyflow/react@12.5.5(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies: dependencies:
'@xyflow/system': 0.0.54 '@xyflow/system': 0.0.55
classcat: 5.0.5 classcat: 5.0.5
react: 19.1.0 react: 19.1.0
react-dom: 18.3.1(react@19.1.0) react-dom: 19.1.0(react@19.1.0)
zustand: 4.5.6(@types/react@19.1.0)(react@19.1.0) zustand: 4.5.6(@types/react@19.1.0)(react@19.1.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/react' - '@types/react'
- immer - immer
'@xyflow/system@0.0.54': '@xyflow/system@0.0.55':
dependencies: dependencies:
'@types/d3-drag': 3.0.7 '@types/d3-drag': 3.0.7
'@types/d3-selection': 3.0.11 '@types/d3-selection': 3.0.11
@ -4876,17 +4902,17 @@ snapshots:
dependencies: dependencies:
humanize-ms: 1.2.1 humanize-ms: 1.2.1
ai@4.3.2(react@18.3.1)(zod@3.24.2): ai@4.3.2(react@19.1.0)(zod@3.24.2):
dependencies: dependencies:
'@ai-sdk/provider': 1.1.0 '@ai-sdk/provider': 1.1.0
'@ai-sdk/provider-utils': 2.2.4(zod@3.24.2) '@ai-sdk/provider-utils': 2.2.4(zod@3.24.2)
'@ai-sdk/react': 1.2.6(react@18.3.1)(zod@3.24.2) '@ai-sdk/react': 1.2.6(react@19.1.0)(zod@3.24.2)
'@ai-sdk/ui-utils': 1.2.5(zod@3.24.2) '@ai-sdk/ui-utils': 1.2.5(zod@3.24.2)
'@opentelemetry/api': 1.9.0 '@opentelemetry/api': 1.9.0
jsondiffpatch: 0.6.0 jsondiffpatch: 0.6.0
zod: 3.24.2 zod: 3.24.2
optionalDependencies: optionalDependencies:
react: 18.3.1 react: 19.1.0
ansi-align@3.0.1: ansi-align@3.0.1:
dependencies: dependencies:
@ -5878,9 +5904,9 @@ snapshots:
dependencies: dependencies:
yallist: 3.1.1 yallist: 3.1.1
lucide-react@0.452.0(react@18.3.1): lucide-react@0.452.0(react@19.1.0):
dependencies: dependencies:
react: 18.3.1 react: 19.1.0
lucide-react@0.474.0(react@19.1.0): lucide-react@0.474.0(react@19.1.0):
dependencies: dependencies:
@ -6330,7 +6356,7 @@ snapshots:
regex: 6.0.1 regex: 6.0.1
regex-recursion: 6.0.2 regex-recursion: 6.0.2
openai@4.91.1(zod@3.24.2): openai@4.92.0(zod@3.24.2):
dependencies: dependencies:
'@types/node': 18.19.86 '@types/node': 18.19.86
'@types/node-fetch': 2.6.12 '@types/node-fetch': 2.6.12
@ -6615,57 +6641,46 @@ snapshots:
range-parser@1.2.1: {} range-parser@1.2.1: {}
react-calendar-heatmap@1.10.0(react@18.3.1): react-calendar-heatmap@1.10.0(react@19.1.0):
dependencies: dependencies:
memoize-one: 5.2.1 memoize-one: 5.2.1
prop-types: 15.8.1 prop-types: 15.8.1
react: 18.3.1 react: 19.1.0
react-confetti@6.4.0(react@18.3.1): react-confetti@6.4.0(react@19.1.0):
dependencies: dependencies:
react: 18.3.1 react: 19.1.0
tween-functions: 1.2.0 tween-functions: 1.2.0
react-dom@18.3.1(react@18.3.1): react-dom@19.1.0(react@19.1.0):
dependencies:
loose-envify: 1.4.0
react: 18.3.1
scheduler: 0.23.2
react-dom@18.3.1(react@19.1.0):
dependencies: dependencies:
loose-envify: 1.4.0
react: 19.1.0 react: 19.1.0
scheduler: 0.23.2 scheduler: 0.26.0
react-is@16.13.1: {} react-is@16.13.1: {}
react-refresh@0.14.2: {} react-refresh@0.14.2: {}
react-resizable-panels@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): react-resizable-panels@2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies: dependencies:
react: 18.3.1 react: 19.1.0
react-dom: 18.3.1(react@18.3.1) react-dom: 19.1.0(react@19.1.0)
react-textarea-autosize@8.5.9(@types/react@18.3.20)(react@18.3.1): react-textarea-autosize@8.5.9(@types/react@19.1.0)(react@19.1.0):
dependencies: dependencies:
'@babel/runtime': 7.27.0 '@babel/runtime': 7.27.0
react: 18.3.1 react: 19.1.0
use-composed-ref: 1.4.0(@types/react@18.3.20)(react@18.3.1) use-composed-ref: 1.4.0(@types/react@19.1.0)(react@19.1.0)
use-latest: 1.3.0(@types/react@18.3.20)(react@18.3.1) use-latest: 1.3.0(@types/react@19.1.0)(react@19.1.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/react' - '@types/react'
react-tooltip@5.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): react-tooltip@5.28.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies: dependencies:
'@floating-ui/dom': 1.6.13 '@floating-ui/dom': 1.6.13
classnames: 2.5.1 classnames: 2.5.1
react: 18.3.1 react: 19.1.0
react-dom: 18.3.1(react@18.3.1) react-dom: 19.1.0(react@19.1.0)
react@18.3.1:
dependencies:
loose-envify: 1.4.0
react@19.1.0: {} react@19.1.0: {}
@ -6872,9 +6887,7 @@ snapshots:
sax@1.4.1: {} sax@1.4.1: {}
scheduler@0.23.2: scheduler@0.26.0: {}
dependencies:
loose-envify: 1.4.0
section-matter@1.0.0: section-matter@1.0.0:
dependencies: dependencies:
@ -7053,15 +7066,15 @@ snapshots:
dependencies: dependencies:
s.color: 0.0.15 s.color: 0.0.15
swr@2.3.3(react@18.3.1): swr@2.3.3(react@19.1.0):
dependencies: dependencies:
dequal: 2.0.3 dequal: 2.0.3
react: 18.3.1 react: 19.1.0
use-sync-external-store: 1.5.0(react@18.3.1) use-sync-external-store: 1.5.0(react@19.1.0)
tailwind-merge@2.6.0: {} tailwind-merge@2.6.0: {}
tailwind-merge@3.1.0: {} tailwind-merge@3.2.0: {}
tailwindcss@4.1.3: {} tailwindcss@4.1.3: {}
@ -7239,28 +7252,24 @@ snapshots:
escalade: 3.2.0 escalade: 3.2.0
picocolors: 1.1.1 picocolors: 1.1.1
use-composed-ref@1.4.0(@types/react@18.3.20)(react@18.3.1): use-composed-ref@1.4.0(@types/react@19.1.0)(react@19.1.0):
dependencies: dependencies:
react: 18.3.1 react: 19.1.0
optionalDependencies: optionalDependencies:
'@types/react': 18.3.20 '@types/react': 19.1.0
use-isomorphic-layout-effect@1.2.0(@types/react@18.3.20)(react@18.3.1): use-isomorphic-layout-effect@1.2.0(@types/react@19.1.0)(react@19.1.0):
dependencies: dependencies:
react: 18.3.1 react: 19.1.0
optionalDependencies: optionalDependencies:
'@types/react': 18.3.20 '@types/react': 19.1.0
use-latest@1.3.0(@types/react@18.3.20)(react@18.3.1): use-latest@1.3.0(@types/react@19.1.0)(react@19.1.0):
dependencies: dependencies:
react: 18.3.1 react: 19.1.0
use-isomorphic-layout-effect: 1.2.0(@types/react@18.3.20)(react@18.3.1) use-isomorphic-layout-effect: 1.2.0(@types/react@19.1.0)(react@19.1.0)
optionalDependencies: optionalDependencies:
'@types/react': 18.3.20 '@types/react': 19.1.0
use-sync-external-store@1.5.0(react@18.3.1):
dependencies:
react: 18.3.1
use-sync-external-store@1.5.0(react@19.1.0): use-sync-external-store@1.5.0(react@19.1.0):
dependencies: dependencies:
@ -7293,6 +7302,18 @@ snapshots:
fsevents: 2.3.3 fsevents: 2.3.3
lightningcss: 1.29.2 lightningcss: 1.29.2
vite@6.2.5(@types/node@18.19.86)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3):
dependencies:
esbuild: 0.25.2
postcss: 8.5.3
rollup: 4.39.0
optionalDependencies:
'@types/node': 18.19.86
fsevents: 2.3.3
jiti: 2.4.2
lightningcss: 1.29.2
tsx: 4.19.3
vitefu@1.0.6(vite@5.4.17(@types/node@18.19.86)(lightningcss@1.29.2)): vitefu@1.0.6(vite@5.4.17(@types/node@18.19.86)(lightningcss@1.29.2)):
optionalDependencies: optionalDependencies:
vite: 5.4.17(@types/node@18.19.86)(lightningcss@1.29.2) vite: 5.4.17(@types/node@18.19.86)(lightningcss@1.29.2)
@ -7378,12 +7399,6 @@ snapshots:
'@types/react': 19.1.0 '@types/react': 19.1.0
react: 19.1.0 react: 19.1.0
zustand@5.0.3(@types/react@18.3.20)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)):
optionalDependencies:
'@types/react': 18.3.20
react: 18.3.1
use-sync-external-store: 1.5.0(react@18.3.1)
zustand@5.0.3(@types/react@19.1.0)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)): zustand@5.0.3(@types/react@19.1.0)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)):
optionalDependencies: optionalDependencies:
'@types/react': 19.1.0 '@types/react': 19.1.0

@ -28,6 +28,7 @@ export interface Props {
isUpcoming?: boolean; isUpcoming?: boolean;
hasSearch?: boolean; hasSearch?: boolean;
projectCount?: number; projectCount?: number;
coursesCount?: number;
question?: RoadmapFrontmatter['question']; question?: RoadmapFrontmatter['question'];
hasTopics?: boolean; hasTopics?: boolean;
isForkable?: boolean; isForkable?: boolean;
@ -45,12 +46,11 @@ const {
projectCount = 0, projectCount = 0,
question, question,
activeTab = 'roadmap', activeTab = 'roadmap',
coursesCount = 0,
} = Astro.props; } = Astro.props;
const roadmapTitle = const hasCourses = coursesCount > 0;
roadmapId === 'devops' const hasProjects = projectCount > 0;
? 'DevOps'
: `${roadmapId.charAt(0).toUpperCase()}${roadmapId.slice(1)}`;
--- ---
<LoginPopup /> <LoginPopup />
@ -72,7 +72,7 @@ const roadmapTitle =
} }
<div <div
class='relative rounded-none border bg-white px-5 pb-0 pt-4 sm:rounded-lg' class='relative rounded-none border bg-white px-5 pt-4 pb-0 sm:rounded-lg'
> >
<div class='flex items-start justify-between'> <div class='flex items-start justify-between'>
<a <a
@ -91,7 +91,7 @@ const roadmapTitle =
&larr;&nbsp;<span>&nbsp;All Roadmaps</span> &larr;&nbsp;<span>&nbsp;All Roadmaps</span>
</a> </a>
<div <div
class='relative right-0 top-0 flex items-center gap-1 sm:-right-2 sm:-top-0.5' class='relative top-0 right-0 flex items-center gap-1 sm:-top-0.5 sm:-right-2'
> >
<MarkFavorite <MarkFavorite
resourceId={roadmapId} resourceId={roadmapId}
@ -113,51 +113,55 @@ const roadmapTitle =
/> />
</div> </div>
</div> </div>
<div class='mb-5 mt-5 sm:mb-12 sm:mt-12'> <div class:list={['mt-5 mb-5 sm:mt-12 sm:mb-12']}>
<h1 class='mb-0.5 text-2xl font-bold sm:mb-3.5 sm:text-5xl'> <h1 class='mb-0.5 text-2xl font-bold sm:mb-3.5 sm:text-5xl'>
{title} {title}
</h1> </h1>
<p class='text-balance text-sm text-gray-500 sm:text-lg'> <p class='text-sm text-balance text-gray-500 sm:text-lg'>
{description} {description}
</p> </p>
</div> </div>
<div class='flex justify-between gap-2 sm:gap-0'> {
<div class='relative top-px flex gap-1 sm:gap-3'> (
<TabLink <div class='flex justify-between gap-2 sm:gap-0'>
url={`/${roadmapId}`} <div class='relative top-px flex gap-1 sm:gap-3'>
icon={MapIcon}
isActive={activeTab === 'roadmap'}
text='Roadmap'
/>
<TabLink
url={`/${roadmapId}/projects`}
icon={FolderKanbanIcon}
text='Projects'
isActive={activeTab === 'projects'}
badgeText={projectCount > 0 ? '' : 'soon'}
/>
{
roadmapId === 'sql' && (
<TabLink <TabLink
url={`/${roadmapId}/courses`} url={`/${roadmapId}`}
icon={BookOpenIcon} icon={MapIcon}
text='Courses' isActive={activeTab === 'roadmap'}
isActive={activeTab === 'courses'} text='Roadmap'
badgeText='New'
/> />
) {hasProjects && (
} <TabLink
</div> url={`/${roadmapId}/projects`}
icon={FolderKanbanIcon}
text='Projects'
isActive={activeTab === 'projects'}
badgeText={projectCount > 0 ? '' : 'soon'}
/>
)}
{hasCourses && (
<TabLink
url={`/${roadmapId}/courses`}
icon={BookOpenIcon}
text='Courses'
isActive={activeTab === 'courses'}
badgeText='New'
/>
)}
</div>
<TabLink <TabLink
url={`https://github.com/kamranahmedse/developer-roadmap/issues/new/choose`} url={`https://github.com/kamranahmedse/developer-roadmap/issues/new/choose`}
icon={MessageCircle} icon={MessageCircle}
text='Suggest Changes' text='Suggest Changes'
isExternal={true} isExternal={true}
hideTextOnMobile={true} hideTextOnMobile={true}
isActive={false} isActive={false}
/> />
</div> </div>
)
}
</div> </div>
</div> </div>

@ -48,25 +48,7 @@ const nounTitle =
descriptionNoun[roadmapData.briefTitle] || roadmapData.briefTitle; descriptionNoun[roadmapData.briefTitle] || roadmapData.briefTitle;
const seoDescription = `Seeking ${nounTitle.toLowerCase()} courses to enhance your skills? Explore our top free and paid courses to help you become a ${nounTitle} expert!`; const seoDescription = `Seeking ${nounTitle.toLowerCase()} courses to enhance your skills? Explore our top free and paid courses to help you become a ${nounTitle} expert!`;
const courses = [ const courses = roadmapData.courses || [];
{
title: 'Complete Course to Master SQL',
description: 'Learn SQL from scratch with this comprehensive course',
link: 'https://roadmap.sh/courses/sql',
instructor: {
name: 'Kamran Ahmed',
image: 'https://github.com/kamranahmedse.png',
title: 'Founder roadmap.sh',
},
features: [
'55+ Lessons',
'AI Tutor',
'Coding Environment',
'Quizzes',
'Certification',
],
},
];
--- ---
<BaseLayout <BaseLayout
@ -91,17 +73,20 @@ const courses = [
isUpcoming={roadmapData.isUpcoming} isUpcoming={roadmapData.isUpcoming}
isForkable={roadmapData.isForkable} isForkable={roadmapData.isForkable}
question={roadmapData.question} question={roadmapData.question}
coursesCount={courses.length}
activeTab='courses' activeTab='courses'
/> />
<div class='container'> <div class='container'>
<div class='relative mb-8 mt-2.5'> <div class='relative mt-2.5 mb-8'>
<div class='my-4 flex items-center justify-between'> <div class='my-4 flex items-center justify-between'>
<p class='border-l-4 border-black pl-2 text-sm text-black'> <p class='border-l-4 border-black pl-2 text-sm text-black'>
Official Courses by <span class='font-semibold'> roadmap.sh </span> Official Courses by <span class='font-semibold'> roadmap.sh </span>
team team
</p> </p>
<div class='text-sm text-gray-500'>More coming soon</div> <div class='hidden text-sm text-gray-500 sm:block'>
More coming soon
</div>
</div> </div>
<div class='grid grid-cols-1 gap-5 md:grid-cols-1'> <div class='grid grid-cols-1 gap-5 md:grid-cols-1'>
@ -127,7 +112,7 @@ const courses = [
</span> </span>
))} ))}
</div> </div>
<div class='flex items-center justify-between'> <div class='mt-6 flex flex-col items-start justify-between gap-3 sm:mt-0 sm:flex-row sm:items-center sm:gap-0'>
<div class='flex items-center'> <div class='flex items-center'>
<img <img
src={course.instructor.image} src={course.instructor.image}
@ -144,7 +129,7 @@ const courses = [
</div> </div>
</div> </div>
<span class='group rounded-lg border border-gray-900 bg-gray-900 px-4 py-1.5 text-sm font-medium text-white transition-colors duration-300 hover:opacity-80'> <span class='group w-full sm:py-1.5 sm:w-auto mt-3 rounded-lg border border-gray-900 bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors duration-300 hover:opacity-80 sm:mt-0'>
View Course <span class='ml-1'>&rarr;</span> View Course <span class='ml-1'>&rarr;</span>
</span> </span>
</div> </div>

@ -70,6 +70,7 @@ const ogImageUrl =
const question = roadmapData?.question; const question = roadmapData?.question;
const note = roadmapData.note; const note = roadmapData.note;
const projects = await getProjectsByRoadmapId(roadmapId); const projects = await getProjectsByRoadmapId(roadmapId);
const courses = roadmapData.courses || [];
--- ---
<BaseLayout <BaseLayout
@ -114,6 +115,7 @@ const projects = await getProjectsByRoadmapId(roadmapId);
isForkable={roadmapData.isForkable} isForkable={roadmapData.isForkable}
question={roadmapData.question} question={roadmapData.question}
projectCount={projects.length} projectCount={projects.length}
coursesCount={courses.length}
/> />
<div class='container mt-2.5'> <div class='container mt-2.5'>

@ -83,6 +83,7 @@ const { response: userCounts } =
question={roadmapData.question} question={roadmapData.question}
activeTab='projects' activeTab='projects'
projectCount={projects.length} projectCount={projects.length}
coursesCount={roadmapData.courses?.length || 0}
/> />
<div class='container'> <div class='container'>

Loading…
Cancel
Save