diff --git a/package-lock.json b/package-lock.json
index d368edf..3caa740 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,9 @@
"name": "web-business",
"version": "0.1.0",
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^6.5.1",
+ "@fortawesome/free-solid-svg-icons": "^6.5.1",
+ "@fortawesome/react-fontawesome": "^0.2.0",
"@heroicons/react": "^2.1.1",
"@material-tailwind/react": "^2.1.9",
"@stomp/stompjs": "^7.0.0",
@@ -26,6 +29,7 @@
"react-dom": "^17.0.2",
"react-i18next": "^14.0.5",
"react-icons": "^5.0.1",
+ "react-modal": "^3.16.1",
"react-redux": "^7.2.9",
"react-router-dom": "^6.22.1",
"react-scripts": "5.0.1",
@@ -2468,6 +2472,51 @@
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
},
+ "node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz",
+ "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==",
+ "hasInstallScript": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-svg-core": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz",
+ "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz",
+ "integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/react-fontawesome": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz",
+ "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==",
+ "dependencies": {
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "@fortawesome/fontawesome-svg-core": "~1 || ~6",
+ "react": ">=16.3"
+ }
+ },
"node_modules/@heroicons/react": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.1.tgz",
@@ -8548,6 +8597,11 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
+ "node_modules/exenv": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
+ "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="
+ },
"node_modules/exit": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
@@ -16804,6 +16858,29 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
+ "node_modules/react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "node_modules/react-modal": {
+ "version": "3.16.1",
+ "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz",
+ "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==",
+ "dependencies": {
+ "exenv": "^1.2.0",
+ "prop-types": "^15.7.2",
+ "react-lifecycles-compat": "^3.0.0",
+ "warning": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18",
+ "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18"
+ }
+ },
"node_modules/react-redux": {
"version": "7.2.9",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
@@ -19922,6 +19999,14 @@
"makeerror": "1.0.12"
}
},
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
diff --git a/package.json b/package.json
index 9a70c2f..590b0ad 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,9 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^6.5.1",
+ "@fortawesome/free-solid-svg-icons": "^6.5.1",
+ "@fortawesome/react-fontawesome": "^0.2.0",
"@heroicons/react": "^2.1.1",
"@material-tailwind/react": "^2.1.9",
"@stomp/stompjs": "^7.0.0",
@@ -21,6 +24,7 @@
"react-dom": "^17.0.2",
"react-i18next": "^14.0.5",
"react-icons": "^5.0.1",
+ "react-modal": "^3.16.1",
"react-redux": "^7.2.9",
"react-router-dom": "^6.22.1",
"react-scripts": "5.0.1",
diff --git a/src/App.js b/src/App.js
index bdfa21a..485e353 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,4 +1,4 @@
-import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom";
+import {BrowserRouter, Route, Routes} from "react-router-dom";
import React from "react";
@@ -20,9 +20,6 @@ import Plans from "./pages/business/plans";
import BusinessPlanDetail from "./pages/business/plan";
import AdminPlainDetail from "./pages/administrator/adminPlanDetail";
import AdminChat from "./pages/administrator/adminChat";
-import Chat from "./pages/business/chat";
-import BusinessChatRoom from "./pages/business/businessChatRoom";
-import AdminChatRoom from "./pages/administrator/adminChatRoom";
/**
* @since 2024.02.25
@@ -31,13 +28,13 @@ import AdminChatRoom from "./pages/administrator/adminChatRoom";
function App() {
const businessColor = "bg-main-color-600 border-r border-gray-200 sm:translate-x-0 dark:bg-gray-800 dark:border-gray-700";
- const businessSideBarColor = "bg-main-color-600 dark:bg-blue-600";
+ const businessSideBarColor = "bg-main-color-600 text-white dark:bg-blue-600";
const adminColor = "bg-main-blue-600 border-r border-gray-200 sm:translate-x-0 dark:bg-gray-800 dark:border-gray-700";
const adminSideBarColor = "bg-main-blue-600 dark:bg-blue-600";
const businessSideBarList = [
- ['대시보드', '/dashboard'], ['나의 사업계획서 목록', '/plans'], ['팝업 스토어 제안', '/plan/create'], ['광고 신청', '/ad/create'], ['1:1 채팅상담', '/chat']
+ ['대시보드', '/dashboard'], ['나의 사업계획서 목록', '/plans'], ['팝업 스토어 제안', '/plan/create'], ['광고 신청', '/ad/create']
];
const adminSideBarList = [
['사용자 관리', [['일반 사용자 관리', '/admin/users'], ['사업체 관리', '/admin/business']]],
@@ -61,8 +58,6 @@ function App() {
{generateRoute(businessColor, businessSideBarColor, businessSideBarList, businessHomeUrl, "/plans/:planId", BusinessPlanDetail)}
{generateRoute(businessColor, businessSideBarColor, businessSideBarList, businessHomeUrl, "/plan/create", CreatePlan)}
{generateRoute(businessColor, businessSideBarColor, businessSideBarList, businessHomeUrl, "/ad/create", Ad)}
- {generateRoute(businessColor, businessSideBarColor, businessSideBarList, businessHomeUrl, "/chat", Chat)}
- {generateRoute(businessColor, businessSideBarColor, businessSideBarList, businessHomeUrl, "/chat/:roomId", BusinessChatRoom)}
{generateRoute(adminColor, adminSideBarColor, adminSideBarList, adminHomeUrl, '/admin/users', User)}
{generateRoute(adminColor, adminSideBarColor, adminSideBarList, adminHomeUrl, '/admin/business', Business)}
@@ -70,23 +65,20 @@ function App() {
{generateRoute(adminColor, adminSideBarColor, adminSideBarList, adminHomeUrl, '/admin/plan/:planId', AdminPlainDetail)}
{generateRoute(adminColor, adminSideBarColor, adminSideBarList, adminHomeUrl, '/admin/community', Community)}
{generateRoute(adminColor, adminSideBarColor, adminSideBarList, adminHomeUrl, '/admin/chat', AdminChat)}
- {generateRoute(adminColor, adminSideBarColor, adminSideBarList, adminHomeUrl, '/admin/chat/:roomId', AdminChatRoom)}
+
);
}
const generateRoute = (color, sideBarColor, sideBarList, homeUrl, path, Component) => (
-
} homeUrl={homeUrl} />
-
]}
/>
);
diff --git a/src/api/administrator/adminChatApi.js b/src/api/administrator/adminChatApi.js
index b87de87..72ce1fc 100644
--- a/src/api/administrator/adminChatApi.js
+++ b/src/api/administrator/adminChatApi.js
@@ -1,14 +1,7 @@
-import axios from "axios";
-
-import GetTokenFromLocalStorage from "../Common/token";
-
-const Token = GetTokenFromLocalStorage('admin')
-if (Token) {
- axios.defaults.headers.common['Authorization'] = `Bearer ${Token}`
-}
+import adminInstance from "../adminBaseApi";
/**
- * @since 2024.03.52
+ * @since 2024.03.02
* @author 이상민
*/
const ChatApi = {
@@ -19,7 +12,17 @@ const ChatApi = {
* @author 이상민
*/
getChatRooms: async (pageNo = 0, amount = 10) => {
- return await axios.get(`/api/v1/chat/rooms/admin?pageNo=${pageNo}&amount=${amount}`);
+ return await adminInstance.get(`/chat/rooms/admin?pageNo=${pageNo}&amount=${amount}`);
+ },
+
+ /**
+ * 나의 채팅방 메시지 리스트 조회
+ *
+ * @since 2024.03.02
+ * @author 이상민
+ */
+ getMessages: async (roomId = 0) => {
+ return await adminInstance.get(`/chat/rooms/${roomId}`);
},
}
diff --git a/src/api/chatApi.js b/src/api/chatApi.js
index 10c7e63..5e40dee 100644
--- a/src/api/chatApi.js
+++ b/src/api/chatApi.js
@@ -1,10 +1,4 @@
-import GetTokenFromLocalStorage from "./Common/token";
-import axios from "axios";
-
-const Token = GetTokenFromLocalStorage('user')
-if (Token) {
- axios.defaults.headers.common['Authorization'] = `Bearer ${Token}`
-}
+import userBaseApi from "./userBaseApi";
/**
* @since 2024.03.52
@@ -18,7 +12,7 @@ const ChatApi = {
* @author 이상민
*/
getChatRooms: async (pageNo = 0, amount = 10) => {
- return await axios.get(`/api/v1/chat/rooms?pageNo=${pageNo}&amount=${amount}`);
+ return await userBaseApi.get(`/chat/rooms?pageNo=${pageNo}&amount=${amount}`);
},
/**
@@ -28,7 +22,7 @@ const ChatApi = {
* @author 이상민
*/
getMessages: async (roomId = 0) => {
- return await axios.get(`/api/v1/chat/rooms/${roomId}`);
+ return await userBaseApi.get(`/chat/rooms/${roomId}`);
},
}
diff --git a/src/components/common/Chat/ChatPage.jsx b/src/components/administrator/ChatPage.jsx
similarity index 91%
rename from src/components/common/Chat/ChatPage.jsx
rename to src/components/administrator/ChatPage.jsx
index 3c6b62e..99e85cc 100644
--- a/src/components/common/Chat/ChatPage.jsx
+++ b/src/components/administrator/ChatPage.jsx
@@ -1,8 +1,7 @@
import React, { useEffect, useState } from "react";
-import ContentBox from "../../common/ContentBox/ContentBox";
-import Pagination from "../../common/Pagination/Pagination";
-import Table from "../Table/Table";
-import ChatTable from "../Table/ChatTable";
+import ContentBox from "../../components/common/ContentBox/ContentBox";
+import Pagination from "../../components/common/Pagination/Pagination";
+import ChatTable from "./ChatTable";
const ChatPage = ({ api, title }) => {
const [data, setData] = useState(null);
@@ -63,4 +62,4 @@ const ChatPage = ({ api, title }) => {
);
};
-export default ChatPage;
+export default ChatPage;
\ No newline at end of file
diff --git a/src/components/administrator/ChatRoomDetail.jsx b/src/components/administrator/ChatRoomDetail.jsx
new file mode 100644
index 0000000..f1a628e
--- /dev/null
+++ b/src/components/administrator/ChatRoomDetail.jsx
@@ -0,0 +1,227 @@
+import React, {useEffect, useRef, useState} from "react";
+import SockJS from "sockjs-client";
+import {Stomp} from "@stomp/stompjs";
+import GetTokenFromLocalStorage from "../../api/Common/token";
+import AdminChatApi from "../../api/administrator/adminChatApi";
+import MeChatMessage from "../common/Chat/MeChatMessage";
+import OtherChatMessage from "../common/Chat/OtherChatMessage";
+
+/**
+ * 채팅방 컴포넌트
+ *
+ * @since 2024.03.02
+ * @author 이상민
+ */
+const ChatRoomDetail = ({selectedChatRoomId, setModalIsOpen}) => {
+
+ const [stompClient, setStompClient] = useState(null);
+ const [messages, setMessages] = useState([]);
+ const [newMessage, setNewMessage] = useState("");
+
+ const roomId = selectedChatRoomId;
+ const role = 'admin'
+ console.log("채팅방 : " + roomId)
+
+ const messagesContainerRef = useRef(null);
+ const token = GetTokenFromLocalStorage(role)
+
+ const [chatRoomDetail, setChatRoomDetail] = useState([]);
+
+ /**
+ * WebSocket 연결
+ *
+ * @since 2024.03.03
+ * @author 이상민
+ */
+ useEffect(() => {
+ const socket = new SockJS(`http://15.164.236.13:8080/ws`);
+ const stomp = Stomp.over(socket);
+ setStompClient(stomp);
+ // 고유한 ID 생성
+ const uniqueId = `sub-${Math.random().toString(36).substr(2, 9)}`;
+ stomp.connect({ Authorization: `Bearer ${token}` }, (frame) => {
+ console.log("연결 성공!", frame);
+ console.log("1 : " + role)
+
+ // 2. 특정 채팅방에 구독
+ stomp.subscribe(`/topic/messages`, (message) => {
+ const newMessage = JSON.parse(message.body);
+
+ if(newMessage.role === role){
+ newMessage.checkedMe = true;
+ }else{
+ newMessage.checkedMe = false;
+ }
+
+ // 기존 메시지와 새 메시지를 합쳐서 상태 업데이트
+ // setMessages((prevMessages) => [...prevMessages, newMessage]);
+
+ // 새 메시지가 현재 채팅방에 대한 것인지 확인
+ if (roomId === newMessage.roomId) {
+ showMessage(newMessage);
+ }
+ }, { id: uniqueId }); // 고유한 ID로 구독
+ }, (error) => {
+ console.error("연결 실패:", error);
+ });
+ // 3. 이전 메시지 불러오기
+ loadPreviousMessages();
+ return () => {
+ stomp.disconnect();
+ };
+ }, [roomId]);
+
+ useEffect(() => {
+ scrollToBottom(); // 새로운 메시지가 추가될 때마다 스크롤을 최하단으로 이동
+ }, [messages]);
+
+ const loadPreviousMessages = async () => {
+ try {
+ const response = await AdminChatApi.getMessages(roomId);
+ console.log(response)
+
+ const data = response.data;
+ setMessages(data?.data?.chatMessageDetailResponse || []);
+ setChatRoomDetail(data?.data?.chatRoomDetailResponse || []);
+ } catch (error) {
+ console.error("이전 메시지 불러오기 실패:", error);
+ }
+ };
+
+ /**
+ * 메시지 전송
+ *
+ * @since 2024.03.03
+ * @author 이상민
+ */
+ const sendMessage = () => {
+ if (newMessage.trim() !== '' && stompClient) {
+ const chatMessageRequest = {
+ roomId: roomId,
+ message: newMessage,
+ role : role,
+ sendingTime: new Date().toISOString(),
+ };
+
+ // 'send' 메서드의 옵션으로 'Authorization' 헤더를 설정
+ const headers = { Authorization: `Bearer ${token}` };
+
+ // 헤더를 포함하여 메시지를 전송
+ stompClient.send("/app/sendMessage", headers, JSON.stringify(chatMessageRequest));
+
+ // 메시지 입력을 지웁니다
+ setNewMessage('');
+ }
+ };
+
+ /**
+ * 메시지 보여주기
+ *
+ * @since 2024.03.03
+ * @author 이상민
+ */
+ const showMessage = (message) => {
+ const formattedMessage = {
+ sender: message.senderNickname,
+ sendingTime: message.sendingTime,
+ message: message.message,
+ checkedMe: message.checkedMe,
+ };
+
+ console.log("포맷 :: " + formattedMessage);
+
+ setMessages((prevMessages) => [...prevMessages, formattedMessage]);
+ scrollToBottom();
+ };
+
+ /**
+ * 최하단으로 스크롤 이동
+ *
+ * @since 2024.03.03
+ * @author 이상민
+ */
+ const scrollToBottom = () => {
+ if (messagesContainerRef.current) {
+ messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
+ }
+ };
+
+ const handleGoBack = () => {
+ setModalIsOpen(false)
+ };
+
+ return (
+
+
+
+
+ {chatRoomDetail.name}
+
+
+
+
+
+
+ {messages.map((message, index) => (
+ -
+ {message.checkedMe === true ? (
+
+ ) : (
+
+ )}
+
+
+ ))}
+
+
+
+
+
+ setNewMessage(e.target.value)}
+ className="border p-1 rounded-full w-80 border-gray-"
+ style={{fontSize: '12px', padding: '6px'}}
+ />
+
+
+
+
+
+ );
+};
+
+export default ChatRoomDetail;
diff --git a/src/components/administrator/ChatTable.jsx b/src/components/administrator/ChatTable.jsx
new file mode 100644
index 0000000..5f321c4
--- /dev/null
+++ b/src/components/administrator/ChatTable.jsx
@@ -0,0 +1,70 @@
+import React, {useState} from "react";
+import TableHeader from "../common/Table/TableHeader";
+import TableRow from "../common/Table/TableRow";
+import Modal from "react-modal";
+import ChatRoomDetail from "./ChatRoomDetail";
+
+/**
+ * Table 컴포넌트 생성
+ *
+ * @since 2024.02.25
+ * @author 이상민
+ */
+const ChatTable = ({ headerTitles, sampleData }) => {
+
+ const [modalIsOpen, setModalIsOpen] = useState(false);
+ const [selectedChatRoomId, setSelectedChatRoomId] = useState(null);
+
+ const handleRowClick = (id) => {
+ console.log("테이블 : " + id)
+
+ setSelectedChatRoomId(id);
+ setModalIsOpen(true);
+ };
+
+ const renderTableBody = (sampleData) => (
+
+ {sampleData.map((rowData, index) => (
+ handleRowClick(rowData[0])} />
+ ))}
+
+ );
+
+ return (
+
+
+
+ {renderTableBody(sampleData)}
+
+
+
setModalIsOpen(false)}
+ contentLabel="ChatRoomDetail Modal"
+ style={{
+ overlay: {
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ },
+ content: {
+ top: '50%',
+ left: '50%',
+ right: 'auto',
+ bottom: 'auto',
+ marginRight: '-50%',
+ transform: 'translate(-50%, -50%)',
+ maxWidth: '80%', // 필요에 따라 최대 너비 조정
+ padding: 0,
+ borderRadius: 25,
+ boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.1)',
+ },
+ }}
+ >
+ {selectedChatRoomId && (
+
+ )}
+
+
+ );
+};
+
+export default ChatTable;
diff --git a/src/components/business/Chat/ChatList.jsx b/src/components/business/Chat/ChatList.jsx
new file mode 100644
index 0000000..f7c66ad
--- /dev/null
+++ b/src/components/business/Chat/ChatList.jsx
@@ -0,0 +1,69 @@
+import React, {useEffect, useState} from 'react';
+import '../../common/Chat/ChatApp.css';
+
+/**
+ * 채팅 리스트 컴포넌트
+ *
+ * @since 2024.03.02
+ * @author 이상민
+ */
+const ChatList = ({api, onChatRoomClick, closeModal}) => {
+
+ const [data, setData] = useState([]);
+ const [limit, setLimit] = useState(10);
+ const [page, setPage] = useState(1);
+
+ const handleChatRoomClick = (chatRoom) => {
+ onChatRoomClick(chatRoom[0]);
+ };
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const response = await api.getChatRooms(page - 1, limit);
+ const mappedData = response.data.data.list.map((chatRoom, index) => {
+ const sequenceNumber = index + 1 + (page - 1) * limit;
+ return {
+ pkId: chatRoom.chatRoomId || "-",
+ sequenceNumber,
+ title: chatRoom.name ? chatRoom.name : "-",
+ createdDate: chatRoom.createdDate ? chatRoom.createdDate : "-",
+ modifiedDate: chatRoom.modifiedDate ? chatRoom.modifiedDate : "-",
+ };
+ });
+ setData( mappedData.map((chatRoom) => [...Object.values(chatRoom)]));
+ } catch (error) {
+ console.error("사용자 데이터를 가져오는 중 오류 발생:", error);
+ }
+ };
+ fetchData();
+ }, [page, limit]);
+
+ return (
+
+
+
대화
+ {data && data.map((chatRoom, index) => (
+
handleChatRoomClick(chatRoom)}>
+
{chatRoom[0]} {chatRoom[2]} {chatRoom[3]}{' '}
+
+
+ ))}
+
+
+
+
+ );
+};
+
+export default ChatList;
diff --git a/src/components/common/Chat/ChatRoom.jsx b/src/components/business/Chat/ChatRoom.jsx
similarity index 60%
rename from src/components/common/Chat/ChatRoom.jsx
rename to src/components/business/Chat/ChatRoom.jsx
index bc2c364..bd7c1ea 100644
--- a/src/components/common/Chat/ChatRoom.jsx
+++ b/src/components/business/Chat/ChatRoom.jsx
@@ -1,26 +1,32 @@
+import ChatApi from "../../../api/chatApi";
import React, {useEffect, useRef, useState} from "react";
-import SockJS from "sockjs-client";
-import { Stomp } from "@stomp/stompjs";
-import { useParams } from "react-router-dom";
import GetTokenFromLocalStorage from "../../../api/Common/token";
-import ChatApi from "../../../api/chatApi";
-import MeChatMessage from "./MeChatMessage";
-import OtherChatMessage from "./OtherChatMessage";
+import SockJS from "sockjs-client";
+import {Stomp} from "@stomp/stompjs";
+import MeChatMessage from "../../common/Chat/MeChatMessage";
+import OtherChatMessage from "../../common/Chat/OtherChatMessage";
+import '../../common/Chat/ChatApp.css';
/**
- * 채팅방
+ * 채팅방 컴포넌트
*
- * @since 2024.03.03
+ * @since 2024.03.02
* @author 이상민
*/
-const ChatRoom = ({role}) => {
+const ChatRoom= ({selectedChatRoom, setSelectedChatRoom}) => {
+
+ console.log("선택된 채팅방:", selectedChatRoom);
+
const [stompClient, setStompClient] = useState(null);
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState("");
- const { roomId } = useParams();
+ const roomId = selectedChatRoom;
+
+
+ const role = 'user'
const messagesContainerRef = useRef(null);
- const token = GetTokenFromLocalStorage(role)
+ const token = GetTokenFromLocalStorage('role')
const [chatRoomDetail, setChatRoomDetail] = useState([]);
@@ -38,14 +44,21 @@ const ChatRoom = ({role}) => {
const uniqueId = `sub-${Math.random().toString(36).substr(2, 9)}`;
stomp.connect({ Authorization: `Bearer ${token}` }, (frame) => {
console.log("연결 성공!", frame);
+ console.log("1 : " + role)
+
// 2. 특정 채팅방에 구독
stomp.subscribe(`/topic/messages`, (message) => {
const newMessage = JSON.parse(message.body);
- newMessage.checkedMe = true;
+ if(newMessage.role === role){
+ newMessage.checkedMe = true;
+ }else{
+ newMessage.checkedMe = false;
+ }
// 기존 메시지와 새 메시지를 합쳐서 상태 업데이트
- setMessages((prevMessages) => [...prevMessages, newMessage]);
+ // setMessages((prevMessages) => [...prevMessages, newMessage]);
+
// 새 메시지가 현재 채팅방에 대한 것인지 확인
if (roomId === newMessage.roomId) {
showMessage(newMessage);
@@ -68,6 +81,8 @@ const ChatRoom = ({role}) => {
const loadPreviousMessages = async () => {
try {
const response = await ChatApi.getMessages(roomId);
+ console.log(response)
+
const data = response.data;
setMessages(data?.data?.chatMessageDetailResponse || []);
setChatRoomDetail(data?.data?.chatRoomDetailResponse || []);
@@ -87,6 +102,7 @@ const ChatRoom = ({role}) => {
const chatMessageRequest = {
roomId: roomId,
message: newMessage,
+ role : role,
sendingTime: new Date().toISOString(),
};
@@ -133,20 +149,45 @@ const ChatRoom = ({role}) => {
}
};
+ const handleGoBack = () => {
+ setSelectedChatRoom(null);
+ };
+
return (
-
-
{chatRoomDetail.name}
+
+
+
+
+ {chatRoomDetail.name}
+
+
+
-
Messages:
+ className="h-[475px] w-[325px] overflow-y-auto mb-4
+ scrollbar-thin scrollbar-thumb-blue-500 scrollbar-track-gray-200"
+ style={{
+ margin: '16px'
+ }}>
+
{messages.map((message, index) => (
-
{message.checkedMe === true ? (
-
+
) : (
{
-
-
setNewMessage(e.target.value)}
- className="flex-grow border p-2 rounded"
- />
-
+
+
);
};
diff --git a/src/components/business/DashBoard/PopupRanking.js b/src/components/business/DashBoard/PopupRanking.js
index b825555..9c216b0 100644
--- a/src/components/business/DashBoard/PopupRanking.js
+++ b/src/components/business/DashBoard/PopupRanking.js
@@ -23,23 +23,20 @@ function PopupRanking() {
}, []);
return (
-
+
{myPopup.length > 0 && (
-
+
MY
-
-
-
- {myPopup[0].popupView}
-
+
- 회
+ {myPopup[0].popupView} 회
diff --git a/src/components/business/FloatingButton/FloatingButton.css b/src/components/business/FloatingButton/FloatingButton.css
new file mode 100644
index 0000000..ed98fe7
--- /dev/null
+++ b/src/components/business/FloatingButton/FloatingButton.css
@@ -0,0 +1,33 @@
+.floating-button {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ background-color: #49675a;
+ color: #fff;
+ font-size: 24px;
+ border: none;
+ cursor: pointer;
+ outline: none;
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
+ transition: background-color 0.3s ease;
+}
+
+.floating-button:hover {
+ background-color: #96b2a4;
+}
+
+.floating-button-modal {
+ position: fixed;
+ top: calc(64%); /* 버튼 바로 위에 위치하도록 수정 */
+ right: 20px; /* 화면 오른쪽에 위치하도록 수정 */
+ transform: translate(0, -50%);
+ overflow: auto;
+ outline: none;
+ border-radius: 25px;
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
+ background-color: #fff; /* 배경 색상 추가 */
+ /* 추가적인 스타일 설정 */
+}
diff --git a/src/components/business/FloatingButton/FloatingButton.jsx b/src/components/business/FloatingButton/FloatingButton.jsx
new file mode 100644
index 0000000..89bd0f3
--- /dev/null
+++ b/src/components/business/FloatingButton/FloatingButton.jsx
@@ -0,0 +1,79 @@
+import React, {useState} from 'react';
+import './FloatingButton.css'; // 스타일 파일을 추가합니다.
+import Modal from 'react-modal';
+import ChatApi from "../../../api/chatApi";
+import ChatRoom from "../../business/Chat/ChatRoom";
+import ChatList from "../../business/Chat/ChatList";
+
+/**
+ * FloatingButton 컴포넌트
+ *
+ * @since 2024.03.04
+ * @author 이상민
+ */
+const FloatingButton = () => {
+ const [modalIsOpen, setModalIsOpen] = useState(false);
+ // const [buttonPosition, setButtonPosition] = useState({ top: 0, left: 0 });
+ const [selectedChatRoom, setSelectedChatRoom] = useState(null);
+
+ const openModal = () => {
+ const button = document.querySelector('.floating-button');
+ if (button) {
+ const rect = button.getBoundingClientRect();
+ // setButtonPosition({ top: rect.bottom + window.scrollY, left: rect.left + window.scrollX });
+ }
+ setModalIsOpen(true);
+ document.body.style.overflow = 'hidden';
+ };
+
+ /**
+ * 모달 닫을 때 사용
+ *
+ * @since 2024.03.04
+ * @author 이상민
+ */
+ const closeModal = () => {
+ setModalIsOpen(false);
+ // 모달이 닫힐 때 선택된 채팅방 정보 초기화
+ document.body.style.overflow = 'auto';
+ setSelectedChatRoom(null);
+ };
+
+ const handleChatRoomClick = (chatRoom) => {
+ console.log("chat room id : " + chatRoom)
+ setSelectedChatRoom(chatRoom);
+ };
+
+ return (
+ <>
+
+
+
+ {/* 선택된 채팅방이 없을 때에만 ChatList를 모달 안에 렌더링 */}
+ {!selectedChatRoom && }
+
+ {/* 선택된 채팅방이 있을 때 ChatRoomDetail을 표시 */}
+ {selectedChatRoom && }
+
+ >
+ );
+};
+
+export default FloatingButton;
diff --git a/src/components/common/Chat/ChatApp.css b/src/components/common/Chat/ChatApp.css
new file mode 100644
index 0000000..827a4dc
--- /dev/null
+++ b/src/components/common/Chat/ChatApp.css
@@ -0,0 +1,62 @@
+
+.rounded-rectangle {
+ position: relative;
+ width: 350px;
+ height: 600px;
+ border-radius: 20px;
+ overflow: hidden;
+ background-color: #ffffff;
+ box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
+}
+
+.content {
+ padding: 20px;
+ overflow-y: auto;
+}
+
+.bottom-nav {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ background-color: #f0f0f0;
+ padding: 10px;
+}
+
+.bottom-nav ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ justify-content: space-around;
+}
+
+.close-button {
+ position: absolute;
+ top: 17px;
+ right: 10px;
+ padding: 8px 12px;
+ border: none;
+ cursor: pointer;
+ font-size: 15px;
+}
+
+.chat-room:hover {
+ background-color: #f0f0f0; /* 호버 시 바뀔 배경색을 지정하세요 */
+ cursor: pointer;
+}
+
+/* 스크롤바의 너비를 조절합니다. */
+#messagesContainer::-webkit-scrollbar {
+ width: 8px; /* 웹킷 브라우저에서 스크롤바의 너비를 설정합니다. */
+}
+
+/* 스크롤바의 색상을 설정합니다. */
+#messagesContainer::-webkit-scrollbar-thumb {
+ background-color: #3498db; /* 스크롤바 색상 설정 */
+ border-radius: 4px; /* 스크롤바의 둥근 모서리를 설정합니다. */
+}
+
+/* 스크롤바 트랙의 색상을 설정합니다. */
+#messagesContainer::-webkit-scrollbar-track {
+ background-color: #f1f1f1; /* 스크롤바 트랙 색상 설정 */
+}
diff --git a/src/components/common/Chat/FormattedTime.jsx b/src/components/common/Chat/FormattedTime.jsx
new file mode 100644
index 0000000..1e9bf1c
--- /dev/null
+++ b/src/components/common/Chat/FormattedTime.jsx
@@ -0,0 +1,19 @@
+import React from 'react';
+
+/**
+ * 시간
+ *
+ * @since 2024.03.04
+ * @author 이상민
+ */
+const FormattedTime = ({ time }) => {
+ const dateObject = new Date(time);
+ const formattedTime = `${(dateObject.getMonth() + 1).toString().padStart(2, '0')}.${dateObject.getDate().toString().padStart(2, '0')} ${dateObject.getHours().toString().padStart(2, '0')}:${dateObject.getMinutes().toString().padStart(2, '0')}`;
+
+ return (
+
{formattedTime}
+ );
+};
+
+export default FormattedTime;
+
diff --git a/src/components/common/Chat/MeChatMessage.jsx b/src/components/common/Chat/MeChatMessage.jsx
index 0172786..2958c12 100644
--- a/src/components/common/Chat/MeChatMessage.jsx
+++ b/src/components/common/Chat/MeChatMessage.jsx
@@ -1,4 +1,5 @@
import React from "react";
+import FormattedTime from "./FormattedTime";
/**
* 나의 채팅 메시지 컴포넌트
@@ -7,16 +8,16 @@ import React from "react";
* @author 이상민
*/
const MeChatMessage = ({ nickname, time, text }) => {
+
return (
+ className="flex flex-col w-full max-w-[240px]
+ leading-1.5 p-4 border-gray-200 rounded-tl-xl rounded-bl-xl rounded-br-xl bg-gray-700"
+ style={{ height: 'auto' }} >
{nickname}
- {time}
+
{text}
diff --git a/src/components/common/Chat/OtherChatMessage.jsx b/src/components/common/Chat/OtherChatMessage.jsx
index b56b8b2..33dc0e2 100644
--- a/src/components/common/Chat/OtherChatMessage.jsx
+++ b/src/components/common/Chat/OtherChatMessage.jsx
@@ -1,4 +1,5 @@
import React from "react";
+import FormattedTime from "./FormattedTime";
/**
* 나 이외의 채팅 메시지 컴포넌트
@@ -10,13 +11,14 @@ const OtherChatMessage = ({ nickname, time, text }) => {
return (
+ className="flex flex-col w-full max-w-[240px]
+ leading-1.5 p-4 border-gray-200 bg-gray-100 rounded-e-xl rounded-es-xl"
+ style={{ height: 'auto' }} >
{nickname}
- {time}
+
{text}
-
Delivered
);
diff --git a/src/components/common/InfoList/RankingList.js b/src/components/common/InfoList/RankingList.js
index 025fe64..bd038e8 100644
--- a/src/components/common/InfoList/RankingList.js
+++ b/src/components/common/InfoList/RankingList.js
@@ -9,16 +9,16 @@ export default RankingList;
function RankingList(props) {
return (
-
+
-
+
{props.id}위
-
-
+
{formatNumberWithCommas(props.content)} 회
diff --git a/src/components/common/Table/ChatTable.jsx b/src/components/common/Table/ChatTable.jsx
deleted file mode 100644
index 99ded25..0000000
--- a/src/components/common/Table/ChatTable.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from "react";
-import TableHeader from "./TableHeader";
-import TableRow from "./TableRow";
-
-/**
- * Table 컴포넌트 생성
- *
- * @since 2024.02.25
- * @author 이상민
- */
-const ChatTable = ({ headerTitles, sampleData }) => {
-
- const handleRowClick = (id) => {
- const currentUrl = window.location.pathname;
- const newUrl = `${currentUrl}/${id}`;
- // 팝업으로 새로운 페이지 열기
- window.open(newUrl, '_blank', 'width=400,height=700,toolbar=no,scrollbars=yes,resizable=yes');
-
- // const currentUrl = window.location.pathname;
- // window.location.href = `${currentUrl}/${id}`;
- };
-
- const renderTableBody = (sampleData) => (
-
- {sampleData.map((rowData, index) => (
- handleRowClick(rowData[0])} />
- ))}
-
- );
-
- return (
-
-
-
- {renderTableBody(sampleData)}
-
-
- );
- };
-
- export default ChatTable;
-
diff --git a/src/components/common/Table/MyPostListTable.js b/src/components/common/Table/MyPostListTable.js
index 7aae700..0ad9088 100644
--- a/src/components/common/Table/MyPostListTable.js
+++ b/src/components/common/Table/MyPostListTable.js
@@ -23,17 +23,16 @@ function MyPostListTable({ posts }) {
<>
- {displayedData.map((post, index) => (
-
-
- {index + 1}
- |
-
- {post.title}
- |
- {post.pulledDate} |
-
- ))}
+ {displayedData.map((post, index) => (
+
+
+ {index + 1}
+ |
+
+ {post.title}
+ |
+
+ ))}
diff --git a/src/components/common/Table/Table.jsx b/src/components/common/Table/Table.jsx
index 4b320c7..c3b0869 100644
--- a/src/components/common/Table/Table.jsx
+++ b/src/components/common/Table/Table.jsx
@@ -22,16 +22,15 @@ const Table = ({ headerTitles, sampleData }) => {
))}
);
-
+
return (
-
-
- {renderTableBody(sampleData)}
-
+
+
+ {renderTableBody(sampleData)}
+
- );
- };
-
- export default Table;
-
+ );
+};
+
+export default Table;
diff --git a/src/components/common/Table/TableRow.jsx b/src/components/common/Table/TableRow.jsx
index 2064ca0..1546687 100644
--- a/src/components/common/Table/TableRow.jsx
+++ b/src/components/common/Table/TableRow.jsx
@@ -26,4 +26,4 @@ const TableRow = ({ rowData, onRowClick }) => {
}
};
-export default TableRow;
\ No newline at end of file
+export default TableRow;
diff --git a/src/pages/administrator/adminChat.jsx b/src/pages/administrator/adminChat.jsx
index 405e121..43ec963 100644
--- a/src/pages/administrator/adminChat.jsx
+++ b/src/pages/administrator/adminChat.jsx
@@ -1,6 +1,6 @@
import React from "react";
import AdminChatApi from "../../api/administrator/adminChatApi";
-import ChatPage from "../../components/common/Chat/ChatPage";
+import ChatPage from "../../components/administrator/ChatPage";
/**
* 채팅 페이지 제작
diff --git a/src/pages/administrator/adminChatRoom.jsx b/src/pages/administrator/adminChatRoom.jsx
deleted file mode 100644
index f1c7b87..0000000
--- a/src/pages/administrator/adminChatRoom.jsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from "react";
-import ChatRoom from "../../components/common/Chat/ChatRoom";
-
-/**
- * 관리자 채팅방
- *
- * @since 2024.03.03
- * @author 이상민
- */
-const AdminChatRoom = () => {
- return (
-
- );
-};
-
-export default AdminChatRoom;
diff --git a/src/pages/business/ad.jsx b/src/pages/business/ad.jsx
index b96fd39..083acab 100644
--- a/src/pages/business/ad.jsx
+++ b/src/pages/business/ad.jsx
@@ -13,6 +13,7 @@ import MyCommunityApi from "../../api/business/createAd/myCommunityApi";
import FileUpload from "../../components/common/Input/FileUpload";
import MyPopupApi from "../../api/business/createAd/myPopupApi";
import Button from "../../components/common/Button/Button";
+import FloatingButton from "../../components/business/FloatingButton/FloatingButton";
/**
* Ad 페이지 제작
@@ -103,6 +104,7 @@ const Ad = () => {
>
diff --git a/src/pages/business/businessChatRoom.jsx b/src/pages/business/businessChatRoom.jsx
deleted file mode 100644
index 75bf8a7..0000000
--- a/src/pages/business/businessChatRoom.jsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from "react";
-import ChatRoom from "../../components/common/Chat/ChatRoom";
-
-/**
- * 사업체 채팅방
- *
- * @since 2024.03.03
- * @author 이상민
- */
-const BusinessChatRoom = () => {
- return (
-
- );
-};
-
-export default BusinessChatRoom;
diff --git a/src/pages/business/chat.jsx b/src/pages/business/chat.jsx
deleted file mode 100644
index e6b872f..0000000
--- a/src/pages/business/chat.jsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from "react";
-import ChatApi from "../../api/chatApi";
-import ChatPage from "../../components/common/Chat/ChatPage";
-
-/**
- * 채팅 페이지 제작
- *
- * @since 2024.03.02
- * @author 이상민
- */
-const Chat = () => {
- return (
-
- );
-};
-
-export default Chat;
diff --git a/src/pages/business/createPlan.jsx b/src/pages/business/createPlan.jsx
index 4eff523..b6876db 100644
--- a/src/pages/business/createPlan.jsx
+++ b/src/pages/business/createPlan.jsx
@@ -7,7 +7,8 @@ import DepartmentDropdown from "../../components/business/DepartmentDropdown/Dep
import FloorDropdown from "../../components/business/FloorDropdown/FloorDropdown";
import Button from "../../components/common/Button/Button";
import PlanApi from "../../api/business/createPlan/planApi";
-import {useRef, useState} from "react";
+import React, {useRef, useState} from "react";
+import FloatingButton from "../../components/business/FloatingButton/FloatingButton";
/**
* CreatePlan 페이지 제작
@@ -86,6 +87,7 @@ const PlanContentBox = ({onOpenDateChange, onCloseDateChange, onPhoneNumberChang
secondInput={
}/>
+
)
}
diff --git a/src/pages/business/dashboard.jsx b/src/pages/business/dashboard.jsx
index 5c3cacc..48c8899 100644
--- a/src/pages/business/dashboard.jsx
+++ b/src/pages/business/dashboard.jsx
@@ -5,6 +5,7 @@ import PopupCurrent from "../../components/business/DashBoard/PopupCurrent";
import PopupRanking from "../../components/business/DashBoard/PopupRanking";
import PopupStatistics from "../../components/business/DashBoard/PopupStatistics";
import PopupPostList from "../../components/business/DashBoard/PopupPostList";
+import FloatingButton from "../../components/business/FloatingButton/FloatingButton";
/**
* DashBoard 페이지 제작
@@ -13,6 +14,7 @@ import PopupPostList from "../../components/business/DashBoard/PopupPostList";
* @author 이승민
*/
const DashBoard = () => {
+
return (
@@ -32,6 +34,7 @@ const DashBoard = () => {
}/>
+
)
}
diff --git a/src/pages/business/plan.jsx b/src/pages/business/plan.jsx
index 5b07d5e..e97bcf1 100644
--- a/src/pages/business/plan.jsx
+++ b/src/pages/business/plan.jsx
@@ -14,6 +14,7 @@ import colorSyntax from '@toast-ui/editor-plugin-color-syntax';
import 'tui-color-picker/dist/tui-color-picker.css';
import '@toast-ui/editor-plugin-color-syntax/dist/toastui-editor-plugin-color-syntax.css';
import './planViewer.css';
+import FloatingButton from "../../components/business/FloatingButton/FloatingButton";
/**
* Plan 페이지 제작
@@ -102,6 +103,7 @@ const Plan = () => {
>
}/>
}/>
+
);
};
diff --git a/src/pages/business/plans.jsx b/src/pages/business/plans.jsx
index 3aef05d..cd529bf 100644
--- a/src/pages/business/plans.jsx
+++ b/src/pages/business/plans.jsx
@@ -2,9 +2,10 @@ import ContentBox from "../../components/common/ContentBox/ContentBox";
import CategoryDropdown from "../../components/business/CategoryDropdown/CategoryDropdown";
import EntranceStatusDropdown from "../../components/business/EntranceStatusDropdown/EntranceStatusDropdown";
import MyPlanTable from "../../components/business/MyPlanTable/MyPlanTable";
-import {useEffect, useState} from "react";
+import React, {useEffect, useState} from "react";
import MyPlansApi from "../../api/business/plans/myPlansApi";
import SearchButton from "../../components/common/Button/SearchButton";
+import FloatingButton from "../../components/business/FloatingButton/FloatingButton";
/**
* Plans 페이지 제작
@@ -38,6 +39,7 @@ function Plans() {
onClick={getPlans(category, entranceStatus, page, limit, setTotal, setPlans)}/>
+
>
}/>