Skip to content

Commit 7a6f7e0

Browse files
authored
Merge pull request #65 from KEA-ReNov8/feat/KAN-184/공유함-api-연결
[feat/KAN-184] 공유글 관련 api 연결
2 parents fc7a3d7 + b8d10f7 commit 7a6f7e0

32 files changed

Lines changed: 875 additions & 356 deletions

src/app/routes/router.jsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,22 @@ export const router = createBrowserRouter([
6767
element: <RootLayout />,
6868
errorElement: <ErrorPage />,
6969
children: [
70-
{ path: 'page/:page', element: <SharePostListPage /> },
71-
{ path: 'create-share', element: <SharePostPage /> },
72-
{ path: ':postId', element: <SharePage /> },
70+
{
71+
path: 'page/:page',
72+
element: <SharePostListPage />,
73+
},
74+
{
75+
path: 'create-share',
76+
element: <SharePostPage />,
77+
},
78+
{
79+
path: 'edit/:postId',
80+
element: <SharePostPage />,
81+
},
82+
{
83+
path: ':postId',
84+
element: <SharePage />,
85+
},
7386
],
7487
},
7588

src/pages/chat/components/Chat.jsx

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import ProgressBar from '@chat/components/ProgressBar';
55
import ChatMessage from '@chat/components/ChatMessage';
66
import ChatForm from '@chat/components/ChatForm';
77
import ChatMenu from '@shared/assets/icons/ChatMenuicon.svg';
8-
import aiLogo from '../../../../public/favicon.svg';
8+
// public 폴더의 assets는 URL로 접근
9+
const aiLogo = '/favicon.svg';
910
import theme from '@app/styles/theme';
1011
import SuspendModal from '@chat/components/SuspendModal';
1112
import SuccessModal from '@chat/components/SuccessModal';
@@ -34,7 +35,7 @@ const Chat = () => {
3435
progress,
3536
incrementProgress,
3637
} = useChatStore();*/
37-
useEffect (() => {
38+
useEffect(() => {
3839
if (params.sessionId) {
3940
setCurrentSessionId(params.sessionId);
4041
return;
@@ -48,13 +49,15 @@ const Chat = () => {
4849
}, [location.pathname, params.sessionId]);
4950

5051
const { data: chatHistoryData, error: historyError } = useChatHistoryQuery(
51-
currentSessionId, !!currentSessionId
52+
currentSessionId,
53+
!!currentSessionId,
5254
);
5355

5456
// 채팅 내역을 메시지 형태로 변환하는 함수
5557
const convertApiMessagesToState = (apiMessages) => {
5658
if (!apiMessages || !Array.isArray(apiMessages)) return [];
5759

60+
5861
return apiMessages.map((msg, index) => {
5962
let cleanedContent = msg.content;
6063

@@ -74,12 +77,18 @@ const Chat = () => {
7477

7578
// 채팅 내역 로드 완료 시 메시지 설정
7679
useEffect(() => {
77-
if (chatHistoryData?.result?.messages && Array.isArray(chatHistoryData.result.messages) && chatHistoryData.result.messages.length > 0) {
80+
if (
81+
chatHistoryData?.result?.messages &&
82+
Array.isArray(chatHistoryData.result.messages) &&
83+
chatHistoryData.result.messages.length > 0
84+
) {
7885
const convertedMessages = convertApiMessagesToState(chatHistoryData.result.messages);
7986
setMessages(convertedMessages);
80-
87+
8188
// 프로그레스 계산 (AI 메시지 개수 * 10, 최대 100)
82-
const aiMessageCount = chatHistoryData.result.messages.filter(msg => msg.type === 'ai').length;
89+
const aiMessageCount = chatHistoryData.result.messages.filter(
90+
(msg) => msg.type === 'ai',
91+
).length;
8392
setProgress(Math.min(aiMessageCount * 10, 100));
8493
}
8594
// chatHistoryData가 있지만 messages가 비어있는 경우는 무시 (새로운 세션이므로 초기 메시지 유지)
@@ -129,6 +138,7 @@ const Chat = () => {
129138
}, [messages]);
130139

131140
const handleChatSuccess = (aiResponse, fullResponse) => {
141+
132142
// "AI:" 접두사와 ** 마크다운 문자 제거
133143
const cleanedResponse = aiResponse.replace(/^AI:\s*/, '').replace(/\*\*/g, '');
134144

@@ -139,9 +149,9 @@ const Chat = () => {
139149
timestamp: new Date(),
140150
};
141151

142-
setMessages(prev => [...prev, aiMessage]);
152+
setMessages((prev) => [...prev, aiMessage]);
143153

144-
setProgress(prev => Math.min(prev + 10, 100));
154+
setProgress((prev) => Math.min(prev + 10, 100));
145155

146156
setIsLoading(false);
147157
};
@@ -150,7 +160,7 @@ const Chat = () => {
150160
console.error('채팅 전송 실패:', error);
151161
};
152162

153-
const sendChatMutation = useSendChatMutation(handleChatSuccess, handleChatError);
163+
const sendChatMutation = useSendChatMutation(handleChatSuccess, handleChatError);
154164

155165
const handleSendMessage = async (text) => {
156166
if (!text.trim() || isLoading) return;
@@ -164,7 +174,7 @@ const Chat = () => {
164174
timestamp: new Date(),
165175
};
166176

167-
setMessages(prev => [...prev, userMessage]);
177+
setMessages((prev) => [...prev, userMessage]);
168178

169179
try {
170180
await sendChat(text, currentSessionId, sendChatMutation);
@@ -199,24 +209,24 @@ const Chat = () => {
199209
)}
200210
</TopContainer>
201211
<MessageContainer>
202-
<MessageList ref={messageListRef}>
203-
{messages.map((message) => (
204-
<ChatMessage key={message.id} message={message} />
205-
))}
206-
{isLoading && (
207-
<LoadingMessage>
208-
<ProfileMark src={aiLogo} />
209-
<LoadingBubble>
210-
<LoadingDots>
211-
<Dot />
212-
<Dot />
213-
<Dot />
214-
</LoadingDots>
215-
</LoadingBubble>
216-
</LoadingMessage>
217-
)}
218-
</MessageList>
219-
<ChatForm onSendMessage={handleSendMessage} isLoading={isLoading} />
212+
<MessageList ref={messageListRef}>
213+
{messages.map((message) => (
214+
<ChatMessage key={message.id} message={message} />
215+
))}
216+
{isLoading && (
217+
<LoadingMessage>
218+
<ProfileMark src={aiLogo} />
219+
<LoadingBubble>
220+
<LoadingDots>
221+
<Dot />
222+
<Dot />
223+
<Dot />
224+
</LoadingDots>
225+
</LoadingBubble>
226+
</LoadingMessage>
227+
)}
228+
</MessageList>
229+
<ChatForm onSendMessage={handleSendMessage} isLoading={isLoading} />
220230
</MessageContainer>
221231
</ChatContainer>
222232
{isSuspendModalOpen && <SuspendModal onClose={handleSuspendModalClose} />}
@@ -336,7 +346,8 @@ const FinishButton = styled.button`
336346
}
337347
`;
338348

339-
const MessageContainer = styled.div` display: flex;
349+
const MessageContainer = styled.div`
350+
display: flex;
340351
flex-direction: column;
341352
height: 770px;
342353
`;
@@ -371,7 +382,7 @@ const LoadingBubble = styled.div`
371382
padding: 16px;
372383
border-radius: 8px;
373384
position: relative;
374-
385+
375386
&::after {
376387
content: '';
377388
position: absolute;
@@ -399,17 +410,19 @@ const Dot = styled.div`
399410
&:nth-child(1) {
400411
animation-delay: 0s;
401412
}
402-
413+
403414
&:nth-child(2) {
404415
animation-delay: 0.2s;
405416
}
406-
417+
407418
&:nth-child(3) {
408419
animation-delay: 0.4s;
409420
}
410421
411422
@keyframes blink {
412-
0%, 80%, 100% {
423+
0%,
424+
80%,
425+
100% {
413426
opacity: 0.3;
414427
transform: scale(0.8);
415428
}
@@ -421,4 +434,3 @@ const Dot = styled.div`
421434
`;
422435

423436
export default Chat;
424-

src/pages/chat/components/ChatMessage.jsx

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,32 @@
11
import styled from 'styled-components';
22
import theme from '@app/styles/theme';
3-
import aiLogo from '../../../../public/favicon.svg';
3+
// public 폴더의 assets는 URL로 접근
4+
const aiLogo = '/favicon.svg';
45

5-
const ChatMessage = ( {message} ) => {
6-
const { text, isUser } = message;
6+
const ChatMessage = ({ message }) => {
7+
const { text, isUser } = message;
78

8-
return (
9-
<Container data-isUser={isUser}>
10-
{!isUser && <ProfileMark src={aiLogo} />}
11-
<MessageBubble data-isUser={isUser}>
12-
{ text }
13-
</MessageBubble>
14-
</Container>
15-
);
9+
return (
10+
<Container data-isUser={isUser}>
11+
{!isUser && <ProfileMark src={aiLogo} />}
12+
<MessageBubble data-isUser={isUser}>{text}</MessageBubble>
13+
</Container>
14+
);
1615
};
1716

1817
const Container = styled.div`
19-
display: flex;
20-
align-items: flex-start;
21-
justify-content: ${(props) => (props['data-isUser'] ? 'flex-end' : 'flex-start')};
22-
gap: 10px;
23-
margin-bottom: 16px;
18+
display: flex;
19+
align-items: flex-start;
20+
justify-content: ${(props) => (props['data-isUser'] ? 'flex-end' : 'flex-start')};
21+
gap: 10px;
22+
margin-bottom: 16px;
2423
`;
2524

2625
const ProfileMark = styled.img`
27-
width: 52px;
28-
height: 52px;
29-
border-radius: 50%;
30-
object-fit: cover;
26+
width: 52px;
27+
height: 52px;
28+
border-radius: 50%;
29+
object-fit: cover;
3130
`;
3231

3332
const MessageBubble = styled.div`
@@ -36,10 +35,11 @@ const MessageBubble = styled.div`
3635
max-width: 70%;
3736
padding: 16px 16px;
3837
border-radius: 8px;
39-
border:none;
38+
border: none;
4039
word-wrap: break-word;
4140
line-height: 1.5;
42-
background-color: ${(props) => (props['data-isUser'] ? theme.colors.gray[300] : theme.colors.green.main)};
41+
background-color: ${(props) =>
42+
props['data-isUser'] ? theme.colors.gray[300] : theme.colors.green.main};
4343
color: ${(props) => (props['data-isUser'] ? theme.colors.black : theme.colors.other.white)};
4444
4545
&::after {

src/pages/post/components/DeleteModal.jsx

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,42 @@
11
import styled from 'styled-components';
22
import theme from '@app/styles/theme';
33
import { useDeleteFreePostMutation } from '@post/feature/hooks/useDeleteFreePostMutation';
4+
import { useDeleteSharePostMutation } from '@post/feature/hooks/useDeleteSharePostMutation';
45
import { useNavigate } from 'react-router-dom';
56

6-
const DeleteModal = ({ onClose, postId }) => {
7+
const DeleteModal = ({ onClose, postId, postType = 'free' }) => {
78
const navigate = useNavigate();
8-
const { mutate: deleteFreePost, isPending } = useDeleteFreePostMutation();
9+
10+
// 게시글 타입에 따라 적절한 삭제 훅 사용
11+
const { mutate: deleteFreePost, isPending: isFreeDeleting } = useDeleteFreePostMutation();
12+
const { mutate: deleteSharePost, isPending: isShareDeleting } = useDeleteSharePostMutation();
13+
14+
const isPending = postType === 'free' ? isFreeDeleting : isShareDeleting;
915

1016
const handleDelete = () => {
11-
deleteFreePost(postId, {
12-
onSuccess: () => {
13-
alert('게시글이 삭제되었습니다.');
14-
navigate('/free/page/1'); // 자유게시판 목록으로 이동
15-
},
16-
onError: (error) => {
17-
console.error('삭제 실패:', error);
18-
alert('게시글 삭제에 실패했습니다.');
19-
},
20-
});
17+
if (postType === 'free') {
18+
deleteFreePost(postId, {
19+
onSuccess: () => {
20+
alert('게시글이 삭제되었습니다.');
21+
navigate('/free/page/1'); // 자유게시판 목록으로 이동
22+
},
23+
onError: (error) => {
24+
console.error('자유글 삭제 실패:', error);
25+
alert('게시글 삭제에 실패했습니다.');
26+
},
27+
});
28+
} else if (postType === 'share') {
29+
deleteSharePost(postId, {
30+
onSuccess: () => {
31+
alert('게시글이 삭제되었습니다.');
32+
navigate('/share/page/1'); // 공유게시판 목록으로 이동
33+
},
34+
onError: (error) => {
35+
console.error('공유글 삭제 실패:', error);
36+
alert('게시글 삭제에 실패했습니다.');
37+
},
38+
});
39+
}
2140
};
2241

2342
return (

src/pages/post/components/PostSelectModal.jsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useState } from 'react';
55
import DeleteModal from '@pages/post/components/DeleteModal';
66

77
//삭제 안될 경우 삭제 모달 복제해서 새로 만들기
8-
const PostSelectModal = ({ postId, onClose }) => {
8+
const PostSelectModal = ({ postId, onClose, postType = 'free' }) => {
99
const navigate = useNavigate();
1010

1111
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
@@ -15,7 +15,11 @@ const PostSelectModal = ({ postId, onClose }) => {
1515
};
1616

1717
const handleEdit = () => {
18-
navigate(`/free/edit/${postId}`);
18+
if (postType === 'share') {
19+
navigate(`/share/edit/${postId}`);
20+
} else {
21+
navigate(`/free/edit/${postId}`);
22+
}
1923
};
2024

2125
return (
@@ -25,7 +29,11 @@ const PostSelectModal = ({ postId, onClose }) => {
2529
<EditButton onClick={handleEdit}>수정</EditButton>
2630
<DeleteButton onClick={handleDelete}>삭제</DeleteButton>
2731
{isDeleteModalOpen && (
28-
<DeleteModal postId={postId} onClose={() => setIsDeleteModalOpen(false)} />
32+
<DeleteModal
33+
postId={postId}
34+
postType={postType}
35+
onClose={() => setIsDeleteModalOpen(false)}
36+
/>
2937
)}
3038
</ModalContainer>
3139
</>

src/pages/post/components/mindreport/AIAnalysisPage.jsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
import styled from 'styled-components';
22
import theme from '@app/styles/theme';
3-
export const AIAnalysisPage = ({ analysisData }) => {
3+
export const AIAnalysisPage = ({ totalComment }) => {
44
return (
55
<FullBox>
66
<ReportTitle>
77
나래의 분석
88
<TitleDivider />
99
</ReportTitle>
1010
<ReportContent>
11-
{analysisData ? (
12-
<p>{analysisData}</p>
13-
) : (
14-
<p>분석 데이터를 불러오는 중입니다...</p>
15-
)}
11+
{totalComment ? <p>{totalComment}</p> : <p>분석 데이터를 불러오는 중입니다...</p>}
1612
</ReportContent>
1713
</FullBox>
1814
);

0 commit comments

Comments
 (0)