diff --git a/app/blog/[slug]/not-found.tsx b/app/blog/[slug]/not-found.tsx
index ae1991c..dfdcf37 100644
--- a/app/blog/[slug]/not-found.tsx
+++ b/app/blog/[slug]/not-found.tsx
@@ -1,13 +1,15 @@
-import Link from "next/link";
+import Link from 'next/link';
-export default function NotFound() {
+const NotFound = () => {
return (
Post Not Found
-
+
←
Go to list page
);
-}
+};
+
+export default NotFound;
diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx
index 089761d..268b739 100644
--- a/app/blog/[slug]/page.tsx
+++ b/app/blog/[slug]/page.tsx
@@ -1,20 +1,16 @@
-import { Metadata } from "next";
-import Link from "next/link";
-import { notFound } from "next/navigation";
+import { Metadata } from 'next';
+import Link from 'next/link';
+import { notFound } from 'next/navigation';
-import { getRecordMap } from "../../../lib/notion";
-import { getAllPostsFromNotion } from "../../../lib/posts";
-import { Post } from "../../../types/post";
-import { PostPageContainer } from "../../../components/posts/custom-container";
+import { getRecordMap } from '../../../lib/notion';
+import { getPostsFromNotion } from '../../../lib/posts';
+import PostView from '../../../components/posts/post-view';
+import type { Post } from '../../../types/post';
-export default async function PostPage({
- params: { slug },
-}: {
- params: { slug: string };
-}) {
- const allPosts = await getAllPostsFromNotion();
+const PostPage = async ({ params: { slug } }: { params: { slug: string } }) => {
+ const posts = await getPostsFromNotion();
- const post = allPosts.find((p) => p.slug === slug);
+ const post = posts.find((p) => p.slug === slug);
if (!post) {
return notFound();
}
@@ -23,7 +19,7 @@ export default async function PostPage({
return (
Post Not Found
-
+
←
Go to list page
@@ -31,7 +27,7 @@ export default async function PostPage({
);
}
- const relatedPosts: Post[] = allPosts.filter(
+ const relatedPosts: Post[] = posts.filter(
(p) =>
p.slug !== slug &&
p.published &&
@@ -41,33 +37,31 @@ export default async function PostPage({
const recordMap = await getRecordMap(post.id);
return (
-
+
);
-}
+};
-export async function generateStaticParams() {
- const allPosts = await getAllPostsFromNotion();
+export const generateStaticParams = async () => {
+ const posts = await getPostsFromNotion();
- return allPosts.map((post) => ({
+ return posts.map((post) => ({
slug: post.slug,
}));
-}
+};
-export async function generateMetadata({
+export const generateMetadata = async ({
params: { slug },
}: {
params: { slug: string };
-}): Promise {
- const allPosts = await getAllPostsFromNotion();
- const post = allPosts.find((p) => p.slug === slug);
+}): Promise => {
+ const posts = await getPostsFromNotion();
+ const post = posts.find((p) => p.slug === slug);
return post
? {
title: post.title,
}
: {};
-}
+};
+
+export default PostPage;
diff --git a/app/blog/page.tsx b/app/blog/page.tsx
index 977a98f..9c2c9bd 100644
--- a/app/blog/page.tsx
+++ b/app/blog/page.tsx
@@ -1,10 +1,10 @@
-import { PostListContainer } from '../../components/posts/custom-container';
-import { getAllPostsFromNotion } from '../../lib/posts';
+import PostList from '../../components/posts/post-list';
+import { getPostsFromNotion } from '../../lib/posts';
const Blog = async () => {
- const posts = await getAllPostsFromNotion();
+ const posts = await getPostsFromNotion();
- return ;
+ return ;
};
export default Blog;
diff --git a/app/layout.tsx b/app/layout.tsx
index 7ff5a8b..425653c 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,25 +1,21 @@
-import { Analytics } from "@vercel/analytics/react";
-import { SpeedInsights } from "@vercel/speed-insights/next";
-import CustomProviders from "../components/custom-providers";
-import "./globals.css";
+import { Analytics } from '@vercel/analytics/react';
+import { SpeedInsights } from '@vercel/speed-insights/next';
+import CustomProvider from '../components/custom-provider';
+import './globals.css';
-const theme = "dark";
-
-export default function RootLayout({
- children,
-}: {
- children: React.ReactNode;
-}) {
- // NOTE: suppressHydrationWarning is a hack to avoid warnings
- // see: https://legacy.reactjs.org/docs/dom-elements.html#suppresshydrationwarning:~:text=It%20only%20works%20one%20level%20deep
+// NOTE: suppressHydrationWarning is a hack to avoid warnings
+// see: https://legacy.reactjs.org/docs/dom-elements.html#suppresshydrationwarning:~:text=It%20only%20works%20one%20level%20deep
+const RootLayout = ({ children }: { children: React.ReactNode }) => {
return (
-
+
- {children}
+ {children}
);
-}
+};
+
+export default RootLayout;
diff --git a/app/not-found.tsx b/app/not-found.tsx
index 89c8683..9272e20 100644
--- a/app/not-found.tsx
+++ b/app/not-found.tsx
@@ -1,4 +1,4 @@
-import Link from "next/link";
+import Link from 'next/link';
const NotFound = () => {
return (
@@ -6,7 +6,7 @@ const NotFound = () => {
Not Found
Could not find requested resource
- Go back to Home
+ Go back to Home
>
);
diff --git a/app/page.tsx b/app/page.tsx
index 20c2846..e19994c 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,23 +1,23 @@
-import type { NextPage, Metadata } from "next";
+import type { NextPage, Metadata } from 'next';
-import About from "../components/about";
-import Experience from "../components/experience";
-import Projects from "../components/projects";
+import About from '../components/about';
+import Experience from '../components/experience';
+import Projects from '../components/projects';
export const metadata: Metadata = {
- title: "Yuta Kusuno Portfolio",
+ title: 'Yuta Kusuno',
};
const Home: NextPage = () => {
return (
<>
-
+
-
+
-
+
>
diff --git a/components/about.tsx b/components/about.tsx
index 1903d56..101d689 100644
--- a/components/about.tsx
+++ b/components/about.tsx
@@ -1,8 +1,8 @@
-import { Container } from "@chakra-ui/react";
+import { Container } from '@chakra-ui/react';
-import { NotionProfilePage } from "./notion-profile-page";
-import { notion } from "../lib/notion";
-import { notionAboutPageId } from "../lib/config";
+import { NotionProfilePage } from './notion/notion-profile-page';
+import { notion } from '../lib/notion';
+import { notionAboutPageId } from '../lib/config';
const About = async () => {
if (!notionAboutPageId) return null;
@@ -12,10 +12,10 @@ const About = async () => {
return (
diff --git a/components/custom-providers.tsx b/components/custom-provider.tsx
similarity index 90%
rename from components/custom-providers.tsx
rename to components/custom-provider.tsx
index accb08b..7d57c04 100644
--- a/components/custom-providers.tsx
+++ b/components/custom-provider.tsx
@@ -7,7 +7,7 @@ import { ThemeProvider as NextThemeProvider } from 'next-themes';
import Navbar from './navbar';
import Footer from './footer';
-const CustomProviders = (props: {
+const CustomProvider = (props: {
theme: string;
children: React.ReactNode;
}) => {
@@ -26,4 +26,4 @@ const CustomProviders = (props: {
);
};
-export default CustomProviders;
+export default CustomProvider;
diff --git a/components/experience.tsx b/components/experience.tsx
index d44dcc7..b79e126 100644
--- a/components/experience.tsx
+++ b/components/experience.tsx
@@ -1,8 +1,8 @@
-import { Container } from "@chakra-ui/react";
+import { Container } from '@chakra-ui/react';
-import { NotionProfilePage } from "./notion-profile-page";
-import { notion } from "../lib/notion";
-import { notionExperiencePageId } from "../lib/config";
+import { NotionProfilePage } from './notion/notion-profile-page';
+import { notion } from '../lib/notion';
+import { notionExperiencePageId } from '../lib/config';
const Experience = async () => {
if (!notionExperiencePageId) return null;
@@ -12,10 +12,10 @@ const Experience = async () => {
return (
diff --git a/components/utils/category-filter.tsx b/components/filters/category-filter.tsx
similarity index 100%
rename from components/utils/category-filter.tsx
rename to components/filters/category-filter.tsx
diff --git a/components/category-list.tsx b/components/filters/category-list.tsx
similarity index 100%
rename from components/category-list.tsx
rename to components/filters/category-list.tsx
diff --git a/components/utils/category.tsx b/components/filters/category.tsx
similarity index 100%
rename from components/utils/category.tsx
rename to components/filters/category.tsx
diff --git a/components/utils/search-bar.tsx b/components/filters/search-bar.tsx
similarity index 100%
rename from components/utils/search-bar.tsx
rename to components/filters/search-bar.tsx
diff --git a/components/navbar.tsx b/components/navbar.tsx
index 0b8d86d..06b2752 100644
--- a/components/navbar.tsx
+++ b/components/navbar.tsx
@@ -1,7 +1,7 @@
-import { useEffect } from "react";
-import { Flex, Button, useColorMode, ButtonGroup } from "@chakra-ui/react";
-import { MoonIcon, SunIcon } from "@chakra-ui/icons";
-import NextLink from "next/link";
+import { useEffect } from 'react';
+import { Flex, Button, useColorMode, ButtonGroup } from '@chakra-ui/react';
+import { MoonIcon, SunIcon } from '@chakra-ui/icons';
+import NextLink from 'next/link';
type PageList = {
name: string;
@@ -9,10 +9,10 @@ type PageList = {
};
const pageList: Array = [
- { name: "About", path: "/#about" },
- { name: "Experience", path: "/#experience" },
- { name: "Projects", path: "/#projects" },
- { name: "Blog", path: "/blog" },
+ { name: 'About', path: '/#about' },
+ { name: 'Experience', path: '/#experience' },
+ { name: 'Projects', path: '/#projects' },
+ { name: 'Blog', path: '/blog' },
];
const Navbar = (props: { theme: string }) => {
@@ -28,24 +28,24 @@ const Navbar = (props: { theme: string }) => {
return (
-
+
{pageList.map((item, idx) => (
))}
diff --git a/components/notion-blog-page.tsx b/components/notion/notion-blog-page.tsx
similarity index 52%
rename from components/notion-blog-page.tsx
rename to components/notion/notion-blog-page.tsx
index aea31b0..dc22643 100644
--- a/components/notion-blog-page.tsx
+++ b/components/notion/notion-blog-page.tsx
@@ -1,13 +1,13 @@
-"use client";
+'use client';
-import dynamic from "next/dynamic";
-import { NotionRenderer } from "react-notion-x";
-import { ExtendedRecordMap } from "notion-types";
-import { Flex, Text, useColorMode } from "@chakra-ui/react";
-import "react-notion-x/src/styles.css";
+import dynamic from 'next/dynamic';
+import { NotionRenderer } from 'react-notion-x';
+import { ExtendedRecordMap } from 'notion-types';
+import { Flex, Text, useColorMode } from '@chakra-ui/react';
+import 'react-notion-x/src/styles.css';
-import CategoryList from "./category-list";
-import { Post } from "../types/post";
+import CategoryList from '../filters/category-list';
+import { Post } from '../../types/post';
export const NotionBlogPage = ({
post,
@@ -21,12 +21,12 @@ export const NotionBlogPage = ({
return (
-
+
Posted on {post.date}
@@ -44,28 +44,28 @@ export const NotionBlogPage = ({
};
const Code = dynamic(() =>
- import("react-notion-x/build/third-party/code").then((m) => m.Code)
+ import('react-notion-x/build/third-party/code').then((m) => m.Code)
);
const Collection = dynamic(() =>
- import("react-notion-x/build/third-party/collection").then(
+ import('react-notion-x/build/third-party/collection').then(
(m) => m.Collection
)
);
const Equation = dynamic(() =>
- import("react-notion-x/build/third-party/equation").then((m) => m.Equation)
+ import('react-notion-x/build/third-party/equation').then((m) => m.Equation)
);
const Pdf = dynamic(
- () => import("react-notion-x/build/third-party/pdf").then((m) => m.Pdf),
+ () => import('react-notion-x/build/third-party/pdf').then((m) => m.Pdf),
{
ssr: false,
}
);
const Modal = dynamic(
- () => import("react-notion-x/build/third-party/modal").then((m) => m.Modal),
+ () => import('react-notion-x/build/third-party/modal').then((m) => m.Modal),
{
ssr: false,
}
diff --git a/components/notion-profile-page.tsx b/components/notion/notion-profile-page.tsx
similarity index 100%
rename from components/notion-profile-page.tsx
rename to components/notion/notion-profile-page.tsx
diff --git a/components/posts/custom-container.tsx b/components/posts/custom-container.tsx
deleted file mode 100644
index b8236d7..0000000
--- a/components/posts/custom-container.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-import { ExtendedRecordMap } from 'notion-types';
-import { Container, Grid, GridItem } from '@chakra-ui/react';
-
-import CategoryFilter from '../utils/category-filter';
-import SearchBar from '../utils/search-bar';
-import PostsGrid from './posts-grid';
-import { NotionBlogPage } from '../notion-blog-page';
-import RelatedPosts from './related-posts';
-import { CategoryProvider } from '../contexts/category-context';
-import { toUniqueArray } from '../../lib/to-unique-array';
-import type { Post } from '../../types/post';
-
-export const PostListContainer = ({ posts }: { posts: Post[] }) => {
- const [query, setQuery] = useState('');
- const categories: string[] = toUniqueArray(
- posts
- .filter((post) => post.published)
- .map((post) => post.categories)
- .flat(1)
- ).sort();
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export const PostPageContainer = (props: {
- post: Post;
- recordMap: ExtendedRecordMap;
- relatedPosts: Post[];
-}) => {
- const { post, recordMap, relatedPosts } = props;
-
- return (
-
-
-
-
- );
-};
diff --git a/components/posts/post-card.tsx b/components/posts/post-card.tsx
index 344f18a..85a31b4 100644
--- a/components/posts/post-card.tsx
+++ b/components/posts/post-card.tsx
@@ -3,8 +3,8 @@
import NextLink from 'next/link';
import { Text, Heading, Card, CardBody, Link, Flex } from '@chakra-ui/react';
-import CategoryList from '../category-list';
-import { Post } from '../../types/post';
+import CategoryList from '../filters/category-list';
+import type { Post } from '../../types/post';
const PostCard = ({
post: { slug, title, date, categories, outerLink },
diff --git a/components/posts/post-list.tsx b/components/posts/post-list.tsx
new file mode 100644
index 0000000..26ed3a1
--- /dev/null
+++ b/components/posts/post-list.tsx
@@ -0,0 +1,39 @@
+'use client';
+
+import { useState } from 'react';
+import { Container, Grid, GridItem } from '@chakra-ui/react';
+
+import CategoryFilter from '../filters/category-filter';
+import SearchBar from '../filters/search-bar';
+import PostsGrid from './posts-grid';
+import { CategoryProvider } from '../contexts/category-context';
+import type { Post } from '../../types/post';
+
+const PostList = ({ posts }: { posts: Post[] }) => {
+ const [query, setQuery] = useState('');
+
+ const categories: string[] = [
+ ...new Set(
+ posts
+ .filter((post) => post.published)
+ .map((post) => post.categories)
+ .flat(1)
+ ),
+ ].sort();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default PostList;
diff --git a/components/posts/post-view.tsx b/components/posts/post-view.tsx
new file mode 100644
index 0000000..31efd68
--- /dev/null
+++ b/components/posts/post-view.tsx
@@ -0,0 +1,25 @@
+'use client';
+
+import { ExtendedRecordMap } from 'notion-types';
+import { Container } from '@chakra-ui/react';
+
+import { NotionBlogPage } from '../notion/notion-blog-page';
+import RelatedPosts from './related-posts';
+import type { Post } from '../../types/post';
+
+const PostView = (props: {
+ post: Post;
+ recordMap: ExtendedRecordMap;
+ relatedPosts: Post[];
+}) => {
+ const { post, recordMap, relatedPosts } = props;
+
+ return (
+
+
+
+
+ );
+};
+
+export default PostView;
diff --git a/components/posts/posts-grid.tsx b/components/posts/posts-grid.tsx
index 08ff0c2..1efe6e9 100644
--- a/components/posts/posts-grid.tsx
+++ b/components/posts/posts-grid.tsx
@@ -5,7 +5,6 @@ import { Box, VStack } from '@chakra-ui/react';
import PostCard from './post-card';
import { CategoryContext } from '../contexts/category-context';
-import { toUniqueArray } from '../../lib/to-unique-array';
import type { Post } from '../../types/post';
const PostsGrid = ({ posts, query }: { posts: Post[]; query: string }) => {
@@ -13,7 +12,7 @@ const PostsGrid = ({ posts, query }: { posts: Post[]; query: string }) => {
const filteredPosts = useMemo(
() =>
- posts.filter((post) => {
+ posts.filter((post: Post) => {
if (!post.published) return false;
if (
@@ -24,7 +23,7 @@ const PostsGrid = ({ posts, query }: { posts: Post[]; query: string }) => {
}
if (selectedCategories.length > 0) {
- const isCategoryMatch = selectedCategories.every((category) =>
+ const isCategoryMatch = selectedCategories.every((category: string) =>
post.categories.includes(category)
);
if (!isCategoryMatch) return false;
@@ -36,19 +35,21 @@ const PostsGrid = ({ posts, query }: { posts: Post[]; query: string }) => {
[posts, selectedCategories, query]
);
- filteredPosts.sort((postA, postB) => (postA.date > postB.date ? -1 : 1));
+ filteredPosts.sort((postA: Post, postB: Post) =>
+ postA.date > postB.date ? -1 : 1
+ );
// Update categories in context
useEffect(() => {
- setCategories(
- toUniqueArray(filteredPosts.map((post) => post.categories).flat())
- );
+ setCategories([
+ ...new Set(filteredPosts.map((post: Post) => post.categories).flat(1)),
+ ]);
}, [filteredPosts, setCategories]);
return (
{filteredPosts.length ? (
- filteredPosts.map((post) => (
+ filteredPosts.map((post: Post) => (
diff --git a/components/posts/related-posts.tsx b/components/posts/related-posts.tsx
index 8fb5a3f..ba3238a 100644
--- a/components/posts/related-posts.tsx
+++ b/components/posts/related-posts.tsx
@@ -4,7 +4,7 @@ import { useState } from 'react';
import { Box, Button, Center, Heading, SimpleGrid } from '@chakra-ui/react';
import PostCard from './post-card';
-import { Post } from '../../types/post';
+import type { Post } from '../../types/post';
const INITIAL_NUM_POSTS = 3;
const ADDITIONAL_NUM_POSTS = 3;
@@ -21,7 +21,7 @@ const RelatedPosts = ({ posts }: { posts: Post[] }) => {
return (
Related Posts
- {posts.slice(0, numPosts).map((post) => (
+ {posts.slice(0, numPosts).map((post: Post) => (
diff --git a/components/projects.tsx b/components/projects.tsx
index e4a9a5d..47367b2 100644
--- a/components/projects.tsx
+++ b/components/projects.tsx
@@ -1,7 +1,7 @@
-"use client";
+'use client';
-import { useState } from "react";
-import type { NextPage } from "next";
+import { useState } from 'react';
+import type { NextPage } from 'next';
import {
Button,
ButtonGroup,
@@ -18,16 +18,16 @@ import {
Text,
Tabs,
SimpleGrid,
-} from "@chakra-ui/react";
+} from '@chakra-ui/react';
-import projectsData from "../public/data/projects.json";
-import CategoryList from "./category-list";
+import projectsData from '../public/data/projects.json';
+import CategoryList from './filters/category-list';
enum TabMenu {
- All = "All",
- Web = "Web",
- Mobile = "Mobile",
- OSS = "OSS",
+ All = 'All',
+ Web = 'Web',
+ Mobile = 'Mobile',
+ OSS = 'OSS',
}
const Projects: NextPage = () => {
@@ -50,16 +50,16 @@ const Projects: NextPage = () => {
};
return (
-
-
+
+
Projects
-
+
{Object.keys(TabMenu).map((menu, idx) => {
return (
- filterProjects(menu)}>
+ filterProjects(menu)}>
{menu}
);
@@ -71,16 +71,16 @@ const Projects: NextPage = () => {
{projectsList.map((project, idx) => {
return (
-
+
-
- {project.title}
- {project.description}
+
+ {project.title}
+ {project.description}
@@ -88,14 +88,14 @@ const Projects: NextPage = () => {
-
+
{project.link.github && (
-