-
Notifications
You must be signed in to change notification settings - Fork 6
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
[FE] 아티클 에디터 페이지를 제작한다. #993
Changes from all commits
681d3ac
188d6ed
0232707
d2a8f07
d89569d
8776465
756b19a
159a8b2
040f1ad
b3d184a
2f2992d
f540185
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import styled from '@emotion/styled'; | ||
|
||
interface Props { | ||
children: React.ReactNode; | ||
} | ||
|
||
const DesktopLayout = ({ children }: Props) => { | ||
return <S.BodyWrapper>{children}</S.BodyWrapper>; | ||
}; | ||
|
||
export default DesktopLayout; | ||
|
||
const S = { | ||
BodyWrapper: styled.div` | ||
max-width: 120rem; | ||
min-height: 100dvh; | ||
|
||
margin: 0 auto; | ||
box-sizing: border-box; | ||
`, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import styled from '@emotion/styled'; | ||
|
||
import theme from '@/styles/theme'; | ||
|
||
interface Props { | ||
children: React.ReactNode; | ||
} | ||
|
||
const MobileLayout = ({ children }: Props) => { | ||
return <S.BodyWrapper>{children}</S.BodyWrapper>; | ||
}; | ||
|
||
export default MobileLayout; | ||
|
||
const S = { | ||
BodyWrapper: styled.div` | ||
max-width: 60rem; | ||
min-height: 100dvh; | ||
|
||
margin: 0 auto; | ||
box-sizing: border-box; | ||
|
||
border-left: 0.1rem solid ${theme.palette.grey200}; | ||
border-right: 0.1rem solid ${theme.palette.grey200}; | ||
box-shadow: 0 0 2rem ${theme.palette.grey100}; | ||
`, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,4 +22,6 @@ export const ROUTE_PATH = { | |
/* etc */ | ||
location: '/location', | ||
myPage: '/my-page', | ||
admin: '/admin', | ||
articleEditor: '/isHaileyGod', | ||
Comment on lines
+25
to
+26
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. 신이었다.. 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. ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 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,21 @@ | ||
import { useMutation } from '@tanstack/react-query'; | ||
import { useNavigate } from 'react-router-dom'; | ||
|
||
import { postArticle } from '@/apis/article'; | ||
import { ROUTE_PATH } from '@/constants/routePath'; | ||
import useToast from '@/hooks/useToast'; | ||
|
||
const usePostArticleQuery = () => { | ||
const { showToast } = useToast(); | ||
const navigate = useNavigate(); | ||
|
||
return useMutation({ | ||
mutationFn: postArticle, | ||
onSuccess: () => { | ||
showToast({ message: '아티클 등록 완료!' }); | ||
navigate(ROUTE_PATH.admin); | ||
}, | ||
}); | ||
}; | ||
|
||
export default usePostArticleQuery; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import styled from '@emotion/styled'; | ||
import { Link } from 'react-router-dom'; | ||
|
||
import { ROUTE_PATH } from '@/constants/routePath'; | ||
import { boxShadowSpread, flexRow, title2, title3 } from '@/styles/common'; | ||
|
||
const AdminPage = () => { | ||
return ( | ||
<S.PageWrapper> | ||
<S.QuestionBox> | ||
<S.QuestionText>Is Hailey God?</S.QuestionText> | ||
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. 여기도 갓있다ㅋㅋㅋㅋㅋ인죵 |
||
<S.ButtonWrapper> | ||
<S.Button color={'red'}>No</S.Button> | ||
<Link to={ROUTE_PATH.articleEditor}> | ||
<S.Button>Yes</S.Button> | ||
</Link> | ||
</S.ButtonWrapper> | ||
</S.QuestionBox> | ||
</S.PageWrapper> | ||
); | ||
}; | ||
|
||
export default AdminPage; | ||
|
||
const S = { | ||
PageWrapper: styled.div` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
height: 100vh; /* 전체 화면 높이에 맞춰 정렬 */ | ||
|
||
background-color: ${({ theme }) => theme.palette.white}; | ||
`, | ||
|
||
QuestionBox: styled.div` | ||
padding: 2rem; | ||
|
||
background-color: ${({ theme }) => theme.palette.white}; | ||
|
||
text-align: center; | ||
border-radius: 10px; | ||
${boxShadowSpread} | ||
`, | ||
|
||
QuestionText: styled.h2` | ||
margin-bottom: 2rem; | ||
|
||
color: ${({ theme }) => theme.palette.black}; | ||
${title2} | ||
`, | ||
ButtonWrapper: styled.div` | ||
${flexRow} | ||
justify-content: center; | ||
gap: 1rem; | ||
`, | ||
Button: styled.button<{ color?: string }>` | ||
padding: 0.8rem 2rem; | ||
border: none; | ||
|
||
background-color: ${({ theme, color }) => (color === 'red' ? theme.palette.red500 : theme.palette.green500)}; | ||
|
||
color: ${({ theme }) => theme.palette.white}; | ||
${title3} | ||
border-radius: 5px; | ||
cursor: pointer; | ||
transition: background-color 0.3s; | ||
|
||
&:hover { | ||
background-color: ${({ theme, color }) => (color === 'red' ? theme.palette.red600 : theme.palette.green600)}; | ||
} | ||
`, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import styled from '@emotion/styled'; | ||
import MarkdownEditor from '@uiw/react-markdown-editor'; | ||
import { useState } from 'react'; | ||
|
||
import Button from '@/components/_common/Button/Button'; | ||
import FormField from '@/components/_common/FormField/FormField'; // 예시로 주신 FormField 컴포넌트 사용 | ||
import DesktopLayout from '@/components/_common/layout/DesktopLayout'; | ||
import usePostArticleQuery from '@/hooks/query/usePostArticleQuery'; | ||
import { flexColumn, flexRow, flexSpaceBetween, title2 } from '@/styles/common'; | ||
|
||
const ArticleEditorPage = () => { | ||
const [title, setTitle] = useState(''); | ||
const [keyword, setKeyword] = useState(''); | ||
const [summary, setSummary] = useState(''); | ||
const [thumbnail] = useState(''); | ||
const [content, setContent] = useState('# 여기에 아티클을 작성해주세요'); | ||
|
||
const { mutate: addArticle } = usePostArticleQuery(); | ||
|
||
const handleSubmit = () => { | ||
addArticle({ | ||
title, | ||
content, | ||
keyword, | ||
thumbnail, | ||
summary, | ||
}); | ||
}; | ||
|
||
return ( | ||
<> | ||
<S.Header> | ||
<S.HeaderContents> | ||
<S.Title>방끗 Article Editor</S.Title> | ||
|
||
{/* 저장 버튼 */} | ||
<Button type="submit" label="저장" isSquare color="dark" onClick={handleSubmit} size="small" /> | ||
</S.HeaderContents> | ||
</S.Header> | ||
<DesktopLayout> | ||
<S.Form | ||
onSubmit={e => { | ||
e.preventDefault(); | ||
handleSubmit(); | ||
}} | ||
> | ||
{/* 제목 폼 필드 */} | ||
<FormField> | ||
<FormField.Label label="아티클 제목" htmlFor="title" required /> | ||
<FormField.Input | ||
placeholder="제목을 입력하세요" | ||
onChange={e => setTitle(e.target.value)} | ||
name="title" | ||
id="title" | ||
value={title} | ||
/> | ||
</FormField> | ||
|
||
{/* 키워드 폼 필드 */} | ||
<FormField> | ||
<FormField.Label label="키워드" htmlFor="keyword" required /> | ||
<FormField.Input | ||
placeholder="키워드를 입력하세요" | ||
onChange={e => setKeyword(e.target.value)} | ||
name="keyword" | ||
id="keyword" | ||
value={keyword} | ||
/> | ||
</FormField> | ||
|
||
{/* 요약 폼 필드 */} | ||
<FormField> | ||
<FormField.Label label="요약" htmlFor="summary" required /> | ||
<FormField.Input | ||
placeholder="요약을 입력하세요" | ||
onChange={e => setSummary(e.target.value)} | ||
name="summary" | ||
id="summary" | ||
value={summary} | ||
/> | ||
</FormField> | ||
|
||
{/* MarkdownEditor로 content 작성 */} | ||
<div style={{ marginBottom: '20px' }}> | ||
<MarkdownEditor | ||
value={content} | ||
height="70vh" | ||
onChange={value => setContent(value)} | ||
visible={true} | ||
enablePreview={true} | ||
/> | ||
</div> | ||
</S.Form> | ||
</DesktopLayout> | ||
</> | ||
); | ||
}; | ||
|
||
export default ArticleEditorPage; | ||
|
||
const S = { | ||
Header: styled.header` | ||
${flexRow} | ||
justify-content: center; | ||
|
||
width: 100vw; | ||
height: 50px; | ||
border-bottom: 1px solid ${({ theme }) => theme.palette.grey200}; | ||
|
||
box-sizing: border-box; | ||
|
||
margin-bottom: 20px; | ||
`, | ||
HeaderContents: styled.div` | ||
width: 120rem; | ||
${flexRow} | ||
${flexSpaceBetween} | ||
align-items: center; | ||
`, | ||
Title: styled.h1` | ||
${title2} | ||
`, | ||
Form: styled.form` | ||
${flexColumn} | ||
gap: 20px; | ||
`, | ||
}; |
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.
return할때 as 타입단언 해주면 좋을거같아요
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.
앗 네넹 ~!
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.
앗 그런데 이부분이 post response 라....! 타입이 따로 없네요
res 를 리턴하지 말까요?
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.
맞네요 !