Merge branch 'main' of github.com:hainatravel/global-sales

2.0/email-builder
Jimmy Liow 1 year ago
commit 86ba6da2b7

@ -1,4 +1,4 @@
import { cloneDeep, isEmpty, olog, fixTo2Decimals } from "@/utils/commons"; import { cloneDeep, isEmpty, olog, fixTo2Decimals, pick } from "@/utils/commons";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
@ -282,7 +282,8 @@ const emailMsgMapped = {
'email.inbound.received': { 'email.inbound.received': {
getMsg: (result) => { getMsg: (result) => {
console.log('email.inbound.received', result); console.log('email.inbound.received', result);
return isEmpty(result?.emailMessage) ? null : { ...result.emailMessage, conversationid: result.conversationid, }; const data1 = pick(result, ['conversationid', 'opi_sn', 'coli_sn', 'coli_id']);
return isEmpty(result?.emailMessage) ? null : { ...result.emailMessage, ...data1, };
}, },
contentToRender: (contentObj) => { contentToRender: (contentObj) => {
// console.log('email.inbound.received to render', contentObj); // console.log('email.inbound.received to render', contentObj);
@ -295,7 +296,8 @@ const emailMsgMapped = {
getMsg: (result) => { getMsg: (result) => {
console.log('email.updated', result); console.log('email.updated', result);
const { emailMessage } = result; const { emailMessage } = result;
return isEmpty(result?.emailMessage) ? null : { ...emailMessage, conversationid: result.conversationid, }; const data1 = pick(result, ['conversationid', 'opi_sn', 'coli_sn', 'coli_id']);
return isEmpty(result?.emailMessage) ? null : { ...emailMessage, ...data1, };
}, },
contentToRender: (contentObj) => null, contentToRender: (contentObj) => null,
contentToUpdate: (msgcontent) => ({ contentToUpdate: (msgcontent) => ({
@ -398,24 +400,24 @@ export const whatsappMsgTypeMapped = {
}, },
text: { text: {
type: 'text', type: 'text',
data: (msg) => ({ id: msg.wamid, text: autoLinkText(msg?.text?.body), originText: msg?.text?.body, title: msg?.customerProfile?.name || '' }), data: (msg) => ({ id: msg.wamid, text: autoLinkText(msg?.text?.body), originText: msg?.text?.body, title: msg?.customerProfile?.name || msg?.from || '' }),
renderForReply: (msg) => ({ id: msg.wamid, message: msg?.text?.body }), renderForReply: (msg) => ({ id: msg.wamid, message: msg?.text?.body }),
}, },
image: { image: {
type: 'photo', type: 'photo',
data: (msg) => ({ data: (msg) => ({
id: msg.wamid, id: msg.wamid,
text: msg.image.caption, text: msg.image?.caption,
onPhotoError: ({ currentTarget }) => { onPhotoError: ({ currentTarget }) => {
currentTarget.onerror = null; currentTarget.onerror = null;
currentTarget.src="https://hiana-crm.oss-accelerate.aliyuncs.com/WAMedia/afe412d4-3acf-4e79-a623-048aeb4d696a.png"; currentTarget.src="https://hiana-crm.oss-accelerate.aliyuncs.com/WAMedia/afe412d4-3acf-4e79-a623-048aeb4d696a.png";
}, },
data: { data: {
id: msg.wamid, id: msg.wamid,
uri: msg.image.link, uri: msg.image?.link,
width: 'auto', width: 'auto',
height: 200, height: 200,
alt: msg.image.caption || '', alt: msg.image?.caption || '',
status: { status: {
click: true, click: true,
loading: 0, loading: 0,
@ -426,7 +428,7 @@ export const whatsappMsgTypeMapped = {
}), }),
renderForReply: (msg) => ({ renderForReply: (msg) => ({
id: msg.wamid, id: msg.wamid,
photoURL: msg.image.link, photoURL: msg.image?.link,
width: 'auto', width: 'auto',
height: 200, height: 200,
alt: msg.image?.caption || '', alt: msg.image?.caption || '',
@ -439,7 +441,7 @@ export const whatsappMsgTypeMapped = {
id: msg.wamid, id: msg.wamid,
data: { data: {
id: msg.wamid, id: msg.wamid,
uri: msg.sticker.link, uri: msg.sticker?.link,
width: '100%', width: '100%',
height: 120, height: 120,
alt: '', alt: '',
@ -602,8 +604,7 @@ export const parseRenderMessageItem = (msg) => {
dateString: dayjs(msg?.sendTime || msg.createTime).format('MM-DD HH:mm'), dateString: dayjs(msg?.sendTime || msg.createTime).format('MM-DD HH:mm'),
from: msg.from, from: msg.from,
sender: msg.from, sender: msg.from,
senderName: msg?.customerProfile?.name || msg?.fromName || msg?.from || 'me', // msg.from, senderName: msg?.customerProfile?.name || msg?.fromName || msg?.from || 'me',
// title: msg.customerProfile.name,
customer_name: msg?.customerProfile?.name || '', customer_name: msg?.customerProfile?.name || '',
whatsapp_name: msg?.customerProfile?.name || '', whatsapp_name: msg?.customerProfile?.name || '',
whatsapp_phone_number: isEmpty(msg?.customerProfile) ? msg.to : msg.from, whatsapp_phone_number: isEmpty(msg?.customerProfile) ? msg.to : msg.from,
@ -665,23 +666,20 @@ export const parseRenderMessageList = (messages) => {
} }
return { return {
...msg, ...msg,
msgOrigin: {...msgContent, ...msgContent.email}, msgOrigin: { ...msgContent, ...msgContent.email },
...(whatsappMsgTypeMapped?.[msgType]?.data(msgContent) || {}), ...(whatsappMsgTypeMapped?.[msgType]?.data(msgContent) || {}),
type: msgContent.type, type: msgContent.type,
...(typeof whatsappMsgTypeMapped[msgType].type === 'function' ? whatsappMsgTypeMapped[msgType].type(msg) : { type: whatsappMsgTypeMapped[msgType].type || 'text' }), ...(typeof whatsappMsgTypeMapped[msgType].type === 'function' ? whatsappMsgTypeMapped[msgType].type(msg) : { type: whatsappMsgTypeMapped[msgType].type || 'text' }),
date: msgContent?.sendTime || msg.msgtime || '', date: msg.msgtime, // msgContent?.sendTime || msg.msgtime || '',
dateText: dayjs(msgContent?.sendTime || msg.msgtime).format('MM-DD HH:mm'), dateText: dayjs(msg.msgtime).format('MM-DD HH:mm'),
dateString: dayjs(msgContent?.sendTime || msg.msgtime).format('MM-DD HH:mm'), dateString: dayjs(msg.msgtime).format('MM-DD HH:mm'),
localDate: (msg.msgtime || '').replace('T', ' '), localDate: (msg.msgtime || '').replace('T', ' '),
from: msgContent.from, from: msgContent.from,
sender: msgContent.from, sender: msgContent.from,
senderName: msgContent?.customerProfile?.name || 'me', // msgContent.from, senderName: msgContent?.customerProfile?.name || msgContent.from || 'me',
replyButton: ['text', 'document', 'image', 'email'].includes(msgContent.type) && (msgContent?.status || '') !== 'failed', replyButton: ['text', 'document', 'image', 'email'].includes(msgContent.type) && (msgContent?.status || '') !== 'failed',
// 用forwarded表示Resend, 与Reply互斥 // 用forwarded表示Resend, 与Reply互斥
forwarded: msg.msg_direction === 'outbound' forwarded: msg.msg_direction === 'outbound' && msg.msg_source === 'email' && ['email'].includes(msgContent.type) && (msgContent?.status || 'failed') === 'failed',
&& msg.msg_source === 'email'
&& ['email'].includes(msgContent.type)
&& (msgContent?.status || 'failed') === 'failed',
...(msg.msg_direction === 'outbound' ...(msg.msg_direction === 'outbound'
? { ? {
sender: 'me', sender: 'me',
@ -704,7 +702,7 @@ export const parseRenderMessageList = (messages) => {
? whatsappMsgTypeMapped[messageorigin?.type || 'unsupported'].renderForReply(messageorigin) ? whatsappMsgTypeMapped[messageorigin?.type || 'unsupported'].renderForReply(messageorigin)
: {}), : {}),
// titleColor: messageorigin?.customerProfile?.name ? '#a791ff' : '#128c7e', // titleColor: messageorigin?.customerProfile?.name ? '#a791ff' : '#128c7e',
titleColor: msg.messageorigin_direction === 'inbound' ? '#a791ff' : "#128c7e", titleColor: msg.messageorigin_direction === 'inbound' ? '#a791ff' : '#128c7e',
id: msgContent.context?.id || msgContent.context?.message_id || msgContent.reaction?.message_id || messageorigin?.wamid, id: msgContent.context?.id || msgContent.context?.message_id || msgContent.reaction?.message_id || messageorigin?.wamid,
}, },
origin: messageorigin, origin: messageorigin,

@ -51,10 +51,10 @@ const DnDModal = ({ children, open, setOpen, onCancel, onMove, onResize, initial
footerClassName='!p-2' footerClassName='!p-2'
className={`!rounded-t !rounded-b-none !border !border-solid !shadow-heavy ${props.rootClassName}`} className={`!rounded-t !rounded-b-none !border !border-solid !shadow-heavy ${props.rootClassName}`}
zIndex={2} zIndex={2}
initialWidth={(mobile ? window.innerWidth : (initial.width || 680))} // window.innerWidth < 680 initialWidth={(mobile ? window.innerWidth : (initial.width ?? 680))} // window.innerWidth < 680
initialHeight={(mobile ? window.innerHeight : (initial.height || 600))} // window.innerHeight < 700 initialHeight={(mobile ? window.innerHeight : (initial.height ?? 600))} // window.innerHeight < 700
initialTop={mobile ? 0 : (initial.top || 74)} initialTop={mobile ? 0 : (initial.top ?? 74)}
initialLeft={mobile ? 0 : (initial.left || (window.innerWidth - 700))} initialLeft={mobile ? 0 : (initial.left ?? (window.innerWidth - 700))}
title={title} title={title}
minimizeButton={<></>} minimizeButton={<></>}
onMove={onHandleMove} onMove={onHandleMove}
@ -63,6 +63,7 @@ const DnDModal = ({ children, open, setOpen, onCancel, onMove, onResize, initial
// onOk={onHandleOk} // onOk={onHandleOk}
onStageChange={onStageChange} onStageChange={onStageChange}
footer={footer} footer={footer}
{...props}
{...(mobile ? { maximizeButton: <></> } : {})}> {...(mobile ? { maximizeButton: <></> } : {})}>
<>{children}</> <>{children}</>
</Modal> </Modal>

@ -4,7 +4,7 @@ import { Input, Button, Empty, Tooltip, List } from 'antd';
import { PlusOutlined, LoadingOutlined, HistoryOutlined, FireOutlined,AudioTwoTone } from '@ant-design/icons'; import { PlusOutlined, LoadingOutlined, HistoryOutlined, FireOutlined,AudioTwoTone } from '@ant-design/icons';
import { fetchConversationsList, fetchOrderConversationsList, CONVERSATION_PAGE_SIZE } from '@/actions/ConversationActions'; import { fetchConversationsList, fetchOrderConversationsList, CONVERSATION_PAGE_SIZE } from '@/actions/ConversationActions';
import ConversationsNewItem from './ConversationsNewItem'; import ConversationsNewItem from './ConversationsNewItem';
import { debounce, isEmpty, isNotEmpty, pick } from '@/utils/commons'; import { debounce, flush, isEmpty, isNotEmpty, pick } from '@/utils/commons';
import useConversationStore from '@/stores/ConversationStore'; import useConversationStore from '@/stores/ConversationStore';
import useAuthStore from '@/stores/AuthStore'; import useAuthStore from '@/stores/AuthStore';
import { useVisibilityState } from '@/hooks/useVisibilityState'; import { useVisibilityState } from '@/hooks/useVisibilityState';
@ -76,8 +76,10 @@ const Conversations = () => {
} else { } else {
addToConversationList(_list, 'next'); addToConversationList(_list, 'next');
} }
const pageTimeArr = flush(_list.map(item => item.lasttime));
const _lasttime = pageTimeArr.pop()
setFilter({ setFilter({
lastpagetime: _list.length > 0 ? _list[_list.length - 1].lasttime : '', lastpagetime: _lasttime || '',
loadNextPage: !(_list.length === 0 || _list.length < CONVERSATION_PAGE_SIZE), loadNextPage: !(_list.length === 0 || _list.length < CONVERSATION_PAGE_SIZE),
// ...((_list.length === 0 || _list.length < CONVERSATION_PAGE_SIZE) ? { // ...((_list.length === 0 || _list.length < CONVERSATION_PAGE_SIZE) ? {
// lastactivetime: dayjs(filterState.lastactivetime).subtract(6, 'months').format(DATETIME_FORMAT), // lastactivetime: dayjs(filterState.lastactivetime).subtract(6, 'months').format(DATETIME_FORMAT),

@ -13,6 +13,8 @@ export const ConversationItemForm = ({ initialValues, onFormInstanceReady }) =>
const [formatPhone, setFormatPhone] = useState(''); const [formatPhone, setFormatPhone] = useState('');
const [formatVisible, setFormatVisible] = useState(false); const [formatVisible, setFormatVisible] = useState(false);
const [contactValidateStatus, setContactValidateStatus] = useState('success');
useEffect(() => { useEffect(() => {
onFormInstanceReady(form); onFormInstanceReady(form);
setFormatPhone(phoneNumberToWAID(initialValues.phone_number || '')); setFormatPhone(phoneNumberToWAID(initialValues.phone_number || ''));
@ -29,42 +31,45 @@ export const ConversationItemForm = ({ initialValues, onFormInstanceReady }) =>
setFormatPhone(_t); setFormatPhone(_t);
setFormatVisible(_t !== changeValues.phone_number); setFormatVisible(_t !== changeValues.phone_number);
} }
setContactValidateStatus((isEmpty(allValues.email) && isEmpty(allValues.phone_number) )? 'error' : 'success');
}; };
return ( return (
<Form layout='horizontal' form={form} name='form_in_modal' initialValues={{ ...fromCurrent, ...initialValues, wa_id: formatPhone }} onValuesChange={onValuesChange} labelCol={{span: 4}} labelWrap={true} > <Form
{/* {!initialValues.is_current_order && ( */} layout='horizontal'
<Form.Item name={'name'} label='名称' rules={[{ required: true, message: '请输入联系人名称' },]} > form={form}
<Input placeholder='请输入联系人名称' /> name='form_in_modal'
</Form.Item> initialValues={{ ...fromCurrent, ...initialValues, wa_id: formatPhone }}
{/* )} */} onValuesChange={onValuesChange}
<Form.Item name={'remark'} label='备注' rules={[{type: 'string', max: 500, message: '备注最多500字' }]}> labelCol={{ span: 4 }}
labelWrap={true}>
<Form.Item name={'name'} label='名称' rules={[{ required: true, message: '请输入联系人名称' }]}>
<Input placeholder='请输入联系人名称' />
</Form.Item>
<Form.Item name={'remark'} label='备注' rules={[{ type: 'string', max: 500, message: '备注最多500字' }]}>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name={'phone_number'} name={'phone_number'}
label='手机' label='手机'
validateStatus={formatVisible ? 'warning' : undefined} validateStatus={contactValidateStatus === 'success' ? (formatVisible ? 'warning' : undefined) : contactValidateStatus}
help={formatVisible ? `WhatsApp格式: ${formatPhone}` : undefined} help={formatVisible ? `WhatsApp格式: ${formatPhone}` : undefined}
rules={[{ required: true, message: '请输入联系人手机号' }]}> rules={[{ required: false, message: '请输入联系人手机号' }]}>
<Input placeholder='请输入联系人手机号' disabled={!initialValues.is_new && isNotEmpty(initialValues.phone_number)} /> <Input placeholder='请输入联系人手机号' disabled={!initialValues.is_new && isNotEmpty(initialValues.phone_number)} />
</Form.Item> </Form.Item>
<Form.Item label='WhatsApp' name={'wa_id'} dependencies={['phone_number']} > <Form.Item label='WhatsApp' name={'wa_id'} dependencies={['phone_number']}>
<Input disabled={!initialValues.is_new && isNotEmpty(initialValues.wa_id)} /> <Input disabled={!initialValues.is_new && isNotEmpty(initialValues.wa_id)} />
</Form.Item> </Form.Item>
{/* <Form.Item name={'phone'} label=''> <Form.Item
<Input /> name={'email'}
</Form.Item> */} label='邮箱'
<Form.Item name={'email'} label='邮箱' rules={[{ type: 'email', message: '请输入正确的邮箱地址' }]}> validateStatus={contactValidateStatus}
help={contactValidateStatus === 'success' ? '' : '手机号或邮箱至少输入一个, 否则无法发送消息'}
rules={[{ type: 'email', message: '请输入正确的邮箱地址' }]}>
<Input disabled={!initialValues.is_new && isNotEmpty(initialValues.email)} /> <Input disabled={!initialValues.is_new && isNotEmpty(initialValues.email)} />
</Form.Item> </Form.Item>
{initialValues.is_current_order && ( {initialValues.is_current_order && (
<> <>
<Form.Item <Form.Item name={'coli_id'} label={'关联当前订单'} rules={[{ required: true, message: '关联的订单' }]} validateStatus='warning' help='请务必确认关联的订单是否正确'>
name={'coli_id'}
label={'关联当前订单'}
rules={[{ required: true, message: '关联的订单' }]}
validateStatus='warning'
help='请务必确认关联的订单是否正确'>
<Input placeholder='请先关联订单' disabled={initialValues.is_current_order} /> <Input placeholder='请先关联订单' disabled={initialValues.is_current_order} />
</Form.Item> </Form.Item>
<Form.Item name={'coli_sn'} label='订单SN' hidden={initialValues.is_current_order} rules={[{ required: true, message: '订单SN' }]}> <Form.Item name={'coli_sn'} label='订单SN' hidden={initialValues.is_current_order} rules={[{ required: true, message: '订单SN' }]}>
@ -74,7 +79,7 @@ export const ConversationItemForm = ({ initialValues, onFormInstanceReady }) =>
)} )}
{/* <div className=' text-neutral-500 italic'>如果会话已存在, 将直接切换</div> */} {/* <div className=' text-neutral-500 italic'>如果会话已存在, 将直接切换</div> */}
</Form> </Form>
); )
}; };
/** /**
* 新建会话弹窗 * 新建会话弹窗

@ -143,7 +143,7 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, toEmail, convers
const [htmlContent, setHtmlContent] = useState(''); const [htmlContent, setHtmlContent] = useState('');
const [textContent, setTextContent] = useState(''); const [textContent, setTextContent] = useState('');
const [showCc, setShowCc] = useState(false); const [showCc, setShowCc] = useState(true);
const [showBcc, setShowBcc] = useState(false); const [showBcc, setShowBcc] = useState(false);
const handleShowCc = () => { const handleShowCc = () => {
setShowCc(true); setShowCc(true);
@ -174,7 +174,8 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, toEmail, convers
return () => {}; return () => {};
} }
const { info, } = mailData const { info, } = mailData
setShowCc(!isEmpty(mailData.info?.MAI_CS)); // setShowCc(!isEmpty(mailData.info?.MAI_CS));
setShowCc(true);
const preQuoteBody = generateQuoteContent(mailData); const preQuoteBody = generateQuoteContent(mailData);
@ -372,7 +373,10 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, fromUser, toEmail, convers
rootClassName='email-editor-wrapper !border-indigo-300 ' rootClassName='email-editor-wrapper !border-indigo-300 '
open={open} open={open}
setOpen={setOpen} setOpen={setOpen}
initial={{ top: isEmpty(quoteid) ? 20 : 74 }} // initial={{ top: isEmpty(quoteid) ? 20 : 74 }}
initial={{ width: window.innerWidth-40, height: window.innerHeight-40, left: 20, top: 20 }}
maximizeButton={<></>}
initialStage={'FULLSCREEN'}
onCancel={() => { onCancel={() => {
form.resetFields(); form.resetFields();
}} }}

Loading…
Cancel
Save