From bd5db784fce1ec031f2dc7a13861f89fdd811ff6 Mon Sep 17 00:00:00 2001 From: yoonvinjeong Date: Mon, 16 Mar 2026 22:25:29 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20LLMInputBox=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=B3=84=20API=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mine/src/api/magazine.ts | 24 +++++++- Mine/src/components/LLMInputLayout.tsx | 57 ++++++++++++++++--- Mine/src/hooks/usePostAddSection.ts | 17 ++++++ .../hooks/usePostAddSectionInSectionPage.ts | 18 ++++++ Mine/src/types/magazine.ts | 40 ++++++++++++- 5 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 Mine/src/hooks/usePostAddSection.ts create mode 100644 Mine/src/hooks/usePostAddSectionInSectionPage.ts diff --git a/Mine/src/api/magazine.ts b/Mine/src/api/magazine.ts index 325764d..2732a49 100644 --- a/Mine/src/api/magazine.ts +++ b/Mine/src/api/magazine.ts @@ -4,7 +4,11 @@ import type { FeedDto, PatchMagazineTitleDto, PostMagazineDto, + RequestAddSection, + RequestAddSectionInSectionPage, RequestDeleteMagazine, + ResponseAddSection, + ResponseAddSectionInSectionPage, ResponseFeed, ResponseRecentSection, SectionDetailDto, @@ -91,4 +95,22 @@ export const getMagazineFeed = async ({ cursorId, limit = 10 }: FeedDto): Promis export const createMoodboard = async (magazineId: number) => { const res = await axiosInstance.post(`api/magazines/${magazineId}/moodboards`) return res.data -} \ No newline at end of file +} +export const postAddSection = async ({ magazineId, message }: RequestAddSection): Promise => { + const body = { + message: message, + } + const res = await axiosInstance.post(`api/magazines/${magazineId}/interact`, body) + return res.data +} +export const postAddSectionInSectionPage = async ({ + magazineId, + sectionId, + message, +}: RequestAddSectionInSectionPage): Promise => { + const body = { + message: message, + } + const res = await axiosInstance.post(`api/magazines/${magazineId}/sections/${sectionId}/interact`, body) + return res.data +} diff --git a/Mine/src/components/LLMInputLayout.tsx b/Mine/src/components/LLMInputLayout.tsx index dc9f5df..49a021c 100644 --- a/Mine/src/components/LLMInputLayout.tsx +++ b/Mine/src/components/LLMInputLayout.tsx @@ -1,16 +1,59 @@ -import { useLocation } from 'react-router-dom' +import { matchPath, useLocation } from 'react-router-dom' import LLMInputBox from './LLMInputBox' +import usePostAddSection from '../hooks/usePostAddSection' +import usePostAddSectionInSectionPage from '../hooks/usePostAddSectionInSectionPage' +import usePostMagazine from '../hooks/usePostMagazine' +import { useAuthStore } from '../stores/auth' export default function LLMInputLayout() { + const { isLoggedIn } = useAuthStore() const location = useLocation() const hiddenPath = ['/login', '/signup', '/landing', '/'] const isHiddenPath = hiddenPath.includes(location.pathname) - if (!isHiddenPath) - return ( -
- + const postAddSectionMutation = usePostAddSection() + const postAddInSectionPageMutation = usePostAddSectionInSectionPage() + const postMagazineMutation = usePostMagazine() + + const sectionMatch = matchPath('/magazine/:magazineId/section/:sectionId', location.pathname) + const magazineMatch = matchPath('/magazine/:magazineId', location.pathname) + const currentMagazineId = magazineMatch?.params.magazineId + + let handleSend = (value: string) => { + console.log('기본 전송:', value) + } + if (!isLoggedIn || isHiddenPath) return null + if (!isHiddenPath && sectionMatch) { + const { magazineId, sectionId } = sectionMatch.params + handleSend = async (value: string) => { + postAddInSectionPageMutation.mutate({ + magazineId: Number(magazineId), + sectionId: Number(sectionId), + message: value, + }) + } + } else if (!isHiddenPath && magazineMatch && currentMagazineId) { + const { magazineId } = magazineMatch.params + handleSend = async (value: string) => { + postAddSectionMutation.mutate({ + magazineId: Number(magazineId), + message: value, + }) + } + } else if (!isHiddenPath) { + handleSend = async (value: string) => { + postMagazineMutation.mutate({ + topic: value, + user_mood: '', + }) + } + } + + return ( +
+
+
- ) - return null +
+ ) } diff --git a/Mine/src/hooks/usePostAddSection.ts b/Mine/src/hooks/usePostAddSection.ts new file mode 100644 index 0000000..6c1f83e --- /dev/null +++ b/Mine/src/hooks/usePostAddSection.ts @@ -0,0 +1,17 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' +import type { RequestAddSection } from '../types/magazine' +import { postAddSection } from '../api/magazine' + +export default function usePostAddSection() { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: ({ magazineId, message }: RequestAddSection) => postAddSection({ magazineId, message }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['mymagazines'] }) + }, + onError: (error) => { + console.log('섹션 생성:', error) + alert('섹션 생성에 실패했습니다.') + }, + }) +} diff --git a/Mine/src/hooks/usePostAddSectionInSectionPage.ts b/Mine/src/hooks/usePostAddSectionInSectionPage.ts new file mode 100644 index 0000000..6c9f967 --- /dev/null +++ b/Mine/src/hooks/usePostAddSectionInSectionPage.ts @@ -0,0 +1,18 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' +import type { RequestAddSectionInSectionPage } from '../types/magazine' +import { postAddSectionInSectionPage } from '../api/magazine' + +export default function usePostAddSectionInSectionPage() { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: ({ magazineId, sectionId, message }: RequestAddSectionInSectionPage) => + postAddSectionInSectionPage({ magazineId, sectionId, message }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['mymagazines', 'sections'] }) + }, + onError: (error) => { + console.log('섹션 생성:', error) + alert('섹션 생성에 실패했습니다.') + }, + }) +} diff --git a/Mine/src/types/magazine.ts b/Mine/src/types/magazine.ts index 7f4cbcb..5faa190 100644 --- a/Mine/src/types/magazine.ts +++ b/Mine/src/types/magazine.ts @@ -136,4 +136,42 @@ export type ResponseFeed = { content: Magazine[] nextCursor: number hasNext: boolean -} \ No newline at end of file +} + +export type RequestAddSection = { + magazineId: number + message: string +} + +export type RequestAddSectionInSectionPage = { + magazineId: number + sectionId: number + message: string +} +export type ResponseAddSection = { + message: string + actionType: string + section: { + heading: string + paragraphs: Paragraph[] + sectionId: number + thumbnailUrl: string + layoutType: string + layoutHint: string + displayOrder: number + } +} + +export type ResponseAddSectionInSectionPage = { + message: string + actionType: string + section: { + heading: string + paragraphs: Paragraph[] + sectionId: number + thumbnailUrl: string + layoutType: string + layoutHint: string + displayOrder: number + } +} From 568eb72c1675912baa74110856395e8008271571 Mon Sep 17 00:00:00 2001 From: yoonvinjeong Date: Mon, 16 Mar 2026 23:36:01 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9D=EC=8B=9C=20?= =?UTF-8?q?=EC=9A=B0=ED=9A=8C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mine/vite.config.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mine/vite.config.ts b/Mine/vite.config.ts index 3f971e0..504e3b1 100644 --- a/Mine/vite.config.ts +++ b/Mine/vite.config.ts @@ -9,10 +9,9 @@ export default defineConfig({ server: { proxy: { '/api': { - target: 'http://52.63.142.228:8080', + target: 'https://api.minelover.com', changeOrigin: true, }, }, }, }) - From 98d683218b38b661b7183f6d943cb05126ec253c Mon Sep 17 00:00:00 2001 From: yoonvinjeong Date: Tue, 17 Mar 2026 00:01:21 +0900 Subject: [PATCH 3/5] Update LLMInputLayout.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - explore와 loved 일시 매거진 생성(usePostMagazine)으로 이어지도록 수정 --- Mine/src/components/LLMInputLayout.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Mine/src/components/LLMInputLayout.tsx b/Mine/src/components/LLMInputLayout.tsx index 49a021c..d7b1379 100644 --- a/Mine/src/components/LLMInputLayout.tsx +++ b/Mine/src/components/LLMInputLayout.tsx @@ -17,8 +17,11 @@ export default function LLMInputLayout() { const sectionMatch = matchPath('/magazine/:magazineId/section/:sectionId', location.pathname) const magazineMatch = matchPath('/magazine/:magazineId', location.pathname) + const currentMagazineId = magazineMatch?.params.magazineId + const isNumericMagazineId = currentMagazineId && !isNaN(Number(currentMagazineId)) + let handleSend = (value: string) => { console.log('기본 전송:', value) } @@ -32,7 +35,7 @@ export default function LLMInputLayout() { message: value, }) } - } else if (!isHiddenPath && magazineMatch && currentMagazineId) { + } else if (!isHiddenPath && magazineMatch && isNumericMagazineId) { const { magazineId } = magazineMatch.params handleSend = async (value: string) => { postAddSectionMutation.mutate({ From 8e618da3804501e05c3278dda065542f1ffcba66 Mon Sep 17 00:00:00 2001 From: yoonvinjeong Date: Thu, 19 Mar 2026 16:32:18 +0900 Subject: [PATCH 4/5] =?UTF-8?q?style:=20llm=EC=9D=B8=ED=92=8B=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EC=8A=A4=ED=83=80=EC=9D=BC=EB=A7=81=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mine/src/components/LLMInputBox.tsx | 6 +++--- Mine/src/style/global.css | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Mine/src/components/LLMInputBox.tsx b/Mine/src/components/LLMInputBox.tsx index d349dde..84339b1 100644 --- a/Mine/src/components/LLMInputBox.tsx +++ b/Mine/src/components/LLMInputBox.tsx @@ -40,7 +40,7 @@ export default function LLMInputBox({ onSend }: LLMInputBoxProps) { return (