Skip to content

Commit

Permalink
Merge pull request #89 from PoolC/dev
Browse files Browse the repository at this point in the history
내가 쓴 글 페이지
  • Loading branch information
jinoov authored Apr 11, 2024
2 parents 953bde7 + cbbbdeb commit 61ade5b
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 3 deletions.
2 changes: 1 addition & 1 deletion apps/web-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"sync:type": "yarn dlx typesync",
"check:type": "tsc --noEmit",
"check:type:watch": "tsc --noEmit --watch",
"codegen": "openapi -i https://poolc.org/api/v2/api-docs -o src/lib/api-v2/__generated__ -c axios --useUnionTypes --useOptions",
"codegen": "openapi -i https://dev.poolc.org/api/v2/api-docs -o src/lib/api-v2/__generated__ -c axios --useUnionTypes --useOptions",
"postinstall": "yarn codegen",
"prettier": "prettier --write ."
},
Expand Down
2 changes: 2 additions & 0 deletions apps/web-client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const BoardWritePage = lazy(() => import('~/pages/board/BoardWritePage'));

const MyPage = lazy(() => import('./pages/my-page/MyPage'));
const MyPageBadgeListPage = lazy(() => import('./pages/my-page/MyPageBadgeListPage'));
const MyPageMyPostsPage = lazy(() => import('./pages/my-page/MyPageMyPostsPage'));

const MessageAllListPage = lazy(() => import('./pages/message/MessageAllListPage'));
const MessageListPage = lazy(() => import('./pages/message/MessageListPage'));
Expand Down Expand Up @@ -106,6 +107,7 @@ function App() {
<Route component={MessageFormPage} path={`/${MENU.MESSAGE_FORM}`} />
<Route component={MyPage} path={`/${MENU.MY_PAGE}`} exact />
<Route component={MyPageBadgeListPage} path={`/${MENU.MY_PAGE}/${MENU.MY_PAGE_BADGE_LIST}`} />
<Route component={MyPageMyPostsPage} path={`/${MENU.MY_PAGE}/${MENU.MY_PAGE_MY_POSTS}`} />
<Route component={SpaceReservationPage} path={`/${MENU.ROOM_RESERVATION}`} />
<Route component={NotFoundPage} path="/" />
</Switch>
Expand Down
21 changes: 21 additions & 0 deletions apps/web-client/src/components/@business/BusinessErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { PropsWithChildren } from 'react';

export default class BusinessErrorBoundary extends React.Component<PropsWithChildren> {
state = { hasError: false };

static getDerivedStateFromError(error: Error) {
return { hasError: true };
}

render() {
if (this.state.hasError) {
return (
<div style={{ padding: '20px' }}>
<h1>에러가 발생했습니다. 잠시 후 다시 시도해주세요.</h1>
</div>
);
}

return this.props.children;
}
}
133 changes: 133 additions & 0 deletions apps/web-client/src/components/my-page/MyPageMyPostsEntry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { CommentOutlined } from '@ant-design/icons';
import { Space, Typography, Avatar, Empty, Pagination } from 'antd';
import { createStyles } from 'antd-style';
import Table, { ColumnsType } from 'antd/es/table';
import dayjs from 'dayjs';
import { stringify } from 'qs';
import { Link, useHistory } from 'react-router-dom';
import { MENU } from '~/constants/menus';
import { useSearchParams } from '~/hooks/useSearchParams';
import { PostControllerService, PostResponse, queryKey, useAppSuspenseQuery } from '~/lib/api-v2';
import getFileUrl from '~/lib/utils/getFileUrl';
import { getInnerTextFromMarkdown } from '~/lib/utils/getInnerTextFromMarkdown';

const useStyles = createStyles(({ css }) => ({
metaInfoArea: css`
width: 100%;
justify-content: space-between;
`,
search: css`
max-width: 300px;
`,
link: css`
display: block;
width: 100%;
`,
topArea: css`
display: flex;
justify-content: flex-end;
align-items: center;
`,
wrapper: css`
display: flex;
align-items: stretch;
flex-direction: column;
gap: 8px;
`,
paginationWrap: css`
display: flex;
justify-content: center;
margin-top: 10px;
`,
commentWrap: css`
display: flex;
align-items: center;
gap: 8px;
`,
avatar: css`
width: 40px;
height: 40px;
`,
badge: css`
width: 35px;
height: 35px;
border: 1px solid #47be9b;
`,
clamp: css`
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
`,
fullWidth: css`
width: 100%;
`,
}));

export default function MyPageMyPostsEntry() {
const { styles } = useStyles();

const searchParams = useSearchParams();
const page = Number(searchParams.get('page') ?? 1);

const {
data: { posts, maxPage },
} = useAppSuspenseQuery({
queryKey: queryKey.post.myPosts(page - 1),
queryFn: () => PostControllerService.viewMyPostsUsingGet({ page: page - 1 }),
});

const filteredList = (posts ?? []).filter(Boolean);

const history = useHistory();

const onPageChange = (page: number) =>
history.push(
`/${MENU.MY_PAGE_MY_POSTS}?${stringify({
page,
})}`,
);

const columns: ColumnsType<PostResponse> = [
{
render: (_, post) => (
<Link to={`/${MENU.BOARD}/${post.postId}`} className={styles.link}>
<Space direction={'vertical'} className={styles.fullWidth} size={'middle'}>
<Space className={styles.metaInfoArea} size={'middle'}>
<Space>
<Typography.Text>{post.writerName}</Typography.Text>
{post.badge && <Avatar src={getFileUrl(post.badge?.imageUrl)} className={styles.badge} />}
</Space>
<Space size={'middle'}>
<Typography.Text type={'secondary'}>{dayjs(post.createdAt).format('YYYY. MM. DD')}</Typography.Text>
<div className={styles.commentWrap}>
<CommentOutlined />
{post.commentCount ?? 0}
</div>
</Space>
</Space>
<Space direction={'vertical'} size={0}>
<Typography.Title level={5}>{post.title}</Typography.Title>
{post?.body && <Typography.Text className={styles.clamp}>{getInnerTextFromMarkdown(post.body)}</Typography.Text>}
</Space>
</Space>
</Link>
),
},
];

return (
<div className={styles.fullWidth}>
{filteredList.length === 0 ? (
<Empty />
) : (
<>
<Table dataSource={filteredList} columns={columns} showHeader={false} pagination={false} rowKey={'postId'} />
<div className={styles.paginationWrap}>
<Pagination current={page} total={maxPage ? maxPage * 10 : 0} showSizeChanger={false} onChange={onPageChange} />
</div>
</>
)}
</div>
);
}
1 change: 1 addition & 0 deletions apps/web-client/src/constants/menus.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const MENU = {
BOARD: 'board',
MY_PAGE: 'my-page',
MY_PAGE_BADGE_LIST: 'badge-list',
MY_PAGE_MY_POSTS: 'my-posts',
MESSAGE_LIST: 'message-list',
MESSAGE_FORM: 'message-form',
MESSAGE_ALL_LIST: 'message-all-list',
Expand Down
5 changes: 4 additions & 1 deletion apps/web-client/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Global } from '@emotion/react';
import { globalStyles } from '~/styles/globalStyles';
import { theme } from '~/styles/theme';
import BusinessShowProgressOnRouteChange from './components/@business/BusinessShowProgressOnRouteChange';
import BusinessErrorBoundary from './components/@business/BusinessErrorBoundary';

const sagaMiddleware = createSagaMiddleware();
export const store = process.env.NODE_ENV === 'production' ? createStore(rootReducer, applyMiddleware(sagaMiddleware)) : createStore(rootReducer, composeWithDevTools(applyMiddleware(sagaMiddleware)));
Expand Down Expand Up @@ -44,7 +45,9 @@ ReactDOM.createRoot($root).render(
<BrowserRouter>
<BusinessScrollTopOnRouteChange />
<BusinessShowProgressOnRouteChange />
<App />
<BusinessErrorBoundary>
<App />
</BusinessErrorBoundary>
</BrowserRouter>
</MessageProvider>
</QueryClientProvider>
Expand Down
1 change: 1 addition & 0 deletions apps/web-client/src/lib/api-v2/queryKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const queryKey = {
post: {
all: (boardType: BoardType, page: number) => ['post.all', boardType, page] as const,
post: (id: number) => ['post.post', id] as const,
myPosts: (page: number) => ['post.myPosts', page] as const,
},
baekjoon: {
baekjoon: ['baekjoon.baekjoon'] as const,
Expand Down
2 changes: 1 addition & 1 deletion apps/web-client/src/pages/my-page/MyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default function MyPage() {
{
title: '내가 쓴 글',
icon: <EditTwoTone size={24} twoToneColor="#ffd43b" />,
onClick: () => message.info('기능 준비중입니다!'),
link: `/${MENU.MY_PAGE}/${MENU.MY_PAGE_MY_POSTS}`,
},
{
title: '내가 스크랩한 글',
Expand Down
52 changes: 52 additions & 0 deletions apps/web-client/src/pages/my-page/MyPageMyPostsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Space, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { Suspense } from 'react';
import Skeleton from '~/components/common/Skeleton';
import MyPageMyPostsEntry from '~/components/my-page/MyPageMyPostsEntry';
import { Block, WhiteBlock } from '~/styles/common/Block.styles';

const useStyles = createStyles(({ css }) => ({
whiteBlock: css`
&.scope {
padding: 30px 0;
}
`,
wrapper: css`
width: 100%;
max-width: 1200px;
padding: 0 20px;
box-sizing: border-box;
`,
heading: css`
font-size: 18px;
`,
paragraph: css`
font-size: 14px;
color: #666;
`,
fullWidth: css`
width: 100%;
`,
}));

export default function MyPageMyPostsPage() {
const { styles, cx } = useStyles();

return (
<Block>
<WhiteBlock className={cx(styles.whiteBlock, 'scope')}>
<div className={styles.wrapper}>
<Space direction="vertical" size="large" className={styles.fullWidth}>
<Space direction="vertical" size="middle">
<h2 className={styles.heading}>내가 쓴 글</h2>
<p className={styles.paragraph}>내가 쓴 글 목록입니다.</p>
</Space>
<Suspense fallback={<Skeleton />}>
<MyPageMyPostsEntry />
</Suspense>
</Space>
</div>
</WhiteBlock>
</Block>
);
}

0 comments on commit 61ade5b

Please sign in to comment.