-
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.
- Loading branch information
Showing
22 changed files
with
435 additions
and
83 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
import { NoticeCreateType } from '@/lib/types/noticeType'; | ||
|
||
interface UpdateNoticeRequestType { | ||
noticeData: NoticeCreateType; | ||
noticeId: number; | ||
} | ||
|
||
const updateNotice = async ({ noticeData, noticeId }: UpdateNoticeRequestType) => { | ||
await axiosInstance.put<ResponseType>(`/admin/notices/${noticeId}`, noticeData); | ||
}; | ||
|
||
export default updateNotice; |
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,21 @@ | ||
import { style, styleVariants } from '@vanilla-extract/css'; | ||
import { BodyBold, BodyRegular } from '@/styles/font.css'; | ||
import { vars } from '@/styles/theme.css'; | ||
|
||
export const nav = style({ | ||
minWidth: 200, | ||
|
||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: '2rem', | ||
}); | ||
|
||
export const variantLink = styleVariants({ | ||
default: [BodyRegular], | ||
selected: [ | ||
BodyBold, | ||
{ | ||
color: vars.color.blue, | ||
}, | ||
], | ||
}); |
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,31 @@ | ||
'use client'; | ||
|
||
import { usePathname } from 'next/navigation'; | ||
import Link from 'next/link'; | ||
|
||
import * as styles from './NavLinks.css'; | ||
|
||
interface NavLinksProps { | ||
links: Array<Record<string, string>>; | ||
} | ||
|
||
export default function NavLinks({ links }: NavLinksProps) { | ||
const pathname = usePathname(); | ||
|
||
return ( | ||
<nav className={styles.nav}> | ||
{links.map((link) => { | ||
const isActive = pathname && pathname.startsWith(link.path); | ||
return ( | ||
<Link | ||
key={link.path} | ||
href={link.path} | ||
className={isActive ? styles.variantLink.selected : styles.variantLink.default} | ||
> | ||
{link.label} | ||
</Link> | ||
); | ||
})} | ||
</nav> | ||
); | ||
} |
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
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,145 @@ | ||
'use client'; | ||
|
||
import { useRouter } from 'next/navigation'; | ||
import { BaseSyntheticEvent, useEffect } from 'react'; | ||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | ||
import { FormProvider, useForm } from 'react-hook-form'; | ||
|
||
import * as styles from '../../create/page.css'; | ||
|
||
import updateNotice from '@/app/_api/notice/updateNotice'; | ||
import uploadNoticeImages from '@/app/_api/notice/uploadNoticeImages'; | ||
import getNoticeDetail from '@/app/_api/notice/getNoticeDetail'; | ||
|
||
import { NoticeCreateType, NoticeDetailType } from '@/lib/types/noticeType'; | ||
import { noticeDescriptionRules, noticeTitleRules } from '@/lib/constants/formInputValidationRules'; | ||
import { QUERY_KEYS } from '@/lib/constants/queryKeys'; | ||
import { NOTICE_CATEGORY_NAME } from '@/lib/constants/notice'; | ||
import { formatImageData, formatNoticeData } from '@/lib/utils/formatDataForNotice'; | ||
|
||
import CategoryDropdown from '../../create/_components/CategoryDropdown'; | ||
import ContentsBody from '../../create/_components/ContentsBody'; | ||
|
||
type NoticeCategoryNameType = (typeof NOTICE_CATEGORY_NAME)[keyof typeof NOTICE_CATEGORY_NAME]; | ||
|
||
/** 카테고리 값에 해당하는 카테고리 코드 반환 */ | ||
function getCodeByObject(value: NoticeCategoryNameType) { | ||
return Object.values(NOTICE_CATEGORY_NAME).findIndex((field) => field === value) + 1; | ||
} | ||
|
||
export default function EditNotice({ params }: { params: { noticeId: number } }) { | ||
const noticeId = params.noticeId; | ||
|
||
const { data: notice } = useQuery<NoticeDetailType>({ | ||
queryKey: [QUERY_KEYS.getNoticeDetail], | ||
queryFn: () => getNoticeDetail(params.noticeId), | ||
enabled: !!params.noticeId, | ||
}); | ||
|
||
const router = useRouter(); | ||
const queryClient = useQueryClient(); | ||
const methods = useForm<NoticeCreateType>({ | ||
mode: 'onChange', | ||
defaultValues: { | ||
categoryCode: 1, | ||
title: notice?.title, | ||
description: notice?.description, | ||
contents: [ | ||
{ | ||
order: 0, | ||
type: 'subtitle', | ||
description: '', | ||
}, | ||
], | ||
}, | ||
}); | ||
|
||
// prettier-ignore | ||
const { register, handleSubmit, formState: { errors, isValid }, reset } = methods; | ||
|
||
const uploadImageMutation = useMutation({ | ||
mutationFn: uploadNoticeImages, | ||
onError: () => alert('이미지 업로드를 다시 시도해주세요.'), | ||
}); | ||
|
||
const editNoticeMutation = useMutation({ | ||
mutationFn: updateNotice, | ||
onSuccess: () => { | ||
const originData = methods.getValues(); | ||
const { imageExtensionData, imageFileData } = formatImageData(originData); | ||
|
||
// 생성된 공지 ID에 이미지 업로드 | ||
if (imageExtensionData.length !== 0) { | ||
uploadImageMutation.mutate({ | ||
noticeId, | ||
imageExtensionData, | ||
imageFileData, | ||
}); | ||
} | ||
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.getAdminAllNotice] }); | ||
router.push('/admin/notice'); | ||
}, | ||
onError: () => alert('게시물 수정을 다시 시도해주세요.'), | ||
}); | ||
|
||
/** 게시물 수정 */ | ||
const onSubmit = (data: NoticeCreateType, e?: BaseSyntheticEvent) => { | ||
e?.preventDefault(); | ||
|
||
const noticeData = formatNoticeData(data); | ||
editNoticeMutation.mutate({ noticeData, noticeId }); | ||
}; | ||
|
||
useEffect(() => { | ||
if (notice) { | ||
reset({ | ||
title: notice.title, | ||
categoryCode: getCodeByObject(notice.category), | ||
description: notice.description, | ||
contents: notice.contents.map((obj) => | ||
Object.fromEntries(Object.entries(obj).filter(([_, value]) => value !== null)) | ||
), | ||
}); | ||
} | ||
}, [notice, reset]); | ||
|
||
return ( | ||
<FormProvider {...methods}> | ||
<form className={styles.container}> | ||
<h1>게시물 수정</h1> | ||
<CategoryDropdown /> | ||
<div className={styles.row}> | ||
<label className={styles.rowLabel}>제목 *</label> | ||
<div className={styles.field}> | ||
<input | ||
className={styles.rowInput} | ||
placeholder="제목 또는 알림 메시지 문구를 입력해 주세요. (최대 30자)" | ||
{...register('title', noticeTitleRules)} | ||
/> | ||
<p className={styles.rowErrorMessage}>{errors.title && errors.title?.message}</p> | ||
</div> | ||
</div> | ||
<div className={styles.row}> | ||
<label className={styles.rowLabel}>소개 *</label> | ||
<div className={styles.field}> | ||
<input | ||
className={styles.rowInput} | ||
placeholder="글 소개하는 짧은 문구를 입력해 주세요. (최대 30자)" | ||
{...register('description', noticeDescriptionRules)} | ||
/> | ||
<p className={styles.rowErrorMessage}>{errors.description && errors.description.message}</p> | ||
</div> | ||
</div> | ||
<ContentsBody /> | ||
<button | ||
type="button" | ||
onClick={handleSubmit(onSubmit)} | ||
disabled={!isValid} | ||
className={isValid ? styles.savedButton.active : styles.savedButton.default} | ||
> | ||
수정하기 | ||
</button> | ||
</form> | ||
</FormProvider> | ||
); | ||
} |
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
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
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
Oops, something went wrong.