diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..130459b --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + } +} diff --git a/package.json b/package.json index 6aee119..fde34d1 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "mobx-react": "^9.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.21.1" + "react-router-dom": "^6.21.1", + "rxjs": "^7.8.1" }, "devDependencies": { "@types/react": "^18.2.15", diff --git a/src/lib/websocketLib.js b/src/lib/websocketLib.js index b91895b..c9d1b3e 100644 --- a/src/lib/websocketLib.js +++ b/src/lib/websocketLib.js @@ -1,21 +1,23 @@ // websocketLib.js import { WebSocketSubject } from 'rxjs/webSocket'; import { retryWhen, delay, take, catchError } from 'rxjs/operators'; -import { throwError } from 'rxjs'; class WebSocketLib { - constructor(url, authToken) { + constructor(url, authToken, protocol) { this.url = url; this.authToken = authToken; + this.protocol = protocol; } connect() { this.socket$ = new WebSocketSubject({ url: this.url, - protocol: this.authToken, // Using the protocol parameter for authentication + protocol: this.protocol, // Use protocol for message type openObserver: { next: () => { console.log('Connection established'); + // Send authentication message as soon as connection is established + this.socket$.next({ type: 'authenticate', token: this.authToken }); }, }, closeObserver: { @@ -31,7 +33,7 @@ class WebSocketLib { errors.pipe( delay(1000), // Retry connection every 1 second take(10), // Maximum of 10 retries - catchError(error => throwError(`Failed to reconnect: ${error}`)) // Throw error after 10 failed retries + catchError(error => new Error(`Failed to reconnect: ${error}`)) // Throw error after 10 failed retries ) ) ) diff --git a/src/main.jsx b/src/main.jsx index 742a2c9..fc6b817 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -16,6 +16,8 @@ import DingdingQRCode from '@/views/DingdingQRCode' import DingdingCallbak from '@/views/DingdingCallbak' import ErrorPage from '@/views/ErrorPage' +import Conversations from '@/views/Conversations/Components/ChatWindow'; + configure({ useProxies: 'ifavailable', enforceActions: 'observed', @@ -30,18 +32,19 @@ const router = createBrowserRouter([ path: '/', element: , errorElement: , - children: [ + children: [ { index: true, element: }, ] }, { path: '/s', element: , - children: [ + children: [ { index: true, element: }, { path: 'order/follow', element: }, { path: 'chat/history', element: }, { path: 'sales/management', element: }, + { path: 'order/chat', element: }, ] }, { diff --git a/src/views/Conversations/App.jsx b/src/views/Conversations/App.jsx new file mode 100644 index 0000000..e54cd21 --- /dev/null +++ b/src/views/Conversations/App.jsx @@ -0,0 +1,12 @@ +import Chat from './Index'; +// import 'antd/dist/reset.css'; + +function App() { + return ( +
+ +
+ ); +} + +export default App; diff --git a/src/views/Conversations/App1.jsx b/src/views/Conversations/App1.jsx new file mode 100644 index 0000000..b1a985d --- /dev/null +++ b/src/views/Conversations/App1.jsx @@ -0,0 +1,57 @@ +import { useState } from 'react'; +import { List, Input, Avatar, Button } from 'antd'; + +const messages = [ + { + sender: 'John Doe', + message: 'Hey!', + }, + { + sender: 'Jane Doe', + message: 'Hi John!', + }, +]; + +function App() { + const [message, setMessage] = useState(''); + + const sendMessage = () => { + // Update messages with new message data + const newMessage = { + sender: 'You', + message, + }; + messages.push(newMessage); + setMessage(''); + }; + + return ( +
+ ( + + } + title={message.sender} + description={message.message} + /> + + )} + /> + + setMessage(e.target.value)} + /> + + +
+ ); +} + +export default App; diff --git a/src/views/Conversations/Chat.css b/src/views/Conversations/Chat.css new file mode 100644 index 0000000..7d22a0c --- /dev/null +++ b/src/views/Conversations/Chat.css @@ -0,0 +1,28 @@ +.chat-layout { + height: 100vh; +} + +.chat-sider { + overflow: auto; +} + +.chat-content { + padding: 24px; + overflow: auto; +} + +.chat-input { + position: fixed; + bottom: 0; + width: calc(100% - 300px); + display: flex; + padding: 24px; +} + +.chat-input .ant-input { + margin-right: 24px; +} + +.chat-button { + height: 40px; +} \ No newline at end of file diff --git a/src/views/Conversations/Components/ChatWindow.jsx b/src/views/Conversations/Components/ChatWindow.jsx new file mode 100644 index 0000000..201f6ad --- /dev/null +++ b/src/views/Conversations/Components/ChatWindow.jsx @@ -0,0 +1,38 @@ + +import { Layout } from 'antd'; +import Messages from './Messages'; +import InputBox from './InputBox'; +import Conversations from './Conversations'; +import CustomerProfile from './CustomerProfile'; + +const {Sider, Content } = Layout; + +const CList = [ + { name: 'Customer_1', label: 'Customer_1', key: 'Customer_1', value: 'Customer_1' }, + { name: 'Customer_2', label: 'Customer_2', key: 'Customer_2', value: 'Customer_2' }, + { name: 'Customer_3', label: 'Customer_3', key: 'Customer_3', value: 'Customer_3' }, + { name: 'Customer_4', label: 'Customer_4', key: 'Customer_4', value: 'Customer_4' }, +]; +function ChatWindow() { + + return ( + + + + + + + + + + + + + + + + + ); +} + +export default ChatWindow; diff --git a/src/views/Conversations/Components/Conversations.jsx b/src/views/Conversations/Components/Conversations.jsx new file mode 100644 index 0000000..020bc15 --- /dev/null +++ b/src/views/Conversations/Components/Conversations.jsx @@ -0,0 +1,31 @@ +import { List, Avatar } from 'antd'; +import PropTypes from 'prop-types'; + +const ColorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae']; +function Conversations({ conversations }) { + return ( + ( + + + {item.name} + + } + title={item.name} + /> + + )} + /> + ); +} +Conversations.propTypes = { + conversations: PropTypes.string.isRequired, +}; +export default Conversations; diff --git a/src/views/Conversations/Components/CustomerProfile.jsx b/src/views/Conversations/Components/CustomerProfile.jsx new file mode 100644 index 0000000..961b524 --- /dev/null +++ b/src/views/Conversations/Components/CustomerProfile.jsx @@ -0,0 +1,8 @@ +import { Card } from 'antd'; + +function CustomerProfile({ customer }) { + return {/* other profile details */}; +} +CustomerProfile.propTypes = { +}; +export default CustomerProfile; diff --git a/src/views/Conversations/Components/InputBox.jsx b/src/views/Conversations/Components/InputBox.jsx new file mode 100644 index 0000000..a665e8b --- /dev/null +++ b/src/views/Conversations/Components/InputBox.jsx @@ -0,0 +1,22 @@ + +import { Input, Button } from 'antd'; + +function InputBox({ onSend }) { + + function handleSend() { + // Logic to get message and call onSend + } + + return ( +
+ +
+ ); +} + +export default InputBox; diff --git a/src/views/Conversations/Components/Messages.jsx b/src/views/Conversations/Components/Messages.jsx new file mode 100644 index 0000000..49d071c --- /dev/null +++ b/src/views/Conversations/Components/Messages.jsx @@ -0,0 +1,21 @@ + +import { List, Avatar } from 'antd'; + +function Messages({ messages }) { + return ( + ( + + {message.sender[0]}} + title={message.sender} + /> +
{message.text}
+
+ )} + /> + ); +} + +export default Messages; diff --git a/src/views/Conversations/Index.jsx b/src/views/Conversations/Index.jsx new file mode 100644 index 0000000..92b3c60 --- /dev/null +++ b/src/views/Conversations/Index.jsx @@ -0,0 +1,54 @@ +import { Layout, List, Input, Button } from 'antd'; +import './Chat.css'; + +const { Sider, Content } = Layout; +const { TextArea } = Input; + +const Chat = () => { + const channels = ['Channel 1', 'Channel 2']; + const messages = [ + { user: 'User 1', text: 'Hello!' }, + { user: 'User 2', text: 'Hi!' }, + ]; + + return ( + + + Channels} + bordered + dataSource={channels} + renderItem={item => ( + + {item} + + )} + /> + + + + ( + + + + )} + /> +
+