jaehyuk-jang

Intergrated views for webpages

1 +//@ts-nocheck
2 +
3 +import React from 'react';
4 +import { Table as TableContainer, TableProps } from 'antd';
5 +import { useWindowSize } from '@src/hooks';
6 +import TableHeader from './TableHeader';
7 +
8 +interface NewTableProps extends TableProps<any> {
9 + title: string;
10 + columns: Object[];
11 + data: Object[];
12 +}
13 +
14 +export default function Table({
15 + title,
16 + columns,
17 + data,
18 + ...rest
19 +}: NewTableProps) {
20 + const [width, height] = useWindowSize();
21 +
22 + const size = width > 1000 ? 'middle' : 'small';
23 + const cellHeight = size === 'small' ? 39 : 55;
24 + const pageSize = Math.ceil((height * 2) / 3 / cellHeight);
25 +
26 + const emptyRowNum = pageSize - (data.length % pageSize);
27 + const emptyRow = columns.reduce(
28 + (acc, curr) => ((acc[curr['dataIndex']] = '-'), acc),
29 + {},
30 + );
31 +
32 + const body = [...data, ...Array(emptyRowNum || 0).fill(emptyRow)];
33 +
34 + return (
35 + <TableContainer
36 + size={size}
37 + title={() => <TableHeader title={title} />}
38 + columns={columns}
39 + dataSource={body}
40 + pagination={{
41 + responsive: true,
42 + showLessItems: true,
43 + pageSize,
44 + }}
45 + {...rest}
46 + />
47 + );
48 +}
1 +import React from 'react';
2 +import { Button } from 'antd';
3 +import Link from 'next/link';
4 +import { useRouter } from 'next/router';
5 +
6 +export default function TableHeader({ title }) {
7 + const { query } = useRouter();
8 +
9 + return (
10 + <div className={'ant-table-title'}>
11 + <h2>{title} 게시판</h2>
12 + <Button>
13 + <Link href={`/${query.name}/create`}>{'글쓰기'}</Link>
14 + </Button>
15 + </div>
16 + );
17 +}
1 +import Table from './Table';
2 +import {
3 + makeArticleURLWithNumber,
4 + makeCategoryTableBody,
5 +} from '@shared/functions';
6 +import { useRouter } from 'next/router';
7 +
8 +export default function Category({ category, articleList }) {
9 + const router = useRouter();
10 +
11 + // example
12 + const newData = makeCategoryTableBody(articleList);
13 +
14 + const handleRoute = ({ id }) => (e) => {
15 + e.preventDefault();
16 +
17 + if (id === '-') return;
18 +
19 + const {
20 + query: { name: categoryName },
21 + } = router;
22 + const URL = makeArticleURLWithNumber(categoryName as string, id);
23 +
24 + router.push(URL);
25 + };
26 +
27 + return (
28 + <div className={'outer-container category-table-container'}>
29 + <Table
30 + title={category}
31 + columns={[
32 + {
33 + key: '1',
34 + title: 'No.',
35 + dataIndex: 'id',
36 + align: 'center',
37 + },
38 + { key: '2', title: '제목', dataIndex: 'title', width: '70%' },
39 + {
40 + key: '3',
41 + title: '작성자',
42 + dataIndex: 'author',
43 + align: 'center',
44 + },
45 + {
46 + key: '4',
47 + title: '등록일',
48 + dataIndex: 'created_date',
49 + align: 'center',
50 + },
51 + ]}
52 + data={newData}
53 + onRow={(record) => ({
54 + onClick: handleRoute(record),
55 + })}
56 + />
57 + </div>
58 + );
59 +}
1 +import React from 'react';
2 +import { Button } from 'antd';
3 +
4 +export default function Buttons({ buttons }) {
5 + return (
6 + <div>
7 + {buttons.map(({ title, ...rest }) => (
8 + <Button key={title} {...rest}>
9 + {title}
10 + </Button>
11 + ))}
12 + </div>
13 + );
14 +}
1 +import React from 'react';
2 +import { Form } from 'antd';
3 +
4 +export default function Inputs({ forms }) {
5 + return (
6 + <>
7 + {forms.map(({ form, input: { Item, value, ...rest } }) => (
8 + <Form.Item {...form} required key={value}>
9 + <Item placeholder={value} name={value} {...rest} />
10 + </Form.Item>
11 + ))}
12 + </>
13 + );
14 +}
1 +export { default as CreateInputs } from './Inputs';
2 +export { default as CreateButtons } from './Buttons';
1 +import Link from 'next/link';
2 +import { Card as CardItem } from 'antd';
3 +import { Row } from './Row';
4 +
5 +function MoreButton(category) {
6 + return <Link href={`/category/${category}`}>더보기</Link>;
7 +}
8 +
9 +export default function Card({ category, posts, ...rest }) {
10 + const postsNum = posts.length;
11 + const emptyRows = postsNum < 5 ? Array(5 - postsNum).fill(null) : [];
12 + const sliced = postsNum > 5 ? posts.slice(postsNum - 5, postsNum) : posts;
13 +
14 + return (
15 + <CardItem title={category} extra={MoreButton(category)} {...rest}>
16 + {sliced.map(({ title, id }) => (
17 + <Row key={title} category={category} title={title} id={id} />
18 + ))}
19 + {emptyRows.map((_, idx) => (
20 + <div className={'card-row'} key={idx} />
21 + ))}
22 + </CardItem>
23 + );
24 +}
1 +import { useRouter } from 'next/router';
2 +import { makeArticleURLWithNumber } from '@src/shared/functions';
3 +
4 +export const Row = ({ category, title, id }) => {
5 + const router = useRouter();
6 + const sliced = title.length > 20 ? title.substr(0, 20) + '...' : title;
7 +
8 + const handleClickArticle = () => {
9 + const URL = makeArticleURLWithNumber(category, id);
10 + router.push(URL);
11 + };
12 +
13 + return (
14 + <div className={'card-row has-content'} onClick={handleClickArticle}>
15 + <h2 className={'card-row-title'}>{sliced}</h2>
16 + <span className={'card-row-recomment'}>{'?'}</span>
17 + </div>
18 + );
19 +};
1 +import Card from '@src/views/Main/Card';
2 +
3 +export default function Main({ categories, posts }) {
4 + return (
5 + <div className={'outer-container main-card-container'}>
6 + {categories?.map((category) => {
7 + const filtered = posts.filter((post) => post.category === category);
8 + return (
9 + <Card
10 + key={category}
11 + category={category}
12 + posts={filtered}
13 + className={'main-card'}
14 + />
15 + );
16 + })}
17 + </div>
18 + );
19 +}
1 +import React, { useState } from 'react';
2 +import { Comment as CommentItem, Divider } from 'antd';
3 +
4 +import Profile from './Profile';
5 +import Datetime from './Datetime';
6 +import Like from './Like';
7 +import Dislike from './Dislike';
8 +
9 +export default function Comment({
10 + author,
11 + content,
12 + created_date,
13 + idx,
14 + commentsNum,
15 +}) {
16 + const [likes, setLikes] = useState(0);
17 + const [dislikes, setDislikes] = useState(0);
18 + const [action, setAction] = useState(null);
19 +
20 + const like = () => {
21 + setLikes(1);
22 + setDislikes(0);
23 + setAction('liked');
24 + };
25 +
26 + const dislike = () => {
27 + setLikes(0);
28 + setDislikes(1);
29 + setAction('disliked');
30 + };
31 +
32 + return (
33 + <>
34 + <CommentItem
35 + actions={[
36 + Like({ action, like, likes }),
37 + Dislike({
38 + action,
39 + dislike,
40 + dislikes,
41 + }),
42 + ]}
43 + author={<a>{author}</a>}
44 + avatar={Profile({ src: '', author })}
45 + content={<div dangerouslySetInnerHTML={{ __html: content }} />}
46 + datetime={Datetime({ created_date })}
47 + />
48 + {commentsNum - 1 !== idx && <Divider />}
49 + </>
50 + );
51 +}
1 +import React from 'react';
2 +import { Tooltip } from 'antd';
3 +import moment from 'moment';
4 +
5 +export default function Datetime({ created_date }) {
6 + return (
7 + <Tooltip title={moment(created_date).format('YYYY-MM-DD HH:mm:ss')}>
8 + <span>{moment(created_date).fromNow()}</span>
9 + </Tooltip>
10 + );
11 +}
1 +import React, { createElement } from 'react';
2 +import { Tooltip } from 'antd';
3 +import { DislikeOutlined, DislikeFilled } from '@ant-design/icons';
4 +
5 +export default function Dislike({ dislike, action, dislikes }) {
6 + return (
7 + <Tooltip key="comment-basic-dislike" title="Dislike">
8 + <span onClick={dislike}>
9 + {createElement(action === 'disliked' ? DislikeFilled : DislikeOutlined)}
10 + <span className="comment-action">{dislikes}</span>
11 + </span>
12 + </Tooltip>
13 + );
14 +}
1 +import React, { createElement } from 'react';
2 +import { Tooltip } from 'antd';
3 +import { LikeOutlined, LikeFilled } from '@ant-design/icons';
4 +
5 +export default function Like({ action, like, likes }) {
6 + return (
7 + <Tooltip key="comment-basic-like" title="Like">
8 + <span onClick={like}>
9 + {createElement(action === 'liked' ? LikeFilled : LikeOutlined)}
10 + <span className="comment-action">{likes}</span>
11 + </span>
12 + </Tooltip>
13 + );
14 +}
1 +import React from 'react';
2 +import { Avatar } from 'antd';
3 +
4 +export default function Profile({ src, author }) {
5 + return (
6 + <Avatar
7 + src={
8 + src
9 + ? src
10 + : 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png'
11 + }
12 + alt={author}
13 + />
14 + );
15 +}
1 +import React from 'react';
2 +import TextArea from 'antd/lib/input/TextArea';
3 +import { Button } from 'antd';
4 +import { useMutation } from '@apollo/client';
5 +import { CREATE_COMMENT } from '@src/gql/create-comment';
6 +
7 +export default function Submit({ title, postId, addCommentList }) {
8 + const [content, setContent] = React.useState('');
9 + const [createComment] = useMutation(CREATE_COMMENT);
10 +
11 + const handleChange = (e) => {
12 + setContent(e.target.value);
13 + };
14 +
15 + const handleSubmit = async () => {
16 + const { data } = await createComment({
17 + variables: { input: { content, post_id: postId } },
18 + });
19 + setContent('');
20 + data && addCommentList(data.createComment);
21 + };
22 +
23 + return (
24 + <>
25 + <TextArea
26 + value={content}
27 + onChange={handleChange}
28 + placeholder={'댓글을 입력하세요'}
29 + autoSize={{ minRows: 3, maxRows: 5 }}
30 + className={'comments-textarea'}
31 + />
32 + <Button
33 + type={'primary'}
34 + size={'large'}
35 + onClick={handleSubmit}
36 + className={'comments-submit-button'}
37 + >
38 + {title}
39 + </Button>
40 + </>
41 + );
42 +}
1 +import React, { useState } from 'react';
2 +import { Card } from 'antd';
3 +
4 +import Submit from './Submit';
5 +import Comment from './Comment';
6 +
7 +export default function CommentsContainer({ comments, postId }) {
8 + const [tempComments, setTempComments] = useState(comments);
9 + const commentsNum = tempComments?.length;
10 +
11 + const addCommentList = (data) => {
12 + setTempComments((prev) => [...prev, data]);
13 + };
14 +
15 + React.useEffect(() => {
16 + comments && setTempComments(comments);
17 + }, [comments]);
18 +
19 + return (
20 + <Card className={'post-comments'}>
21 + <h1 className={'post-comments-num'}>{`COMMENTS (${commentsNum})`}</h1>
22 + {tempComments?.map(({ author, content, created_date }, idx) => (
23 + <Comment
24 + key={created_date}
25 + author={author}
26 + content={content}
27 + created_date={created_date}
28 + idx={idx}
29 + commentsNum={commentsNum}
30 + />
31 + ))}
32 + <Submit title={'작성'} postId={postId} addCommentList={addCommentList} />
33 + </Card>
34 + );
35 +}
1 +import React from 'react';
2 +import { Card, Descriptions } from 'antd';
3 +import moment from 'moment';
4 +
5 +export default function Content({
6 + id,
7 + author,
8 + category,
9 + created_date,
10 + title,
11 + content,
12 +}) {
13 + return (
14 + <Card className={'post-content'}>
15 + <Descriptions title={title} layout={'horizontal'} column={3}>
16 + <Descriptions.Item label={'작성자'} span={3}>
17 + {author}
18 + </Descriptions.Item>
19 + <Descriptions.Item
20 + label={'작성일'}
21 + span={3}
22 + style={{ textAlign: 'right' }}
23 + >
24 + {moment(created_date).format('YYYY.MM.DD HH:mm:ss')}
25 + </Descriptions.Item>
26 + <Descriptions.Item label={'게시글'} span={3}>
27 + <br />
28 + <div dangerouslySetInnerHTML={{ __html: content }} />
29 + </Descriptions.Item>
30 + </Descriptions>
31 + </Card>
32 + );
33 +}