Update changelog page

pull/7211/head
Kamran Ahmed 4 weeks ago
parent 086c790837
commit 53e87095ef
  1. 146
      package-lock.json
  2. 1
      package.json
  3. 10
      pnpm-lock.yaml
  4. 16
      src/components/Changelog/ChangelogItem.astro
  5. 108
      src/components/ChangelogImages.tsx
  6. 6
      src/data/changelogs/leaderboard-page.md
  7. 1
      src/lib/changelog.ts
  8. 16
      src/pages/changelog.astro

146
package-lock.json generated

@ -29,16 +29,18 @@
"jose": "^5.6.3",
"js-cookie": "^3.0.5",
"lucide-react": "^0.419.0",
"luxon": "^3.5.0",
"nanoid": "^5.0.7",
"nanostores": "^0.10.3",
"node-html-parser": "^6.1.13",
"npm-check-updates": "^17.0.0",
"playwright": "^1.45.3",
"playwright": "^1.47.1",
"prismjs": "^1.29.0",
"react": "^18.3.1",
"react-calendar-heatmap": "^1.9.0",
"react-confetti": "^6.1.0",
"react-dom": "^18.3.1",
"react-slick": "^0.30.2",
"react-tooltip": "^5.27.1",
"reactflow": "^11.11.4",
"rehype-external-links": "^3.0.0",
@ -47,6 +49,7 @@
"satori": "^0.10.14",
"satori-html": "^0.3.2",
"sharp": "^0.33.4",
"slick-carousel": "^1.8.1",
"slugify": "^1.6.6",
"tailwind-merge": "^2.4.0",
"tailwindcss": "^3.4.7",
@ -59,8 +62,10 @@
"@tailwindcss/typography": "^0.5.13",
"@types/dom-to-image": "^2.6.7",
"@types/js-cookie": "^3.0.6",
"@types/luxon": "^3.4.2",
"@types/prismjs": "^1.26.4",
"@types/react-calendar-heatmap": "^1.6.7",
"@types/react-slick": "^0.23.13",
"@types/turndown": "^5.0.5",
"csv-parser": "^3.0.0",
"gh-pages": "^6.1.1",
@ -1749,6 +1754,50 @@
"node": ">=18"
}
},
"node_modules/@playwright/test/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/@playwright/test/node_modules/playwright": {
"version": "1.46.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz",
"integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==",
"dev": true,
"dependencies": {
"playwright-core": "1.46.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/@playwright/test/node_modules/playwright-core": {
"version": "1.46.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz",
"integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@reactflow/background": {
"version": "11.3.14",
"resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz",
@ -2622,6 +2671,12 @@
"integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
"dev": true
},
"node_modules/@types/luxon": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz",
"integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==",
"dev": true
},
"node_modules/@types/mdast": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
@ -2698,6 +2753,15 @@
"@types/react": "*"
}
},
"node_modules/@types/react-slick": {
"version": "0.23.13",
"resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.13.tgz",
"integrity": "sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/sax": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
@ -3931,6 +3995,11 @@
"iconv-lite": "^0.6.2"
}
},
"node_modules/enquire.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
"integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw=="
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
@ -4953,6 +5022,12 @@
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/jquery": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"peer": true
},
"node_modules/js-cookie": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
@ -4988,6 +5063,14 @@
"node": ">=4"
}
},
"node_modules/json2mq": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
"integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
"dependencies": {
"string-convert": "^0.2.0"
}
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@ -5114,6 +5197,11 @@
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
"dev": true
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
@ -5199,6 +5287,14 @@
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/luxon": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz",
"integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/magic-string": {
"version": "0.30.11",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
@ -6656,11 +6752,11 @@
}
},
"node_modules/playwright": {
"version": "1.46.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz",
"integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==",
"version": "1.47.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz",
"integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==",
"dependencies": {
"playwright-core": "1.46.0"
"playwright-core": "1.47.2"
},
"bin": {
"playwright": "cli.js"
@ -6673,9 +6769,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.46.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz",
"integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==",
"version": "1.47.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz",
"integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==",
"bin": {
"playwright-core": "cli.js"
},
@ -7125,6 +7221,22 @@
"node": ">=0.10.0"
}
},
"node_modules/react-slick": {
"version": "0.30.2",
"resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.30.2.tgz",
"integrity": "sha512-XvQJi7mRHuiU3b9irsqS9SGIgftIfdV5/tNcURTb5LdIokRA5kIIx3l4rlq2XYHfxcSntXapoRg/GxaVOM1yfg==",
"dependencies": {
"classnames": "^2.2.5",
"enquire.js": "^2.1.6",
"json2mq": "^0.2.0",
"lodash.debounce": "^4.0.8",
"resize-observer-polyfill": "^1.5.0"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-tooltip": {
"version": "5.27.1",
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.27.1.tgz",
@ -7329,6 +7441,11 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@ -7771,6 +7888,14 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
},
"node_modules/slick-carousel": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz",
"integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==",
"peerDependencies": {
"jquery": ">=1.8.0"
}
},
"node_modules/slugify": {
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
@ -7820,6 +7945,11 @@
"resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz",
"integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w=="
},
"node_modules/string-convert": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
"integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A=="
},
"node_modules/string-width": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",

@ -84,6 +84,7 @@
"@types/luxon": "^3.4.2",
"@types/prismjs": "^1.26.4",
"@types/react-calendar-heatmap": "^1.6.7",
"@types/react-slick": "^0.23.13",
"@types/turndown": "^5.0.5",
"csv-parser": "^3.0.0",
"gh-pages": "^6.1.1",

@ -168,6 +168,9 @@ importers:
'@types/react-calendar-heatmap':
specifier: ^1.6.7
version: 1.6.7
'@types/react-slick':
specifier: ^0.23.13
version: 0.23.13
'@types/turndown':
specifier: ^5.0.5
version: 5.0.5
@ -1285,6 +1288,9 @@ packages:
'@types/react-dom@18.3.0':
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
'@types/react-slick@0.23.13':
resolution: {integrity: sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==}
'@types/react@18.3.8':
resolution: {integrity: sha512-syBUrW3/XpnW4WJ41Pft+I+aPoDVbrBVQGEnbD7NijDGlVC+8gV/XKRY+7vMDlfPpbwYt0l1vd/Sj8bJGMbs9Q==}
@ -4286,6 +4292,10 @@ snapshots:
dependencies:
'@types/react': 18.3.8
'@types/react-slick@0.23.13':
dependencies:
'@types/react': 18.3.8
'@types/react@18.3.8':
dependencies:
'@types/prop-types': 15.7.13

@ -1,7 +1,7 @@
---
import type { ChangelogFileType } from '../../lib/changelog';
import { DateTime } from 'luxon';
import MarkdownFile from '../MarkdownFile.astro';
import type { ChangelogFileType } from '../../lib/changelog';
import ChangelogImages from '../ChangelogImages';
interface Props {
changelog: ChangelogFileType;
@ -16,7 +16,9 @@ const formattedDate = DateTime.fromISO(frontmatter.date).toFormat(
---
<div class='relative'>
<span class='h-2 w-2 flex-shrink-0 rounded-full bg-gray-300 absolute top-2 -left-6'></span>
<span
class='absolute -left-6 top-2 h-2 w-2 flex-shrink-0 rounded-full bg-gray-300'
></span>
<div class='mb-3 flex items-center gap-2'>
<span class='flex-shrink-0 text-xs tracking-wide text-gray-400'>
@ -28,8 +30,14 @@ const formattedDate = DateTime.fromISO(frontmatter.date).toFormat(
</div>
<div class='rounded-xl border bg-white p-6'>
{frontmatter.images && (
<div class='mb-5 -mx-6'>
<ChangelogImages images={frontmatter.images} client:load />
</div>
)}
<div
class='prose prose-h2:text-lg prose-h2:font-medium prose-h2:mt-3 prose-sm prose-p:mb-0 prose-blockquote:font-normal prose-blockquote:text-gray-500 prose-ul:my-0 prose-img:mt-0 prose-img:rounded-lg [&>blockquote>p]:mt-0 prose-ul:bg-gray-100 prose-ul:rounded-lg prose-ul:px-4 prose-ul:py-4 prose-ul:pl-7 [&>ul>li]:my-0 [&>ul>li]:mb-1 [&>ul]:mt-3'
class='prose prose-sm prose-h2:mt-3 prose-h2:text-lg prose-h2:font-medium prose-p:mb-0 prose-blockquote:font-normal prose-blockquote:text-gray-500 prose-ul:my-0 prose-ul:rounded-lg prose-ul:bg-gray-100 prose-ul:px-4 prose-ul:py-4 prose-ul:pl-7 prose-img:mt-0 prose-img:rounded-lg [&>blockquote>p]:mt-0 [&>ul>li]:my-0 [&>ul>li]:mb-1 [&>ul]:mt-3'
>
<changelog.Content />
</div>

@ -0,0 +1,108 @@
import { ChevronLeft, ChevronRight, MoveRight } from 'lucide-react';
import React, { useState, useEffect, useCallback } from 'react';
interface ChangelogImagesProps {
images: { [key: string]: string };
}
const ChangelogImages: React.FC<ChangelogImagesProps> = ({ images }) => {
const [enlargedImage, setEnlargedImage] = useState<string | null>(null);
const imageArray = Object.entries(images);
const handleImageClick = (src: string) => {
setEnlargedImage(src);
};
const handleCloseEnlarged = () => {
setEnlargedImage(null);
};
const handleNavigation = useCallback(
(direction: 'prev' | 'next') => {
if (!enlargedImage) return;
const currentIndex = imageArray.findIndex(
([_, src]) => src === enlargedImage,
);
let newIndex;
if (direction === 'prev') {
newIndex = currentIndex > 0 ? currentIndex - 1 : imageArray.length - 1;
} else {
newIndex = currentIndex < imageArray.length - 1 ? currentIndex + 1 : 0;
}
setEnlargedImage(imageArray[newIndex][1]);
},
[enlargedImage, imageArray],
);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
handleCloseEnlarged();
} else if (event.key === 'ArrowLeft') {
handleNavigation('prev');
} else if (event.key === 'ArrowRight') {
handleNavigation('next');
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [handleNavigation]);
return (
<>
<div className="flex gap-3 px-6 pb-1">
{imageArray.map(([title, src]) => (
<div
key={title}
className="group relative cursor-pointer overflow-hidden rounded-lg transition hover:scale-105"
onClick={() => handleImageClick(src)}
>
<img
src={src}
alt={title}
className="h-[120px] w-full object-cover object-left-top"
/>
<span className="absolute group-hover:opacity-0 inset-0 bg-gradient-to-b from-transparent to-black/40" />
<div className="absolute font-medium inset-x-0 top-full group-hover:inset-y-0 flex items-center justify-center px-2 text-center text-xs bg-black/50 text-white py-0.5 opacity-0 group-hover:opacity-100">
<span className='bg-black py-0.5 rounded px-1'>{title}</span>
</div>
</div>
))}
</div>
{enlargedImage && (
<div
className="fixed inset-0 z-[999] flex items-center justify-center bg-black bg-opacity-75"
onClick={handleCloseEnlarged}
>
<img
src={enlargedImage}
alt="Enlarged view"
className="max-h-[90%] max-w-[90%] rounded-xl object-contain"
/>
<button
className="absolute left-4 top-1/2 -translate-y-1/2 transform rounded-full bg-white hover:bg-opacity-100 bg-opacity-50 p-2"
onClick={(e) => {
e.stopPropagation();
handleNavigation('prev');
}}
>
<ChevronLeft size={24} />
</button>
<button
className="absolute right-4 top-1/2 -translate-y-1/2 transform rounded-full bg-white hover:bg-opacity-100 bg-opacity-50 p-2"
onClick={(e) => {
e.stopPropagation();
handleNavigation('next');
}}
>
<ChevronRight size={24} />
</button>
</div>
)}
</>
);
};
export default ChangelogImages;

@ -1,6 +1,10 @@
---
title: 'New Dashboard, Leaderboards and Projects'
description: 'New leaderboard page showing the most active users'
images:
"Personal Dashboard": "https://assets.roadmap.sh/guest/personal-dashboard.png"
"Projects Page": "https://assets.roadmap.sh/guest/projects-page.png"
"Leaderboard": "https://assets.roadmap.sh/guest/leaderboard.png"
seo:
title: 'Leaderboard Page - roadmap.sh'
description: ''
@ -16,8 +20,6 @@ TL;DR: new dashboard, leaderboard page and projects page.
- Frontend and backend content improvements
- Bug fixes
![Leaderboard Page](https://assets.roadmap.sh/guest/personal-dashboard.png)
We just launched a dedicated dashboard for logged-in users to showing progress, projects, bookmarks and more. You can still access the old homepage by visiting [this page](https://roadmap.sh/home).
We also launched a new [leaderboard page](/leaderboard) showing the most active users, users who completed most projects and more.

@ -3,6 +3,7 @@ import type { MarkdownFileType } from './file';
export interface ChangelogFrontmatter {
title: string;
description: string;
images: Record<string, string>;
seo: {
title: string;
description: string;

@ -9,22 +9,22 @@ const allChangelogs = await getAllChangelogs();
<BaseLayout
title='Changelogs'
description='Changelogs for the updates and changes in the website'
description='Changelogs for the updates and changes to roadmap.sh'
permalink='/changelogs'
noIndex={true}
>
<div class='bg-gray-100'>
<div class='rounded-lg border-b bg-white text-left'>
<div class='mx-auto max-w-[500px] pt-12 pb-10 text-center'>
<h1 class='mb-3 text-4xl font-semibold'>Changelog</h1>
<p>Here is everything we have been shipping recently</p>
<div class='bg-gray-100 px-4'>
<div class='border-b bg-white text-left -mx-4'>
<div class='mx-auto max-w-[500px] py-5 sm:pb-10 sm:pt-12 text-left px-4 sm:text-center'>
<h1 class='mb-0.5 sm:mb-3 text-2xl sm:text-4xl font-semibold'>Changelog</h1>
<p>We are constantly improving and updating roadmap.sh</p>
</div>
</div>
<div class='relative mx-auto max-w-[500px] pb-8 pt-4'>
<div class='relative mx-auto max-w-[600px] pb-8 pt-4'>
<div class='space-y-6'>
<div
class='absolute inset-y-0 -left-5 w-px -translate-x-[0.5px] bg-gray-300'
class='absolute sm:block hidden inset-y-0 -left-5 w-px -translate-x-[0.5px] bg-gray-300'
>
</div>

Loading…
Cancel
Save