Skip to content
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
153 changes: 153 additions & 0 deletions src/components/frontWorkList/CommentBox.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { useState } from "react";
import styled from "styled-components";
import useModal from "../../hooks/useModal";

const CommentBox = ({commentData}) => {
const [commentText, setCommentText] = useState('');
const [charCount, setCharCount] = useState(0);
// useModal 사용해서 글자수 제한 모달 구현
const { isModalOpen, openModal, closeModal, Modal } = useModal();

// 댓글 갯수
const commentCount = commentData.length;

// open modal
const handleModal = (e) => {
const text = e.target.value;
if (text.length <= 1000 && !isModalOpen) {
setCommentText(text);
setCharCount(text.length);
} else {
setCommentText(text.slice(0,1000)); // 글자수 제한 초과 부분을 제외하고 저장
setCharCount(10);
openModal();
}
};

// close modal
const handelModalConfirm = () => {
closeModal();
}

return (
<CommentWrapper>
<CommentCount>댓글 {commentCount}</CommentCount>
<CommentContainer>
<TextAreaContainer>
<CommentTextArea
placeholder="이 문제집에 대한 평가를 댓글로 남겨주세요."
value={commentText}
onChange={handleModal}
maxLength={1000}
/>
<CharCount>{charCount} / 1000</CharCount>
</TextAreaContainer>
<SubmitButton>등록</SubmitButton>
</CommentContainer>
{/* modal */}
<Modal style={{ width: "400px", height: "220px" }}>
<ModalContent>
<div>
<ModalBody>제한된 글자수를 초과했습니다.</ModalBody>
</div>
<Button onClick={handelModalConfirm}>확인</Button>
</ModalContent>
</Modal>
</CommentWrapper>

);
};

const CommentWrapper = styled.div`
padding: 16px;
margin: 0 auto 40px;
background-color: #3F424E;
flex-direction: column;
align-items: flex-start;
`;

const CommentCount = styled.div`
margin-bottom: 11px;
front-size: 15px;
`;

const CommentContainer = styled.div`
display: flex;
justify-content: space-between;
`;

const TextAreaContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
`;

const CommentTextArea = styled.textarea`
padding: 8px;
resize: none;
background-color: #A7A7A7;
color: black;
font-size: 13.5px;
border: 1px solid #A7A7A7;
height: 57px;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
overflow: hidden;
outline: none;
border: none;
`;

const CharCount = styled.div`
text-align: right;
color: #393939;
padding: 0 15px;
background-color: #A7A7A7;
border-bottom-right-radius: 6px;
border-bottom-left-radius: 6px;;
`;

const SubmitButton = styled.button`
background-color: #007bff;
font-size: 15px;
color: black;
border: none;
padding: 10px 16px;
cursor: pointer;
background-color: #A7A7A7;
margin-left: 11px;
align-self: flex-end;
border-radius: 6px;
width: 91px;
height: 91px;
`;

const ModalContent = styled.div`
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
height: 100%;
margin-top: 60px;
font-size: 14px;
text-align: center;
`;

const ModalBody = styled.div`
margin-bottom: 20px;
color: #c7c7c7;
text-align: center;
margin-bottom: 30px;
`;

const Button = styled.button`
width: 301px;
height: 37px;
background-color: #5263ff;
font-size: 14px;
color: #ffffff;
border: none;
border-radius: 6px;
margin-top: 20px;
`;

export default CommentBox;
64 changes: 64 additions & 0 deletions src/components/frontWorkList/CommentItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import styled from "styled-components";
import UserIcon from "../../assets/people.png";
import LikeIcon from "../../assets/heart.png";

const CommentItem = ({ item }) => {
return (
<Wrapper>
<Name>
<Icon src={UserIcon} alt="작성자" /> {item.user}
</Name>
<Content>{item.content}</Content>
<Container>
<Date>{item.date}</Date>
<Like>
<Icon src={LikeIcon} alt="추천" /> {item.like}
</Like>
</Container>
</Wrapper>
);
};

const Wrapper = styled.div`
background-color: transparent;
border-color: #868686 transparent;
border-style: solid;
border-width: 1px 0;
padding: 17px;

&:not(:first-child) {
margin-top: -1px; // 첫 번째 div는 마진을 설정 안함
}
`;

const Name = styled.div`
display: flex;
align-items: center;
`;

const Content = styled.div`
padding: 14px 24px 7px;
`;

const Date = styled.div`
padding: 7px 24px;
`;

const Like = styled.div`
display: flex;
align-items: center;
background-color: #3F424E;
border-radius: 6px;
padding: 8px;
`;

const Icon = styled.img`
margin-right: 8px;
`;

const Container = styled.div`
display: flex;
justify-content: space-between;
`;

export default CommentItem;
125 changes: 125 additions & 0 deletions src/components/frontWorkList/CommentList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { useState, useEffect } from "react";
import styled from "styled-components";
import CommentItem from "./CommentItem";
import CommentBox from "./CommentBox";

function isEqual(arr1, arr2) {
if (arr1.length !== arr2.length) {
return false;
}

for (let i = 0; i < arr1.length; i++) {
if (arr1[i].id !== arr2[i].id) {
return false;
}
}

return true;
}

export default function CommentList() {
// 임시 댓글 data
const commentData = [
{id: 0, user: "소밍밍", content: "안녕하세요. 문제가 너무 좋네요. 덕분에 학습 잘 하고 갑니다.", date: "2023.08.20 21:23", like: 7},
{id: 1, user: "소밍밍", content: "안녕하세요. 문제가 너무 좋네요. 덕분에 학습 잘 하고 갑니다.", date: "2023.08.23 21:23", like: 5},
{id: 2, user: "소밍밍", content: "안녕하세요. 문제가 너무 좋네요. 덕분에 학습 잘 하고 갑니다.", date: "2023.08.10 21:23", like: 0},
{id: 3, user: "소밍밍", content: "안녕하세요. 문제가 너무 좋네요. 덕분에 학습 잘 하고 갑니다.", date: "2023.08.09 21:23", like: 5},
];

const [data, setData] = useState(commentData);
const [showMore, setShowMore] = useState(false); // 답안 댓글 토글
const [sortOption, setSortOption] = useState('latest'); // 초기 정렬 기준 : 최신순

useEffect(() => {
// 댓글 정렬
const sortData = (sortKey) => {
const sortedData = [...data]; // 원본 데이터를 변경하지 않기 위해 복사

if (sortKey === 'latest') {
sortedData.sort((a, b) => new Date(b.date) - new Date(a.date)); // 최신순
} else if (sortKey === 'oldest') {
sortedData.sort((a, b) => new Date(a.date) - new Date(b.date)); // 등록순
} else if (sortKey === 'mostLiked') {
sortedData.sort((a, b) => b.like - a.like); // 추천순
}

return sortedData;
};
const sortedData = sortData(sortOption);

// 정렬된 데이터와 현재 데이터가 다를 경우에만 업데이트
if (!isEqual(sortedData, data)) {
setData(sortedData);
}
}, [sortOption, data]);

const visibleBtn= data.length > 3; // 댓글이 3개 이상일 때만 버튼이 보임
const toggleShowMore = () => {
setShowMore(!showMore);
}

return(
<div>
<CommentBox commentData={commentData}/>

<SortOptions>
<SortButton
onClick={() => setSortOption('latest')}
active={sortOption === 'latest'}
>
최신순
</SortButton>
<SortButton
onClick={() => setSortOption('oldest')}
active={sortOption === 'oldest'}
>
등록순
</SortButton>
<SortButton
onClick={() => setSortOption('mostLiked')}
active={sortOption === 'mostLiked'}
>
추천순
</SortButton>
</SortOptions>

{data.slice(0, showMore ? data.length : 3).map((comment, index) => (
<CommentItem key={index} item={comment} />
))}
{visibleBtn && (
<ShowMoreBtn onClick={toggleShowMore}>
{showMore ? "댓글 숨기기" : "댓글 더보기"}
</ShowMoreBtn>
)}
</div>
);
};

const SortOptions = styled.div`
align-items: center;
display: flex;
padding: 5px 0;
margin-bottom: 11px;
`;

const SortButton = styled.button`
margin-right: 15spx;
background-color: transparent;
color: ${(props) => (props.active ? '#FFFFFF' : '#838383')};
font-size: 16px;
border: none;
cursor: pointer;
`;

const ShowMoreBtn = styled.button`
width: 1090px;
height: 37px;
border: none;
border-radius: 6px;
background-color: #3f424e;
color: #ffffff;
font-size: 14px;
font-weight: 500;
margin-bottom: 35px;
margin-top: 20px;
`;
Loading