Skip to content

Commit

Permalink
Merge pull request #121 from soohwanpak/next-박수환-sprint8
Browse files Browse the repository at this point in the history
[박수환] Sprint8
  • Loading branch information
pers0n4 authored Dec 12, 2024
2 parents fd58012 + fddcc20 commit 2f5f746
Show file tree
Hide file tree
Showing 52 changed files with 6,516 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
40 changes: 40 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
84 changes: 84 additions & 0 deletions components/ArticleCommentDropDown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useState, useRef, useEffect } from 'react';
import styles from '@/css/Dropdown.module.css';
import Image from 'next/image';
import instance from '@/lib/axios';

export default function ArticleCommentDropDown({ id, onCommentAdded }) {
const [isOpen, setIsOpen] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [editContent, setEditContent] = useState('댓글 수정');
const dropdownRef = useRef(null);


const toggleDropdown = () => {
setIsOpen(!isOpen);
};

const handlePatch = () => {
try {
instance.patch(`/articleComment/${id}`, {
content: editContent,
});
alert('댓글이 수정되었습니다!');
setIsEditing(!isEditing);
setIsOpen(!isOpen);
onCommentAdded();
} catch (error) {
console.error('댓글 수정 중 오류 발생:', error);
}
}

const handleDelete = () => {
instance.delete(`/articleComment/${id}`)
.then((response) => {
alert("댓글이 삭제되었습니다!");
onCommentAdded();
setIsOpen(!isOpen);
}).catch((error) => {
console.error(error);
});
};

useEffect(() => {
const handleClickOutside = (e) => {
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
setIsOpen(false);
}
};

window.addEventListener('click', handleClickOutside);
return () => {
window.removeEventListener('click', handleClickOutside);
};
}, []);

return (
<div className={styles.dropdownContainer} ref={dropdownRef}>
<button
className={styles.dropdownButton1}
onClick={toggleDropdown}
>
<Image
width={24}
height={24}
src="/images/dropdownbutton.png" alt="User" />
</button>
{isOpen && (
<ul className={styles.dropdownMenu}>
<div
className={styles.menuItem1}
onClick={() => handlePatch()}
>
수정하기
</div>
<div
className={styles.menuItem1}
onClick={() => handleDelete('삭제하기')}
>
삭제하기
</div>
</ul>
)}
</div>
);
}
142 changes: 142 additions & 0 deletions components/ArticleDetail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import styles from '@/css/articleDetail.module.css';
import Image from 'next/image';
import dayjs from "dayjs";
import { useRouter } from 'next/router';
import { useState } from 'react';
import instance from '@/lib/axios';
import ArticleDropDown from './ArticleDropDown';
import ArticleCommentDropDown from './ArticleCommentDropDown';

export default function ArticleDetail({ article, onCommentAdded }) {
const [content, setContent] = useState('');
const router = useRouter();

const handleClick = () => {
router.push(`/`);
};

const handlePostSubmit = () => {
if (!content) {
alert('내용을 입력해주세요!');
}
if (content) {
instance.post(`/articleComment`, {
articleId: article.id,
content,
}).then((response) => {
alert("댓글이 등록되었습니다!");
onCommentAdded();
setContent('');
}).catch((error) => {
console.error(error);
});
}
};

const handleKeyDown = (e) => {
if (e.key === 'Enter') {
handlePostSubmit();
}
};

return (
<div className={styles.articleDetailContain}>
<div className={styles.ArticleDetailHead}>
<div className={styles.ArticleDetailText}>
{article.title}
</div>
<ArticleDropDown id={article.id} />
</div>
<div className={styles.ArticleDetailMain}>
<div className={styles.ArticleDetailUser}>
<div>
<Image
width={40}
height={40}
src="/images/userIcon.png" alt="User" />
</div>
<div className={styles.ArticleDetailUserName}>{article.user}</div>
<div className={styles.ArticleDetailDate}>{dayjs(article.createdAt).format('YYYY. MM. DD')}</div>
</div>
<div className={styles.ArticleDetailLikeContain}>
<div className={styles.heartIconImage}>
<Image
width={36}
height={36}
src="/images/heart.png" alt="heart" />
</div>
<div className={styles.ArticleDetailLikeCount}>{article.like}</div>
</div>
</div>

<div className={styles.ArticleDetailLikeContain2}>
{article.content}
</div>

<div className={styles.ArticleDetailCommentTitle}>댓글 달기</div>
<textarea
value={content}
onKeyDown={handleKeyDown}
onChange={(e) => setContent(e.target.value)}
className={styles.ArticleDetailInput}
placeholder='내용을 입력해주세요' />
<div className={styles.ArticleDetailCommentRegContain}>
<div
onClick={handlePostSubmit}
className={styles.ArticleDetailCommentReg}>등록</div>
</div>


{article.ArticleComment && article.ArticleComment.length > 0 ? (
article.ArticleComment.map((comment, index) => (
<div key={index} className={styles.ArticleDetailCommentBoxContain}>
<div className={styles.ArticleDetailCommentBox}>
<div>
{comment.content}
</div>
<ArticleCommentDropDown id={comment.id} onCommentAdded={onCommentAdded} />
</div>

<div className={styles.ArticleDetailCommentUserAll}>
<div>
<Image
width={40}
height={40}
src="/images/userIcon.png"
alt="User"
/>
</div>
<div className={styles.ArticleDetailCommentUser}>
<div className={styles.ArticleDetailCommentUserName}>
{comment.user}
</div>
<div className={styles.ArticleDetailCommentUserTime}>
{dayjs(comment.createdAt).format('YYYY. MM. DD')}
</div>
</div>
</div>
</div>
))
) : (
<div className={styles.noCommentImage}>
<Image
width={1200}
height={208}
src="/images/noComment.png"
alt="User"
/>
</div>
)}
<div
onClick={handleClick}
className={styles.returnImage}>
<Image
width={240}
height={48}
src="/images/return.png"
alt="User"
/>
</div>
</div>
);
}
72 changes: 72 additions & 0 deletions components/ArticleDropDown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useState, useRef, useEffect } from 'react';
import styles from '@/css/Dropdown.module.css';
import Image from 'next/image';
import instance from '@/lib/axios';
import { useRouter } from 'next/router';

export default function ArticleDropDown({ id }) {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
const router = useRouter();

const toggleDropdown1 = () => {
setIsOpen(!isOpen);
};

const handleDelete1 = () => {
instance.delete(`/article/${id}`)
.then((response) => {
alert("게시글이 삭제되었습니다!");
router.push(`/`);
}).catch((error) => {
console.error(error);
});
};

const handlePatch = () => {
router.push(`/${id}/articleUpdate`);
};

useEffect(() => {
const handleClickOutside = (e) => {
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
setIsOpen(false);
}
};

window.addEventListener('click', handleClickOutside);
return () => {
window.removeEventListener('click', handleClickOutside);
};
}, []);

return (
<div className={styles.dropdownContainer} ref={dropdownRef}>
<button
className={styles.dropdownButton2}
onClick={toggleDropdown1}
>
<Image
width={24}
height={24}
src="/images/dropdownbutton.png" alt="dropdownbutton" />
</button>
{isOpen && (
<ul className={styles.dropdownMenu}>
<div
className={styles.menuItem2}
onClick={() => handlePatch()}
>
수정하기
</div>
<div
className={styles.menuItem2}
onClick={() => handleDelete1()}
>
삭제하기
</div>
</ul>
)}
</div>
);
}
28 changes: 28 additions & 0 deletions components/BestItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import BestItemList from './BestItemList';
import { useState, useEffect } from "react";
import styles from '@/css/BestItem.module.css'
import instance from "@/lib/axios";

export default function BestItem({ }) {

const [posts, setPosts] = useState([]);

useEffect(() => {
instance.get(`/article/articleList?pageSize=3&orderBy='like'`)
.then((response) => {
setPosts(response.data.Articles);
})
.catch((error) => console.error(error));
}, []);

return (
<div className={styles.BestItemContain}>
<div className={styles.BestItemHeader}>베스트 게시글 </div>
<div className={styles.test123}>
{posts.map((post) => (
<BestItemList key={post.id} post={post} />
))}
</div>
</div>
);
}
Loading

0 comments on commit 2f5f746

Please sign in to comment.