会话切换

dev/mobile
Lei OT 1 year ago
parent abdc22a6bf
commit 5c174ebe8b

@ -15,7 +15,6 @@ import AccountProfile from '@/views/AccountProfile'
import ErrorPage from '@/components/ErrorPage'
import Conversations from '@/views/Conversations/ChatWindow'
// import Conversations from '@/views/Conversations/ChatApp'
import '@/assets/index.css'
configure({

@ -1,59 +0,0 @@
import { makeAutoObservable, runInAction, toJS } from 'mobx';
import { RealTimeAPI } from '@/lib/realTimeAPI';
const URL = 'ws://202.103.68.144:8888/whatever/';
let realtimeAPI = new RealTimeAPI({ url: URL, protocol: 'aaa' });
class Conversations {
constructor() {
makeAutoObservable(this, { rootStore: false });
// this.sendMessage = this.sendMessage.bind(this);
realtimeAPI.onError(this.addError.bind(this, 'Error'));
realtimeAPI.onMessage(this.handleMessage.bind(this));
realtimeAPI.onCompletion(this.addError.bind(this, 'Not Connected to Server'));
realtimeAPI.keepAlive(); // Ping Server
// realtimeAPI.onMessage().subscribe(message => {
// this.addMessage(message);
// });
}
addError = (reason) => {
// this.errors.push({ reason });
this.errors = [...this.errors, { reason }];
}
addMessage = (message) => {
// this.messages.push(msg.message); // Mobx will not work
this.messages = [...this.messages, message];
}
handleMessage = (data) => {
const msg = data.result;
if (!msg) {
return false;
}
if (typeof msg.type === 'string' && msg.type === 'error') {
this.addError('Error Connecting to Server');
}
// todo: handle message
runInAction(() => {
this.addMessage({ ...msg.message, sender: 'other', id: Date.now().toString(16), });
// console.log(toJS(this.messages), 'messages');
});
}
sendMessage = (msg) => {
const msgObj = {
type: 'message',
message: msg,
};
realtimeAPI.sendMessage(msgObj);
this.addMessage(msgObj.message);
}
errors = [];
messages = [];
}
export default Conversations;

@ -1,7 +1,7 @@
import React, { createContext, useContext, useState, useEffect, useRef } from 'react';
import { RealTimeAPI } from '@/lib/realTimeAPI';
import { createContext, useContext, useState, useEffect, useRef } from 'react';
import { receivedMsgTypeMapped, sentMsgTypeMapped } from '@/lib/msgUtils';
import { groupBy, isEmpty } from '@/utils/utils';
import { groupBy, isEmpty, pick } from '@/utils/utils';
import { v4 as uuid } from 'uuid';
export const ConversationContext = createContext();
export const useConversationContext = () => useContext(ConversationContext);
@ -81,8 +81,6 @@ export const useConversations = ({loginUser, realtimeAPI}) => {
}
};
// const templates = AllTemplates.filter((_t) => _t.status !== 'REJECTED').map((ele) => ({ ...ele, components: groupBy(ele.components, (_c) => _c.type.toLowerCase()) })); // test: 0
// const [templates, setTemplates] = useState([]);
const [templatesList, setTemplatesList] = useState([]);
const getTemplates = async () => {
const data = await fetchJSON(`${API_HOST}/listtemplates`);
@ -105,23 +103,40 @@ export const useConversations = ({loginUser, realtimeAPI}) => {
} else {
// reset chat window
setMessages([]);
if (isEmpty(data.contact?.[0]?.whatsapp_phone_number)) {
setCurrentConversation({ sn: '', customer_name: '', coli_sn: '' });
// todo: 加入新会话
return false;
}
// 加入新会话
const newChat = {
'sn': uuid(),
// 'opi_sn': 354,
'coli_sn': colisn,
'whatsapp_phone_number': data.contact[0].whatsapp_phone_number,
"last_received_time": '',
"last_send_time": '',
'unread_msg_count': 0,
'whatsapp_name': data.contact[0].name,
'customer_name': data.contact[0].name,
};
setConversationsList((pre) => [...pre, newChat]);
setActiveConversations((pre) => ({ ...pre, [`${newChat.sn}`]: [] }));
setCurrentConversation(pick(newChat, ['sn', 'coli_sn', 'whatsapp_phone_number', 'whatsapp_name', 'customer_name']));
}
};
const switchConversation = (cc) => {
setCurrentID(`${cc.sn}`);
setCurrentConversation({...cc, id: cc.sn, customer_name: cc.whatsapp_name});
// Get customer profile when switching conversation
// getCustomerProfile(??);
setCurrentConversation({...cc, id: cc.sn, customer_name: cc.whatsapp_name.trim()});
if (isEmpty(cc.coli_sn) || cc.coli_sn === '0') {
setCustomerProfile({});
}
};
// Get customer profile when switching conversation
useEffect(() => {
console.log('currentConversation', currentConversation);
// const colisn = currentConversation.coli_sn;
// getCustomerProfile(colisn);
setMessages([...(activeConversations[currentID] || [])]);
return () => {};
}, [currentConversation]);
@ -164,7 +179,6 @@ export const useConversations = ({loginUser, realtimeAPI}) => {
const addMessage = (message) => {
setMessages((prevMessages) => [...prevMessages, message]);
// addMessageToConversations(currentConversationRef.current.sn, message);
addMessageToConversations(message.conversationid, message);
};
@ -238,7 +252,6 @@ export const useConversations = ({loginUser, realtimeAPI}) => {
getConversationsList,
switchConversation,
templates: templatesList, // setTemplates, getTemplates,
// templates, // debug: 0
customerOrderProfile, getCustomerProfile
};
};
@ -251,340 +264,7 @@ export const useConversations = ({loginUser, realtimeAPI}) => {
// test: 0 "type": "whatsapp.inbound_message.received",
const all = [
// {
// 'id': '65b06828619a1d82777eb4c6',
// 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBQzNDNzBFNjFCREJBNDIyQjQ2AA==',
// 'wabaId': '190290134156880',
// 'from': '+8613317835586',
// 'customerProfile': {
// 'name': 'qqs',
// },
// 'to': '+8617607730395',
// 'sendTime': '2024-01-24T01:30:14.000Z',
// 'type': 'image',
// 'image': {
// 'link':
// 'https://api.ycloud.com/v2/whatsapp/media/download/934379820978291?sig=t%3D1706059814%2Cs%3D91a79a0e4007ad2f6a044a28307affe663f7f81903b3537bd80e758d3c0d0563&payload=eyJpZCI6IjkzNDM3OTgyMDk3ODI5MSIsIndhYmFJZCI6IjE5MDI5MDEzNDE1Njg4MCIsImluYm91bmRNZXNzYWdlSWQiOiI2NWIwNjgyODYxOWExZDgyNzc3ZWI0YzYiLCJtaW1lVHlwZSI6ImltYWdlL2pwZWciLCJzaGEyNTYiOiJPVTJjdkN2eHplMUdMMmQ5NUxyTGVaNmpNb2ZscUZYM1RvcXdTTUNWZkxNPSJ9',
// 'id': '934379820978291',
// 'sha256': 'OU2cvCvxze1GL2d95LrLeZ6jMoflqFX3ToqwSMCVfLM=',
// 'mime_type': 'image/jpeg',
// },
// },
// {
// 'id': '65b06ce6619a1d8277c97fc0',
// 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBMUJBOUZCODY4NkNBMkM2NUEzAA==',
// 'wabaId': '190290134156880',
// 'from': '+8613317835586',
// 'customerProfile': {
// 'name': 'qqs',
// },
// 'to': '+8617607730395',
// 'sendTime': '2024-01-24T01:50:29.000Z',
// 'type': 'text',
// 'text': {
// 'body': 'eeee',
// },
// },
// {
// 'id': '65b06b2f619a1d8277b5ab06',
// 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBRkU0RUZGRUI1OUQzQUFBMEExAA==',
// 'wabaId': '190290134156880',
// 'from': '+8613317835586',
// 'customerProfile': {
// 'name': 'qqs',
// },
// 'to': '+8617607730395',
// 'sendTime': '2024-01-24T01:43:09.000Z',
// 'type': 'audio',
// 'audio': {
// 'link':
// 'https://api.ycloud.com/v2/whatsapp/media/download/901696271448320?sig=t%3D1706060589%2Cs%3Dca75dbd57e4867783390c913491263f07c9738d69c141d4ae622c76df9fa033b&payload=eyJpZCI6IjkwMTY5NjI3MTQ0ODMyMCIsIndhYmFJZCI6IjE5MDI5MDEzNDE1Njg4MCIsImluYm91bmRNZXNzYWdlSWQiOiI2NWIwNmIyZjYxOWExZDgyNzdiNWFiMDYiLCJtaW1lVHlwZSI6ImF1ZGlvL29nZzsgY29kZWNzPW9wdXMiLCJzaGEyNTYiOiJoZUNSUDdEMjM3bG9ydkZ4eFhSdHZpU1ZsNDR3Rlk4TytaMFhic2k5cy9rPSJ9',
// 'id': '901696271448320',
// 'sha256': 'heCRP7D237lorvFxxXRtviSVl44wFY8O+Z0Xbsi9s/k=',
// 'mime_type': 'audio/ogg; codecs=opus',
// },
// },
// {
// 'id': '65b06b12619a1d8277b3c0c4',
// 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBREZEMEM0MURDNjJGREVEQjY3AA==',
// 'wabaId': '190290134156880',
// 'from': '+8613317835586',
// 'customerProfile': {
// 'name': 'qqs',
// },
// 'to': '+8617607730395',
// 'sendTime': '2024-01-24T01:42:40.000Z',
// 'type': 'video',
// 'video': {
// 'link':
// 'https://api.ycloud.com/v2/whatsapp/media/download/742404324517058?sig=t%3D1706060560%2Cs%3D53eeb1508c2103e310fb14a72563a8e07c5a84c7e6192a25f3608ac9bea32334&payload=eyJpZCI6Ijc0MjQwNDMyNDUxNzA1OCIsIndhYmFJZCI6IjE5MDI5MDEzNDE1Njg4MCIsImluYm91bmRNZXNzYWdlSWQiOiI2NWIwNmIxMjYxOWExZDgyNzdiM2MwYzQiLCJtaW1lVHlwZSI6InZpZGVvL21wNCIsInNoYTI1NiI6IlNJcjRlZFlPb1BDTGtETEgrVTY2d3dkMDgra2JndFV5OHRDd2RjQU5FaFU9In0',
// 'caption': 'and',
// 'id': '742404324517058',
// 'sha256': 'SIr4edYOoPCLkDLH+U66wwd08+kbgtUy8tCwdcANEhU=',
// 'mime_type': 'video/mp4',
// },
// },
// {
// 'id': '65b06aa7619a1d8277ac806e',
// 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBOTFBOTU5RDE2QjgxQTQ1MEE2AA==',
// 'wabaId': '190290134156880',
// 'from': '+8613317835586',
// 'customerProfile': {
// 'name': 'qqs',
// },
// 'to': '+8617607730395',
// 'sendTime': '2024-01-24T01:40:53.000Z',
// 'type': 'sticker',
// 'sticker': {
// 'link':
// 'https://api.ycloud.com/v2/whatsapp/media/download/1156118002042289?sig=t%3D1706060453%2Cs%3Dfbd5f881856614e35715b1e3e1097b3bbe56f8a36aaa67bfbef25a37d9143d51&payload=eyJpZCI6IjExNTYxMTgwMDIwNDIyODkiLCJ3YWJhSWQiOiIxOTAyOTAxMzQxNTY4ODAiLCJpbmJvdW5kTWVzc2FnZUlkIjoiNjViMDZhYTc2MTlhMWQ4Mjc3YWM4MDZlIiwibWltZVR5cGUiOiJpbWFnZS93ZWJwIiwic2hhMjU2IjoibUNaLzdhNnNaNlRNYTE0WW9rUkNTZnVsdGpZNmFRRVZFNVoxMVRwanNQOD0ifQ',
// 'id': '1156118002042289',
// 'sha256': 'mCZ/7a6sZ6TMa14YokRCSfultjY6aQEVE5Z11TpjsP8=',
// 'mime_type': 'image/webp',
// 'animated': false,
// },
// },
// {
// 'id': '65b06a91619a1d8277aaf05e',
// 'wamid': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBRjUxNzdCQ0FEOTlFQzc5MzQ1AA==',
// 'wabaId': '190290134156880',
// 'from': '+8613317835586',
// 'customerProfile': {
// 'name': 'qqs',
// },
// 'to': '+8617607730395',
// 'sendTime': '2024-01-24T01:40:32.000Z',
// 'type': 'unsupported',
// 'errors': [
// {
// 'code': '131051',
// 'title': 'Message type unknown',
// 'message': 'Message type unknown',
// 'error_data': {
// 'details': 'Message type is currently not supported.',
// },
// },
// ],
// },
// {
// "id": "65b38323619a1d827778986d",
// "wamid": "wamid.HBgMOTE4NTg3OTAxMDg2FQIAEhgWM0VCMDMwMjc5OThGN0EyN0JERjY5QwA=",
// "wabaId": "190290134156880",
// "from": "+918587901086",
// "customerProfile": {
// "name": "Shailesh"
// },
// "to": "+8617607730395",
// "sendTime": "2024-01-26T10:02:09.000Z",
// "type": "document",
// "document": {
// "link": "https://api.ycloud.com/v2/whatsapp/media/download/773594381476592?sig=t%3D1706263329%2Cs%3D0e0221fc921b29f6e2f896a2f62a3b99a60d09b40e7332d509e7b0cd2a4a630e&payload=eyJpZCI6Ijc3MzU5NDM4MTQ3NjU5MiIsIndhYmFJZCI6IjE5MDI5MDEzNDE1Njg4MCIsIndhbWlkIjoid2FtaWQuSEJnTU9URTROVGczT1RBeE1EZzJGUUlBRWhnV00wVkNNRE13TWpjNU9UaEdOMEV5TjBKRVJqWTVRd0E9IiwibWltZVR5cGUiOiJhcHBsaWNhdGlvbi92bmQub3BlbnhtbGZvcm1hdHMtb2ZmaWNlZG9jdW1lbnQud29yZHByb2Nlc3NpbmdtbC5kb2N1bWVudCIsInNoYTI1NiI6ImFUK3hnSGRhaGNtekVFa0g3bmdZbHloVSt1R2ZuTnIvdlEwNGtKSTBaMFU9In0",
// "filename": "NEPAL INDIA TOUR - NOV 2024 - revised.docx",
// "id": "773594381476592",
// "sha256": "aT+xgHdahcmzEEkH7ngYlyhU+uGfnNr/vQ04kJI0Z0U=",
// "mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
// }
// }
];
const all2 = [
{
'id': '63f71fb8741c165b434292fb',
'wamid': 'wamid.HBgNOD...',
'wabaId': 'WABA-ID',
'from': 'CUSTOMER-PHONE-NUMBER',
'customerProfile': {
'name': 'Joe',
},
'to': 'BUSINESS-PHONE-NUMBER',
'sendTime': '2023-02-22T12:00:00.000Z',
'type': 'reaction',
'reaction': {
'message_id': 'wamid.HBgNODYxMzMxNzgzNTU4NhUCABIYFDNBMUJBOUZCODY4NkNBMkM2NUEzAA==',
'emoji': '👍',
},
},
{
'id': '65b1de2f3f0bb66a91377930',
'wamid': 'wamid.HBgNODYxODc3NzM5Njk1MRUCABEYEkM4NTU5MjMyRDFCRkE5NjM2RAA=',
'status': 'sent',
'from': '+8617607730395',
'to': '+8618777396951',
'wabaId': '190290134156880',
'type': 'template',
'template': {
'name': 'hello',
'language': {
'code': 'zh_CN',
},
},
'conversation': {
'id': 'ddb659076d0e970f16fb19520c116a1b',
'originType': 'marketing',
'expireTime': '2024-01-26T04:07:00.000Z',
},
'createTime': '2024-01-25T04:06:07.850Z',
'updateTime': '2024-01-25T04:06:09.151Z',
'sendTime': '2024-01-25T04:06:08.000Z',
'totalPrice': 0.0782,
'currency': 'USD',
'bizType': 'whatsapp',
},
];
const templateExample = [
{
'id': 'evt_eEMtA0PkkyACiS5o',
'type': 'whatsapp.template.reviewed',
'apiVersion': 'v2',
'createTime': '2023-02-20T12:00:00.000Z',
'whatsappTemplate': {
'wabaId': 'WABA-ID',
'name': 'template_name',
'language': 'en',
'category': 'MARKETING',
'status': 'APPROVED',
'reason': 'NONE',
'statusUpdateEvent': 'APPROVED',
},
},
];
const AllTemplates = [
{
'wabaId': '190290134156880',
'name': 'say_hi',
'language': 'en',
'components': [
{
'type': 'BODY',
'text':
"Hi {{customer_name}} I'm {{your_name}} from Asia Highlights.\n\nWe provide *families* and *couples* with personalized and stress-free experiences, whether for _milestone trips_, _birthday trips_, _graduation trips_, or _bucketlist trips_.",
'example': {
'body_text': [['Mike', 'Jimmy']],
},
},
{
'type': 'BUTTONS',
'buttons': [
{
'type': 'URL',
'text': 'Asia Highlights',
'url': 'https://www.asiahighlights.com/',
},
],
},
],
'category': 'UTILITY',
'status': 'REJECTED',
'qualityRating': 'UNKNOWN',
'reason': 'INCORRECT_CATEGORY',
'createTime': '2024-01-23T02:26:33.012Z',
'updateTime': '2024-01-23T02:26:35.397Z',
'statusUpdateEvent': 'REJECTED',
},
{
'wabaId': '190290134156880',
'name': 'i_hope_this_message_finds_you_well',
'language': 'en',
'components': [
{
'type': 'BODY',
'text': 'Hi {{customer_name}}, I hope this message finds you well. Did you see my previous message?',
'example': {
'body_text': [['Mike']],
},
},
],
'category': 'UTILITY',
'status': 'REJECTED',
'qualityRating': 'UNKNOWN',
'reason': 'INCORRECT_CATEGORY',
'createTime': '2024-01-23T02:22:20.232Z',
'updateTime': '2024-01-23T02:22:22.937Z',
'statusUpdateEvent': 'REJECTED',
},
{
'wabaId': '190290134156880',
'name': 'asia_highlights_has_receive_your_inquiry',
'language': 'en',
'components': [
{
'type': 'BODY',
'text':
'Dear {{customer_name}},\n\nThank you for choosing Asia Highlights. Your inquiry has been submitted to Asia Highlights. One of our travel advisors will respond within 24 hours.',
'example': {
'body_text': [['Jimmy Liow']],
},
},
{
'type': 'HEADER',
'format': 'TEXT',
'text': 'Asia highlights has receive your inquiry',
},
{
'type': 'FOOTER',
'text': 'Kind regards, Asia Highlights Team',
},
{
'type': 'BUTTONS',
'buttons': [
{
'type': 'URL',
'text': 'Asia Highlights',
'url': 'https://www.asiahighlights.com/',
},
],
},
],
'category': 'UTILITY',
'status': 'APPROVED',
'qualityRating': 'UNKNOWN',
'reason': 'NONE',
'createTime': '2024-01-19T05:59:32.933Z',
'updateTime': '2024-01-19T05:59:55.581Z',
'statusUpdateEvent': 'APPROVED',
},
{
'wabaId': '190290134156880',
'name': 'hello',
'language': 'zh_CN',
'components': [
{
'type': 'BODY',
'text': '你好,这是一个测试程序',
},
{
'type': 'HEADER',
'format': 'TEXT',
'text': 'Hello 同学',
},
{
'type': 'FOOTER',
'text': 'Global Highlights',
},
{
'type': 'BUTTONS',
'buttons': [
{
'type': 'URL',
'text': 'about us',
'url': 'https://www.globalhighlights.com/',
},
],
},
],
'category': 'MARKETING',
'status': 'APPROVED',
'qualityRating': 'UNKNOWN',
'reason': 'NONE',
'createTime': '2023-11-17T03:26:10.961Z',
'updateTime': '2023-11-17T13:36:33.623Z',
'statusUpdateEvent': 'APPROVED',
},
];
// test:
const testConversations = [
{
'sn': 3001,

@ -44,7 +44,7 @@ const ChatWindow = (() => {
<Layout className='h-full'>
<Header className='ant-layout-sider-light ant-card p-1 h-auto'>
<Flex gap={16}>
<Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${currentConversation.customer_name}`} />
{currentConversation.customer_name && <Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${currentConversation.customer_name}`} />}
<Flex flex={'1'} justify='space-between'>
<Flex vertical={true} justify='space-between'>
<Typography.Text strong>{currentConversation.customer_name}</Typography.Text>

@ -1,7 +1,7 @@
import { Card, Flex, Avatar, Typography, Radio, Button, Table } from 'antd';
import { useAuthContext } from '@/stores/AuthContext.js';
import { useConversationContext } from '@/stores/Conversations/ConversationContext';
import { HomeOutlined, LoadingOutlined, SettingFilled, SmileOutlined, SyncOutlined, PhoneOutlined, MailOutlined } from '@ant-design/icons';
import { HomeOutlined, LoadingOutlined, SettingFilled, SmileOutlined, SyncOutlined, PhoneOutlined, MailOutlined, WhatsAppOutlined, SmileTwoTone } from '@ant-design/icons';
import CreatePayment from './CreatePayment';
import QuotesHistory from './QuotesHistory';
@ -40,8 +40,9 @@ const CustomerProfile = (({ customer }) => {
{/* <Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${contact?.name}`} /> */}
<Flex vertical={true} justify='space-between'>
<Typography.Text strong >{contact?.[0]?.name}</Typography.Text>
<Typography.Text ><PhoneOutlined className=' pr-1' />{contact?.[0]?.phone}</Typography.Text>
<Typography.Text ><MailOutlined className=' pr-1' />{contact?.[0]?.email}</Typography.Text>
{contact?.[0]?.phone && <Typography.Text ><PhoneOutlined className=' pr-1' />{contact?.[0]?.phone}</Typography.Text>}
{contact?.[0]?.email && <Typography.Text ><MailOutlined className=' pr-1' />{contact?.[0]?.email}</Typography.Text>}
<Typography.Text ><WhatsAppOutlined className='pr-1 text-whatsapp' />{contact?.[0]?.whatsapp_phone_number}</Typography.Text>
{/* <div>{order?.order_no}</div> */}
{/* <div>
{order?.location} <span>{order?.local_datetime}</span>
@ -51,16 +52,16 @@ const CustomerProfile = (({ customer }) => {
</Flex>
</Card>
<Flex vertical={true} className='p-2 '>
<div>最新报价</div>
<Typography.Text strong>最新报价</Typography.Text>
<p className='m-0 py-2 line-clamp-2 '>{quotes?.[0]?.lettertitle}</p>
<Flex justify={'space-between'} >
<CreatePayment />
<QuotesHistory />
</Flex>
</Flex>
<pre className='p-2 overflow-auto h-32' dangerouslySetInnerHTML={{__html: order?.order_detail}}></pre>
<pre className='p-2 overflow-auto max-h-32 m-0' dangerouslySetInnerHTML={{__html: order?.order_detail}}></pre>
<Flex vertical={true} className='p-2 '>
<div>沟通记录</div>
<Typography.Text strong>沟通记录</Typography.Text>
<Table size={'small'} columns={[{ title: '进程', dataIndex: 'title' }, { title: '状态', dataIndex: 'title2' },]} />
</Flex>
</div>

@ -5,7 +5,10 @@ export default {
theme: {
colors: {
'whatsapp': '#25D366',
// 'whatsappme': '#1ba784',
'whatsapp-dark': '#075E54',
'whatsapp-second': '#128c7e',
'whatsapp-gossip': '#dcf8c6',
'whatsapp-bg': '#ece5dd',
'whatsappme': '#ccd5ae',
},
extend: {

Loading…
Cancel
Save