From 85a5eff70fa74db56302b10970f408fc07f705d1 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Sat, 21 Feb 2026 01:26:33 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=EB=A7=88=EA=B7=B8=EB=84=B7=20?= =?UTF-8?q?=EA=B8=80=20=EA=B4=80=EB=A6=AC=20=ED=83=80=EC=9E=85=20=EB=B0=8F?= =?UTF-8?q?=20mock=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MagnetPostContent, MagnetPostDetail, MagnetPostReqBody 타입과 fetchMagnetPost, saveMagnetPost, fetchManageableMagnets mock 함수 추가 Co-Authored-By: Claude Opus 4.6 --- src/domain/admin/blog/magnet/mock.ts | 59 ++++++++++++++++++++++++++- src/domain/admin/blog/magnet/types.ts | 43 +++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/src/domain/admin/blog/magnet/mock.ts b/src/domain/admin/blog/magnet/mock.ts index bd70823fe..016215604 100644 --- a/src/domain/admin/blog/magnet/mock.ts +++ b/src/domain/admin/blog/magnet/mock.ts @@ -1,6 +1,12 @@ // TODO: API 준비 후 src/api/magnet/ 폴더로 이동하고 실제 API 호출로 교체 import { IPageInfo } from '@/types/interface'; -import { MagnetListItem, MagnetTypeKey } from './types'; +import { + MANAGEABLE_MAGNET_TYPES, + MagnetListItem, + MagnetPostDetail, + MagnetPostReqBody, + MagnetTypeKey, +} from './types'; const MOCK_MAGNETS: MagnetListItem[] = [ { @@ -153,3 +159,54 @@ export function deleteMagnet(id: number): void { const index = MOCK_MAGNETS.findIndex((m) => m.id === id); if (index !== -1) MOCK_MAGNETS.splice(index, 1); } + +// --- 마그넷 글 관리 (포스트) --- + +const MOCK_MAGNET_POSTS: Record = {}; + +function buildDefaultPost(magnetId: number): MagnetPostDetail { + const magnet = MOCK_MAGNETS.find((m) => m.id === magnetId); + return { + magnetId, + type: magnet?.type ?? 'RESOURCE', + title: magnet?.title ?? '', + metaDescription: '', + thumbnail: '', + displayDate: magnet?.displayDate ?? null, + endDate: magnet?.endDate ?? null, + hasCommonForm: false, + content: '', + isVisible: false, + }; +} + +// TODO: API 준비 후 server-side fetch로 교체 +export async function fetchMagnetPost( + magnetId: number, +): Promise { + return MOCK_MAGNET_POSTS[magnetId] ?? buildDefaultPost(magnetId); +} + +// TODO: API 준비 후 useSaveMagnetPostMutation React Query 훅으로 교체 +export function saveMagnetPost(body: MagnetPostReqBody): void { + const magnet = MOCK_MAGNETS.find((m) => m.id === body.magnetId); + MOCK_MAGNET_POSTS[body.magnetId] = { + magnetId: body.magnetId, + type: magnet?.type ?? 'RESOURCE', + title: magnet?.title ?? '', + metaDescription: body.metaDescription, + thumbnail: body.thumbnail, + displayDate: body.displayDate, + endDate: body.endDate, + hasCommonForm: body.hasCommonForm, + content: body.content, + isVisible: body.isVisible, + }; +} + +// TODO: API 준비 후 React Query 훅으로 교체 +export function fetchManageableMagnets(): MagnetListItem[] { + return MOCK_MAGNETS.filter((m) => + MANAGEABLE_MAGNET_TYPES.includes(m.type), + ); +} diff --git a/src/domain/admin/blog/magnet/types.ts b/src/domain/admin/blog/magnet/types.ts index e14f7c8db..4f67a3787 100644 --- a/src/domain/admin/blog/magnet/types.ts +++ b/src/domain/admin/blog/magnet/types.ts @@ -45,3 +45,46 @@ export interface MagnetFilterValues { type: string; titleKeyword: string; } + +// --- 마그넷 글 관리 (포스트) --- + +/** 프로그램 추천 슬롯 */ +export interface MagnetProgramRecommendItem { + id: string | null; + ctaTitle?: string; + ctaLink?: string; +} + +/** 마그넷 콘텐츠 (JSON으로 직렬화하여 저장) */ +export interface MagnetPostContent { + programRecommend: MagnetProgramRecommendItem[]; + magnetRecommend: (number | null)[]; + lexicalBefore?: string; + lexicalAfter?: string; +} + +/** 마그넷 포스트 상세 (단건 조회) */ +export interface MagnetPostDetail { + magnetId: number; + type: MagnetTypeKey; + title: string; + metaDescription: string; + thumbnail: string; + displayDate: string | null; + endDate: string | null; + hasCommonForm: boolean; + content: string; + isVisible: boolean; +} + +/** 마그넷 포스트 저장 요청 */ +export interface MagnetPostReqBody { + magnetId: number; + metaDescription: string; + thumbnail: string; + displayDate: string | null; + endDate: string | null; + hasCommonForm: boolean; + content: string; + isVisible: boolean; +} From 82960b51e6615eda3facfca4d27bce820cebdf09 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Sat, 21 Feb 2026 01:27:26 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=EB=A7=88=EA=B7=B8=EB=84=B7=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EA=B7=B8=EB=9E=A8=20=EC=B6=94=EC=B2=9C=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A7=88=EA=B7=B8=EB=84=B7=20=EC=B6=94=EC=B2=9C=20?= =?UTF-8?q?=EC=84=B9=EC=85=98=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 챌린지 프로그램 필터링 + CTA 소제목 자동완성 기능 포함 Co-Authored-By: Claude Opus 4.6 --- .../section/MagnetProgramRecommendSection.tsx | 165 ++++++++++++++++++ .../magnet/section/MagnetRecommendSection.tsx | 72 ++++++++ 2 files changed, 237 insertions(+) create mode 100644 src/domain/admin/blog/magnet/section/MagnetProgramRecommendSection.tsx create mode 100644 src/domain/admin/blog/magnet/section/MagnetRecommendSection.tsx diff --git a/src/domain/admin/blog/magnet/section/MagnetProgramRecommendSection.tsx b/src/domain/admin/blog/magnet/section/MagnetProgramRecommendSection.tsx new file mode 100644 index 000000000..ca2e93744 --- /dev/null +++ b/src/domain/admin/blog/magnet/section/MagnetProgramRecommendSection.tsx @@ -0,0 +1,165 @@ +'use client'; + +import { useGetProgramAdminQuery } from '@/api/program'; +import { MagnetProgramRecommendItem } from '@/domain/admin/blog/magnet/types'; +import Heading2 from '@/domain/admin/ui/heading/Heading2'; +import { ProgramStatusEnum } from '@/schema'; +import { programStatusToText } from '@/utils/convert'; +import { + FormControl, + InputLabel, + MenuItem, + Select, + SelectChangeEvent, + TextField, +} from '@mui/material'; +import { ChangeEvent, useMemo } from 'react'; + +const { PROCEEDING, PREV } = ProgramStatusEnum.enum; + +// TODO: 백엔드에서 challengeType 필드가 admin program API에 추가되면 제목 추론 대신 직접 사용 +const CTA_SUBTITLE_MAP: Record = { + 경험정리: '취업 서류 준비의 기초·필수 코어가 될', + 이력서: '매력적인 이력서를 완성하는 1주일', + 자기소개서: '만능 답변으로 진짜 나를 드러내는', + 포트폴리오: '나의 경험을 200% 활용하여 제작하는', + 마케팅: '마케팅 현직자 멘토와 함께하는', + 대기업: '현직자와 함께 끝내는 공채준비', +}; + +function inferCtaSubtitle(title: string): string { + for (const [keyword, subtitle] of Object.entries(CTA_SUBTITLE_MAP)) { + if (title.includes(keyword)) return subtitle; + } + return ''; +} + +interface MagnetProgramRecommendSectionProps { + programRecommend: MagnetProgramRecommendItem[]; + onChangeProgramRecommend: (items: MagnetProgramRecommendItem[]) => void; +} + +const MagnetProgramRecommendSection = ({ + programRecommend, + onChangeProgramRecommend, +}: MagnetProgramRecommendSectionProps) => { + const { data } = useGetProgramAdminQuery({ + page: 1, + size: 10000, + type: 'CHALLENGE', + status: [PROCEEDING, PREV], + }); + + const { menuItems, titleByValue } = useMemo(() => { + const items: React.JSX.Element[] = [ + + 선택 안 함 + , + ]; + const titleMap = new Map(); + + data?.programList + .filter( + (p) => + p.programInfo.isVisible && + (p.programInfo.title ?? '').endsWith('챌린지'), + ) + .forEach((p) => { + const value = `CHALLENGE-${p.programInfo.id}`; + const title = p.programInfo.title ?? ''; + titleMap.set(value, title); + items.push( + + {`[챌린지/${programStatusToText[p.programInfo.programStatusType]}] ${title}`} + , + ); + }); + + return { menuItems: items, titleByValue: titleMap }; + }, [data]); + + const handleChange = ( + e: + | SelectChangeEvent + | ChangeEvent, + index: number, + ) => { + const list = [...programRecommend]; + const item = { ...list[index], [e.target.name]: e.target.value }; + const notSelectProgram = e.target.value === 'null'; + + if (e.target.name === 'id' && notSelectProgram) { + item.id = null; + delete item.ctaLink; + delete item.ctaTitle; + } + + if (e.target.name === 'id' && !notSelectProgram) { + const title = titleByValue.get(e.target.value as string) ?? ''; + item.ctaTitle = inferCtaSubtitle(title); + } + + onChangeProgramRecommend([ + ...list.slice(0, index), + item, + ...list.slice(index + 1), + ]); + }; + + return ( +
+
+ 프로그램 추천 + + *노출된 프로그램 중 모집중, 모집예정인 프로그램만 불러옵니다. + +
+
+ {programRecommend.map((item, index) => ( +
+ + 프로그램 선택 + + + handleChange(e, index)} + /> + {!item.id && ( + handleChange(e, index)} + /> + )} +
+ ))} + + { + "*CTA링크: 'latest:{text}'으로 설정하면, text를 제목에 포함하는 챌린지 상세페이지로 이동합니다. (예시) latest:인턴" + } + +
+
+ ); +}; + +export default MagnetProgramRecommendSection; diff --git a/src/domain/admin/blog/magnet/section/MagnetRecommendSection.tsx b/src/domain/admin/blog/magnet/section/MagnetRecommendSection.tsx new file mode 100644 index 000000000..c6449cd90 --- /dev/null +++ b/src/domain/admin/blog/magnet/section/MagnetRecommendSection.tsx @@ -0,0 +1,72 @@ +'use client'; + +import { fetchManageableMagnets } from '@/domain/admin/blog/magnet/mock'; +import { MAGNET_TYPE } from '@/domain/admin/blog/magnet/types'; +import Heading2 from '@/domain/admin/ui/heading/Heading2'; +import { + FormControl, + InputLabel, + MenuItem, + Select, + SelectChangeEvent, +} from '@mui/material'; +import { useMemo } from 'react'; + +interface MagnetRecommendSectionProps { + magnetRecommend: (number | null)[]; + onChangeMagnetRecommend: (items: (number | null)[]) => void; +} + +const MagnetRecommendSection = ({ + magnetRecommend, + onChangeMagnetRecommend, +}: MagnetRecommendSectionProps) => { + // TODO: API 준비 후 React Query 훅으로 교체 + const magnetMenuItems = useMemo( + () => [ + + 선택 안 함 + , + ...fetchManageableMagnets().map((m) => ( + + {`[${m.id}] ${MAGNET_TYPE[m.type]} - ${m.title}`} + + )), + ], + [], + ); + + const handleChange = ( + e: SelectChangeEvent, + index: number, + ) => { + const list = [...magnetRecommend]; + const value = e.target.value; + list[index] = value === 'null' ? null : Number(value); + onChangeMagnetRecommend(list); + }; + + return ( +
+ 마그넷 추천 +
+ {magnetRecommend.map((id, index) => ( + + 자료집 ID {index + 1} + + + ))} +
+
+ ); +}; + +export default MagnetRecommendSection; From 194d075df87b28cfae4672373998221ac5375f32 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Sat, 21 Feb 2026 01:28:06 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=EB=A7=88=EA=B7=B8=EB=84=B7=20?= =?UTF-8?q?=EA=B8=80=20=EA=B4=80=EB=A6=AC=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MagnetPostPage 메인 컴포넌트, useMagnetPostForm 훅, App page 연결 타입/제목/메타디스크립션/썸네일/프로그램추천/마그넷추천/노출기간/공통신청폼/콘텐츠편집1,2/액션버튼 포함 Co-Authored-By: Claude Opus 4.6 --- src/app/admin/blog/magnet/[id]/post/page.tsx | 15 +- .../admin/blog/magnet/MagnetPostPage.tsx | 173 ++++++++++++++++++ .../blog/magnet/hooks/useMagnetPostForm.ts | 153 ++++++++++++++++ 3 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 src/domain/admin/blog/magnet/MagnetPostPage.tsx create mode 100644 src/domain/admin/blog/magnet/hooks/useMagnetPostForm.ts diff --git a/src/app/admin/blog/magnet/[id]/post/page.tsx b/src/app/admin/blog/magnet/[id]/post/page.tsx index 5b6925490..7d09a3b52 100644 --- a/src/app/admin/blog/magnet/[id]/post/page.tsx +++ b/src/app/admin/blog/magnet/[id]/post/page.tsx @@ -1,4 +1,11 @@ -// TODO: 마그넷 글 관리 페이지 구현 예정 -export default function MagnetPostPage() { - return
글 관리 (준비 중)
; -} +import { fetchMagnetPost } from '@/domain/admin/blog/magnet/mock'; +import MagnetPostPage from '@/domain/admin/blog/magnet/MagnetPostPage'; + +const Page = async ({ params }: { params: Promise<{ id: string }> }) => { + const { id } = await params; + const initialData = await fetchMagnetPost(Number(id)); + + return ; +}; + +export default Page; diff --git a/src/domain/admin/blog/magnet/MagnetPostPage.tsx b/src/domain/admin/blog/magnet/MagnetPostPage.tsx new file mode 100644 index 000000000..412ef5da2 --- /dev/null +++ b/src/domain/admin/blog/magnet/MagnetPostPage.tsx @@ -0,0 +1,173 @@ +'use client'; + +import TextFieldLimit from '@/domain/admin/blog/TextFieldLimit'; +import { useMagnetPostForm } from '@/domain/admin/blog/magnet/hooks/useMagnetPostForm'; +import MagnetProgramRecommendSection from '@/domain/admin/blog/magnet/section/MagnetProgramRecommendSection'; +import MagnetRecommendSection from '@/domain/admin/blog/magnet/section/MagnetRecommendSection'; +import { MAGNET_TYPE, MagnetPostDetail } from '@/domain/admin/blog/magnet/types'; +import Heading from '@/domain/admin/ui/heading/Heading'; +import Heading2 from '@/domain/admin/ui/heading/Heading2'; +import ImageUpload from '@/domain/admin/program/ui/form/ImageUpload'; +import { Button, Checkbox, FormControlLabel } from '@mui/material'; +import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; +import dynamic from 'next/dynamic'; + +const EditorApp = dynamic( + () => import('@/domain/admin/lexical/EditorApp'), + { ssr: false }, +); + +const MAX_META_DESCRIPTION_LENGTH = 100; + +interface MagnetPostPageProps { + magnetId: string; + initialData: MagnetPostDetail; +} + +const MagnetPostPage = ({ magnetId, initialData }: MagnetPostPageProps) => { + const { + type, + title, + formState, + displayDate, + endDate, + content, + initialEditorStateBefore, + initialEditorStateAfter, + onChangeMetaDescription, + onChangeThumbnailFile, + onChangeHasCommonForm, + onChangeProgramRecommend, + onChangeMagnetRecommend, + onChangeEditorBefore, + onChangeEditorAfter, + setDisplayDate, + setEndDate, + savePost, + navigateToList, + } = useMagnetPostForm({ magnetId, initialData }); + + return ( +
+
+ 마그넷 글 관리 +
+
+
+ {/* 4.1 타입 */} +

+ 타입:  {MAGNET_TYPE[type]} +

+ + {/* 4.2 제목 */} +

+ 제목:  {title} +

+ + {/* 4.3 메타 디스크립션 */} + + + {/* 4.4 썸네일 */} +
+ +
+ + {/* 4.5 프로그램 추천 + 4.6 마그넷 추천 */} +
+ + +
+ + {/* 4.7 노출 기간 */} +
+ 노출 기간 +
+ + +
+
+ + {/* 4.8 공통 신청폼 추가 */} + onChangeHasCommonForm(e.target.checked)} + /> + } + label="공통 신청폼 추가" + /> + + {/* 4.9 콘텐츠 편집1 (신청 전 공개) */} +
+ 콘텐츠 편집1(신청 전 공개) + +
+ + {/* 4.10 콘텐츠 편집2 (신청 후 공개) */} +
+ 콘텐츠 편집2(신청 후 공개) + +
+ + {/* 4.11 액션 버튼 */} +
+ + +
+
+
+
+ ); +}; + +export default MagnetPostPage; diff --git a/src/domain/admin/blog/magnet/hooks/useMagnetPostForm.ts b/src/domain/admin/blog/magnet/hooks/useMagnetPostForm.ts new file mode 100644 index 000000000..1c75b62a7 --- /dev/null +++ b/src/domain/admin/blog/magnet/hooks/useMagnetPostForm.ts @@ -0,0 +1,153 @@ +import { uploadFile } from '@/api/file'; +import { saveMagnetPost } from '@/domain/admin/blog/magnet/mock'; +import { + MagnetPostContent, + MagnetPostDetail, + MagnetProgramRecommendItem, +} from '@/domain/admin/blog/magnet/types'; +import { useAdminSnackbar } from '@/hooks/useAdminSnackbar'; +import dayjs from '@/lib/dayjs'; +import { Dayjs } from 'dayjs'; +import { useRouter } from 'next/navigation'; +import { ChangeEvent, useMemo, useState } from 'react'; + +const RECOMMEND_SLOT_COUNT = 4; + +function createEmptyContent(): MagnetPostContent { + return { + programRecommend: Array.from({ length: RECOMMEND_SLOT_COUNT }, () => ({ + id: null, + })), + magnetRecommend: Array.from({ length: RECOMMEND_SLOT_COUNT }, () => null), + }; +} + +function parseInitialContent(data: MagnetPostDetail): MagnetPostContent { + if (!data.content || data.content === '') return createEmptyContent(); + try { + return JSON.parse(data.content); + } catch { + return createEmptyContent(); + } +} + +interface FormState { + metaDescription: string; + thumbnail: string; + hasCommonForm: boolean; +} + +function buildInitialFormState(data: MagnetPostDetail): FormState { + return { + metaDescription: data.metaDescription ?? '', + thumbnail: data.thumbnail ?? '', + hasCommonForm: data.hasCommonForm ?? false, + }; +} + +interface UseMagnetPostFormParams { + magnetId: string; + initialData: MagnetPostDetail; +} + +export const useMagnetPostForm = ({ + magnetId, + initialData, +}: UseMagnetPostFormParams) => { + const router = useRouter(); + const { snackbar: setSnackbar } = useAdminSnackbar(); + + const initialContent = useMemo( + () => parseInitialContent(initialData), + [initialData], + ); + const initialFormState = useMemo( + () => buildInitialFormState(initialData), + [initialData], + ); + + const [formState, setFormState] = useState(initialFormState); + const [displayDate, setDisplayDate] = useState( + initialData.displayDate ? dayjs(initialData.displayDate) : null, + ); + const [endDate, setEndDate] = useState( + initialData.endDate ? dayjs(initialData.endDate) : null, + ); + const [content, setContent] = useState(initialContent); + + const onChangeMetaDescription = (e: ChangeEvent) => { + setFormState((prev) => ({ ...prev, metaDescription: e.target.value })); + }; + + const onChangeThumbnailFile = async ( + e: ChangeEvent, + ) => { + const file = e.target.files?.item(0); + if (!file) { + setSnackbar('파일이 없습니다.'); + return; + } + const url = await uploadFile({ file, type: 'BLOG' }); + setFormState((prev) => ({ ...prev, thumbnail: url })); + }; + + const onChangeHasCommonForm = (checked: boolean) => { + setFormState((prev) => ({ ...prev, hasCommonForm: checked })); + }; + + const onChangeProgramRecommend = (items: MagnetProgramRecommendItem[]) => { + setContent((prev) => ({ ...prev, programRecommend: items })); + }; + + const onChangeMagnetRecommend = (items: (number | null)[]) => { + setContent((prev) => ({ ...prev, magnetRecommend: items })); + }; + + const onChangeEditorBefore = (jsonString: string) => { + setContent((prev) => ({ ...prev, lexicalBefore: jsonString })); + }; + + const onChangeEditorAfter = (jsonString: string) => { + setContent((prev) => ({ ...prev, lexicalAfter: jsonString })); + }; + + const savePost = async () => { + saveMagnetPost({ + magnetId: Number(magnetId), + metaDescription: formState.metaDescription, + thumbnail: formState.thumbnail, + displayDate: displayDate?.format('YYYY-MM-DDTHH:mm') ?? null, + endDate: endDate?.format('YYYY-MM-DDTHH:mm') ?? null, + hasCommonForm: formState.hasCommonForm, + content: JSON.stringify(content), + isVisible: false, + }); + setSnackbar('마그넷 글이 저장되었습니다.'); + }; + + const navigateToList = () => { + router.push('/admin/blog/magnet/list'); + }; + + return { + type: initialData.type, + title: initialData.title, + formState, + displayDate, + endDate, + content, + initialEditorStateBefore: initialContent.lexicalBefore, + initialEditorStateAfter: initialContent.lexicalAfter, + onChangeMetaDescription, + onChangeThumbnailFile, + onChangeHasCommonForm, + onChangeProgramRecommend, + onChangeMagnetRecommend, + onChangeEditorBefore, + onChangeEditorAfter, + setDisplayDate, + setEndDate, + savePost, + navigateToList, + }; +}; From 1b9b495c79bfc5cf37b34a5623d664038172af06 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Wed, 25 Feb 2026 22:29:44 +0900 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 상수화, await추가, key추가 --- src/domain/admin/blog/magnet/hooks/useMagnetPostForm.ts | 2 +- .../blog/magnet/section/MagnetProgramRecommendSection.tsx | 5 +++-- .../admin/blog/magnet/section/MagnetRecommendSection.tsx | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/domain/admin/blog/magnet/hooks/useMagnetPostForm.ts b/src/domain/admin/blog/magnet/hooks/useMagnetPostForm.ts index 1c75b62a7..edf133f36 100644 --- a/src/domain/admin/blog/magnet/hooks/useMagnetPostForm.ts +++ b/src/domain/admin/blog/magnet/hooks/useMagnetPostForm.ts @@ -112,7 +112,7 @@ export const useMagnetPostForm = ({ }; const savePost = async () => { - saveMagnetPost({ + await saveMagnetPost({ magnetId: Number(magnetId), metaDescription: formState.metaDescription, thumbnail: formState.thumbnail, diff --git a/src/domain/admin/blog/magnet/section/MagnetProgramRecommendSection.tsx b/src/domain/admin/blog/magnet/section/MagnetProgramRecommendSection.tsx index ca2e93744..66bb18cc0 100644 --- a/src/domain/admin/blog/magnet/section/MagnetProgramRecommendSection.tsx +++ b/src/domain/admin/blog/magnet/section/MagnetProgramRecommendSection.tsx @@ -16,6 +16,7 @@ import { import { ChangeEvent, useMemo } from 'react'; const { PROCEEDING, PREV } = ProgramStatusEnum.enum; +const MAX_PROGRAM_FETCH_SIZE = 10000; // TODO: 백엔드에서 challengeType 필드가 admin program API에 추가되면 제목 추론 대신 직접 사용 const CTA_SUBTITLE_MAP: Record = { @@ -45,7 +46,7 @@ const MagnetProgramRecommendSection = ({ }: MagnetProgramRecommendSectionProps) => { const { data } = useGetProgramAdminQuery({ page: 1, - size: 10000, + size: MAX_PROGRAM_FETCH_SIZE, type: 'CHALLENGE', status: [PROCEEDING, PREV], }); @@ -116,7 +117,7 @@ const MagnetProgramRecommendSection = ({
{programRecommend.map((item, index) => ( -
+
프로그램 선택