-
Notifications
You must be signed in to change notification settings - Fork 1
[Feat] content-detail-page #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import { IContent } from '@/types/content'; | ||
| import { httpClient } from './http.api'; | ||
|
|
||
| export interface FetchContentParams { | ||
| content_id: string | undefined; | ||
| } | ||
|
|
||
| export const fetchContent = async ({ content_id }: FetchContentParams) => { | ||
| try { | ||
| const response = await httpClient.get<IContent>(`/api/posts/${content_id}`); | ||
| return response.data; | ||
| } catch (e) { | ||
| console.log('에러났어', e); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 앗, 에러 메시지가 귀엽네요 😄 |
||
| } | ||
| }; | ||
|
|
||
| export const fetchLikedPost = async ({ content_id }: FetchContentParams) => { | ||
| try { | ||
| const response = await httpClient.post(`/api/posts/${content_id}/like`); | ||
| return response.data; | ||
| } catch (e) { | ||
| console.log('에러났어', e); | ||
| } | ||
| }; | ||
|
|
||
| export const fetchSolved = async ({ content_id }: FetchContentParams) => { | ||
| try { | ||
| const response = await httpClient.post<void>( | ||
| `api/posts/${content_id}/solved` | ||
| ); | ||
| return response.data; | ||
| } catch (e) { | ||
| console.log('에러났어', e); | ||
| } | ||
| }; | ||
|
|
||
| export const fetchContentDelete = async ({ | ||
| content_id, | ||
| }: FetchContentParams) => { | ||
| try { | ||
| const response = await httpClient.delete<void>(`api/posts/${content_id}`); | ||
| return response.data; | ||
| } catch (e) { | ||
| console.log('에러났어', e); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import { useRef } from 'react'; | ||
| import styled from 'styled-components'; | ||
|
|
||
| export default function QuestionComment() { | ||
| const textRef = useRef<HTMLTextAreaElement>(null); | ||
|
|
||
| const handleInput = () => { | ||
| if (textRef.current) { | ||
| textRef.current.style.height = 'auto'; | ||
| textRef.current.style.height = `${textRef.current.scrollHeight}px`; | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <QuestionCommentStyle> | ||
| <h1 className='commentTitle'>답변</h1> | ||
| <div className='commentInput'> | ||
| <textarea ref={textRef} onInput={handleInput}></textarea> | ||
| </div> | ||
| </QuestionCommentStyle> | ||
| ); | ||
| } | ||
|
|
||
| const QuestionCommentStyle = styled.div` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 스타일 컴포넌트와 classname을 혼합해서 쓰시는 이유가 있을까요? 👀 물론 class name을 혼합하여 쓰는 것이 잘못된 방식은 아닙니다. |
||
| .commentTitle { | ||
| font-size: 1rem; | ||
| font-weight: bold; | ||
| } | ||
|
|
||
| .commentInput { | ||
| textarea { | ||
| resize: none; | ||
| width: 100%; | ||
| max-height: 220px; | ||
| font-size: 1rem; | ||
| margin-top: 1rem; | ||
| padding: 1rem 1.5rem; | ||
| border: 1px solid #727272; | ||
| border-radius: 20px; | ||
| outline: none; | ||
| } | ||
|
|
||
| textarea::-webkit-scrollbar { | ||
| display: none; | ||
| } | ||
| } | ||
| `; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import styled from 'styled-components'; | ||
|
|
||
| interface Props { | ||
| content: string | undefined; | ||
| } | ||
|
|
||
| export default function QuestionContent({ content }: Props) { | ||
| return <QuestionContentStyle>{content}</QuestionContentStyle>; | ||
| } | ||
|
|
||
| const QuestionContentStyle = styled.div` | ||
| font-size: 15px; | ||
| `; | ||
|
Comment on lines
+7
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이정도 컴포넌트라면 단순 스타일 컴포넌트만으로 부모에서 만들어 써도 되지 않았을까 싶기는 하네요 🤔 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| import { useLiked } from '@/hooks/useLiked'; | ||
| import { HeartIcon } from '@heroicons/react/24/outline'; | ||
| import styled from 'styled-components'; | ||
|
|
||
| interface Props { | ||
| solve: boolean | undefined; | ||
| nicknameCheck: string | undefined; | ||
| handleSolvedClick(): void; | ||
| } | ||
|
|
||
| export default function QuestionFooter({ | ||
| solve, | ||
| nicknameCheck, | ||
| handleSolvedClick, | ||
| }: Props) { | ||
| const { like, post, handleHeartClick } = useLiked(); | ||
|
|
||
| return ( | ||
| <QuestionFooterStyle> | ||
| <div className='tags'> | ||
| {post?.tags && | ||
| post?.tags.split(',').map((tag) => ( | ||
| <div className='tag' key={tag}> | ||
| {tag} | ||
| </div> | ||
| ))} | ||
| </div> | ||
| <div className='footerWrap'> | ||
| <div className='likes' onClick={handleHeartClick}> | ||
| <span className='heartIcon'> | ||
| <HeartIcon | ||
| className={`likeHeart ${like === true ? 'likeFill' : ''}`} | ||
| /> | ||
| </span> | ||
| <span className='likeCount'>{post?.like_count}</span> | ||
| </div> | ||
| </div> | ||
| {post?.nickname === nicknameCheck && ( | ||
| <div className={`solvedWrap ${solve ? 'solve' : 'problem'}`}> | ||
| <div className='solvedStatus'> | ||
| <div className='left'> | ||
| {solve ? ( | ||
| <div className='solvedTitle'>해결된 질문이에요!</div> | ||
| ) : ( | ||
| <> | ||
| <div className='solvedTitle'>질문이 해결 되었나요?</div> | ||
| <div>해결되었다면 상태를 변경해주세요.</div> | ||
| </> | ||
| )} | ||
| </div> | ||
| <div className='right'> | ||
| <button | ||
| className={`solveButton ${solve ? 'solve' : 'problem'}`} | ||
| onClick={handleSolvedClick} | ||
| > | ||
| {solve ? 'problem' : 'solve'} | ||
| </button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| )} | ||
| </QuestionFooterStyle> | ||
| ); | ||
| } | ||
|
|
||
| const QuestionFooterStyle = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 12px; | ||
| font-size: 15px; | ||
|
|
||
| .tags { | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| gap: 10px; | ||
| .tag { | ||
| background-color: #deffe2; | ||
| color: #858585; | ||
| font-family: 'Pretendard-Light', Helvetica; | ||
| font-size: 12px; | ||
| padding: 8px 16px; | ||
| border-radius: 12px; | ||
| } | ||
| } | ||
|
|
||
| .footerWrap { | ||
| display: flex; | ||
| line-height: 24px; | ||
| gap: 20px; | ||
| .likes { | ||
| display: flex; | ||
| cursor: pointer; | ||
|
|
||
| .likeHeart { | ||
| width: 24px; | ||
| } | ||
| .likeFill { | ||
| fill: #ff9bd2; | ||
| stroke: #ff9bd2; | ||
| } | ||
| } | ||
|
|
||
| .scrapped { | ||
| display: flex; | ||
| cursor: pointer; | ||
|
|
||
| .scrapIcon { | ||
| svg { | ||
| width: 24px; | ||
| } | ||
|
|
||
| .fill { | ||
| fill: #575757; | ||
| stroke: #575757; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| .scrapped:active, | ||
| .likes:active { | ||
| transform: translateY(5px); | ||
| } | ||
| } | ||
|
|
||
| .solvedWrap { | ||
| width: 100%; | ||
| border-radius: 20px; | ||
| .solvedStatus { | ||
| padding: 2.5rem 3.5rem; | ||
| display: flex; | ||
| justify-content: space-between; | ||
| font-size: 1.3rem; | ||
|
|
||
| .left { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 20px; | ||
|
|
||
| .solvedTitle { | ||
| font-size: 2rem; | ||
| } | ||
| } | ||
| .right { | ||
| display: flex; | ||
| align-items: center; | ||
| .solveButton { | ||
| cursor: pointer; | ||
| border: 1px solid #e6e6e6; | ||
| font-size: 1.5rem; | ||
| height: fit-content; | ||
| padding: 1rem 1.5rem; | ||
| border-radius: 50px; | ||
| } | ||
|
|
||
| .solveButton:hover { | ||
| scale: 1.1; | ||
| transition: all 50ms ease-in-out; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| .solve { | ||
| background-color: #c9ffcf; | ||
|
|
||
| .right { | ||
| .solveButton { | ||
| background-color: #f7f7f7; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| .problem { | ||
| background-color: #f7f7f7; | ||
|
|
||
| .right { | ||
| .solveButton { | ||
| background-color: #c9ffcf; | ||
| } | ||
| } | ||
| } | ||
| `; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined가 올 수 있는 경우에는 ?를 이용하여 표시하기도 한답니다.
근데, 단 하나의 인자만 온다면 굳이 interface로 선언할 필요가 있을까요? 🤔
type으로 선언해도 충분할 것 같기도 합니다.
그렇다면 ?는 못쓰겠지만요 😅