Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 메인 페이지 글로벌 피드에 실 데이터를 적용 합니다. #123

Merged
merged 5 commits into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BACKEND_URL=https://api.realworld.io/api
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ web_modules/
.yarn-integrity

# dotenv environment variable files
.env
# .env
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

.env 는 레포지토리에 시켜도 좋다고 하여 .gitignore 에서 제외 합니당

.env.development.local
.env.test.local
.env.production.local
Expand Down
29 changes: 29 additions & 0 deletions app/(home)/@feeds/(feeds)/@global/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { FeedItem } from 'components/home/feed';
import { Pagination } from 'components/ui';
import { ARTICLE_PAGE_LIMIT } from 'constants/article';
import { articleApi } from 'services';

type Props = {
searchParams: Record<string, string>;
};
const GlobalPage = async ({ searchParams }: Props) => {
const page = Number(searchParams.page ?? 1);

const { articles, articlesCount } = await articleApi.getArticles({
limit: ARTICLE_PAGE_LIMIT,
offset: (page - 1) * ARTICLE_PAGE_LIMIT,
});

return (
<>
<ul className="p-0">
{articles.map((feed) => {
return <FeedItem feed={feed} key={feed.slug} />;
})}
</ul>
<Pagination total={articlesCount / ARTICLE_PAGE_LIMIT} current={page} />
</>
);
};

export default GlobalPage;
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { ReactNode } from 'react';

import { Tabs } from 'components/ui';
import { FeedList } from '../feed-list';
import { clsx } from 'lib/utils';

export const ArticleSection = () => {
type Props = {
global: ReactNode;
};

const FeedsLayout = ({ global }: Props) => {
const tabs = [
{
key: 'feed',
Expand All @@ -29,10 +34,10 @@ export const ArticleSection = () => {
<div className={clsx('pt-[24px]', 'pb-[24px]')}>No articles are here... yet</div>
</Tabs.Content>

<Tabs.Content value="all">
<FeedList />
</Tabs.Content>
<Tabs.Content value="all">{global}</Tabs.Content>
</Tabs.Root>
</div>
);
};

export default FeedsLayout;
19 changes: 12 additions & 7 deletions components/home/index.tsx → app/(home)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { ArticleSection } from './article-section';
import { Banner } from './banner';
import { TagList } from './tag-list';
import { ReactNode } from 'react';

export const Home = () => {
import { Banner } from 'components/home/banner';
import { TagList } from 'components/home/tag-list';

type Props = {
feeds: ReactNode;
};

const HomeLayout = ({ feeds }: Props) => {
return (
<div className="home-page">
<Banner />

<div className="page container">
<div className="row">
<div className="col-md-9">
<ArticleSection />
</div>
<div className="col-md-9">{feeds}</div>

<div className="col-md-3">
<div className="sidebar">
Expand All @@ -24,3 +27,5 @@ export const Home = () => {
</div>
);
};

export default HomeLayout;
6 changes: 4 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const inter = Inter({
variable: '--font-inter',
});

export default async function RootLayout({ children }: { children: ReactNode }) {
const RootLayout = ({ children }: { children: ReactNode }) => {
return (
<html lang="en" className={inter.variable}>
<body>
Expand All @@ -30,4 +30,6 @@ export default async function RootLayout({ children }: { children: ReactNode })
</body>
</html>
);
}
};

export default RootLayout;
7 changes: 0 additions & 7 deletions app/page.tsx

This file was deleted.

1 change: 0 additions & 1 deletion components/home/article-section/index.ts

This file was deleted.

16 changes: 0 additions & 16 deletions components/home/feed-list/feed-list.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
import { Avatar, Tag } from 'components/ui';
import { Article } from 'models';

export const FeedItem = () => {
const feed = {
slug: 'how-to-train-your-dragon',
title: 'How to train your dragon',
description: 'Ever wonder how?',
body: 'It takes a Jacobian',
tagList: ['dragons', 'training'],
createdAt: '2016-02-18T03:22:56.637Z',
updatedAt: '2016-02-18T03:48:35.824Z',
favorited: false,
favoritesCount: 0,
author: {
username: 'jake',
bio: 'I work at statefarm',
image: 'https://i.stack.imgur.com/xHWG8.jpg',
following: false,
},
};
type Props = {
feed: Article;
};

export const FeedItem = ({ feed }: Props) => {
const { slug, title, description, author, favoritesCount, tagList } = feed;

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from './feed-list';
export * from './feed-item';
15 changes: 6 additions & 9 deletions components/ui/pagination/pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
'use client';

import { ComponentPropsWithoutRef, forwardRef } from 'react';
import { clsx } from 'lib/utils';
import { Overwrite } from 'lib/type-utils';

type PaginationProps = {
total: number;
current?: number;
onChange?: (page: number) => void;
};

type Props = Overwrite<ComponentPropsWithoutRef<'ul'>, PaginationProps>;

export const Pagination = forwardRef<HTMLUListElement, Props>(function pagination(
{ total, current = 1, onChange, ...otherProps },
{ total, current = 1, ...otherProps },
ref,
) {
const pages = Array.from({ length: total }, (_, i) => i + 1);

const handleItemClick = (page: number) => () => {
onChange?.(page);
};
if (total < 2) {
return null;
}

return (
<ul ref={ref} {...otherProps} className="pagination">
{pages.map((page) => (
<li className={clsx('page-item', { active: page === current })} key={page} onClick={handleItemClick(page)}>
<a className="page-link" href="">
<li className={clsx('page-item', { active: page === current })} key={page}>
<a className="page-link" href={`?page=${page}`}>
{page}
</a>
</li>
Expand Down
1 change: 1 addition & 0 deletions constants/article.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const ARTICLE_PAGE_LIMIT = 10;
22 changes: 22 additions & 0 deletions models/article.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { z } from 'zod';
import { AuthorSchema } from './author';

export const ArticleSchema = z.object({
slug: z.string(),
title: z.string(),
description: z.string(),
body: z.string(),
tagList: z.array(z.string()),
createdAt: z.string(),
updatedAt: z.string(),
favorited: z.boolean(),
favoritesCount: z.number(),
author: AuthorSchema,
});

export const ArticleListSchema = z.object({
articles: z.array(ArticleSchema),
articlesCount: z.number(),
});

export type Article = z.infer<typeof ArticleSchema>;
8 changes: 8 additions & 0 deletions models/author.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { z } from 'zod';

export const AuthorSchema = z.object({
username: z.string(),
bio: z.string().nullable(),
image: z.string(),
following: z.boolean(),
});
3 changes: 3 additions & 0 deletions models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './article';
export * from './author';
export * from './search-params';
7 changes: 7 additions & 0 deletions models/search-params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type SearchParams = {
limit: number;
offset: number;
author: string;
tag: string;
favorited: string;
};
2 changes: 1 addition & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module.exports = {
remotePatterns: [
{
protocol: 'https',
hostname: 'i.stack.imgur.com',
hostname: 'api.realworld.io',
},
],
},
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"prettier-plugin-tailwindcss": "0.5.4",
"storybook": "7.4.0",
"tailwindcss": "3.3.3",
"typescript": "5.2.2"
"typescript": "5.2.2",
"zod": "3.22.2"
}
}
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions services/apis/article.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { get } from 'services/fetch-utils';
import { ArticleListSchema, SearchParams } from 'models';

class ArticleApiService {
async getArticles({ limit = 10, offset = 0 }: Partial<SearchParams> = {}) {
const params = new URLSearchParams({
limit: limit.toString(),
offset: offset.toString(),
});

return await get(`/articles?${params}`, ArticleListSchema);
}
}

export const articleApi = new ArticleApiService();
1 change: 1 addition & 0 deletions services/apis/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './article';
Loading