mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-12 17:51:53 +08:00
Compare commits
16 Commits
6d8b8d8400
...
feat/roadm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e06bd1f61 | ||
|
|
41054ba97e | ||
|
|
febeb6f586 | ||
|
|
9c82a4d35c | ||
|
|
4a3030948f | ||
|
|
3d012f9c64 | ||
|
|
7856a78bba | ||
|
|
7b4ced400c | ||
|
|
bd01586e8e | ||
|
|
33af126728 | ||
|
|
e48fa4f593 | ||
|
|
f00f5d16ea | ||
|
|
4fcf1f6d50 | ||
|
|
7c1c295c63 | ||
|
|
39e55a06e8 | ||
|
|
8d6facd983 |
@@ -1,10 +1,10 @@
|
||||
// https://astro.build/config
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import node from '@astrojs/node';
|
||||
import compress from 'astro-compress';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import rehypeExternalLinks from 'rehype-external-links';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { serializeSitemap, shouldIndexPage } from './sitemap.mjs';
|
||||
|
||||
import react from '@astrojs/react';
|
||||
@@ -41,9 +41,18 @@ export default defineConfig({
|
||||
],
|
||||
],
|
||||
},
|
||||
build: {
|
||||
format: 'file',
|
||||
},
|
||||
// @FIXME:
|
||||
// This should be "hybrid" but there is a bug in the current version of Astro
|
||||
// that adds trailing slashes to the URLs when using "hybrid" mode.
|
||||
// ----------------------------------------------
|
||||
// https://github.com/withastro/astro/issues/7808
|
||||
// ----------------------------------------------
|
||||
// For now, we are using "server" mode and then using cloudfront to cache the
|
||||
// pages and serve them as static.
|
||||
output: 'server',
|
||||
adapter: node({
|
||||
mode: 'standalone',
|
||||
}),
|
||||
integrations: [
|
||||
tailwind({
|
||||
config: {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^7.0.2",
|
||||
"@astrojs/react": "^3.0.8",
|
||||
"@astrojs/sitemap": "^3.0.3",
|
||||
"@astrojs/tailwind": "^5.0.4",
|
||||
@@ -33,6 +34,7 @@
|
||||
"astro-compress": "^2.2.3",
|
||||
"clsx": "^2.0.0",
|
||||
"dracula-prism": "^2.1.13",
|
||||
"express": "^4.18.2",
|
||||
"jose": "^5.1.3",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.300.0",
|
||||
|
||||
426
pnpm-lock.yaml
generated
426
pnpm-lock.yaml
generated
@@ -5,6 +5,9 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@astrojs/node':
|
||||
specifier: ^7.0.2
|
||||
version: 7.0.2(astro@4.0.7)
|
||||
'@astrojs/react':
|
||||
specifier: ^3.0.8
|
||||
version: 3.0.8(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0)(vite@5.0.10)
|
||||
@@ -38,6 +41,9 @@ dependencies:
|
||||
dracula-prism:
|
||||
specifier: ^2.1.13
|
||||
version: 2.1.13
|
||||
express:
|
||||
specifier: ^4.18.2
|
||||
version: 4.18.2
|
||||
jose:
|
||||
specifier: ^5.1.3
|
||||
version: 5.1.3
|
||||
@@ -178,6 +184,18 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@astrojs/node@7.0.2(astro@4.0.7):
|
||||
resolution: {integrity: sha512-g07KV9JBbUubfzLyu2ql+zu09dW3a7zVX1fDr6UriQdz6BvPh0Y0Qn+DqA5P8JwSKAw93mJDuChDW7JrI7CBJg==}
|
||||
peerDependencies:
|
||||
astro: ^4.0.0
|
||||
dependencies:
|
||||
astro: 4.0.7(typescript@5.2.2)
|
||||
send: 0.18.0
|
||||
server-destroy: 1.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@astrojs/prism@3.0.0:
|
||||
resolution: {integrity: sha512-g61lZupWq1bYbcBnYZqdjndShr/J3l/oFobBKPA3+qMat146zce3nz2kdO4giGbhYDt4gYdhmoBz0vZJ4sIurQ==}
|
||||
engines: {node: '>=18.14.1'}
|
||||
@@ -1720,6 +1738,14 @@ packages:
|
||||
event-target-shim: 5.0.1
|
||||
dev: true
|
||||
|
||||
/accepts@1.3.8:
|
||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dependencies:
|
||||
mime-types: 2.1.35
|
||||
negotiator: 0.6.3
|
||||
dev: false
|
||||
|
||||
/acorn@8.10.0:
|
||||
resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@@ -1828,6 +1854,10 @@ packages:
|
||||
/argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
/array-flatten@1.1.1:
|
||||
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
|
||||
dev: false
|
||||
|
||||
/array-iterate@2.0.1:
|
||||
resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==}
|
||||
dev: false
|
||||
@@ -1970,6 +2000,7 @@ packages:
|
||||
|
||||
/b4a@1.6.4:
|
||||
resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/bail@2.0.2:
|
||||
@@ -1997,6 +2028,7 @@ packages:
|
||||
|
||||
/bl@4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
buffer: 5.7.1
|
||||
inherits: 2.0.4
|
||||
@@ -2011,6 +2043,26 @@ packages:
|
||||
readable-stream: 3.6.2
|
||||
dev: false
|
||||
|
||||
/body-parser@1.20.1:
|
||||
resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
content-type: 1.0.5
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
destroy: 1.2.0
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.4.24
|
||||
on-finished: 2.4.1
|
||||
qs: 6.11.0
|
||||
raw-body: 2.5.1
|
||||
type-is: 1.6.18
|
||||
unpipe: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/boolbase@1.0.0:
|
||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||
dev: false
|
||||
@@ -2064,6 +2116,7 @@ packages:
|
||||
|
||||
/buffer@5.7.1:
|
||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
@@ -2082,6 +2135,11 @@ packages:
|
||||
semver: 7.5.4
|
||||
dev: false
|
||||
|
||||
/bytes@3.1.2:
|
||||
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/cacache@17.1.4:
|
||||
resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
@@ -2118,6 +2176,14 @@ packages:
|
||||
responselike: 3.0.0
|
||||
dev: false
|
||||
|
||||
/call-bind@1.0.5:
|
||||
resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
get-intrinsic: 1.2.2
|
||||
set-function-length: 1.1.1
|
||||
dev: false
|
||||
|
||||
/camel-case@4.1.2:
|
||||
resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
|
||||
dependencies:
|
||||
@@ -2196,6 +2262,7 @@ packages:
|
||||
|
||||
/chownr@1.1.4:
|
||||
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/chownr@2.0.0:
|
||||
@@ -2279,10 +2346,12 @@ packages:
|
||||
|
||||
/color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/color-string@1.9.1:
|
||||
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
simple-swizzle: 0.2.2
|
||||
@@ -2296,6 +2365,7 @@ packages:
|
||||
/color@4.2.3:
|
||||
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
|
||||
engines: {node: '>=12.5.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
color-string: 1.9.1
|
||||
@@ -2367,10 +2437,31 @@ packages:
|
||||
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
|
||||
dev: false
|
||||
|
||||
/content-disposition@0.5.4:
|
||||
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/content-type@1.0.5:
|
||||
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/convert-source-map@2.0.0:
|
||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||
dev: false
|
||||
|
||||
/cookie-signature@1.0.6:
|
||||
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
|
||||
dev: false
|
||||
|
||||
/cookie@0.5.0:
|
||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/cookie@0.6.0:
|
||||
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -2566,6 +2657,7 @@ packages:
|
||||
/deep-extend@0.6.0:
|
||||
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/deepmerge-ts@5.1.0:
|
||||
@@ -2578,6 +2670,15 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/define-data-property@1.1.1:
|
||||
resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
get-intrinsic: 1.2.2
|
||||
gopd: 1.0.1
|
||||
has-property-descriptors: 1.0.1
|
||||
dev: false
|
||||
|
||||
/delayed-stream@1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@@ -2587,11 +2688,21 @@ packages:
|
||||
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
|
||||
dev: false
|
||||
|
||||
/depd@2.0.0:
|
||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/dequal@2.0.3:
|
||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/destroy@1.2.0:
|
||||
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
dev: false
|
||||
|
||||
/detect-libc@1.0.3:
|
||||
resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
|
||||
engines: {node: '>=0.10'}
|
||||
@@ -2601,6 +2712,7 @@ packages:
|
||||
/detect-libc@2.0.2:
|
||||
resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
|
||||
engines: {node: '>=8'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/deterministic-object-hash@2.0.2:
|
||||
@@ -2699,6 +2811,10 @@ packages:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
dev: false
|
||||
|
||||
/ee-first@1.1.1:
|
||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||
dev: false
|
||||
|
||||
/electron-to-chromium@1.4.565:
|
||||
resolution: {integrity: sha512-XbMoT6yIvg2xzcbs5hCADi0dXBh4//En3oFXmtPX+jiyyiCTiM9DGFT2SLottjpEs9Z8Mh8SqahbR96MaHfuSg==}
|
||||
dev: false
|
||||
@@ -2719,6 +2835,11 @@ packages:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
dev: false
|
||||
|
||||
/encodeurl@1.0.2:
|
||||
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/encoding@0.1.13:
|
||||
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
|
||||
requiresBuild: true
|
||||
@@ -2729,6 +2850,7 @@ packages:
|
||||
|
||||
/end-of-stream@1.4.4:
|
||||
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
dev: false
|
||||
@@ -2832,6 +2954,10 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/escape-html@1.0.3:
|
||||
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
||||
dev: false
|
||||
|
||||
/escape-string-regexp@1.0.5:
|
||||
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
@@ -2853,6 +2979,11 @@ packages:
|
||||
'@types/estree': 1.0.3
|
||||
dev: false
|
||||
|
||||
/etag@1.8.1:
|
||||
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/event-target-shim@5.0.1:
|
||||
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -2880,12 +3011,52 @@ packages:
|
||||
/expand-template@2.0.3:
|
||||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||
engines: {node: '>=6'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/exponential-backoff@3.1.1:
|
||||
resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
|
||||
dev: false
|
||||
|
||||
/express@4.18.2:
|
||||
resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
dependencies:
|
||||
accepts: 1.3.8
|
||||
array-flatten: 1.1.1
|
||||
body-parser: 1.20.1
|
||||
content-disposition: 0.5.4
|
||||
content-type: 1.0.5
|
||||
cookie: 0.5.0
|
||||
cookie-signature: 1.0.6
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
encodeurl: 1.0.2
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
finalhandler: 1.2.0
|
||||
fresh: 0.5.2
|
||||
http-errors: 2.0.0
|
||||
merge-descriptors: 1.0.1
|
||||
methods: 1.1.2
|
||||
on-finished: 2.4.1
|
||||
parseurl: 1.3.3
|
||||
path-to-regexp: 0.1.7
|
||||
proxy-addr: 2.0.7
|
||||
qs: 6.11.0
|
||||
range-parser: 1.2.1
|
||||
safe-buffer: 5.2.1
|
||||
send: 0.18.0
|
||||
serve-static: 1.15.0
|
||||
setprototypeof: 1.2.0
|
||||
statuses: 2.0.1
|
||||
type-is: 1.6.18
|
||||
utils-merge: 1.0.1
|
||||
vary: 1.1.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/extend-shallow@2.0.1:
|
||||
resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2899,6 +3070,7 @@ packages:
|
||||
|
||||
/fast-fifo@1.3.2:
|
||||
resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/fast-glob@3.3.1:
|
||||
@@ -2959,6 +3131,21 @@ packages:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
|
||||
/finalhandler@1.2.0:
|
||||
resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
encodeurl: 1.0.2
|
||||
escape-html: 1.0.3
|
||||
on-finished: 2.4.1
|
||||
parseurl: 1.3.3
|
||||
statuses: 2.0.1
|
||||
unpipe: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/find-cache-dir@3.3.2:
|
||||
resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -3029,6 +3216,11 @@ packages:
|
||||
web-streams-polyfill: 4.0.0-beta.3
|
||||
dev: true
|
||||
|
||||
/forwarded@0.2.0:
|
||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/fp-and-or@0.1.4:
|
||||
resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -3038,8 +3230,14 @@ packages:
|
||||
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
||||
dev: false
|
||||
|
||||
/fresh@0.5.2:
|
||||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/fs-constants@1.0.0:
|
||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/fs-extra@10.1.0:
|
||||
@@ -3119,6 +3317,15 @@ packages:
|
||||
engines: {node: '>=18'}
|
||||
dev: false
|
||||
|
||||
/get-intrinsic@1.2.2:
|
||||
resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
has-proto: 1.0.1
|
||||
has-symbols: 1.0.3
|
||||
hasown: 2.0.0
|
||||
dev: false
|
||||
|
||||
/get-stdin@8.0.0:
|
||||
resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -3150,6 +3357,7 @@ packages:
|
||||
|
||||
/github-from-package@0.0.0:
|
||||
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/github-slugger@2.0.0:
|
||||
@@ -3235,6 +3443,12 @@ packages:
|
||||
pinkie-promise: 2.0.1
|
||||
dev: true
|
||||
|
||||
/gopd@1.0.1:
|
||||
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
||||
dependencies:
|
||||
get-intrinsic: 1.2.2
|
||||
dev: false
|
||||
|
||||
/got@12.6.1:
|
||||
resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==}
|
||||
engines: {node: '>=14.16'}
|
||||
@@ -3279,6 +3493,22 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/has-property-descriptors@1.0.1:
|
||||
resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==}
|
||||
dependencies:
|
||||
get-intrinsic: 1.2.2
|
||||
dev: false
|
||||
|
||||
/has-proto@1.0.1:
|
||||
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: false
|
||||
|
||||
/has-symbols@1.0.3:
|
||||
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: false
|
||||
|
||||
/has-unicode@2.0.1:
|
||||
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
|
||||
dev: false
|
||||
@@ -3438,6 +3668,17 @@ packages:
|
||||
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
|
||||
dev: false
|
||||
|
||||
/http-errors@2.0.0:
|
||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dependencies:
|
||||
depd: 2.0.0
|
||||
inherits: 2.0.4
|
||||
setprototypeof: 1.2.0
|
||||
statuses: 2.0.1
|
||||
toidentifier: 1.0.1
|
||||
dev: false
|
||||
|
||||
/http-proxy-agent@5.0.0:
|
||||
resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -3487,6 +3728,7 @@ packages:
|
||||
/iconv-lite@0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
dev: false
|
||||
@@ -3554,6 +3796,11 @@ packages:
|
||||
resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==}
|
||||
dev: false
|
||||
|
||||
/ipaddr.js@1.9.1:
|
||||
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
||||
engines: {node: '>= 0.10'}
|
||||
dev: false
|
||||
|
||||
/is-absolute-url@4.0.1:
|
||||
resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
@@ -3561,6 +3808,7 @@ packages:
|
||||
|
||||
/is-arrayish@0.3.2:
|
||||
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/is-binary-path@2.1.0:
|
||||
@@ -4255,6 +4503,15 @@ packages:
|
||||
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
|
||||
dev: true
|
||||
|
||||
/media-typer@0.3.0:
|
||||
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/merge-descriptors@1.0.1:
|
||||
resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
|
||||
dev: false
|
||||
|
||||
/merge-stream@2.0.0:
|
||||
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
||||
dev: false
|
||||
@@ -4263,6 +4520,11 @@ packages:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
/methods@1.1.2:
|
||||
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/micromark-core-commonmark@2.0.0:
|
||||
resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==}
|
||||
dependencies:
|
||||
@@ -4520,14 +4782,18 @@ packages:
|
||||
/mime-db@1.52.0:
|
||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: true
|
||||
|
||||
/mime-types@2.1.35:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
dev: true
|
||||
|
||||
/mime@1.6.0:
|
||||
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/mime@3.0.0:
|
||||
resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
|
||||
@@ -4548,6 +4814,7 @@ packages:
|
||||
/mimic-response@3.1.0:
|
||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||
engines: {node: '>=10'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/mimic-response@4.0.0:
|
||||
@@ -4643,6 +4910,7 @@ packages:
|
||||
|
||||
/mkdirp-classic@0.5.3:
|
||||
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/mkdirp@1.0.4:
|
||||
@@ -4687,6 +4955,7 @@ packages:
|
||||
|
||||
/napi-build-utils@1.0.2:
|
||||
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/needle@2.9.1:
|
||||
@@ -4722,12 +4991,14 @@ packages:
|
||||
/node-abi@3.51.0:
|
||||
resolution: {integrity: sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==}
|
||||
engines: {node: '>=10'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
semver: 7.5.4
|
||||
dev: false
|
||||
|
||||
/node-addon-api@6.1.0:
|
||||
resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/node-domexception@1.0.0:
|
||||
@@ -4944,6 +5215,17 @@ packages:
|
||||
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
/object-inspect@1.13.1:
|
||||
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
|
||||
dev: false
|
||||
|
||||
/on-finished@2.4.1:
|
||||
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dependencies:
|
||||
ee-first: 1.1.1
|
||||
dev: false
|
||||
|
||||
/once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
dependencies:
|
||||
@@ -5122,6 +5404,11 @@ packages:
|
||||
entities: 4.5.0
|
||||
dev: false
|
||||
|
||||
/parseurl@1.3.3:
|
||||
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/pascal-case@3.1.2:
|
||||
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
|
||||
dependencies:
|
||||
@@ -5158,6 +5445,10 @@ packages:
|
||||
minipass: 7.0.4
|
||||
dev: false
|
||||
|
||||
/path-to-regexp@0.1.7:
|
||||
resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
|
||||
dev: false
|
||||
|
||||
/path-to-regexp@6.2.1:
|
||||
resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==}
|
||||
dev: false
|
||||
@@ -5296,6 +5587,7 @@ packages:
|
||||
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
detect-libc: 2.0.2
|
||||
expand-template: 2.0.3
|
||||
@@ -5455,8 +5747,17 @@ packages:
|
||||
resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
|
||||
dev: false
|
||||
|
||||
/proxy-addr@2.0.7:
|
||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
dependencies:
|
||||
forwarded: 0.2.0
|
||||
ipaddr.js: 1.9.1
|
||||
dev: false
|
||||
|
||||
/pump@3.0.0:
|
||||
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
end-of-stream: 1.4.4
|
||||
once: 1.4.0
|
||||
@@ -5474,11 +5775,19 @@ packages:
|
||||
escape-goat: 4.0.0
|
||||
dev: false
|
||||
|
||||
/qs@6.11.0:
|
||||
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
|
||||
engines: {node: '>=0.6'}
|
||||
dependencies:
|
||||
side-channel: 1.0.4
|
||||
dev: false
|
||||
|
||||
/queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
/queue-tick@1.0.1:
|
||||
resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/quick-lru@5.1.1:
|
||||
@@ -5486,6 +5795,21 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/range-parser@1.2.1:
|
||||
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/raw-body@2.5.1:
|
||||
resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.4.24
|
||||
unpipe: 1.0.0
|
||||
dev: false
|
||||
|
||||
/rc-config-loader@4.1.3:
|
||||
resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==}
|
||||
dependencies:
|
||||
@@ -5899,6 +6223,39 @@ packages:
|
||||
lru-cache: 6.0.0
|
||||
dev: false
|
||||
|
||||
/send@0.18.0:
|
||||
resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
destroy: 1.2.0
|
||||
encodeurl: 1.0.2
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
fresh: 0.5.2
|
||||
http-errors: 2.0.0
|
||||
mime: 1.6.0
|
||||
ms: 2.1.3
|
||||
on-finished: 2.4.1
|
||||
range-parser: 1.2.1
|
||||
statuses: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/serve-static@1.15.0:
|
||||
resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
dependencies:
|
||||
encodeurl: 1.0.2
|
||||
escape-html: 1.0.3
|
||||
parseurl: 1.3.3
|
||||
send: 0.18.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/server-destroy@1.0.1:
|
||||
resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==}
|
||||
dev: false
|
||||
@@ -5907,6 +6264,20 @@ packages:
|
||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||
dev: false
|
||||
|
||||
/set-function-length@1.1.1:
|
||||
resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
define-data-property: 1.1.1
|
||||
get-intrinsic: 1.2.2
|
||||
gopd: 1.0.1
|
||||
has-property-descriptors: 1.0.1
|
||||
dev: false
|
||||
|
||||
/setprototypeof@1.2.0:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
dev: false
|
||||
|
||||
/sharp@0.32.6:
|
||||
resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==}
|
||||
engines: {node: '>=14.15.0'}
|
||||
@@ -5949,6 +6320,14 @@ packages:
|
||||
hast-util-to-html: 9.0.0
|
||||
dev: false
|
||||
|
||||
/side-channel@1.0.4:
|
||||
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
|
||||
dependencies:
|
||||
call-bind: 1.0.5
|
||||
get-intrinsic: 1.2.2
|
||||
object-inspect: 1.13.1
|
||||
dev: false
|
||||
|
||||
/signal-exit@3.0.7:
|
||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||
dev: false
|
||||
@@ -5974,10 +6353,12 @@ packages:
|
||||
|
||||
/simple-concat@1.0.1:
|
||||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/simple-get@4.0.1:
|
||||
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
decompress-response: 6.0.0
|
||||
once: 1.4.0
|
||||
@@ -5986,6 +6367,7 @@ packages:
|
||||
|
||||
/simple-swizzle@0.2.2:
|
||||
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
is-arrayish: 0.3.2
|
||||
dev: false
|
||||
@@ -6099,6 +6481,11 @@ packages:
|
||||
minipass: 7.0.4
|
||||
dev: false
|
||||
|
||||
/statuses@2.0.1:
|
||||
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/stdin-discarder@0.1.0:
|
||||
resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
@@ -6116,6 +6503,7 @@ packages:
|
||||
|
||||
/streamx@2.15.1:
|
||||
resolution: {integrity: sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
fast-fifo: 1.3.2
|
||||
queue-tick: 1.0.1
|
||||
@@ -6202,6 +6590,7 @@ packages:
|
||||
/strip-json-comments@2.0.1:
|
||||
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/strip-json-comments@5.0.1:
|
||||
@@ -6304,6 +6693,7 @@ packages:
|
||||
|
||||
/tar-fs@2.1.1:
|
||||
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
chownr: 1.1.4
|
||||
mkdirp-classic: 0.5.3
|
||||
@@ -6313,6 +6703,7 @@ packages:
|
||||
|
||||
/tar-fs@3.0.4:
|
||||
resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
mkdirp-classic: 0.5.3
|
||||
pump: 3.0.0
|
||||
@@ -6322,6 +6713,7 @@ packages:
|
||||
/tar-stream@2.2.0:
|
||||
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
||||
engines: {node: '>=6'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
bl: 4.1.0
|
||||
end-of-stream: 1.4.4
|
||||
@@ -6332,6 +6724,7 @@ packages:
|
||||
|
||||
/tar-stream@3.1.6:
|
||||
resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
b4a: 1.6.4
|
||||
fast-fifo: 1.3.2
|
||||
@@ -6383,6 +6776,11 @@ packages:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
/toidentifier@1.0.1:
|
||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||
engines: {node: '>=0.6'}
|
||||
dev: false
|
||||
|
||||
/tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
dev: true
|
||||
@@ -6435,6 +6833,7 @@ packages:
|
||||
|
||||
/tunnel-agent@0.6.0:
|
||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
@@ -6453,6 +6852,14 @@ packages:
|
||||
engines: {node: '>=12.20'}
|
||||
dev: false
|
||||
|
||||
/type-is@1.6.18:
|
||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dependencies:
|
||||
media-typer: 0.3.0
|
||||
mime-types: 2.1.35
|
||||
dev: false
|
||||
|
||||
/typedarray-to-buffer@3.1.5:
|
||||
resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
|
||||
dependencies:
|
||||
@@ -6681,6 +7088,11 @@ packages:
|
||||
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
/unpipe@1.0.0:
|
||||
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/untildify@4.0.0:
|
||||
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -6728,6 +7140,11 @@ packages:
|
||||
/util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
/utils-merge@1.0.1:
|
||||
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
dev: false
|
||||
|
||||
/validate-npm-package-license@3.0.4:
|
||||
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
||||
dependencies:
|
||||
@@ -6742,6 +7159,11 @@ packages:
|
||||
builtins: 5.0.1
|
||||
dev: false
|
||||
|
||||
/vary@1.1.2:
|
||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/vfile-location@5.0.2:
|
||||
resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==}
|
||||
dependencies:
|
||||
|
||||
@@ -29,4 +29,4 @@ done
|
||||
|
||||
|
||||
# ignore the worktree changes for the editor directory
|
||||
git update-index --assume-unchanged editor/readonly-editor.tsx
|
||||
git update-index --assume-unchanged editor/readonly-editor.tsx || true
|
||||
@@ -14,6 +14,7 @@ type ProgressResponse = {
|
||||
done: number;
|
||||
total: number;
|
||||
isCustomResource: boolean;
|
||||
roadmapSlug?: string;
|
||||
};
|
||||
|
||||
export type ActivityResponse = {
|
||||
@@ -52,7 +53,7 @@ export function ActivityPage() {
|
||||
|
||||
async function loadActivity() {
|
||||
const { error, response } = await httpGet<ActivityResponse>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-stats`
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-stats`,
|
||||
);
|
||||
|
||||
if (!response || error) {
|
||||
@@ -107,6 +108,7 @@ export function ActivityPage() {
|
||||
.map((roadmap) => (
|
||||
<ResourceProgress
|
||||
key={roadmap.id}
|
||||
roadmapSlug={roadmap.roadmapSlug}
|
||||
isCustomResource={roadmap.isCustomResource}
|
||||
doneCount={roadmap.done || 0}
|
||||
learningCount={roadmap.learning || 0}
|
||||
|
||||
@@ -17,6 +17,7 @@ type ResourceProgressType = {
|
||||
onCleared?: () => void;
|
||||
showClearButton?: boolean;
|
||||
isCustomResource: boolean;
|
||||
roadmapSlug?: string;
|
||||
};
|
||||
|
||||
export function ResourceProgress(props: ResourceProgressType) {
|
||||
@@ -37,6 +38,7 @@ export function ResourceProgress(props: ResourceProgressType) {
|
||||
doneCount,
|
||||
skippedCount,
|
||||
onCleared,
|
||||
roadmapSlug,
|
||||
} = props;
|
||||
|
||||
async function clearProgress() {
|
||||
@@ -46,7 +48,7 @@ export function ResourceProgress(props: ResourceProgressType) {
|
||||
{
|
||||
resourceId,
|
||||
resourceType,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
@@ -72,7 +74,7 @@ export function ResourceProgress(props: ResourceProgressType) {
|
||||
: `/best-practices/${resourceId}`;
|
||||
|
||||
if (isCustomResource) {
|
||||
url = `/r?id=${resourceId}`;
|
||||
url = `/r/${roadmapSlug}`;
|
||||
}
|
||||
|
||||
const totalMarked = doneCount + skippedCount;
|
||||
|
||||
@@ -14,6 +14,7 @@ import { useToast } from '../../hooks/use-toast';
|
||||
|
||||
export type TeamResourceConfig = {
|
||||
isCustomResource: boolean;
|
||||
roadmapSlug?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
visibility?: AllowedRoadmapVisibility;
|
||||
@@ -80,7 +81,7 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
|
||||
{
|
||||
resourceId: roadmapId,
|
||||
resourceType: 'roadmap',
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
@@ -114,7 +115,7 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
|
||||
resourceId: roadmapId,
|
||||
resourceType: 'roadmap',
|
||||
removed: [],
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
@@ -312,7 +313,7 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
|
||||
`${
|
||||
import.meta.env.PUBLIC_EDITOR_APP_URL
|
||||
}/${resourceId}`,
|
||||
'_blank'
|
||||
'_blank',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -335,7 +336,7 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -82,7 +82,7 @@ export function CreateVersion(props: CreateVersionProps) {
|
||||
return (
|
||||
<div className={'flex items-center'}>
|
||||
<a
|
||||
href={`/r?id=${userVersion._id}`}
|
||||
href={`/r/${userVersion?.slug}`}
|
||||
className="flex items-center rounded-md border border-blue-400 bg-gray-50 px-2.5 py-1 text-xs font-medium text-blue-600 hover:bg-blue-100 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:hover:bg-gray-100 max-sm:hidden sm:text-sm"
|
||||
>
|
||||
<Map size="15px" className="mr-1.5" />
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface RoadmapDocument {
|
||||
_id?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
slug?: string;
|
||||
creatorId: string;
|
||||
teamId?: string;
|
||||
isDiscoverable: boolean;
|
||||
@@ -145,7 +146,7 @@ export function CreateRoadmapModal(props: CreateRoadmapModalProps) {
|
||||
name="title"
|
||||
id="title"
|
||||
required
|
||||
className="block text-black w-full rounded-md border border-gray-300 px-2.5 py-2 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-none focus:border-black sm:text-sm"
|
||||
placeholder="Enter Title"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
@@ -165,8 +166,8 @@ export function CreateRoadmapModal(props: CreateRoadmapModalProps) {
|
||||
name="description"
|
||||
required
|
||||
className={cn(
|
||||
'block text-black h-24 w-full resize-none rounded-md border border-gray-300 px-2.5 py-2 outline-none focus:border-black sm:text-sm',
|
||||
isInvalidDescription && 'border-red-300 bg-red-100'
|
||||
'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',
|
||||
isInvalidDescription && 'border-red-300 bg-red-100',
|
||||
)}
|
||||
placeholder="Enter Description"
|
||||
value={description}
|
||||
|
||||
@@ -62,10 +62,11 @@ export function hideRoadmapLoader() {
|
||||
|
||||
type CustomRoadmapProps = {
|
||||
isEmbed?: boolean;
|
||||
slug?: string;
|
||||
};
|
||||
|
||||
export function CustomRoadmap(props: CustomRoadmapProps) {
|
||||
const { isEmbed = false } = props;
|
||||
const { isEmbed = false, slug } = props;
|
||||
|
||||
const { id, secret } = getUrlParams() as { id: string; secret: string };
|
||||
|
||||
@@ -76,9 +77,11 @@ export function CustomRoadmap(props: CustomRoadmapProps) {
|
||||
async function getRoadmap() {
|
||||
setIsLoading(true);
|
||||
|
||||
const roadmapUrl = new URL(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap/${id}`,
|
||||
);
|
||||
const roadmapUrl = slug
|
||||
? new URL(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap-by-slug/${slug}`,
|
||||
)
|
||||
: new URL(`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap/${id}`);
|
||||
|
||||
if (secret) {
|
||||
roadmapUrl.searchParams.set('secret', secret);
|
||||
@@ -102,12 +105,12 @@ export function CustomRoadmap(props: CustomRoadmapProps) {
|
||||
}
|
||||
|
||||
async function trackVisit() {
|
||||
if (!isLoggedIn() || isEmbed) {
|
||||
if (!isLoggedIn() || isEmbed || !roadmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
await httpPost(`${import.meta.env.PUBLIC_API_URL}/v1-visit`, {
|
||||
resourceId: id,
|
||||
resourceId: roadmap?._id,
|
||||
resourceType: 'roadmap',
|
||||
});
|
||||
}
|
||||
@@ -116,9 +119,16 @@ export function CustomRoadmap(props: CustomRoadmapProps) {
|
||||
getRoadmap().finally(() => {
|
||||
hideRoadmapLoader();
|
||||
});
|
||||
trackVisit().then();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!roadmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
trackVisit().then();
|
||||
}, [roadmap]);
|
||||
|
||||
if (isLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { PersonalRoadmapActionDropdown } from './PersonalRoadmapActionDropdown';
|
||||
import type { GetRoadmapListResponse } from './RoadmapListPage';
|
||||
import { useState, type Dispatch, type SetStateAction } from 'react';
|
||||
import { ShareOptionsModal } from '../ShareOptions/ShareOptionsModal';
|
||||
import {RoadmapIcon} from "../ReactIcons/RoadmapIcon.tsx";
|
||||
import { RoadmapIcon } from '../ReactIcons/RoadmapIcon.tsx';
|
||||
|
||||
type PersonalRoadmapListType = {
|
||||
roadmaps: GetRoadmapListResponse['personalRoadmaps'];
|
||||
@@ -37,7 +37,7 @@ export function PersonalRoadmapList(props: PersonalRoadmapListType) {
|
||||
|
||||
async function deleteRoadmap(roadmapId: string) {
|
||||
const { response, error } = await httpDelete<RoadmapDocument[]>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-delete-roadmap/${roadmapId}`
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-delete-roadmap/${roadmapId}`,
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
@@ -61,6 +61,7 @@ export function PersonalRoadmapList(props: PersonalRoadmapListType) {
|
||||
|
||||
const shareSettingsModal = selectedRoadmap && (
|
||||
<ShareOptionsModal
|
||||
roadmapSlug={selectedRoadmap?.slug}
|
||||
isDiscoverable={selectedRoadmap.isDiscoverable}
|
||||
description={selectedRoadmap.description}
|
||||
visibility={selectedRoadmap.visibility}
|
||||
@@ -129,7 +130,7 @@ type CustomRoadmapItemProps = {
|
||||
roadmap: GetRoadmapListResponse['personalRoadmaps'][number];
|
||||
onRemove: (roadmapId: string) => Promise<void>;
|
||||
setSelectedRoadmap: (
|
||||
roadmap: GetRoadmapListResponse['personalRoadmaps'][number] | null
|
||||
roadmap: GetRoadmapListResponse['personalRoadmaps'][number] | null,
|
||||
) => void;
|
||||
};
|
||||
|
||||
@@ -183,9 +184,9 @@ function CustomRoadmapItem(props: CustomRoadmapItemProps) {
|
||||
Edit
|
||||
</a>
|
||||
<a
|
||||
href={`/r?id=${roadmap._id}`}
|
||||
href={`/r/${roadmap?.slug}`}
|
||||
className={
|
||||
'ml-2 flex items-center gap-2 rounded-md border border-blue-400 bg-white px-2 py-1.5 text-xs hover:bg-blue-50 focus:outline-none text-blue-600'
|
||||
'ml-2 flex items-center gap-2 rounded-md border border-blue-400 bg-white px-2 py-1.5 text-xs text-blue-600 hover:bg-blue-50 focus:outline-none'
|
||||
}
|
||||
target={'_blank'}
|
||||
>
|
||||
|
||||
@@ -24,6 +24,7 @@ export function ResourceProgressStats(props: ResourceProgressStatsProps) {
|
||||
<>
|
||||
{isSharing && $canManageCurrentRoadmap && $currentRoadmap && (
|
||||
<ShareOptionsModal
|
||||
roadmapSlug={$currentRoadmap?.slug}
|
||||
isDiscoverable={$currentRoadmap.isDiscoverable}
|
||||
description={$currentRoadmap?.description}
|
||||
visibility={$currentRoadmap?.visibility}
|
||||
@@ -47,7 +48,7 @@ export function ResourceProgressStats(props: ResourceProgressStatsProps) {
|
||||
{
|
||||
'rounded-bl-md rounded-br-md': isSecondaryBanner,
|
||||
'rounded-md': !isSecondaryBanner,
|
||||
}
|
||||
},
|
||||
)}
|
||||
>
|
||||
<p
|
||||
|
||||
@@ -23,6 +23,7 @@ export function RoadmapHeader(props: RoadmapHeaderProps) {
|
||||
title,
|
||||
description,
|
||||
_id: roadmapId,
|
||||
slug: roadmapSlug,
|
||||
creator,
|
||||
team,
|
||||
visibility,
|
||||
@@ -78,6 +79,7 @@ export function RoadmapHeader(props: RoadmapHeaderProps) {
|
||||
>
|
||||
<ShareSuccess
|
||||
visibility="public"
|
||||
roadmapSlug={roadmapSlug}
|
||||
roadmapId={roadmapId!}
|
||||
description={description}
|
||||
onClose={() => setIsSharingWithOthers(false)}
|
||||
@@ -132,7 +134,7 @@ export function RoadmapHeader(props: RoadmapHeaderProps) {
|
||||
<ShareRoadmapButton
|
||||
roadmapId={roadmapId!}
|
||||
description={description!}
|
||||
pageUrl={`https://roadmap.sh/r?id=${roadmapId}`}
|
||||
pageUrl={`https://roadmap.sh/r/${roadmapSlug}`}
|
||||
allowEmbed={true}
|
||||
/>
|
||||
</div>
|
||||
@@ -141,6 +143,7 @@ export function RoadmapHeader(props: RoadmapHeaderProps) {
|
||||
<>
|
||||
{isSharing && $currentRoadmap && (
|
||||
<ShareOptionsModal
|
||||
roadmapSlug={$currentRoadmap?.slug}
|
||||
isDiscoverable={$currentRoadmap.isDiscoverable}
|
||||
description={$currentRoadmap?.description}
|
||||
visibility={$currentRoadmap?.visibility}
|
||||
|
||||
@@ -91,7 +91,7 @@ export function SharedRoadmapList(props: SharedRoadmapListProps) {
|
||||
className="relative flex w-full border-t"
|
||||
>
|
||||
<a
|
||||
href={`/r?id=${roadmap._id}`}
|
||||
href={`/r/=${roadmap?.slug}`}
|
||||
className="group inline-grid w-full grid-cols-[auto,16px] items-center justify-between gap-2 px-3 py-2 text-sm text-gray-600 transition-colors hover:bg-gray-100 hover:text-black"
|
||||
target={'_blank'}
|
||||
>
|
||||
|
||||
@@ -16,6 +16,7 @@ export type UserProgressResponse = {
|
||||
total: number;
|
||||
updatedAt: Date;
|
||||
isCustomResource: boolean;
|
||||
roadmapSlug?: string;
|
||||
team?: {
|
||||
name: string;
|
||||
id: string;
|
||||
@@ -41,7 +42,7 @@ function renderProgress(progressList: UserProgressResponse) {
|
||||
resourceType: progress.resourceType,
|
||||
isFavorite: progress.isFavorite,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
const totalDone = progress.done + progress.skipped;
|
||||
@@ -89,7 +90,7 @@ export function FavoriteRoadmaps() {
|
||||
setIsLoading(true);
|
||||
|
||||
const { response: progressList, error } = await httpGet<ProgressResponse>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-hero-roadmaps`
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-hero-roadmaps`,
|
||||
);
|
||||
|
||||
if (error || !progressList) {
|
||||
@@ -121,7 +122,7 @@ export function FavoriteRoadmaps() {
|
||||
|
||||
const hasProgress = progress?.length > 0;
|
||||
const customRoadmaps = progress?.filter(
|
||||
(p) => p.isCustomResource && !p.team?.name
|
||||
(p) => p.isCustomResource && !p.team?.name,
|
||||
);
|
||||
const defaultRoadmaps = progress?.filter((p) => !p.isCustomResource);
|
||||
const teamRoadmaps: HeroTeamRoadmaps = progress
|
||||
|
||||
@@ -172,7 +172,7 @@ export function HeroRoadmaps(props: ProgressListProps) {
|
||||
customRoadmap.total) *
|
||||
100
|
||||
}
|
||||
url={`/r?id=${customRoadmap.resourceId}`}
|
||||
url={`/r/${customRoadmap?.roadmapSlug}`}
|
||||
allowFavorite={false}
|
||||
/>
|
||||
);
|
||||
@@ -187,7 +187,7 @@ export function HeroRoadmaps(props: ProgressListProps) {
|
||||
const currentTeam: UserProgressResponse[0]['team'] =
|
||||
teamRoadmaps?.[teamName]?.[0]?.team;
|
||||
const roadmapsList = teamRoadmaps[teamName].filter(
|
||||
(roadmap) => !!roadmap.resourceTitle
|
||||
(roadmap) => !!roadmap.resourceTitle,
|
||||
);
|
||||
const canManageTeam = ['admin', 'manager'].includes(currentTeam?.role!);
|
||||
|
||||
@@ -242,7 +242,7 @@ export function HeroRoadmaps(props: ProgressListProps) {
|
||||
customRoadmap.total) *
|
||||
100
|
||||
}
|
||||
url={`/r?id=${customRoadmap.resourceId}`}
|
||||
url={`/r/${customRoadmap?.roadmapSlug}`}
|
||||
allowFavorite={false}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import {
|
||||
type ReactNode,
|
||||
useCallback,
|
||||
useState,
|
||||
useMemo,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import { type ReactNode, useCallback, useState, useMemo } from 'react';
|
||||
import { Globe2, Loader2, Lock } from 'lucide-react';
|
||||
import { type ListFriendsResponse, ShareFriendList } from './ShareFriendList';
|
||||
import { TransferToTeamList } from './TransferToTeamList';
|
||||
@@ -37,6 +31,7 @@ type ShareOptionsModalProps = {
|
||||
teamId?: string;
|
||||
roadmapId?: string;
|
||||
description?: string;
|
||||
roadmapSlug?: string;
|
||||
|
||||
onShareSettingsUpdate: OnShareSettingsUpdate;
|
||||
};
|
||||
@@ -44,6 +39,7 @@ type ShareOptionsModalProps = {
|
||||
export function ShareOptionsModal(props: ShareOptionsModalProps) {
|
||||
const {
|
||||
roadmapId,
|
||||
roadmapSlug,
|
||||
onClose,
|
||||
isDiscoverable: defaultIsDiscoverable = false,
|
||||
visibility: defaultVisibility,
|
||||
@@ -68,10 +64,10 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
|
||||
const [visibility, setVisibility] = useState(defaultVisibility);
|
||||
const [isDiscoverable, setIsDiscoverable] = useState(defaultIsDiscoverable);
|
||||
const [sharedTeamMemberIds, setSharedTeamMemberIds] = useState<string[]>(
|
||||
defaultSharedMemberIds
|
||||
defaultSharedMemberIds,
|
||||
);
|
||||
const [sharedFriendIds, setSharedFriendIds] = useState<string[]>(
|
||||
defaultSharedFriendIds
|
||||
defaultSharedFriendIds,
|
||||
);
|
||||
const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
|
||||
|
||||
@@ -120,7 +116,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
|
||||
sharedFriendIds,
|
||||
sharedTeamMemberIds,
|
||||
isDiscoverable,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (error) {
|
||||
@@ -151,7 +147,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
|
||||
teamId,
|
||||
sharedTeamMemberIds,
|
||||
isDiscoverable,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (error) {
|
||||
@@ -162,7 +158,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
|
||||
|
||||
window.location.reload();
|
||||
},
|
||||
[roadmapId]
|
||||
[roadmapId],
|
||||
);
|
||||
|
||||
if (isSettingsUpdated) {
|
||||
@@ -173,6 +169,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
|
||||
bodyClassName="p-4 flex flex-col"
|
||||
>
|
||||
<ShareSuccess
|
||||
roadmapSlug={roadmapSlug}
|
||||
visibility={visibility}
|
||||
roadmapId={roadmapId!}
|
||||
description={description}
|
||||
@@ -212,11 +209,11 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
|
||||
setSharedFriendIds([]);
|
||||
} else if (visibility === 'friends') {
|
||||
setSharedFriendIds(
|
||||
defaultSharedFriendIds.length > 0 ? defaultSharedFriendIds : []
|
||||
defaultSharedFriendIds.length > 0 ? defaultSharedFriendIds : [],
|
||||
);
|
||||
} else if (visibility === 'team' && teamId) {
|
||||
setSharedTeamMemberIds(
|
||||
defaultSharedMemberIds?.length > 0 ? defaultSharedMemberIds : []
|
||||
defaultSharedMemberIds?.length > 0 ? defaultSharedMemberIds : [],
|
||||
);
|
||||
setSharedFriendIds([]);
|
||||
} else {
|
||||
@@ -329,7 +326,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
|
||||
}
|
||||
onClick={() => {
|
||||
handleTransferToTeam(selectedTeamId!, sharedTeamMemberIds).then(
|
||||
() => null
|
||||
() => null,
|
||||
);
|
||||
}}
|
||||
>
|
||||
@@ -374,7 +371,7 @@ function UpdateAction(props: {
|
||||
className={cn(
|
||||
'flex min-w-[120px] items-center justify-center gap-1.5 rounded-md border border-gray-900 bg-gray-900 px-4 py-2 text-white hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-75',
|
||||
disabled && 'border-gray-700 bg-gray-700 text-white hover:bg-gray-700',
|
||||
className
|
||||
className,
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { cn } from '../../lib/classname';
|
||||
import type { AllowedRoadmapVisibility } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal';
|
||||
|
||||
type ShareSuccessProps = {
|
||||
roadmapSlug?: string;
|
||||
roadmapId: string;
|
||||
onClose: () => void;
|
||||
visibility: AllowedRoadmapVisibility;
|
||||
@@ -13,6 +14,7 @@ type ShareSuccessProps = {
|
||||
|
||||
export function ShareSuccess(props: ShareSuccessProps) {
|
||||
const {
|
||||
roadmapSlug,
|
||||
roadmapId,
|
||||
onClose,
|
||||
description,
|
||||
@@ -23,7 +25,9 @@ export function ShareSuccess(props: ShareSuccessProps) {
|
||||
const baseUrl = import.meta.env.DEV
|
||||
? 'http://localhost:3000'
|
||||
: 'https://roadmap.sh';
|
||||
const shareLink = `${baseUrl}/r?id=${roadmapId}`;
|
||||
const shareLink = roadmapSlug
|
||||
? `${baseUrl}/r/${roadmapSlug}`
|
||||
: `${baseUrl}/r?id=${roadmapId}`;
|
||||
|
||||
const { copyText, isCopied } = useCopyText();
|
||||
|
||||
@@ -84,13 +88,13 @@ export function ShareSuccess(props: ShareSuccessProps) {
|
||||
</p>
|
||||
<div className="mt-2">
|
||||
<input
|
||||
onClick={(e) => {
|
||||
e.currentTarget.select();
|
||||
copyText(embedHtml);
|
||||
}}
|
||||
readOnly={true}
|
||||
className="w-full resize-none rounded-md border bg-gray-50 p-2 text-sm"
|
||||
value={embedHtml}
|
||||
onClick={(e) => {
|
||||
e.currentTarget.select();
|
||||
copyText(embedHtml);
|
||||
}}
|
||||
readOnly={true}
|
||||
className="w-full resize-none rounded-md border bg-gray-50 p-2 text-sm"
|
||||
value={embedHtml}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -127,7 +131,7 @@ export function ShareSuccess(props: ShareSuccessProps) {
|
||||
<button
|
||||
className={cn(
|
||||
'flex w-full items-center justify-center gap-1.5 rounded bg-black px-4 py-2.5 text-sm font-medium text-white hover:opacity-80',
|
||||
isCopied && 'bg-green-300 text-green-800'
|
||||
isCopied && 'bg-green-300 text-green-800',
|
||||
)}
|
||||
disabled={isCopied}
|
||||
onClick={() => {
|
||||
@@ -139,7 +143,7 @@ export function ShareSuccess(props: ShareSuccessProps) {
|
||||
</button>
|
||||
<button
|
||||
className={cn(
|
||||
'flex w-full items-center justify-center gap-1.5 rounded border border-black px-4 py-2 text-sm font-medium hover:bg-gray-100'
|
||||
'flex w-full items-center justify-center gap-1.5 rounded border border-black px-4 py-2 text-sm font-medium hover:bg-gray-100',
|
||||
)}
|
||||
onClick={onClose}
|
||||
>
|
||||
|
||||
@@ -11,7 +11,7 @@ type GroupRoadmapItemProps = {
|
||||
|
||||
export function GroupRoadmapItem(props: GroupRoadmapItemProps) {
|
||||
const { onShowResourceProgress } = props;
|
||||
const { members, resourceTitle, resourceId, isCustomResource } =
|
||||
const { members, resourceTitle, resourceId, isCustomResource, roadmapSlug } =
|
||||
props.roadmap;
|
||||
|
||||
const { t: teamId } = getUrlParams();
|
||||
@@ -19,7 +19,7 @@ export function GroupRoadmapItem(props: GroupRoadmapItemProps) {
|
||||
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
const roadmapLink = isCustomResource
|
||||
? `/r?id=${resourceId}`
|
||||
? `/r/${roadmapSlug}`
|
||||
: `/${resourceId}?t=${teamId}`;
|
||||
|
||||
return (
|
||||
|
||||
@@ -22,6 +22,7 @@ export type UserProgress = {
|
||||
total: number;
|
||||
updatedAt: string;
|
||||
isCustomResource?: boolean;
|
||||
roadmapSlug?: string;
|
||||
};
|
||||
|
||||
export type TeamMember = {
|
||||
@@ -39,6 +40,7 @@ export type GroupByRoadmap = {
|
||||
resourceTitle: string;
|
||||
resourceType: string;
|
||||
isCustomResource?: boolean;
|
||||
roadmapSlug?: string;
|
||||
members: {
|
||||
member: TeamMember;
|
||||
progress: UserProgress | undefined;
|
||||
@@ -71,7 +73,7 @@ export function TeamProgressPage() {
|
||||
|
||||
async function getTeamProgress() {
|
||||
const { response, error } = await httpGet<TeamMember[]>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-team-progress/${teamId}`
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-team-progress/${teamId}`,
|
||||
);
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Failed to get team progress');
|
||||
@@ -87,7 +89,7 @@ export function TeamProgressPage() {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -116,7 +118,7 @@ export function TeamProgressPage() {
|
||||
const members: GroupByRoadmap['members'] = [];
|
||||
for (const member of teamMembers) {
|
||||
const progress = member.progress.find(
|
||||
(progress) => progress.resourceId === roadmap
|
||||
(progress) => progress.resourceId === roadmap,
|
||||
);
|
||||
if (!progress) {
|
||||
continue;
|
||||
@@ -139,6 +141,7 @@ export function TeamProgressPage() {
|
||||
resourceId: roadmap,
|
||||
resourceTitle: members?.[0].progress?.resourceTitle || '',
|
||||
resourceType: 'roadmap',
|
||||
roadmapSlug: members?.[0].progress?.roadmapSlug,
|
||||
members,
|
||||
isCustomResource,
|
||||
});
|
||||
@@ -174,7 +177,7 @@ export function TeamProgressPage() {
|
||||
setShowMemberProgress({
|
||||
resourceId: showMemberProgress.resourceId,
|
||||
member: teamMembers.find(
|
||||
(member) => member.email === user?.email
|
||||
(member) => member.email === user?.email,
|
||||
)!,
|
||||
isCustomResource: showMemberProgress.isCustomResource,
|
||||
});
|
||||
|
||||
@@ -473,7 +473,7 @@ export function TeamRoadmaps() {
|
||||
)}
|
||||
|
||||
<a
|
||||
href={`/r?id=${resourceConfig.resourceId}`}
|
||||
href={`/r/${resourceConfig.roadmapSlug}`}
|
||||
className={
|
||||
'ml-2 flex items-center gap-2 rounded-md border border-gray-300 bg-white px-2 py-1.5 text-xs hover:bg-gray-50 focus:outline-none'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import type { FAQType } from '../../components/FAQs/FAQs.astro';
|
||||
import type { FAQType } from '../../../components/FAQs/FAQs.astro';
|
||||
|
||||
export const faqs: FAQType[] = [
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { MarkdownFileType } from './file';
|
||||
import type { BestPracticeFrontmatter } from './best-pratice';
|
||||
import type { BestPracticeFrontmatter } from './best-practice';
|
||||
|
||||
// Generates URL from the topic file path e.g.
|
||||
// -> /src/data/best-practices/frontend-performance/content/100-use-https-everywhere
|
||||
@@ -34,7 +34,7 @@ export async function getAllBestPracticeTopicFiles(): Promise<
|
||||
'/src/data/best-practices/*/content/**/*.md',
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const mapping: Record<string, BestPracticeTopicFileType> = {};
|
||||
@@ -63,4 +63,4 @@ export async function getAllBestPracticeTopicFiles(): Promise<
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export async function getBestPracticeIds() {
|
||||
'/src/data/best-practices/*/*.md',
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return Object.keys(bestPracticeFiles).map(bestPracticePathToId);
|
||||
@@ -64,7 +64,7 @@ export async function getAllBestPractices(): Promise<BestPracticeFileType[]> {
|
||||
'/src/data/best-practices/*/*.md',
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const bestPracticeFiles = Object.values(bestPracticeFilesMap);
|
||||
@@ -74,6 +74,19 @@ export async function getAllBestPractices(): Promise<BestPracticeFileType[]> {
|
||||
}));
|
||||
|
||||
return bestPracticeItems.sort(
|
||||
(a, b) => a.frontmatter.order - b.frontmatter.order
|
||||
(a, b) => a.frontmatter.order - b.frontmatter.order,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getBestPracticeById(
|
||||
id: string,
|
||||
): Promise<BestPracticeFileType> {
|
||||
const bestPracticeFile = await import(
|
||||
`../data/best-practices/${id}/${id}.md`
|
||||
);
|
||||
|
||||
return {
|
||||
...bestPracticeFile,
|
||||
id: bestPracticePathToId(bestPracticeFile.file),
|
||||
};
|
||||
}
|
||||
@@ -48,7 +48,7 @@ export async function getAllGuides(): Promise<GuideFileType[]> {
|
||||
'/src/data/guides/*.md',
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const guideFiles = Object.values(guides);
|
||||
@@ -60,6 +60,15 @@ export async function getAllGuides(): Promise<GuideFileType[]> {
|
||||
return enrichedGuides.sort(
|
||||
(a, b) =>
|
||||
new Date(b.frontmatter.date).valueOf() -
|
||||
new Date(a.frontmatter.date).valueOf()
|
||||
new Date(a.frontmatter.date).valueOf(),
|
||||
);
|
||||
}
|
||||
|
||||
export async function getGuideById(id: string): Promise<GuideFileType> {
|
||||
const guide = await import(`../data/guides/${id}.md`);
|
||||
|
||||
return {
|
||||
...guide,
|
||||
id: guidePathToId(guide.file),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export async function getAllLinkGroups(): Promise<LinkGroupFileType[]> {
|
||||
'/src/data/link-groups/*.md',
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return Object.values(linkGroups).map((linkGroupFile) => ({
|
||||
@@ -37,3 +37,14 @@ export async function getAllLinkGroups(): Promise<LinkGroupFileType[]> {
|
||||
id: linkGroupPathToId(linkGroupFile.file),
|
||||
}));
|
||||
}
|
||||
|
||||
export async function getLinkGroupById(
|
||||
groupId: string,
|
||||
): Promise<LinkGroupFileType> {
|
||||
const linkGroup = await import(`../data/link-groups/${groupId}.md`);
|
||||
|
||||
return {
|
||||
...linkGroup,
|
||||
id: linkGroupPathToId(linkGroup.file),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -117,6 +117,12 @@ export async function getAllQuestionGroups(): Promise<QuestionGroupType[]> {
|
||||
.sort((a, b) => a.frontmatter.order - b.frontmatter.order);
|
||||
}
|
||||
|
||||
export async function getQuestionGroupById(id: string) {
|
||||
const questionGroups = await getAllQuestionGroups();
|
||||
|
||||
return questionGroups.find((group) => group.id === id);
|
||||
}
|
||||
|
||||
export async function getQuestionGroupsByIds(
|
||||
ids: string[],
|
||||
): Promise<{ id: string; title: string; description: string }[]> {
|
||||
|
||||
@@ -130,3 +130,11 @@ export async function getRoadmapsByIds(
|
||||
|
||||
return Promise.all(ids.map((id) => getRoadmapById(id)));
|
||||
}
|
||||
|
||||
export async function getRoadmapFaqsById(roadmapId: string): Promise<string[]> {
|
||||
const { faqs } = await import(
|
||||
`../data/roadmaps/${roadmapId}/faqs.astro`
|
||||
).catch(() => ({}));
|
||||
|
||||
return faqs || [];
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export async function getAllVideos(): Promise<VideoFileType[]> {
|
||||
'/src/data/videos/*.md',
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const videoFiles = Object.values(videos);
|
||||
@@ -59,6 +59,15 @@ export async function getAllVideos(): Promise<VideoFileType[]> {
|
||||
return enrichedVideos.sort(
|
||||
(a, b) =>
|
||||
new Date(b.frontmatter.date).valueOf() -
|
||||
new Date(a.frontmatter.date).valueOf()
|
||||
new Date(a.frontmatter.date).valueOf(),
|
||||
);
|
||||
}
|
||||
|
||||
export async function getVideoById(id: string): Promise<VideoFileType> {
|
||||
const video = await import(`../data/videos/${id}.md`);
|
||||
|
||||
return {
|
||||
...video,
|
||||
id: videoPathToId(video.file),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,34 +1,22 @@
|
||||
---
|
||||
import RoadmapBanner from '../../components/RoadmapBanner.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import {
|
||||
getRoadmapTopicFiles,
|
||||
type RoadmapTopicFileType,
|
||||
} from '../../lib/roadmap-topic';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const topicPathMapping = await getRoadmapTopicFiles();
|
||||
|
||||
return Object.keys(topicPathMapping).map((topicSlug) => {
|
||||
const topicDetails = topicPathMapping[topicSlug];
|
||||
const roadmapId = topicDetails.roadmapId;
|
||||
const topicId = topicSlug.replace(`/${roadmapId}/`, '');
|
||||
|
||||
return {
|
||||
params: {
|
||||
topicId,
|
||||
roadmapId,
|
||||
},
|
||||
props: topicDetails,
|
||||
};
|
||||
});
|
||||
}
|
||||
import { getRoadmapTopicFiles } from '../../lib/roadmap-topic';
|
||||
|
||||
export const partial = true;
|
||||
|
||||
const { topicId } = Astro.params;
|
||||
const { file, url, roadmapId, roadmap, heading } =
|
||||
Astro.props as RoadmapTopicFileType;
|
||||
const { topicId, roadmapId } = Astro.params;
|
||||
if (!topicId) {
|
||||
return new Response();
|
||||
}
|
||||
|
||||
const topicSlug = `/${roadmapId}/${topicId}`;
|
||||
const topicPathMapping = await getRoadmapTopicFiles();
|
||||
|
||||
const topicDetails = topicPathMapping[topicSlug];
|
||||
if (!topicDetails) {
|
||||
return Astro.redirect('/404');
|
||||
}
|
||||
|
||||
const { file } = topicDetails;
|
||||
|
||||
const fileWithoutBasePath = file.file?.replace(/.+?\/src\/data/, '/src/data');
|
||||
const gitHubUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/master${fileWithoutBasePath}`;
|
||||
@@ -36,4 +24,4 @@ const gitHubUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/maste
|
||||
|
||||
<div data-github-url={gitHubUrl}></div>
|
||||
|
||||
<file.Content />
|
||||
<file.Content />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
import FAQs from '../../components/FAQs/FAQs.astro';
|
||||
import FrameRenderer from '../../components/FrameRenderer/FrameRenderer.astro';
|
||||
import MarkdownFile from '../../components/MarkdownFile.astro';
|
||||
import RelatedRoadmaps from '../../components/RelatedRoadmaps.astro';
|
||||
import RoadmapHeader from '../../components/RoadmapHeader.astro';
|
||||
import ShareIcons from '../../components/ShareIcons/ShareIcons.astro';
|
||||
@@ -13,27 +12,25 @@ import {
|
||||
generateArticleSchema,
|
||||
generateFAQSchema,
|
||||
} from '../../lib/jsonld-schema';
|
||||
import { type RoadmapFrontmatter, getRoadmapIds } from '../../lib/roadmap';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const roadmapIds = await getRoadmapIds();
|
||||
|
||||
return roadmapIds.map((roadmapId) => ({
|
||||
params: { roadmapId },
|
||||
}));
|
||||
}
|
||||
import {
|
||||
getRoadmapById,
|
||||
type RoadmapFrontmatter,
|
||||
getRoadmapFaqsById,
|
||||
} from '../../lib/roadmap';
|
||||
|
||||
interface Params extends Record<string, string | undefined> {
|
||||
roadmapId: string;
|
||||
}
|
||||
|
||||
const { roadmapId } = Astro.params as Params;
|
||||
const roadmapFile = await import(
|
||||
`../../data/roadmaps/${roadmapId}/${roadmapId}.md`
|
||||
);
|
||||
const { faqs: roadmapFAQs = [] } = await import(
|
||||
`../../data/roadmaps/${roadmapId}/faqs.astro`
|
||||
);
|
||||
|
||||
const roadmapFile = await getRoadmapById(roadmapId).catch(() => null);
|
||||
if (!roadmapFile) {
|
||||
return Astro.redirect('/404');
|
||||
}
|
||||
|
||||
const roadmapFAQs = await getRoadmapFaqsById(roadmapId);
|
||||
const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
|
||||
|
||||
let jsonLdSchema = [];
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const roadmapJsons = await import.meta.glob('/src/data/roadmaps/**/*.json', {
|
||||
eager: true,
|
||||
});
|
||||
|
||||
return Object.keys(roadmapJsons).map((filePath) => {
|
||||
const roadmapId = filePath.split('/').pop()?.replace('.json', '');
|
||||
const roadmapJson = roadmapJsons[filePath] as Record<string, any>;
|
||||
|
||||
return {
|
||||
params: {
|
||||
roadmapId,
|
||||
},
|
||||
props: {
|
||||
roadmapJson: roadmapJson?.default,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export const GET: APIRoute = async function ({ params, request, props }) {
|
||||
return new Response(JSON.stringify(props.roadmapJson), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const { roadmapId } = params;
|
||||
|
||||
try {
|
||||
const roadmapJson = await import(
|
||||
`../../data/roadmaps/${roadmapId}/${roadmapId}.json`
|
||||
);
|
||||
|
||||
return new Response(JSON.stringify(roadmapJson), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
data: null,
|
||||
error: {
|
||||
message: 'Roadmap not found',
|
||||
},
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
---
|
||||
import { getAllBestPracticeTopicFiles } from '../../../lib/best-practice-topic';
|
||||
import type { BestPracticeTopicFileType } from '../../../lib/best-practice-topic';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const topicPathMapping = await getAllBestPracticeTopicFiles();
|
||||
|
||||
return Object.keys(topicPathMapping).map((topicSlug) => {
|
||||
const topicDetails = topicPathMapping[topicSlug];
|
||||
const bestPracticeId = topicDetails.bestPracticeId;
|
||||
const topicId = topicSlug.replace(`/${bestPracticeId}/`, '');
|
||||
|
||||
return {
|
||||
params: {
|
||||
topicId,
|
||||
bestPracticeId,
|
||||
},
|
||||
props: topicDetails,
|
||||
};
|
||||
});
|
||||
const { topicId, bestPracticeId } = Astro.params;
|
||||
if (!topicId) {
|
||||
return new Response();
|
||||
}
|
||||
|
||||
const { file } = Astro.props as BestPracticeTopicFileType;
|
||||
const topicSlug = `/${bestPracticeId}/${topicId}`;
|
||||
const topicPathMapping = await getAllBestPracticeTopicFiles();
|
||||
|
||||
const topicDetails = topicPathMapping[topicSlug];
|
||||
const { file } = topicDetails;
|
||||
|
||||
const fileWithoutBasePath = file.file?.replace(/.+?\/src\/data/, '/src/data');
|
||||
const gitHubUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/master${fileWithoutBasePath}`;
|
||||
|
||||
@@ -10,21 +10,10 @@ import { UserProgressModal } from '../../../components/UserProgress/UserProgress
|
||||
import {
|
||||
type BestPracticeFileType,
|
||||
type BestPracticeFrontmatter,
|
||||
getAllBestPractices,
|
||||
} from '../../../lib/best-pratice';
|
||||
getBestPracticeById,
|
||||
} from '../../../lib/best-practice';
|
||||
import { generateArticleSchema } from '../../../lib/jsonld-schema';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const bestPractices = await getAllBestPractices();
|
||||
|
||||
return bestPractices.map((bestPractice: BestPracticeFileType) => ({
|
||||
params: { bestPracticeId: bestPractice.id },
|
||||
props: {
|
||||
bestPractice: bestPractice,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
interface Params extends Record<string, string | undefined> {
|
||||
bestPracticeId: string;
|
||||
}
|
||||
@@ -34,7 +23,14 @@ interface Props {
|
||||
}
|
||||
|
||||
const { bestPracticeId } = Astro.params as Params;
|
||||
const { bestPractice } = Astro.props as Props;
|
||||
const bestPractice = await getBestPracticeById(bestPracticeId).catch(
|
||||
() => null,
|
||||
);
|
||||
|
||||
if (!bestPractice) {
|
||||
return Astro.redirect('/404');
|
||||
}
|
||||
|
||||
const bestPracticeData = bestPractice.frontmatter as BestPracticeFrontmatter;
|
||||
|
||||
let jsonLdSchema = [];
|
||||
@@ -49,7 +45,7 @@ if (bestPracticeData.schema) {
|
||||
datePublished: bestPracticeSchema.datePublished,
|
||||
dateModified: bestPracticeSchema.dateModified,
|
||||
imageUrl: bestPracticeSchema.imageUrl,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
---
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const bestPracticeJsons = await import.meta.glob(
|
||||
'/src/data/best-practices/**/*.json',
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
);
|
||||
|
||||
return Object.keys(bestPracticeJsons).map((filePath) => {
|
||||
const bestPracticeId = filePath.split('/').pop()?.replace('.json', '');
|
||||
const bestPracticeJson = bestPracticeJsons[filePath] as Record<string, any>;
|
||||
|
||||
return {
|
||||
params: {
|
||||
bestPracticeId,
|
||||
},
|
||||
props: {
|
||||
bestPracticeJson: bestPracticeJson?.default,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export const GET: APIRoute = async function ({ params, request, props }) {
|
||||
return new Response(JSON.stringify(props.bestPracticeJson), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
});
|
||||
const { bestPracticeId } = params;
|
||||
|
||||
try {
|
||||
const roadmapJson = await import(
|
||||
`../../../data/best-practices/${bestPracticeId}/${bestPracticeId}.json`
|
||||
);
|
||||
|
||||
return new Response(JSON.stringify(roadmapJson), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
data: null,
|
||||
error: {
|
||||
message: 'Best Practices not found',
|
||||
},
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import GridItem from '../../components/GridItem.astro';
|
||||
import SimplePageHeader from '../../components/SimplePageHeader.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { getAllBestPractices } from '../../lib/best-pratice';
|
||||
import { getAllBestPractices } from '../../lib/best-practice';
|
||||
|
||||
const bestPractices = await getAllBestPractices();
|
||||
---
|
||||
|
||||
@@ -1,30 +1,13 @@
|
||||
---
|
||||
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
||||
import SkeletonLayout from '../../../layouts/SkeletonLayout.astro';
|
||||
import { getAllLinkGroups } from '../../../lib/link-group';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const linkGroups = await getAllLinkGroups();
|
||||
|
||||
return linkGroups.flatMap((linkGroup) => {
|
||||
const linkGroupLinks = linkGroup.frontmatter;
|
||||
|
||||
return Object.keys(linkGroupLinks).map((slug) => {
|
||||
return {
|
||||
params: {
|
||||
linkGroupId: linkGroup.id,
|
||||
linkId: slug,
|
||||
},
|
||||
props: {
|
||||
linkGroup,
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
import { getLinkGroupById } from '../../../lib/link-group';
|
||||
|
||||
const { linkId } = Astro.params;
|
||||
const { linkGroup } = Astro.props;
|
||||
const linkGroup = await getLinkGroupById(linkId!).catch(() => null);
|
||||
if (!linkGroup) {
|
||||
return Astro.redirect('/404');
|
||||
}
|
||||
|
||||
const fullUrl = linkGroup.frontmatter[linkId!];
|
||||
---
|
||||
|
||||
@@ -2,23 +2,14 @@
|
||||
import GuideHeader from '../../components/GuideHeader.astro';
|
||||
import MarkdownFile from '../../components/MarkdownFile.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { getAllGuides,GuideFileType } from '../../lib/guide';
|
||||
|
||||
export interface Props {
|
||||
guide: GuideFileType;
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const guides = await getAllGuides();
|
||||
|
||||
return guides.map((guide) => ({
|
||||
params: { guideId: guide.id },
|
||||
props: { guide },
|
||||
}));
|
||||
}
|
||||
import { getGuideById } from '../../lib/guide';
|
||||
|
||||
const { guideId } = Astro.params;
|
||||
const { guide } = Astro.props;
|
||||
const guide = await getGuideById(guideId).catch(() => null);
|
||||
if (!guide) {
|
||||
return Astro.redirect('/404');
|
||||
}
|
||||
|
||||
const { frontmatter: guideData } = guide;
|
||||
---
|
||||
|
||||
@@ -30,7 +21,7 @@ const { frontmatter: guideData } = guide;
|
||||
>
|
||||
<GuideHeader guide={guide} />
|
||||
|
||||
<div class='py-5 sm:py-10 max-w-[700px] mx-auto'>
|
||||
<div class='mx-auto max-w-[700px] py-5 sm:py-10'>
|
||||
<MarkdownFile>
|
||||
<guide.Content />
|
||||
</MarkdownFile>
|
||||
|
||||
@@ -4,7 +4,7 @@ import FeaturedGuides from '../components/FeaturedGuides.astro';
|
||||
import FeaturedItems from '../components/FeaturedItems/FeaturedItems.astro';
|
||||
import HeroSection from '../components/HeroSection/HeroSection.astro';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { getAllBestPractices } from '../lib/best-pratice';
|
||||
import { getAllBestPractices } from '../lib/best-practice';
|
||||
import { getAllGuides } from '../lib/guide';
|
||||
import { getRoadmapsByTag } from '../lib/roadmap';
|
||||
import { getAllVideos } from '../lib/video';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getAllBestPractices } from '../lib/best-pratice';
|
||||
import { getAllBestPractices } from '../lib/best-practice';
|
||||
import { getAllGuides } from '../lib/guide';
|
||||
import { getRoadmapsByTag } from '../lib/roadmap';
|
||||
import { getAllVideos } from '../lib/video';
|
||||
|
||||
@@ -8,24 +8,15 @@ import { QuestionsList } from '../../components/Questions/QuestionsList';
|
||||
|
||||
import {
|
||||
getAllQuestionGroups,
|
||||
type QuestionGroupType,
|
||||
getQuestionGroupById,
|
||||
} from '../../lib/question-group';
|
||||
|
||||
export interface Props {
|
||||
questionGroup: QuestionGroupType;
|
||||
}
|
||||
export async function getStaticPaths() {
|
||||
const questionGroups = await getAllQuestionGroups();
|
||||
|
||||
return questionGroups.map((questionGroup) => {
|
||||
return {
|
||||
params: { questionGroupId: questionGroup.id },
|
||||
props: { questionGroup },
|
||||
};
|
||||
});
|
||||
const { questionGroupId } = Astro.params;
|
||||
const questionGroup = await getQuestionGroupById(questionGroupId);
|
||||
if (!questionGroup) {
|
||||
return Astro.redirect('/404');
|
||||
}
|
||||
|
||||
const { questionGroup } = Astro.props;
|
||||
const { frontmatter } = questionGroup;
|
||||
---
|
||||
|
||||
@@ -38,24 +29,24 @@ const { frontmatter } = questionGroup;
|
||||
>
|
||||
<div class='flex bg-gray-50 pb-14 pt-4 sm:pb-16 sm:pt-8'>
|
||||
<div class='container !max-w-[740px]'>
|
||||
<div class='mb-3 sm:mb-5 mt-2 text-left sm:text-center sm:mt-8'>
|
||||
<div class='mb-3 mt-2 text-left sm:mb-5 sm:mt-8 sm:text-center'>
|
||||
<div class='mb-2 md:mb-6'>
|
||||
<a
|
||||
href='/questions'
|
||||
class='group rounded-md text-sm font-medium text-gray-400 hover:text-gray-800 transition-colors duration-200'
|
||||
class='group rounded-md text-sm font-medium text-gray-400 transition-colors duration-200 hover:text-gray-800'
|
||||
>
|
||||
<span
|
||||
class='inline-block transform transition-transform group-hover:translate-x-[-2px]'
|
||||
>
|
||||
←
|
||||
</span>
|
||||
Back to all Questions
|
||||
Back to all Questions
|
||||
</a>
|
||||
</div>
|
||||
<h1 class='mb-1 text-2xl font-bold sm:mb-5 sm:text-5xl'>
|
||||
{frontmatter.title}
|
||||
</h1>
|
||||
<p class='hidden sm:block text-xl text-gray-500'>
|
||||
<p class='hidden text-xl text-gray-500 sm:block'>
|
||||
{frontmatter.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
24
src/pages/r/[customRoadmapSlug].astro
Normal file
24
src/pages/r/[customRoadmapSlug].astro
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { CustomRoadmap } from '../../components/CustomRoadmap/CustomRoadmap';
|
||||
import { SkeletonRoadmapHeader } from '../../components/CustomRoadmap/SkeletonRoadmapHeader';
|
||||
import Loader from '../../components/Loader.astro';
|
||||
import ProgressHelpPopup from '../../components/ProgressHelpPopup.astro';
|
||||
|
||||
const { customRoadmapSlug } = Astro.params;
|
||||
---
|
||||
|
||||
<BaseLayout title='Roadmaps'>
|
||||
<ProgressHelpPopup />
|
||||
<div>
|
||||
<div class='flex min-h-[550px] flex-col'>
|
||||
<div data-roadmap-loader class='flex w-full grow flex-col'>
|
||||
<SkeletonRoadmapHeader />
|
||||
<div class='flex grow items-center justify-center'>
|
||||
<Loader />
|
||||
</div>
|
||||
</div>
|
||||
<CustomRoadmap slug={customRoadmapSlug} client:only='react' />
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
5
src/pages/v1-health.ts
Normal file
5
src/pages/v1-health.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export async function GET() {
|
||||
return new Response(JSON.stringify({}), {});
|
||||
}
|
||||
33
src/pages/v1-stats.json.ts
Normal file
33
src/pages/v1-stats.json.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function GET() {
|
||||
const commitHash = execSync('git rev-parse HEAD').toString().trim();
|
||||
const commitDate = execSync('git log -1 --format=%cd').toString().trim();
|
||||
const commitMessage = execSync('git log -1 --format=%B').toString().trim();
|
||||
|
||||
const prevCommitHash = execSync('git rev-parse HEAD~1').toString().trim();
|
||||
const prevCommitDate = execSync('git log -1 --format=%cd HEAD~1')
|
||||
.toString()
|
||||
.trim();
|
||||
const prevCommitMessage = execSync('git log -1 --format=%B HEAD~1')
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
current: {
|
||||
hash: commitHash,
|
||||
date: commitDate,
|
||||
message: commitMessage,
|
||||
},
|
||||
previous: {
|
||||
hash: prevCommitHash,
|
||||
date: prevCommitDate,
|
||||
message: prevCommitMessage,
|
||||
},
|
||||
}),
|
||||
{},
|
||||
);
|
||||
}
|
||||
@@ -1,23 +1,14 @@
|
||||
---
|
||||
import VideoHeader from '../../components/VideoHeader.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { getAllVideos,VideoFileType } from '../../lib/video';
|
||||
|
||||
export interface Props {
|
||||
video: VideoFileType;
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const videos = await getAllVideos();
|
||||
|
||||
return videos.map((video) => ({
|
||||
params: { videoId: video.id },
|
||||
props: { video },
|
||||
}));
|
||||
}
|
||||
import { getVideoById } from '../../lib/video';
|
||||
|
||||
const { videoId } = Astro.params;
|
||||
const { video } = Astro.props;
|
||||
|
||||
const video = await getVideoById(videoId).catch(() => null);
|
||||
if (!video) {
|
||||
return Astro.redirect('/404');
|
||||
}
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
@@ -29,7 +20,7 @@ const { video } = Astro.props;
|
||||
|
||||
<div class='bg-gray-50 py-5 sm:py-10'>
|
||||
<div
|
||||
class='container prose prose-code:bg-transparent prose-h2:text-3xl prose-h2:mt-4 prose-h2:mb-2 prose-h3:mt-2 prose-img:mt-1'
|
||||
class='container prose prose-h2:mb-2 prose-h2:mt-4 prose-h2:text-3xl prose-h3:mt-2 prose-code:bg-transparent prose-img:mt-1'
|
||||
>
|
||||
<video.Content />
|
||||
</div>
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user