From cd18943e5110b916e1f81a4d651547fd2ab7e110 Mon Sep 17 00:00:00 2001 From: over-vlad Date: Wed, 11 Dec 2024 18:28:53 +0100 Subject: [PATCH] feat: add notion articles on home page --- src/app/page.tsx | 39 ++++++++++++------- src/components/ui/card.tsx | 76 ++++++++++++++++++++++++++++++++++++++ src/lib/notion/mapper.ts | 11 ++++++ src/lib/notion/notion.ts | 12 +++--- src/lib/notion/schemas.ts | 9 ++++- src/lib/notion/types.ts | 7 ++++ 6 files changed, 134 insertions(+), 20 deletions(-) create mode 100644 src/components/ui/card.tsx create mode 100644 src/lib/notion/mapper.ts create mode 100644 src/lib/notion/types.ts diff --git a/src/app/page.tsx b/src/app/page.tsx index 50f34ed..231f2e3 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,8 +1,16 @@ -import { getNotionPages } from "@/lib/notion/notion"; +import { getArticles } from "@/lib/notion/notion"; import Link from "next/link"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; export default async function Home() { - const posts = await getNotionPages({ pageSize: 10 }); + const articles = await getArticles({ pageSize: 10 }); return (
@@ -11,19 +19,24 @@ export default async function Home() { Франция | Гайд по иммиграции и интеграции -
- {posts.map((post) => ( +
+ {articles.map((article) => ( -

- {post.properties.Page.title[0].plain_text} -

-

- {new Date(post.created_time).toLocaleDateString()} -

+ + + {article.title} + + + {article.description} + + + {new Date(article.publishedAt).toLocaleDateString()} + + ))}
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..cabfbfc --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,76 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/src/lib/notion/mapper.ts b/src/lib/notion/mapper.ts new file mode 100644 index 0000000..a17a866 --- /dev/null +++ b/src/lib/notion/mapper.ts @@ -0,0 +1,11 @@ +import { NotionPage } from "./schemas"; + +export function mapNotionPageToArticle(page: NotionPage): Article { + return { + id: page.id, + title: page.properties.Page.title[0]?.plain_text || "", + description: page.properties.Description.rich_text[0]?.plain_text || "", + slug: page.properties.URL.url, + publishedAt: new Date(page.properties["Publish date"]?.date?.start || ""), + }; +} diff --git a/src/lib/notion/notion.ts b/src/lib/notion/notion.ts index 091811f..6815b3e 100644 --- a/src/lib/notion/notion.ts +++ b/src/lib/notion/notion.ts @@ -1,21 +1,22 @@ import { Client } from "@notionhq/client"; import { validateEnv } from "../env"; -import { NotionPages, pagesSchema } from "./schemas"; +import { pagesSchema } from "./schemas"; import { withNotionErrorHandling } from "./errorHandlers"; +import { mapNotionPageToArticle } from "./mapper"; const env = validateEnv(); const notion = new Client({ auth: env.NOTION_TOKEN, }); -export const getNotionPages = async ({ +export const getArticles = async ({ pageSize, startCursor, }: { pageSize: number; startCursor?: string; }) => { - return withNotionErrorHandling(async () => { + return withNotionErrorHandling(async () => { const response = await notion.databases.query({ database_id: env.NOTION_DATABASE_ID, filter: { @@ -33,7 +34,8 @@ export const getNotionPages = async ({ page_size: pageSize, start_cursor: startCursor, }); - console.log(JSON.stringify(response.results)); - return pagesSchema.parse(response.results); + const parsedNotionPages = pagesSchema.parse(response.results); + + return parsedNotionPages.map(mapNotionPageToArticle); }, "Failed to fetch pages"); }; diff --git a/src/lib/notion/schemas.ts b/src/lib/notion/schemas.ts index 8c8d6eb..14f6e97 100644 --- a/src/lib/notion/schemas.ts +++ b/src/lib/notion/schemas.ts @@ -16,7 +16,7 @@ const multiSelectOptionSchema = z.object({ color: z.string(), }); -const titleTextSchema = z.object({ +const textSchema = z.object({ type: z.string(), text: z .object({ @@ -78,7 +78,12 @@ const pagePropertiesSchema = z.object({ Page: z.object({ id: z.literal("title"), type: z.literal("title"), - title: z.array(titleTextSchema), + title: z.array(textSchema), + }), + Description: z.object({ + id: z.string(), + type: z.literal("rich_text"), + rich_text: z.array(textSchema), }), Owner: z.object({ id: z.string(), diff --git a/src/lib/notion/types.ts b/src/lib/notion/types.ts new file mode 100644 index 0000000..c941e86 --- /dev/null +++ b/src/lib/notion/types.ts @@ -0,0 +1,7 @@ +type Article = { + id: string; + title: string; + description: string; + slug: string; + publishedAt: Date; +};