From 13a842ed1d5a86b55499dd21d9b9f3658a0de636 Mon Sep 17 00:00:00 2001 From: Tanyawat Vittaypalotai Date: Fri, 5 Jul 2024 17:59:54 +0900 Subject: [PATCH] feat: add events page --- apps/astro/eslint.config.js | 16 +-- apps/astro/package.json | 5 +- apps/astro/panda.config.ts | 2 +- .../src/components/layout/Navigation.astro | 1 + apps/astro/src/components/lib/Markdown.tsx | 128 +++++++++++------- apps/astro/src/env.d.ts | 1 + .../src/pages/[locale]/events/index.astro | 77 +++++++++++ apps/astro/tsconfig.json | 2 +- eslint.config.js | 4 +- libs/i18n/en/common.json | 1 + libs/i18n/ja/common.json | 1 + libs/outline/article.ts | 1 + libs/utils/markdown.ts | 14 ++ pnpm-lock.yaml | 46 ++++--- 14 files changed, 217 insertions(+), 82 deletions(-) create mode 100644 apps/astro/src/pages/[locale]/events/index.astro create mode 100644 libs/utils/markdown.ts diff --git a/apps/astro/eslint.config.js b/apps/astro/eslint.config.js index d37ed8a..4316598 100644 --- a/apps/astro/eslint.config.js +++ b/apps/astro/eslint.config.js @@ -37,16 +37,16 @@ const config = tseslint.config( '@typescript-eslint/no-unsafe-call': 'off', '@typescript-eslint/no-unsafe-argument': 'off', '@typescript-eslint/no-unsafe-return': 'off', - "@typescript-eslint/no-unsafe-enum-comparison": "off" + '@typescript-eslint/no-unsafe-enum-comparison': 'off' + } + }, + ...compat.config({ extends: ['plugin:@pandacss/recommended'] }), + { + rules: { + '@pandacss/no-unsafe-token-fn-usage': 'off', + '@pandacss/no-hardcoded-color': 'off' } }, - // ...compat.config({ extends: ['plugin:@pandacss/recommended'] }), - // { - // rules: { - // '@pandacss/no-unsafe-token-fn-usage': 'off', - // '@pandacss/no-hardcoded-color': 'off' - // } - // }, ...eslintPluginAstro.configs['flat/recommended'], { files: ['**/*.astro', '**/*.ts', '**/*.tsx', '**/*.js'], diff --git a/apps/astro/package.json b/apps/astro/package.json index 5f2d89a..f06531c 100644 --- a/apps/astro/package.json +++ b/apps/astro/package.json @@ -36,7 +36,10 @@ "react-markdown": "^9.0.1", "remark-gfm": "^4.0.0", "remark-textr": "^6.1.0", - "typescript": "^5.5.3" + "typescript": "^5.5.3", + "mdast-util-from-markdown": "^2.0.1", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0" }, "devDependencies": { "@pandabox/prettier-plugin": "^0.1.3", diff --git a/apps/astro/panda.config.ts b/apps/astro/panda.config.ts index eec6dad..9f5b3ca 100644 --- a/apps/astro/panda.config.ts +++ b/apps/astro/panda.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ accentColor: 'amber', grayColor: 'neutral', borderRadius: 'lg', - additionalColors: ['red', 'blue', 'orange', 'green', 'purple', 'gray'] + additionalColors: ['red', 'blue', 'orange', 'green', 'purple', 'gray', 'sand'] }) ], diff --git a/apps/astro/src/components/layout/Navigation.astro b/apps/astro/src/components/layout/Navigation.astro index d1e0fc0..9208f2c 100644 --- a/apps/astro/src/components/layout/Navigation.astro +++ b/apps/astro/src/components/layout/Navigation.astro @@ -22,6 +22,7 @@ const NAVIGATION_LINKS = [ { label: t('common.home'), value: '/' }, { label: t('common.about-me'), value: '/about' }, { label: t('common.projects'), value: '/projects' }, + { label: t('common.event'), value: '/events' }, { label: t('common.note'), value: '/notes' }, { label: t('common.contact'), value: '/contact' } ]; diff --git a/apps/astro/src/components/lib/Markdown.tsx b/apps/astro/src/components/lib/Markdown.tsx index f6d71f9..02e603d 100644 --- a/apps/astro/src/components/lib/Markdown.tsx +++ b/apps/astro/src/components/lib/Markdown.tsx @@ -1,63 +1,91 @@ import { Code } from 'astro:components'; +import { join } from 'path'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import remarkTextr from 'remark-textr'; -import { Divider, styled } from 'styled-system/jsx'; +import { Divider, Stack, styled } from 'styled-system/jsx'; import { Heading } from '../ui/heading'; import { Link } from '../ui/link'; import * as Table from '../ui/table'; import { Text } from '../ui/text'; // https://github.com/remarkjs/react-markdown -export const Markdown = ({ content, assetsPrefix }: { content: string; assetsPrefix?: string }) => { +export const Markdown = ({ + content, + assetsPrefix, + linksPrefix, + disableLinks, + disableInternalLinks +}: { + content: string; + assetsPrefix?: string; + linksPrefix?: string; + disableLinks?: string; + disableInternalLinks?: boolean; +}) => { return ( - , - h2: ({ node: _, ...props }) => , - h3: ({ node: _, ...props }) => , - h4: ({ node: _, ...props }) => , - h5: ({ node: _, ...props }) => , - h6: ({ node: _, ...props }) => , - p: ({ ref: _ref, node: _, ...props }) => , - strong: ({ ref: _, node: __, ...props }) => , - a: ({ ref: _, node: __, ...props }) => , - hr: ({ ref: _, node: __, ...props }) => , - blockquote: ({ ref: __, node: _, ...props }) => ( - - ), - ul: ({ ref: _, node: __, ...props }) => ( - - ), - ol: ({ ref: _, node: __, ...props }) => ( - - ), - li: ({ ref: _, node: __, ...props }) => , - code: ({ ref: _, node: __, lang, ...props }) => ( - - ), - table: ({ ref: _, node: __, ...props }) => , - thead: ({ ref: _, node: __, ...props }) => , - th: ({ ref: _, node: __, ...props }) => , - tbody: ({ ref: _, node: __, ...props }) => , - tr: ({ ref: _, node: __, ...props }) => , - td: ({ ref: _, node: __, ...props }) => , - img: ({ ref: _, node: __, ...props }) => { - const rawUrl = props.src; - const url = rawUrl?.[0] === '/' && assetsPrefix ? `${assetsPrefix}${rawUrl}` : rawUrl; - return ; - } - }} - > - {/* {data} */} - {content} - + + ( + + ), + h2: ({ node: _, ...props }) => , + h3: ({ node: _, ...props }) => ( + + ), + h4: ({ node: _, ...props }) => , + h5: ({ node: _, ...props }) => , + h6: ({ node: _, ...props }) => , + p: ({ ref: _ref, node: _, ...props }) => , + strong: ({ ref: _, node: __, ...props }) => ( + + ), + a: ({ ref: _, node: __, ...props }) => { + const { href, ...propsWithoutHref } = props; + const dest = linksPrefix && href?.startsWith('/') ? join(linksPrefix, href) : href; + if (disableLinks || (disableInternalLinks && href?.startsWith('/'))) { + return {props.children}; + } + return ; + }, + hr: ({ ref: _, node: __, ...props }) => , + blockquote: ({ ref: __, node: _, ...props }) => ( + + ), + ul: ({ ref: _, node: __, ...props }) => ( + + ), + ol: ({ ref: _, node: __, ...props }) => ( + + ), + li: ({ ref: _, node: __, ...props }) => , + code: ({ ref: _, node: __, lang, ...props }) => ( + + ), + table: ({ ref: _, node: __, ...props }) => , + thead: ({ ref: _, node: __, ...props }) => , + th: ({ ref: _, node: __, ...props }) => , + tbody: ({ ref: _, node: __, ...props }) => , + tr: ({ ref: _, node: __, ...props }) => , + td: ({ ref: _, node: __, ...props }) => , + img: ({ ref: _, node: __, ...props }) => { + const rawUrl = props.src; + const url = rawUrl?.[0] === '/' && assetsPrefix ? `${assetsPrefix}${rawUrl}` : rawUrl; + return ; + } + }} + > + {/* {data} */} + {content} + + ); }; diff --git a/apps/astro/src/env.d.ts b/apps/astro/src/env.d.ts index dcf9ba7..78c7e2d 100644 --- a/apps/astro/src/env.d.ts +++ b/apps/astro/src/env.d.ts @@ -6,6 +6,7 @@ interface ImportMetaEnv { readonly PRIVATE_BACKEND_API_URL: string; readonly PRIVATE_OUTLINE_SERVER: string; readonly PRIVATE_OUTLINE_API_TOKEN: string; + readonly PRIVATE_OUTLINE_SETTINGS_DOCUMENT_ID: string; // more env variables... } diff --git a/apps/astro/src/pages/[locale]/events/index.astro b/apps/astro/src/pages/[locale]/events/index.astro new file mode 100644 index 0000000..f984e1a --- /dev/null +++ b/apps/astro/src/pages/[locale]/events/index.astro @@ -0,0 +1,77 @@ +--- +import { localePaths } from '~/i18n/paths'; + +import MainLayout from '~/layouts/MainLayout.astro'; + +import type { Link, Table, Text } from 'mdast'; +import { cleanArticleContent } from 'outline/article'; +import { Container, Stack } from 'styled-system/jsx'; +import { parseMarkdown } from 'utils/markdown'; +import { Markdown } from '~/components/lib/Markdown'; +import { Heading } from '~/components/ui/heading'; +import { useTranslations } from '~/i18n/utils'; +import { outlineClient } from '~/utils/outline-api'; + +export function getStaticPaths() { + return localePaths; +} + +const { locale } = Astro.params; +const t = useTranslations(locale); + +const events = await outlineClient.POST('/documents.info', { + body: { id: import.meta.env.PRIVATE_OUTLINE_SETTINGS_DOCUMENT_ID } +}); + +const settingsContent = cleanArticleContent(events.data?.data?.text); + +if (!settingsContent) { + return Astro.redirect(404); +} + +const tree = await parseMarkdown(settingsContent ?? ''); + +const settings = Object.fromEntries( + tree.children + ?.find((c): c is Table => c.type === 'table') + ?.children.slice(1) + .map((c) => [ + (c.children[0].children[0] as Text)?.value, + (c.children[1].children[0] as Link)?.url ?? (c.children[1].children[0] as Text)?.value + ]) as [string, string][] +); + +if (!settings.events) { + return Astro.redirect(404); +} + +const eventData = await outlineClient.POST('/documents.info', { + body: { id: settings.events.replace('/doc/', '') } +}); +const content = cleanArticleContent(eventData.data?.data?.text); + +if (!content) { + return Astro.redirect(404); +} +--- + + + + + {t('common.event')} + + + + + + diff --git a/apps/astro/tsconfig.json b/apps/astro/tsconfig.json index 2a7b0ff..b12e737 100644 --- a/apps/astro/tsconfig.json +++ b/apps/astro/tsconfig.json @@ -15,5 +15,5 @@ "verbatimModuleSyntax": false, "disableSizeLimit": true }, - "include": ["src", "styled-system", "./panda.config.ts", "../../libs"] + "include": ["src", "styled-system", "./panda.config.ts"] } diff --git a/eslint.config.js b/eslint.config.js index a578abd..8a03462 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -8,7 +8,7 @@ const compat = new FlatCompat({ recommendedConfig: js.configs.recommended }); -module.exports = [ +module.exports = tseslint.config( { plugins: { '@nx': nxEslintPlugin } }, { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], @@ -36,4 +36,4 @@ module.exports = [ '@typescript-eslint/triple-slash-reference': 'off' } } -]; +); diff --git a/libs/i18n/en/common.json b/libs/i18n/en/common.json index 27b7fe2..9600e3c 100644 --- a/libs/i18n/en/common.json +++ b/libs/i18n/en/common.json @@ -7,6 +7,7 @@ "blog": "Blog", "hobbies": "Hobbies", "contact": "Contact", + "event": "Events", "experiences": "Experiences", "education": "Education", "note": "Notes", diff --git a/libs/i18n/ja/common.json b/libs/i18n/ja/common.json index 44d5f15..b87255d 100644 --- a/libs/i18n/ja/common.json +++ b/libs/i18n/ja/common.json @@ -10,6 +10,7 @@ "education": "学歴", "note": "日記", "contact": "お問い合わせ", + "event": "イベント", "tags": "タグ", "under-construction": "準備中", "page-not-found": "ページが見つからないようです", diff --git a/libs/outline/article.ts b/libs/outline/article.ts index 84cfb7d..d93baa8 100644 --- a/libs/outline/article.ts +++ b/libs/outline/article.ts @@ -3,6 +3,7 @@ import removeMarkdown from 'remove-markdown'; export const cleanArticleContent = (content?: string) => { return content?.replaceAll('\\\n', '')?.replaceAll('\\n', '\n\n'); }; + export const getArticleBanner = (content?: string) => { return ( /!\[(.*)\]\((.+?)(?:".*?")?\)/ diff --git a/libs/utils/markdown.ts b/libs/utils/markdown.ts new file mode 100644 index 0000000..e568709 --- /dev/null +++ b/libs/utils/markdown.ts @@ -0,0 +1,14 @@ +import { Root } from 'mdast'; + +export async function parseMarkdown(text: string) { + const { fromMarkdown } = await import('mdast-util-from-markdown'); + const { gfmFromMarkdown } = await import('mdast-util-gfm'); + const { gfm } = await import('micromark-extension-gfm'); + + const tree: Root = fromMarkdown(text, { + extensions: [gfm()], + mdastExtensions: [gfmFromMarkdown()] + }); + + return tree; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a438e4..104bc11 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -232,6 +232,15 @@ importers: fets: specifier: ^0.8.1 version: 0.8.1 + mdast-util-from-markdown: + specifier: ^2.0.1 + version: 2.0.1 + mdast-util-gfm: + specifier: ^3.0.0 + version: 3.0.0 + micromark-extension-gfm: + specifier: ^3.0.0 + version: 3.0.0 react: specifier: ^18.3.1 version: 18.3.1 @@ -5579,7 +5588,7 @@ packages: engines: {node: '>= 8.0.0'} deprecated: '**IMPORTANT 10x+ PERFORMANCE UPGRADE**: Please upgrade to v12.0.1+ as we have fixed an issue with debuglog causing 10x slower router benchmark performance, see https://github.com/koajs/router/pull/173' dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@9.4.0) http-errors: 1.8.1 koa-compose: 4.1.0 methods: 1.1.2 @@ -9490,7 +9499,7 @@ packages: dependencies: '@strapi/utils': 4.25.2 date-fns: 2.30.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 fs-extra: 10.0.0 knex: 2.5.0(better-sqlite3@9.4.5)(pg@8.12.0) lodash: 4.17.21 @@ -10317,7 +10326,7 @@ packages: concurrently: 8.2.2 configstore: 5.0.1 copyfiles: 2.4.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 delegates: 1.0.0 dotenv: 14.2.0 execa: 5.1.1 @@ -10574,7 +10583,7 @@ packages: vite: ^5.0.0 dependencies: '@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.8) - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.9 @@ -11302,7 +11311,7 @@ packages: '@typescript-eslint/type-utils': 7.6.0(eslint@9.0.0)(typescript@5.4.4) '@typescript-eslint/utils': 7.6.0(eslint@9.0.0)(typescript@5.4.4) '@typescript-eslint/visitor-keys': 7.6.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 eslint: 9.0.0 graphemer: 1.4.0 ignore: 5.3.1 @@ -11369,7 +11378,7 @@ packages: '@typescript-eslint/types': 7.6.0 '@typescript-eslint/typescript-estree': 7.6.0(typescript@5.4.4) '@typescript-eslint/visitor-keys': 7.6.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 eslint: 9.0.0 typescript: 5.4.4 transitivePeerDependencies: @@ -16143,7 +16152,7 @@ packages: ms: 2.1.3 dev: false - /debug@4.3.4(supports-color@5.5.0): + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -16153,7 +16162,6 @@ packages: optional: true dependencies: ms: 2.1.2 - supports-color: 5.5.0 /debug@4.3.5(supports-color@5.5.0): resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} @@ -16979,7 +16987,7 @@ packages: peerDependencies: esbuild: '>=0.12 <1' dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@9.4.0) esbuild: 0.19.11 transitivePeerDependencies: - supports-color @@ -17336,7 +17344,7 @@ packages: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.0.0) '@jridgewell/sourcemap-codec': 1.4.15 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 eslint: 9.0.0 eslint-compat-utils: 0.5.0(eslint@9.0.0) esutils: 2.0.3 @@ -17444,7 +17452,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 escape-string-regexp: 4.0.0 eslint-scope: 8.0.1 eslint-visitor-keys: 4.0.0 @@ -18062,7 +18070,7 @@ packages: debug: optional: true dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -21029,7 +21037,7 @@ packages: dependencies: '@types/express': 4.17.21 '@types/jsonwebtoken': 9.0.6 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@9.4.0) jose: 4.15.9 limiter: 1.1.5 lru-memoizer: 2.3.0 @@ -21126,7 +21134,7 @@ packages: better-sqlite3: 9.4.5 colorette: 2.0.19 commander: 10.0.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 escalade: 3.1.2 esm: 3.2.25 get-package-type: 0.1.0 @@ -21203,7 +21211,7 @@ packages: /koa-ip@2.1.3: resolution: {integrity: sha512-QLVBByImwDq9enZXVOD3Astk876B7N0IYta7Kik4iyNB462rVzBB1/LD0Ek1F+v9nGUTHBFyhh8043EIlskK9Q==} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 lodash.isplainobject: 4.0.6 request-ip: 3.3.0 transitivePeerDependencies: @@ -21244,7 +21252,7 @@ packages: engines: {node: '>=8.0.0'} dependencies: crc: 3.8.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@9.4.0) is-type-of: 1.4.0 uuid: 8.3.2 transitivePeerDependencies: @@ -23197,7 +23205,7 @@ packages: hasBin: true dependencies: chokidar: 3.6.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) ignore-by-default: 1.0.1 minimatch: 3.1.2 pstree.remy: 1.1.8 @@ -29182,7 +29190,7 @@ packages: '@vite-pwa/assets-generator': optional: true dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 fast-glob: 3.3.2 pretty-bytes: 6.1.1 vite: 5.2.8(@types/node@20.12.7)(sass@1.74.1) @@ -29400,7 +29408,7 @@ packages: '@vitest/utils': 1.4.0 acorn-walk: 8.3.2 chai: 4.4.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4 execa: 8.0.1 local-pkg: 0.5.0 magic-string: 0.30.9