-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev' into feature/page-style
- Loading branch information
Showing
48 changed files
with
2,712 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
import { NoticeCreateType } from '@/lib/types/noticeType'; | ||
|
||
interface ResponseType { | ||
id: number; | ||
} | ||
|
||
const createNotice = async (data: NoticeCreateType) => { | ||
const response = await axiosInstance.post<ResponseType>('/admin/notices', data); | ||
|
||
return response.data; | ||
}; | ||
|
||
export default createNotice; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
|
||
const deleteNotice = async (noticeId: number) => { | ||
await axiosInstance.delete(`/admin/notices/${noticeId}`); | ||
}; | ||
|
||
export default deleteNotice; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
import { AdminNoticeType } from '@/lib/types/noticeType'; | ||
|
||
const getAdminNotices = async () => { | ||
const result = await axiosInstance.get<AdminNoticeType[]>('/admin/notices'); | ||
|
||
return result.data; | ||
}; | ||
|
||
export default getAdminNotices; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
import { NoticeCategoryType } from '@/lib/types/noticeType'; | ||
|
||
const getNoticeCategories = async () => { | ||
const result = await axiosInstance.get<NoticeCategoryType[]>('/admin/notices/categories'); | ||
|
||
return result.data; | ||
}; | ||
|
||
export default getNoticeCategories; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
import { NoticeDetailType } from '@/lib/types/noticeType'; | ||
|
||
const getNoticeDetail = async (noticeId: number) => { | ||
const result = await axiosInstance.get<NoticeDetailType>(`/notices/${noticeId}`); | ||
|
||
return result.data; | ||
}; | ||
|
||
export default getNoticeDetail; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
|
||
const sendNoticeAlarm = async (noticeId: number) => { | ||
await axiosInstance.post(`/admin/notices/${noticeId}/alarm`); | ||
}; | ||
|
||
export default sendNoticeAlarm; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
|
||
const updateNoticePublic = async (noticeId: number) => { | ||
await axiosInstance.patch(`/admin/notices/${noticeId}`); | ||
}; | ||
|
||
export default updateNoticePublic; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import axios from 'axios'; | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
|
||
interface UploadImageType { | ||
order: number; | ||
extension: string; | ||
} | ||
|
||
interface UploadNoticeImagesProps { | ||
noticeId: number; | ||
imageExtensionData: UploadImageType[]; | ||
imageFileData: File[]; | ||
} | ||
|
||
interface PresignedResponseType { | ||
order: number; | ||
presignedUrl: string; | ||
} | ||
|
||
const uploadNoticeImages = async ({ noticeId, imageFileData, imageExtensionData }: UploadNoticeImagesProps) => { | ||
// 1. Presigned url 발급 요청 | ||
const presignedResponse = await axiosInstance.post<PresignedResponseType[]>( | ||
`/admin/notices/${noticeId}/presigned-url`, | ||
imageExtensionData | ||
); | ||
|
||
// 2. 발급 받은 Presigned url로 이미지 업로드 | ||
presignedResponse.data.forEach(async (value, index) => { | ||
await axios.put(value.presignedUrl, imageFileData[index], { | ||
headers: { | ||
'Content-Type': imageFileData[index].type, | ||
}, | ||
}); | ||
}); | ||
|
||
// 3. 이미지 업로드 완료 서버에 알림 | ||
await axiosInstance.post(`/admin/notices/${noticeId}/upload-complete`, imageExtensionData); | ||
}; | ||
|
||
export default uploadNoticeImages; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
import { Header, BodyRegular } from '@/styles/font.css'; | ||
import { vars } from '@/styles/theme.css'; | ||
|
||
export const container = style({ | ||
minHeight: '100vh', | ||
display: 'flex', | ||
}); | ||
|
||
export const nav = style({ | ||
padding: '1.5rem', | ||
|
||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: '3rem', | ||
|
||
borderRight: '2px solid', | ||
borderRightColor: vars.color.bluegray6, | ||
}); | ||
|
||
export const title = style([ | ||
Header, | ||
{ | ||
color: vars.color.bluegray8, | ||
}, | ||
]); | ||
|
||
export const menu = style([ | ||
BodyRegular, | ||
{ | ||
width: 200, | ||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: '2rem', | ||
}, | ||
]); | ||
|
||
export const main = style({ | ||
flexGrow: 1, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { ReactNode } from 'react'; | ||
|
||
import * as styles from './layout.css'; | ||
import Link from 'next/link'; | ||
|
||
interface AdminNoticeLayoutProps { | ||
children: ReactNode; | ||
} | ||
|
||
export default function AdminNoticeLayout({ children }: AdminNoticeLayoutProps) { | ||
return ( | ||
<section className={styles.container}> | ||
<nav className={styles.nav}> | ||
<h1 className={styles.title}>🤍 리스티웨이브 관리</h1> | ||
<ul className={styles.menu}> | ||
<Link href="/admin/topics">요청 주제</Link> | ||
<Link href="/admin/notice">게시물</Link> | ||
</ul> | ||
</nav> | ||
<main className={styles.main}>{children}</main> | ||
</section> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { style, styleVariants } from '@vanilla-extract/css'; | ||
import { vars } from '@/styles/theme.css'; | ||
import { Label } from '@/styles/font.css'; | ||
|
||
export const bodyRow = style([ | ||
Label, | ||
{ | ||
padding: '1rem 0.5rem', | ||
marginBottom: '1rem', | ||
borderBottom: `1px solid ${vars.color.bluegray6}`, | ||
|
||
display: 'grid', | ||
gridTemplateColumns: 'repeat(8, 1fr)', | ||
alignItems: 'center', | ||
|
||
textAlign: 'center', | ||
}, | ||
]); | ||
|
||
export const rowItem = style({ | ||
gridColumn: 'span 2', | ||
|
||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: '0.5rem', | ||
}); | ||
|
||
export const rowText = style({ | ||
overflow: 'hidden', | ||
textOverflow: 'ellipsis', | ||
whiteSpace: 'nowrap', | ||
}); | ||
|
||
export const buttons = style({ | ||
display: 'flex', | ||
justifyContent: 'center', | ||
gap: '0.5rem', | ||
}); | ||
|
||
const button = style({ | ||
padding: '0.5rem 1rem', | ||
borderRadius: '4px', | ||
backgroundColor: vars.color.blue, | ||
color: vars.color.white, | ||
|
||
':hover': { | ||
opacity: 0.7, | ||
}, | ||
}); | ||
|
||
export const variantsButton = styleVariants({ | ||
default: [button], | ||
disabled: [ | ||
button, | ||
{ | ||
opacity: 0.7, | ||
cursor: 'default', | ||
}, | ||
], | ||
}); | ||
|
||
export const modal = style({ | ||
width: '100%', | ||
height: '100vh', | ||
overflow: 'scroll', | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { useQuery } from '@tanstack/react-query'; | ||
|
||
import * as styles from './NoticeItem.css'; | ||
|
||
import useNotice from '@/hooks/queries/useNotice'; | ||
import useBooleanOutput from '@/hooks/useBooleanOutput'; | ||
|
||
import Modal from '@/components/Modal/Modal'; | ||
import NoticeDetailInfo from '@/components/NoticeDetail/NoticeDetailInfo'; | ||
|
||
import { AdminNoticeType, NoticeDetailType } from '@/lib/types/noticeType'; | ||
import formatDate from '@/lib/utils/dateFormat'; | ||
import { QUERY_KEYS } from '@/lib/constants/queryKeys'; | ||
|
||
import getNoticeDetail from '@/app/_api/notice/getNoticeDetail'; | ||
|
||
interface NoticeDetailModalProps { | ||
noticeId: number; | ||
} | ||
|
||
function NoticeDetailModal({ noticeId }: NoticeDetailModalProps) { | ||
const { data: notices } = useQuery<NoticeDetailType>({ | ||
queryKey: [QUERY_KEYS.getNoticeDetail], | ||
queryFn: () => getNoticeDetail(noticeId), | ||
enabled: !!noticeId, | ||
}); | ||
|
||
return <>{notices && <NoticeDetailInfo noticeData={notices} />}</>; | ||
} | ||
|
||
interface NoticeItemProps { | ||
notice: AdminNoticeType; | ||
} | ||
|
||
function NoticeItem({ notice }: NoticeItemProps) { | ||
const { deleteNoticeMutation, sendNoticeAlarmMutation, updateNoticePublicMutation } = useNotice(); | ||
const { isOn, handleSetOn, handleSetOff } = useBooleanOutput(); | ||
|
||
const { id, title, description, didSendAlarm, isExposed, category, createdDate } = notice; | ||
|
||
const handleDeleteNotice = () => { | ||
deleteNoticeMutation.mutate(id); | ||
}; | ||
|
||
const handleSendAlarm = () => { | ||
if (!isExposed) { | ||
alert('공개 게시물만 알림을 보낼 수 있어요.'); | ||
return; | ||
} | ||
if (didSendAlarm) { | ||
alert('이미 알림을 보낸 게시물입니다.'); | ||
return; | ||
} | ||
sendNoticeAlarmMutation.mutate(id, { | ||
onSuccess: () => alert('알림을 보냈어요.'), | ||
}); | ||
}; | ||
|
||
const handleTogglePublic = () => { | ||
updateNoticePublicMutation.mutate(id); | ||
}; | ||
|
||
return ( | ||
<> | ||
<tr className={styles.bodyRow}> | ||
<td>{formatDate(createdDate)}</td> | ||
<td>{category}</td> | ||
<td className={styles.rowItem}> | ||
<span className={styles.rowText}>{title}</span> | ||
<span className={styles.rowText}>{description}</span> | ||
</td> | ||
<td className={styles.buttons}> | ||
<button className={styles.variantsButton.default}>수정</button> | ||
<button className={styles.variantsButton.default} onClick={handleDeleteNotice}> | ||
삭제 | ||
</button> | ||
</td> | ||
<td> | ||
<button className={styles.variantsButton.default} onClick={handleSetOn}> | ||
미리보기 | ||
</button> | ||
</td> | ||
<td> | ||
<button | ||
className={didSendAlarm ? styles.variantsButton.disabled : styles.variantsButton.default} | ||
onClick={handleSendAlarm} | ||
disabled={didSendAlarm} | ||
> | ||
{`알림 ${didSendAlarm ? '완료' : '보내기'}`} | ||
</button> | ||
</td> | ||
<td> | ||
<select onChange={handleTogglePublic} value={isExposed ? '공개' : '비공개'}> | ||
<option>공개</option> | ||
<option>비공개</option> | ||
</select> | ||
</td> | ||
</tr> | ||
|
||
{isOn && ( | ||
<Modal size="large" handleModalClose={handleSetOff}> | ||
<section className={styles.modal}> | ||
<NoticeDetailModal noticeId={id} /> | ||
</section> | ||
</Modal> | ||
)} | ||
</> | ||
); | ||
} | ||
|
||
export default NoticeItem; |
29 changes: 29 additions & 0 deletions
29
src/app/admin/notice/create/_components/BlockContainer.css.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { BodyRegular } from '@/styles/font.css'; | ||
import { vars } from '@/styles/theme.css'; | ||
import { style } from '@vanilla-extract/css'; | ||
|
||
export const container = style({ | ||
padding: '1rem 1rem', | ||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: '1.5rem', | ||
}); | ||
|
||
export const wrapper = style({ | ||
display: 'flex', | ||
justifyContent: 'space-between', | ||
alignItems: 'center', | ||
}); | ||
|
||
export const title = style([BodyRegular]); | ||
|
||
export const deleteButton = style({ | ||
color: vars.color.red, | ||
}); | ||
|
||
export const content = style({ | ||
height: '100%', | ||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: '1rem', | ||
}); |
Oops, something went wrong.