feat: implement leaderboard page

feat/changelog
Arik Chakma 2 months ago
parent 989f7ad5c1
commit fb9031e939
  1. 2
      package.json
  2. 17
      pnpm-lock.yaml
  3. 34
      src/components/Changelog/ChangelogItem.astro
  4. 13
      src/components/MarkdownFile.astro
  5. 69
      src/lib/changelog.ts
  6. 29
      src/pages/changelogs.astro

@ -50,6 +50,7 @@
"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",
@ -80,6 +81,7 @@
"@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/turndown": "^5.0.5",

@ -71,6 +71,9 @@ importers:
lucide-react:
specifier: ^0.419.0
version: 0.419.0(react@18.3.1)
luxon:
specifier: ^3.5.0
version: 3.5.0
nanoid:
specifier: ^5.0.7
version: 5.0.7
@ -156,6 +159,9 @@ importers:
'@types/js-cookie':
specifier: ^3.0.6
version: 3.0.6
'@types/luxon':
specifier: ^3.4.2
version: 3.4.2
'@types/prismjs':
specifier: ^1.26.4
version: 1.26.4
@ -1114,6 +1120,9 @@ packages:
'@types/js-cookie@3.0.6':
resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
'@types/luxon@3.4.2':
resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==}
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
@ -2055,6 +2064,10 @@ packages:
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
luxon@3.5.0:
resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==}
engines: {node: '>=12'}
magic-string@0.30.11:
resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
@ -4070,6 +4083,8 @@ snapshots:
'@types/js-cookie@3.0.6': {}
'@types/luxon@3.4.2': {}
'@types/mdast@4.0.4':
dependencies:
'@types/unist': 3.0.2
@ -5074,6 +5089,8 @@ snapshots:
dependencies:
react: 18.3.1
luxon@3.5.0: {}
magic-string@0.30.11:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0

@ -0,0 +1,34 @@
---
import type { ChangelogFileType } from '../../lib/changelog';
import { DateTime } from 'luxon';
import MarkdownFile from '../MarkdownFile.astro';
interface Props {
changelog: ChangelogFileType;
}
const { changelog } = Astro.props;
const { frontmatter } = changelog;
const formattedDate = DateTime.fromISO(frontmatter.date).toFormat(
'dd LLLL, yyyy',
);
---
<div class='grid gap-4 py-10 sm:grid-cols-4'>
<div class='sticky top-8 col-span-1 mt-1.5 hidden self-start sm:block'>
<span class='text-sm text-gray-600'>{formattedDate}</span>
</div>
<div class='col-span-3'>
<h2 class='text-3xl font-bold text-gray-900 sm:mb-4'>
{changelog.frontmatter.title}
</h2>
<span class='mb-6 mt-3 block text-sm text-gray-600 sm:hidden'
>{formattedDate}</span
>
<MarkdownFile class='p-0'>
<changelog.Content />
</MarkdownFile>
</div>
</div>

@ -1,5 +1,16 @@
---
interface Props {
class?: string;
}
const { class: className } = Astro.props;
---
<div
class='container prose-h2:text-balance prose-h3:text-balance prose-h4:text-balance prose-h5:text-balance prose prose-xl prose-h2:mb-3 prose-h2:mt-10 prose-h2:scroll-mt-5 prose-h2:text-3xl prose-h3:mt-2 prose-h3:scroll-mt-5 prose-h5:font-medium prose-blockquote:font-normal prose-code:bg-transparent prose-img:mt-1 prose-h2:sm:scroll-mt-10 prose-h3:sm:scroll-mt-10'
class:list={[
'container prose prose-xl prose-h2:mb-3 prose-h2:mt-10 prose-h2:scroll-mt-5 prose-h2:text-balance prose-h2:text-3xl prose-h3:mt-2 prose-h3:scroll-mt-5 prose-h3:text-balance prose-h4:text-balance prose-h5:text-balance prose-h5:font-medium prose-blockquote:font-normal prose-code:bg-transparent prose-img:mt-1 prose-h2:sm:scroll-mt-10 prose-h3:sm:scroll-mt-10',
className,
]}
>
<slot />
</div>

@ -0,0 +1,69 @@
import type { MarkdownFileType } from './file';
export interface ChangelogFrontmatter {
title: string;
description: string;
seo: {
title: string;
description: string;
};
date: string;
}
export type ChangelogFileType = MarkdownFileType<ChangelogFrontmatter> & {
id: string;
};
/**
* Generates id from the given changelog file
* @param filePath Markdown file path
*
* @returns unique changelog identifier
*/
function changelogPathToId(filePath: string): string {
const fileName = filePath.split('/').pop() || '';
return fileName.replace('.md', '');
}
/**
* Gets all the changelogs sorted by the publishing date
* @returns Promisifed guide files
*/
export async function getAllChangelogs(): Promise<ChangelogFileType[]> {
// @ts-ignore
const changelogs = import.meta.glob<ChangelogFileType>(
'/src/data/changelogs/*.md',
{
eager: true,
},
);
const changelogFiles = Object.values(changelogs) as ChangelogFileType[];
const enrichedChangelogs: ChangelogFileType[] = changelogFiles.map(
(changelogFile) => ({
...changelogFile,
id: changelogPathToId(changelogFile.file),
}),
);
return enrichedChangelogs.sort(
(a, b) =>
new Date(b.frontmatter.date).valueOf() -
new Date(a.frontmatter.date).valueOf(),
);
}
/**
* Gets the changelog by the given id
* @param id Changelog identifier
* @returns Promisified changelog file
*/
export async function getChangelogById(
id: string,
): Promise<ChangelogFileType | undefined> {
const allChangelogs = await getAllChangelogs();
return allChangelogs.find((changelog) => changelog.id === id);
}

@ -0,0 +1,29 @@
---
import SimplePageHeader from '../components/SimplePageHeader.astro';
import BaseLayout from '../layouts/BaseLayout.astro';
import { getAllChangelogs } from '../lib/changelog';
import ChangelogItem from '../components/Changelog/ChangelogItem.astro';
const allChangelogs = await getAllChangelogs();
---
<BaseLayout
title='Changelogs'
description='Changelogs for the updates and changes in the website'
permalink='/changelogs'
>
<SimplePageHeader
title='Changelogs'
description='Changelogs for the updates and changes in the website'
/>
<div class='bg-gray-100 pb-14 pt-4 sm:pb-16 sm:pt-8'>
<div class='container divide-y divide-gray-300'>
{
allChangelogs.map((changelog) => (
<ChangelogItem changelog={changelog} />
))
}
</div>
</div>
</BaseLayout>
Loading…
Cancel
Save