Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: SSE 핸들러 및 관리자 전용 참여 현황 열람 기능 추가 #56

Merged
merged 3 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,341 changes: 0 additions & 5,341 deletions dist/assets/index-BgC4eDEl.js

This file was deleted.

5,299 changes: 5,299 additions & 0 deletions dist/assets/index-Dp4wxnjx.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<!-- 페이지 타이틀 설정 -->
<title>Good-Buy-Us</title>
<script type="module" crossorigin src="/assets/index-BgC4eDEl.js"></script>
<script type="module" crossorigin src="/assets/index-Dp4wxnjx.js"></script>
</head>
<body>
<div id="root"></div>
Expand Down
63 changes: 27 additions & 36 deletions src/components/common/ChatRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,21 @@ const ChatRoom: React.FC<ChatRoomProps> = ({
useEffect(() => {
// 채팅방 초기 메시지 및 채팅 메시지 가져오기
const fetchMessages = async () => {
try {
const fetchedMessages = await fetchChatMessages(chatRoomId);

// 입장 메시지 추가
const joinMessage: Message = {
senderId: 'system',
content: `${chatMembers
.map((member) => getNicknameDisplay(member))
.join(', ')}님이 입장하셨습니다.`,
timestamp: null, // timestamp 표시하지 않음
};

// 그룹 채팅 안내 메시지 추가
const groupChatNotice: Message = {
senderId: 'system',
content: `
const fetchedMessages = await fetchChatMessages(chatRoomId);

// 입장 메시지 추가
const joinMessage: Message = {
senderId: 'system',
content: `${chatMembers
.map((member) => getNicknameDisplay(member))
.join(', ')}님이 입장하셨습니다.`,
timestamp: null, // timestamp 표시하지 않음
};

// 그룹 채팅 안내 메시지 추가
const groupChatNotice: Message = {
senderId: 'system',
content: `
안내사항: 환불 및 이탈 관련 정책
1. 환불 및 수령 시간/위치 조율
- 공구 진행 중(최종 승인 이후)인 채팅방에서
Expand All @@ -73,13 +72,10 @@ const ChatRoom: React.FC<ChatRoomProps> = ({

공구 진행에 차질이 없도록 적극적인 협조
부탁드립니다. 😊`,
timestamp: null,
};
timestamp: null,
};

setMessages([joinMessage, groupChatNotice, ...fetchedMessages]);
} catch (error) {
console.error('Failed to fetch messages:', error);
}
setMessages([joinMessage, groupChatNotice, ...fetchedMessages]);
};

fetchMessages();
Expand All @@ -96,20 +92,15 @@ const ChatRoom: React.FC<ChatRoomProps> = ({
setMessages((prev) => [...prev, data]);
};

webSocketService.connect(
() => {
webSocketService.subscribe(
`/sub/message/${chatRoomId}`,
(messageOutput) => {
const data = JSON.parse(messageOutput.body);
handleIncomingMessage(data);
}
);
console.log('WebSocket connected to room');
},
() => console.log('WebSocket disconnected'),
() => console.error('WebSocket connection error')
);
webSocketService.connect(() => {
webSocketService.subscribe(
`/sub/message/${chatRoomId}`,
(messageOutput) => {
const data = JSON.parse(messageOutput.body);
handleIncomingMessage(data);
}
);
});

return () => {
webSocketService.unsubscribe(`/sub/message/${chatRoomId}`);
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/PostList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import SearchBar from './SearchBar';
import Pagination from './Pagination';
import { useNavigate } from 'react-router-dom';
import { SSEEvent, Post } from '../../types/postTypes';
import { getImageSrc } from '../../utils/GetImageSrc';
import { formatDateWithOffset } from '../../utils/formatDate';
import { getImageSrc } from '../../utils/GetImageSrc';

interface PostListProps {
selectedCategory: string;
Expand Down
8 changes: 2 additions & 6 deletions src/components/pages/admin/ChatRoomManagementPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,8 @@ const ChatRoomManagementPage = () => {

useEffect(() => {
const fetchData = async () => {
try {
const rooms = await fetchAllChatRooms();
setChatRooms(rooms);
} catch (error) {
console.error('Failed to fetch chat rooms:', error);
}
const rooms = await fetchAllChatRooms();
setChatRooms(rooms);
};

fetchData();
Expand Down
25 changes: 11 additions & 14 deletions src/components/pages/admin/PostApprovalPage.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { useNavigate, useLocation } from 'react-router-dom';
import { fetchPostById } from '../community/api/postApi';
import { useNavigate, useLocation, useParams } from 'react-router-dom';
import { fetchPostById, handleSSEUpdate } from '../community/api/postApi';
import { approvePost, rejectPost } from './api/adminApi';
import { FaBackspace, FaAngleLeft, FaAngleRight } from 'react-icons/fa';
import { Post } from '../../../types/postTypes';
import { getImageSrc } from '../../../utils/GetImageSrc';
import { formatDateWithOffset } from '../../../utils/formatDate';

const PostApprovalPage = () => {
const { postId: paramPostId } = useParams<{ postId: string }>();
const location = useLocation();
const navigate = useNavigate();
const { postId } = location.state || {}; // PostList에서 전달된 postId
const postId = paramPostId || location.state?.communityPostId;
const [post, setPost] = useState<Post | null>(null);
const [currentIndex, setCurrentIndex] = useState(0);

Expand All @@ -21,12 +22,8 @@ const PostApprovalPage = () => {
navigate('/admin/post'); // postId가 없을 경우 관리자 페이지로 리다이렉트
return;
}
try {
const postDetails = await fetchPostById(postId); // 포스트 세부 정보 가져오기
setPost(postDetails.communityPost);
} catch (error) {
console.error('Failed to fetch post details:', error);
}
const postDetails = await fetchPostById(postId); // 포스트 세부 정보 가져오기
setPost(postDetails.communityPost);
};
fetchPost();
}, [postId, navigate]);
Expand All @@ -53,12 +50,12 @@ const PostApprovalPage = () => {
const updatedTitle = post.title.startsWith('(수정요망)')
? post.title.replace(/^\(수정요망\)\s*/, '')
: post.title;

handleSSEUpdate(postId); // SSE 구독 시작
await approvePost(postId, updatedTitle); // 포스트 상태를 APPROVED로 변경
alert('게시물이 승인되었습니다.');
navigate('/admin/post'); // 승인 후 관리자 페이지로 리다이렉트
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
console.error('Failed to approve post:', error);
alert('승인 처리 중 오류가 발생했습니다.');
}
};
Expand All @@ -67,15 +64,15 @@ const PostApprovalPage = () => {
if (!post) return;
try {
// 제목에 '(수정요망)' 추가
const updatedTitle = post.title.startsWith('(수정요망)')
const updatedTitle = post.title.startsWith('(수정요망) ')
? post.title // 이미 '(수정요망)'이 있으면 그대로 유지
: `(수정요망) ${post.title}`;

handleSSEUpdate(postId); // SSE 구독 시작
await rejectPost(postId, updatedTitle); // 포스트 상태를 REJECTED로 변경
alert('게시물이 거절 처리되었습니다.');
navigate('/admin/post'); // 거절 후 관리자 페이지로 리다이렉트
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
console.error('Failed to reject post:', error);
alert('거절 처리 중 오류가 발생했습니다.');
}
};
Expand Down
8 changes: 2 additions & 6 deletions src/components/pages/admin/PostManagementPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,8 @@ const PostManagementPage = () => {

useEffect(() => {
const fetchPosts = async () => {
try {
const response = await fetchPendingPosts();
setPosts(response);
} catch (error) {
console.error('Failed to fetch posts:', error);
}
const response = await fetchPendingPosts();
setPosts(response);
};

fetchPosts();
Expand Down
29 changes: 12 additions & 17 deletions src/components/pages/admin/api/adminApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,15 @@ import { Post } from '../../../../types/postTypes';

// 관리자용 승인 대기 중인 포스트 목록 조회
export const fetchPendingPosts = async (): Promise<Post[]> => {
try {
const response = await axiosInstance.get('/api/admin/post', {
headers: {
Authorization: `Bearer ${localStorage.getItem('jwt')}`,
},
});
if (response.status !== 200) {
throw new Error('Failed to fetch pending posts');
}
return response.data;
} catch (error) {
console.error('Error fetching pending posts:', error);
throw error;
const response = await axiosInstance.get('/api/admin/post', {
headers: {
Authorization: `Bearer ${localStorage.getItem('jwt')}`,
},
});
if (response.status !== 200) {
throw new Error('Failed to fetch pending posts');
}
return response.data;
};

// 관리자용 포스트 승인 처리
Expand All @@ -38,9 +33,9 @@ export const approvePost = async (
throw new Error('Failed to approve post');
}
return response.data;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
console.error(`Error approving post with ID ${communityPostId}:`, error);
throw error;
throw new Error(`Error approving post with ID ${communityPostId}`);
}
};

Expand All @@ -63,8 +58,8 @@ export const rejectPost = async (
throw new Error('Failed to reject post');
}
return response.data;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
console.error(`Error rejecting post with ID ${communityPostId}:`, error);
throw error;
throw new Error(`Error rejecting post with ID ${communityPostId}`);
}
};
40 changes: 40 additions & 0 deletions src/components/pages/community/ParticipantList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { useAtom } from 'jotai';
import { sseDataAtom } from '../../../store/sseStore';
import { useAuth } from '../../../context/AuthContext';

const ParticipantList: React.FC = () => {
const [sseData] = useAtom(sseDataAtom);
const { isAdmin } = useAuth();

if (!isAdmin) {
// 관리자가 아니면 아무것도 표시하지 않음
return null;
}

if (!sseData || !sseData.participants.length) {
return <div>참여자 데이터가 없습니다.</div>;
}

return (
<div>
<h2>참여자 목록 (관리자 전용)</h2>
<ul>
{sseData.participants.map((participant) => (
<li key={participant.userId}>
<p>
닉네임: {participant.nickname} | 수량: {participant.quantity} |{' '}
{participant.isCancelled ? '취소됨' : '참여 중'} |{' '}
{participant.isPaymentCompleted ? '결제 완료' : '결제 대기'} |{' '}
</p>
</li>
))}
</ul>
<p>참여 인원: {sseData.participationCount}</p>
<p>결제 완료 인원: {sseData.paymentCount}</p>
<p>환불 완료 인원: {sseData.refundedCount}</p>
</div>
);
};

export default ParticipantList;
6 changes: 3 additions & 3 deletions src/components/pages/community/PaymentCompletePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ const PaymentCompletePage: React.FC = () => {
if (data.status === 'DONE') {
setStatus('DONE');
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
console.error('결제 상태 조회 오류:', error);
alert('결제 상태를 확인할 수 없습니다.');
}
};
Expand All @@ -62,8 +62,8 @@ const PaymentCompletePage: React.FC = () => {
title: postData.communityPost.title,
unitAmount: postData.communityPost.unitAmount,
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
console.error('게시물 정보 조회 오류:', error);
alert('게시물 정보를 가져올 수 없습니다.');
}
};
Expand All @@ -76,8 +76,8 @@ const PaymentCompletePage: React.FC = () => {
try {
const chatRoom = await createChatRoom(parseInt(communityPostId));
setChatRoom(chatRoom);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
console.error('채팅방 생성 오류:', error);
alert('채팅방을 생성할 수 없습니다.');
}
};
Expand Down
3 changes: 1 addition & 2 deletions src/components/pages/community/PostCreatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ const PostCreatePage = () => {
alert('포스트가 작성되었습니다. 관리자의 승인을 대기 중입니다.');
navigate('/community/post');
},
onError: (error) => {
console.error('포스트 생성 중 오류 발생:', error);
onError: () => {
alert('포스트 생성에 실패했습니다.');
},
});
Expand Down
Loading