Skip to content

Commit

Permalink
refactor: 상품 상세 페이지 컴포넌트 분리(#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
cho1ys committed Dec 6, 2024
1 parent 1c8267c commit 24e9c0e
Show file tree
Hide file tree
Showing 4 changed files with 319 additions and 333 deletions.
4 changes: 3 additions & 1 deletion src/components/common/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ const Container = styled.div`
gap: 8px;
`;

const Button = styled.button<{ active: boolean }>`
const Button = styled.button.withConfig({
shouldForwardProp: (prop) => prop !== 'active',
})<{ active: boolean }>`
padding: 8px 16px;
font-size: 1rem;
font-weight: 500;
Expand Down
2 changes: 1 addition & 1 deletion src/components/pages/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const HomePage: React.FC = () => {
const StyledLink = styled(Link)`
text-decoration: none;
color: inherit;
width: 100%;
&:link,
&:visited,
&:hover,
Expand Down
278 changes: 278 additions & 0 deletions src/components/pages/ProductDetailPage/CommentComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import { addComment, deleteComment, editComment } from './api/commentApi';

interface CommentProps {
reviews: Array<{ review: string; rating: number }>;
productId: number;
}

const CommentComponent: React.FC<CommentProps> = ({ reviews, productId }) => {
const [newComment, setNewComment] = useState('');
const [newCommentStar, setNewCommentStar] = useState(5);
const [visibleCount, setVisibleCount] = useState(10);
const [editingId, setEditingId] = useState<number | null>(null);
const [editingText, setEditingText] = useState('');
const [editingRating, setEditingRating] = useState(5);

const handleShowMore = () => {
setVisibleCount((prevCount) => prevCount + 10);
};

const handleCommentSubmit = (e: React.FormEvent) => {
e.preventDefault();
addComment(productId, { review: newComment, rating: newCommentStar });
setNewComment('');
};

const handleEditComment = (
commentId: number,
text: string,
rating: number
) => {
setEditingId(commentId);
setEditingText(text);
setEditingRating(rating);
};

const handleUpdateComment = async (commentId: number) => {
await editComment(commentId, {
review: editingText,
rating: editingRating,
});
setEditingId(null);
setEditingText('');
setEditingRating(5);
};

const handleCancelEdit = () => {
setEditingId(null);
setEditingText('');
setEditingRating(5);
};

const handleDeleteComment = (commentId: number) => {
deleteComment(commentId);
};

return (
<CommentSection>
<CommentForm onSubmit={handleCommentSubmit}>
<CommentInputWrapper>
<CommentInput
value={newComment}
onChange={(e) => setNewComment(e.target.value)}
placeholder="댓글을 입력하세요"
/>
<StarSelector
value={newCommentStar}
onChange={(e) => setNewCommentStar(Number(e.target.value))}
>
{[1, 2, 3, 4, 5].map((num) => (
<option key={num} value={num}>
{'⭐'.repeat(num)}
</option>
))}
</StarSelector>
<CommentSubmitButton type="submit">댓글 달기</CommentSubmitButton>
</CommentInputWrapper>
</CommentForm>
<div>
{reviews.slice(0, visibleCount).map((review, index) => (
<Comment key={index}>
{editingId === index ? (
<EditCommentForm>
<EditInput
value={editingText}
onChange={(e) => setEditingText(e.target.value)}
/>
<StarSelector
value={editingRating}
onChange={(e) => setEditingRating(Number(e.target.value))}
>
{[1, 2, 3, 4, 5].map((num) => (
<option key={num} value={num}>
{'⭐'.repeat(num)}
</option>
))}
</StarSelector>
<EditActions>
<ActionButton
onClick={() => handleUpdateComment(index)}
color="primary"
>
저장
</ActionButton>
<ActionButton onClick={handleCancelEdit} color="secondary">
취소
</ActionButton>
</EditActions>
</EditCommentForm>
) : (
<>
<CommentContent>
<CommentText>{review.review}</CommentText>
<CommentStars>{'⭐'.repeat(review.rating)}</CommentStars>
</CommentContent>
<CommentActions>
<ActionButton
onClick={() =>
handleEditComment(index, review.review, review.rating)
}
>
수정
</ActionButton>
<ActionButton onClick={() => handleDeleteComment(index)}>
삭제
</ActionButton>
</CommentActions>
</>
)}
</Comment>
))}

<ButtonContainer>
{visibleCount < reviews.length && (
<ViewMore onClick={handleShowMore}>더보기</ViewMore>
)}
</ButtonContainer>
</div>
</CommentSection>
);
};

const CommentForm = styled.form`
margin-bottom: 20px;
`;

const CommentInputWrapper = styled.div`
display: flex;
gap: 10px;
margin-bottom: 20px;
`;

const CommentInput = styled.input`
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
`;

const StarSelector = styled.select`
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: white;
`;

const CommentSubmitButton = styled.button`
padding: 10px 20px;
background-color: #2563eb;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
`;
const CommentActions = styled.div`
margin-left: 10px;
display: flex;
gap: 8px;
opacity: 0;
transition: opacity 0.2s ease-in-out;
`;
const Comment = styled.div`
margin-top: 10px;
padding: 15px;
background-color: #f5f5f5;
border-radius: 4px;
display: flex;
justify-content: space-between;
align-items: center;
&:hover ${CommentActions} {
opacity: 1;
}
`;

const CommentContent = styled.div`
display: flex;
align-items: center;
gap: 10px;
flex: 1;
`;

const CommentText = styled.span`
flex: 1;
`;

const ActionButton = styled.button`
padding: 4px 8px;
font-size: 12px;
background: transparent;
border: 1px solid #ddd;
border-radius: 4px;
color: #666;
cursor: pointer;
transition: all 0.2s ease-in-out;
&:hover {
background-color: #2563eb;
border-color: #2563eb;
color: white;
}
`;

const CommentStars = styled.div`
font-size: 16px;
color: #ffaa00;
`;

const ButtonContainer = styled.div`
display: flex;
justify-content: center;
`;

const ViewMore = styled.button`
margin-top: 20px;
border: 1px solid black;
background-color: transparent;
border-radius: 8px;
width: 200px;
height: 40px;
font-size: 14px;
&:hover {
cursor: pointer;
background-color: #2563eb;
color: white;
}
`;
const EditCommentForm = styled.div`
display: flex;
gap: 8px;
width: 100%;
align-items: center;
`;

const EditInput = styled.input`
flex: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
&:focus {
outline: none;
border-color: #2563eb;
}
`;

const EditActions = styled.div`
display: flex;
gap: 4px;
`;
const CommentSection = styled.div`
margin-top: 40px;
border-top: 1px solid #eee;
padding-top: 20px;
`;
export default CommentComponent;
Loading

0 comments on commit 24e9c0e

Please sign in to comment.