diff --git a/src/lib/msgUtils.js b/src/lib/msgUtils.js index dcd2b7e..c9710dc 100644 --- a/src/lib/msgUtils.js +++ b/src/lib/msgUtils.js @@ -175,7 +175,6 @@ export const whatsappMsgTypeMapped = { type: 'text', data: (msg) => { const templateDataMapped = msg.template?.components ? msg.template.components.reduce((r, v) => ({...r, [v.type]: v}), {}) : null; - console.log(msg.template.name, templateDataMapped); return { id: msg.wamid, text: templateDataMapped?.body?.parameters?.[0]?.text || '', title: msg.template.name } }, }, @@ -226,6 +225,7 @@ export const parseRenderMessageList = (messages, conversationid = null) => { /** * WhatsApp Templates params + * @deprecated */ export const whatsappTemplatesParamMapped = { /** @deprecated */ diff --git a/src/views/Conversations/ChatWindow.jsx b/src/views/Conversations/ChatWindow.jsx index 749d26b..fd7605d 100644 --- a/src/views/Conversations/ChatWindow.jsx +++ b/src/views/Conversations/ChatWindow.jsx @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { Layout, Spin } from 'antd'; import MessagesHeader from './Components/MessagesHeader'; import Messages from './Components/Messages'; -import InputBox from './Components/InputBox'; +import InputBox from './Components/InputComposer'; import ConversationsList from './Components/ConversationsList'; import CustomerProfile from './Components/CustomerProfile'; diff --git a/src/views/Conversations/Components/InputBox.jsx b/src/views/Conversations/Components/InputBox.jsx deleted file mode 100644 index 19ee899..0000000 --- a/src/views/Conversations/Components/InputBox.jsx +++ /dev/null @@ -1,153 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Input, Button, Tabs, List, Space, Popover, Flex } from 'antd'; -// import { Input } from 'react-chat-elements'; -import { useConversationState, useConversationDispatch } from '@/stores/ConversationContext'; -import { sentNewMessage } from '@/actions/ConversationActions'; -import { useAuthContext } from '@/stores/AuthContext'; -import { LikeOutlined, MessageOutlined, StarOutlined, SendOutlined, PlusOutlined, PlusCircleOutlined } from '@ant-design/icons'; -import { cloneDeep, getNestedValue, isEmpty } from '@/utils/utils'; -import { v4 as uuid } from 'uuid'; -import { whatsappTemplatesParamMapped, sentMsgTypeMapped, replaceTemplateString } from '@/lib/msgUtils'; - -const InputBox = () => { - const { loginUser } = useAuthContext(); - const { userId } = loginUser; - const { websocket, currentConversation, templates } = useConversationState(); - const dispatch = useConversationDispatch(); - const [textContent, setTextContent] = useState(''); - - const talkabled = !isEmpty(currentConversation.sn); - - const invokeSendMessage = (msgObj) => { - console.log('sendMessage------------------', msgObj); - const contentToSend = sentMsgTypeMapped[msgObj.type].contentToSend(msgObj); - console.log('content to send-------------------------------------', contentToSend); - websocket.sendMessage({ ...contentToSend, opi_sn: userId, coli_sn: currentConversation.coli_sn }); - const contentToRender = sentMsgTypeMapped[msgObj.type].contentToRender(msgObj); - console.log(contentToRender, 'contentToRender sendMessage------------------'); - dispatch(sentNewMessage(contentToRender)); - }; - - const handleSendText = () => { - if (textContent.trim() !== '') { - const msgObj = { - type: 'text', - text: textContent, - sender: 'me', - to: currentConversation.whatsapp_phone_number, - id: `${currentConversation.sn}.${uuid()}`, // Date.now().toString(16), - date: new Date(), - status: 'waiting', - }; - invokeSendMessage(msgObj); - setTextContent(''); - } - }; - - const handleSendTemplate = (fromTemplate) => { - console.log(fromTemplate, 'fromTemplate'); - const _conversation = { ...cloneDeep(currentConversation) }; - const msgObj = { - type: 'whatsappTemplate', - to: currentConversation.whatsapp_phone_number, - id: `${currentConversation.sn}.${uuid()}`, - date: new Date(), - status: 'waiting', - statusTitle: 'Ready to send', - sender: 'me', - template: { - name: fromTemplate.name, - language: { code: fromTemplate.language }, - components: fromTemplate.components_origin.map(citem => { - const params = whatsappTemplatesParamMapped[fromTemplate.name].map((v) => ({ type: 'text', text: getNestedValue(_conversation, v) || '' })); - const paramText = params.map((p) => p.text); - const fillTemplate = paramText.length ? replaceTemplateString(citem?.text || '', paramText) : citem?.text || ''; - - return { - type: citem.type.toLowerCase(), - parameters: params, - text: fillTemplate, - }; - }), - // ...(fromTemplate.components.body[0]?.example?.body_text?.[0]?.length > 0 - // ? { - // components: [ - // { - // 'type': 'body', - // 'text': fromTemplate.components.body[0]?.text, - // 'parameters': whatsappTemplatesParamMapped[fromTemplate.name].map((v) => ({ type: 'text', text: getNestedValue(_conversation, v) || '' })), - // // [ - // // { - // // 'type': 'text', - // // 'text': getNestedValue(_conversation, whatsappTemplatesParamMapped[fromTemplate.name][0]) , - // // }, - // // { // debug: - // // 'type': 'text', - // // 'text': getNestedValue(_conversation, whatsappTemplatesParamMapped[fromTemplate.name]?.[1] || whatsappTemplatesParamMapped[fromTemplate.name][0]) , - // // }, - // // ], - // }, - // ], - // } - // : {}), - }, - template_origin: fromTemplate, - }; - invokeSendMessage(msgObj); - setOpenTemplates(false); - }; - - const [openTemplates, setOpenTemplates] = useState(false); - const handleOpenChange = (newOpen) => { - setOpenTemplates(newOpen); - }; - return ( -
- - ( - - - <>{item.components.header?.[0]?.text || item.name} - - - } - description={item.components.body?.[0]?.text} - /> - - )} - /> - } - title='🙋打招呼' - trigger='click' - open={openTemplates} - onOpenChange={handleOpenChange}> -
- ); -}; - -export default InputBox; diff --git a/src/views/Conversations/Components/InputComposer.jsx b/src/views/Conversations/Components/InputComposer.jsx new file mode 100644 index 0000000..3c6a0b4 --- /dev/null +++ b/src/views/Conversations/Components/InputComposer.jsx @@ -0,0 +1,68 @@ +import React, { useState } from 'react'; +import { Input, Flex } from 'antd'; +// import { Input } from 'react-chat-elements'; +import { useConversationState, useConversationDispatch } from '@/stores/ConversationContext'; +import { useAuthContext } from '@/stores/AuthContext'; +import { sentNewMessage } from '@/actions/ConversationActions'; +import { SendOutlined } from '@ant-design/icons'; +import { isEmpty } from '@/utils/utils'; +import { v4 as uuid } from 'uuid'; +import { sentMsgTypeMapped } from '@/lib/msgUtils'; +import InputTemplate from './InputTemplate'; + +const InputBox = () => { + const { loginUser } = useAuthContext(); + const { userId } = loginUser; + const { websocket, currentConversation, } = useConversationState(); + const dispatch = useConversationDispatch(); + const [textContent, setTextContent] = useState(''); + + const talkabled = !isEmpty(currentConversation.sn); + + const invokeSendMessage = (msgObj) => { + console.log('sendMessage------------------', msgObj); + const contentToSend = sentMsgTypeMapped[msgObj.type].contentToSend(msgObj); + console.log('content to send-------------------------------------', contentToSend); + websocket.sendMessage({ ...contentToSend, opi_sn: userId, coli_sn: currentConversation.coli_sn }); + const contentToRender = sentMsgTypeMapped[msgObj.type].contentToRender(msgObj); + console.log(contentToRender, 'contentToRender sendMessage------------------'); + dispatch(sentNewMessage(contentToRender)); + }; + + const handleSendText = () => { + if (textContent.trim() !== '') { + const msgObj = { + type: 'text', + text: textContent, + sender: 'me', + to: currentConversation.whatsapp_phone_number, + id: `${currentConversation.sn}.${uuid()}`, // Date.now().toString(16), + date: new Date(), + status: 'waiting', + }; + invokeSendMessage(msgObj); + setTextContent(''); + } + }; + + return ( +
+ + + } + size='large' + onSearch={handleSendText} + value={textContent} + onChange={(e) => setTextContent(e.target.value)} + /> + +
+
+ ); +}; + +export default InputBox; diff --git a/src/views/Conversations/Components/InputTemplate.jsx b/src/views/Conversations/Components/InputTemplate.jsx new file mode 100644 index 0000000..80955b1 --- /dev/null +++ b/src/views/Conversations/Components/InputTemplate.jsx @@ -0,0 +1,149 @@ +import { useState } from 'react'; +import { App, Popover, Flex, Button, List, Input } from 'antd'; +import { MessageOutlined, SendOutlined } from '@ant-design/icons'; +import { useAuthContext } from '@/stores/AuthContext'; +import { useConversationState } from '@/stores/ConversationContext'; +import { cloneDeep, flush, getNestedValue, objectMapper } from '@/utils/utils'; +import { v4 as uuid } from 'uuid'; +import { replaceTemplateString } from '@/lib/msgUtils'; + +const splitTemplate = (template) => { + const placeholders = template.match(/{{(.*?)}}/g) || []; + const keys = placeholders.map((placeholder) => placeholder.slice(2, -2)); + const arr = template.split(/{{(.*?)}}/).filter(Boolean); + const obj = arr.reduce((prev, curr, index) => { + if (keys.includes(curr)) { + prev.push({ key: curr }); + } else { + prev.push(curr); + } + return prev; + }, []); + return obj; +}; +const InputTemplate = ({ disabled = false, invokeSendMessage }) => { + const { notification } = App.useApp() + const { loginUser } = useAuthContext(); + const { currentConversation, templates } = useConversationState(); + // 用于替换变量: customer, agent + const valueMapped = { ...cloneDeep(currentConversation), ...objectMapper(loginUser, { username: [{ key: 'agent_name' }, { key: 'your_name' }] }) }; + + const [openTemplates, setOpenTemplates] = useState(false); + const handleOpenChange = (newOpen) => { + setOpenTemplates(newOpen); + }; + const handleSendTemplate = (fromTemplate) => { + const mergeInput = {...cloneDeep(valueMapped), ...activeInput[fromTemplate.name]}; + let valid = true; + const msgObj = { + type: 'whatsappTemplate', + to: currentConversation.whatsapp_phone_number, + id: `${currentConversation.sn}.${uuid()}`, + date: new Date(), + status: 'waiting', + // statusTitle: 'Ready to send', + sender: 'me', + template: { + name: fromTemplate.name, + language: { code: fromTemplate.language }, + components: fromTemplate.components_origin.map((citem) => { + const keys = (citem?.text || '').match(/{{(.*?)}}/g).map((key) => key.replace(/{{|}}/g, '')); + const params = keys.map((v) => ({ type: 'text', text: getNestedValue(mergeInput, [v]) })); + const paramText = params.map((p) => p.text); + const fillTemplate = paramText.length ? replaceTemplateString(citem?.text || '', paramText) : citem?.text || ''; + valid = keys.length !== flush(paramText).length ? false : valid; + return { + type: citem.type.toLowerCase(), + parameters: params, + text: fillTemplate, + }; + }), + }, + template_origin: fromTemplate, + }; + if (valid !== true) { + notification.warning({ + message: '提示', + description: '信息未填写完整, 请补充填写', + placement: 'top', + duration: 3, + }) + return false; + } + invokeSendMessage(msgObj); + setOpenTemplates(false); + }; + + const [activeInput, setActiveInput] = useState({}); + const onInput = (tempItem, key, val, initObj) => { + setActiveInput((prev) => { + return { ...prev, [tempItem.name]: { ...initObj, ...prev[tempItem.name], [key]: val } }; + }); + }; + + const renderForm = (tempItem) => { + const templateText = tempItem.components.body?.[0]?.text || ''; + const tempArr = splitTemplate(templateText); + const keys = templateText.match(/{{(.*?)}}/g).map((key) => key.replace(/{{|}}/g, '')); + const paramsVal = keys.reduce((r, k) => ({ ...r, [k]: getNestedValue(valueMapped, [k]) }), {}); + + return tempArr.map((ele) => + typeof ele === 'string' ? ( + + {ele} + + ) : ( + onInput(tempItem, ele.key, e.target.value, paramsVal)} + className='w-auto max-w-24' + size={'small'} + title={ele.key} + placeholder={paramsVal[ele.key] || ele.key} + /> + ) + ); + }; + return ( + <> + ( + + + <>{item.components.header?.[0]?.text || item.name} + + + } + description={ + <> +
+
{renderForm(item)}
+ {item.components?.footer?.[0] ?
{item.components.footer[0].text || ''}
: null} +
+ + } + /> +
+ )} + /> + } + title='🙋打招呼' + trigger='click' + open={openTemplates} + onOpenChange={handleOpenChange}> +