From d1dfcf4a46d54fc24dcda3fc1352e7a0f5a72aea Mon Sep 17 00:00:00 2001 From: Dulguun Otgon Date: Mon, 16 Oct 2023 17:13:29 +0800 Subject: [PATCH 01/12] .gitignore for builder --- builder/.gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 builder/.gitignore diff --git a/builder/.gitignore b/builder/.gitignore new file mode 100644 index 0000000000..dbaa9758ee --- /dev/null +++ b/builder/.gitignore @@ -0,0 +1,3 @@ +/erxes + +*.log \ No newline at end of file From 5cbac7d1aa492ddbdd3ed1a5b482a41f7a0842d3 Mon Sep 17 00:00:00 2001 From: Dulguun Otgon Date: Wed, 18 Oct 2023 19:10:49 +0800 Subject: [PATCH 02/12] ignores error and prints error like it used to --- cli/.gitignore | 1 + .../conversationDetail/DmWorkArea.tsx | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 cli/.gitignore diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000000..4858554784 --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1 @@ +enabled-services.json \ No newline at end of file diff --git a/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx b/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx index 938d50b430..a55ebe45d4 100644 --- a/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx +++ b/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx @@ -239,17 +239,22 @@ class WorkArea extends React.Component { } }; - cache.updateQuery(selector, data => { - const key = getQueryResultKey(data || {}); - const messages = data ? data[key] : []; - - // check duplications - if (messages.find(m => m._id === message._id)) { - return; - } + try { + cache.updateQuery(selector, data => { + const key = getQueryResultKey(data || {}); + const messages = data ? data[key] : []; + + // check duplications + if (messages.find(m => m._id === message._id)) { + return; + } - return { [key]: [...messages, message] }; - }); + return { [key]: [...messages, message] }; + }); + } catch (e) { + console.error(e); + return; + } }; } From 908f58ad8514fbc2be9a14867ed89db5b1aba62d Mon Sep 17 00:00:00 2001 From: Dulguun Otgon Date: Wed, 18 Oct 2023 19:12:27 +0800 Subject: [PATCH 03/12] Revert "ignores error and prints error like it used to" This reverts commit 5cbac7d1aa492ddbdd3ed1a5b482a41f7a0842d3. --- cli/.gitignore | 1 - .../conversationDetail/DmWorkArea.tsx | 25 ++++++++----------- 2 files changed, 10 insertions(+), 16 deletions(-) delete mode 100644 cli/.gitignore diff --git a/cli/.gitignore b/cli/.gitignore deleted file mode 100644 index 4858554784..0000000000 --- a/cli/.gitignore +++ /dev/null @@ -1 +0,0 @@ -enabled-services.json \ No newline at end of file diff --git a/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx b/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx index a55ebe45d4..938d50b430 100644 --- a/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx +++ b/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx @@ -239,22 +239,17 @@ class WorkArea extends React.Component { } }; - try { - cache.updateQuery(selector, data => { - const key = getQueryResultKey(data || {}); - const messages = data ? data[key] : []; - - // check duplications - if (messages.find(m => m._id === message._id)) { - return; - } + cache.updateQuery(selector, data => { + const key = getQueryResultKey(data || {}); + const messages = data ? data[key] : []; - return { [key]: [...messages, message] }; - }); - } catch (e) { - console.error(e); - return; - } + // check duplications + if (messages.find(m => m._id === message._id)) { + return; + } + + return { [key]: [...messages, message] }; + }); }; } From 8832a5af216cd71b43d4f7cd69542b19126c02db Mon Sep 17 00:00:00 2001 From: Dulguun Otgon Date: Wed, 18 Oct 2023 19:15:36 +0800 Subject: [PATCH 04/12] ignores error and prints error message like it used to --- cli/.gitignore | 1 + .../conversationDetail/DmWorkArea.tsx | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 cli/.gitignore diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000000..4dab3b982a --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1 @@ +/enabled-services.json \ No newline at end of file diff --git a/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx b/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx index 938d50b430..34e5974373 100644 --- a/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx +++ b/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx @@ -239,17 +239,22 @@ class WorkArea extends React.Component { } }; - cache.updateQuery(selector, data => { - const key = getQueryResultKey(data || {}); - const messages = data ? data[key] : []; - - // check duplications - if (messages.find(m => m._id === message._id)) { - return; - } + try { + cache.updateQuery(selector, data => { + const key = getQueryResultKey(data || {}); + const messages = data ? data[key] : []; + + // check duplications + if (messages.find(m => m._id === message._id)) { + return {}; + } - return { [key]: [...messages, message] }; - }); + return { [key]: [...messages, message] }; + }); + } catch (e) { + console.error(e); + return; + } }; } From 719777204a99d6bf269e45c10116deaa63827eb3 Mon Sep 17 00:00:00 2001 From: Dulguun Otgon Date: Thu, 19 Oct 2023 16:26:17 +0800 Subject: [PATCH 05/12] fix weak random _id generation --- .../src/inbox/containers/conversationDetail/RespondBox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/RespondBox.tsx b/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/RespondBox.tsx index 219c7bc14a..2e762031b6 100644 --- a/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/RespondBox.tsx +++ b/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/RespondBox.tsx @@ -78,14 +78,14 @@ const RespondBoxContainer = (props: FinalProps) => { __typename: 'Mutation', conversationMessageAdd: { __typename: 'ConversationMessage', - _id: Math.round(Math.random() * -1000000), + _id: (Math.random() + "" + Date.now()).slice(2), content, contentType, attachments, internal, mentionedUserIds: [], conversationId, - customerId: Math.random(), + customerId: (Math.random() + "" + Date.now()).slice(2), userId: currentUser._id, createdAt: new Date(), messengerAppData: null, From 9f1b582036110bd5df85053be0b5330bb656d09a Mon Sep 17 00:00:00 2001 From: Dulguun Otgon Date: Thu, 19 Oct 2023 16:26:29 +0800 Subject: [PATCH 06/12] remove misleading comment --- .../src/inbox/containers/conversationDetail/DmWorkArea.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx b/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx index 34e5974373..03e860fc86 100644 --- a/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx +++ b/packages/plugin-inbox-ui/src/inbox/containers/conversationDetail/DmWorkArea.tsx @@ -228,8 +228,6 @@ class WorkArea extends React.Component { messagesQuery = getQueryString('messagesQuery', dmConfig); } - // trying to read query by initial variables. Because currenty it is apollo bug. - // https://github.com/apollographql/apollo-client/issues/2499 const selector = { query: gql(messagesQuery), variables: { From a50a0ed706fb69d4d1417ad70b22ddc5e3b00e93 Mon Sep 17 00:00:00 2001 From: Tuguldur Ch Date: Thu, 19 Oct 2023 20:50:49 +0800 Subject: [PATCH 07/12] update(apiUtils):pass additional comlexFieldKeys to replaceHolders for get related value from nested object --- packages/api-utils/src/automations.ts | 41 ++++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/packages/api-utils/src/automations.ts b/packages/api-utils/src/automations.ts index aa4ee68b26..9f52bfd9e3 100644 --- a/packages/api-utils/src/automations.ts +++ b/packages/api-utils/src/automations.ts @@ -8,7 +8,8 @@ export const replacePlaceHolders = async ({ target, isRelated = true, getRelatedValue, - relatedValueProps + relatedValueProps, + complexFields }: { models; subdomain: string; @@ -17,6 +18,7 @@ export const replacePlaceHolders = async ({ isRelated?: boolean; getRelatedValue: any; relatedValueProps?: any; + complexFields?: string[]; }) => { if (actionData) { const targetKeys = Object.keys(target); @@ -86,20 +88,39 @@ export const replacePlaceHolders = async ({ ); } - for (const complexFieldKey of ['customFieldsData', 'trackedData']) { + for (const complexFieldKey of [ + 'customFieldsData', + 'trackedData' + ].concat(complexFields || [])) { if (actionData[actionDataKey].includes(complexFieldKey)) { const regex = new RegExp(`{{ ${complexFieldKey}.([\\w\\d]+) }}`); const match = regex.exec(actionData[actionDataKey]); const fieldId = match && match.length === 2 ? match[1] : ''; - const complexFieldData = target[complexFieldKey].find( - cfd => cfd.field === fieldId - ); - - actionData[actionDataKey] = actionData[actionDataKey].replace( - `{{ ${complexFieldKey}.${fieldId} }}`, - complexFieldData ? complexFieldData.value : '' - ); + if ((complexFields || [])?.includes(complexFieldKey)) { + const replaceValue = + (await getRelatedValue( + models, + subdomain, + target, + `${complexFieldKey}.${fieldId}`, + relatedValueProps + )) || target[targetKey]; + + actionData[actionDataKey] = actionData[actionDataKey].replace( + `{{ ${complexFieldKey}.${fieldId} }}`, + replaceValue + ); + } else { + const complexFieldData = target[complexFieldKey].find( + cfd => cfd.field === fieldId + ); + + actionData[actionDataKey] = actionData[actionDataKey].replace( + `{{ ${complexFieldKey}.${fieldId} }}`, + complexFieldData ? complexFieldData.value : '' + ); + } } } } From 4334c70f984601140501095a66c138b8dd0e1523 Mon Sep 17 00:00:00 2001 From: Tuguldur Ch Date: Thu, 19 Oct 2023 20:51:58 +0800 Subject: [PATCH 08/12] update(loyalties):change score dynamically in automation action --- packages/plugin-cards-api/src/automations.ts | 25 ++++++++- .../plugin-loyalties-api/src/automations.ts | 52 +++++++++++++++++-- .../src/automations/components/ScoreForm.tsx | 19 ++++--- 3 files changed, 80 insertions(+), 16 deletions(-) diff --git a/packages/plugin-cards-api/src/automations.ts b/packages/plugin-cards-api/src/automations.ts index d2439fe3b3..426691eca6 100644 --- a/packages/plugin-cards-api/src/automations.ts +++ b/packages/plugin-cards-api/src/automations.ts @@ -157,9 +157,31 @@ const getRelatedValue = async ( return result; } + if (targetKey.includes('productsData')) { + const [_parentFieldName, childFieldName] = targetKey.split('.'); + + if (childFieldName === 'amount') { + return generateTotalAmount(target.productsData); + } + } + return false; }; +const generateTotalAmount = productsData => { + let totalAmount = 0; + + (productsData || []).forEach(product => { + if (product.tickUsed) { + return; + } + + totalAmount += product?.amount || 0; + }); + + return totalAmount; +}; + // module related services const relatedServices = ( subdomain: string, @@ -330,7 +352,8 @@ export default { getRelatedValue, actionData: config, target, - relatedValueProps + relatedValueProps, + complexFields: ['productsData'] }); }, constants: { diff --git a/packages/plugin-loyalties-api/src/automations.ts b/packages/plugin-loyalties-api/src/automations.ts index 876057caa5..6f8ca330fe 100644 --- a/packages/plugin-loyalties-api/src/automations.ts +++ b/packages/plugin-loyalties-api/src/automations.ts @@ -87,6 +87,11 @@ const generateIds = async value => { if (typeof value === 'string' && value.includes(', ')) { return [...new Set(value.split(', '))]; } + + if (typeof value === 'string') { + return [value]; + } + return []; }; @@ -229,6 +234,36 @@ const createVoucher = async ({ }); }; +const generateScore = async ({ + serviceName, + subdomain, + target, + scoreString +}) => { + if (scoreString.match(/\{\{\s*([^}]+)\s*\}\}/g)) { + const replacedContent = await sendCommonMessage({ + serviceName, + subdomain, + action: 'automations.replacePlaceHolders', + data: { + target, + config: { + scoreString + } + }, + isRPC: true, + defaultValue: {} + }); + + scoreString = replacedContent?.scoreString || 0; + } + + if (scoreString.match(/[+\-*/]/)) { + return eval(scoreString); + } + return scoreString; +}; + const addScore = async ({ models, subdomain, @@ -244,11 +279,18 @@ const addScore = async ({ contentType: string; config: any; }) => { + const score = await generateScore({ + serviceName, + subdomain, + target: execution.target, + scoreString: config.scoreString + }); + if (!!config?.ownerType && !!config?.ownerIds?.length) { return await models.ScoreLogs.changeOwnersScore({ ownerType: config.ownerType, ownerIds: config.ownerIds, - changeScore: Number(config?.score || 0), + changeScore: score, description: 'from automation' }); } @@ -262,7 +304,7 @@ const addScore = async ({ await models.ScoreLogs.changeOwnersScore({ ownerType: contentType, ownerId: contentTypeId || '', - changeScore: Number(config?.score || 0), + changeScore: score, description: 'from automation' }); attributes = attributes.filter( @@ -299,7 +341,7 @@ const addScore = async ({ await models.ScoreLogs.changeOwnersScore({ ownerType: 'customer', ownerIds: await generateIds(replacedContent['customers']), - changeScore: Number(config?.score || 0), + changeScore: score, description: 'from automation' }); } @@ -308,7 +350,7 @@ const addScore = async ({ await models.ScoreLogs.changeOwnersScore({ ownerType: 'company', ownerIds: await generateIds(replacedContent['companies']), - changeScore: Number(config?.score || 0), + changeScore: score, description: 'from automation' }); } @@ -330,7 +372,7 @@ const addScore = async ({ await models.ScoreLogs.changeOwnersScore({ ownerType: 'user', ownerIds: teamMemberIds || [], - changeScore: Number(config?.score || 0), + changeScore: score, description: 'from automation' }); return 'done'; diff --git a/packages/plugin-loyalties-ui/src/automations/components/ScoreForm.tsx b/packages/plugin-loyalties-ui/src/automations/components/ScoreForm.tsx index 73f9c78396..ceb6517908 100644 --- a/packages/plugin-loyalties-ui/src/automations/components/ScoreForm.tsx +++ b/packages/plugin-loyalties-ui/src/automations/components/ScoreForm.tsx @@ -136,16 +136,15 @@ export default class ScoreForm extends React.Component { onChange={config => this.setState({ config })} customAttributions={additionalAttributes} /> - - {__('Score')} - - handleChange((e.currentTarget as HTMLInputElement).value, 'score') - } - /> - + + this.setState({ config })} + /> ); } From 6cc1c6df9c5230a10a60f71af4057ccc4c724341 Mon Sep 17 00:00:00 2001 From: Dulguun Otgon Date: Mon, 23 Oct 2023 14:29:35 +0800 Subject: [PATCH 09/12] impr(gateway): option to enable introspection in production environment. --- packages/gateway/src/apollo-router/index.ts | 32 ++++++++++++++++----- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/gateway/src/apollo-router/index.ts b/packages/gateway/src/apollo-router/index.ts index e9028cd0c7..aa09024ce3 100644 --- a/packages/gateway/src/apollo-router/index.ts +++ b/packages/gateway/src/apollo-router/index.ts @@ -21,7 +21,8 @@ const { CLIENT_PORTAL_DOMAINS, ALLOWED_ORIGINS, NODE_ENV, - APOLLO_ROUTER_PORT + APOLLO_ROUTER_PORT, + INTROSPECTION } = process.env; let routerProcess: ChildProcess | undefined = undefined; @@ -30,7 +31,9 @@ export const stopRouter = (sig: NodeJS.Signals) => { if (!routerProcess) { return; } - try { routerProcess.kill(sig); } catch (e) { + try { + routerProcess.kill(sig); + } catch (e) { console.error(e); } }; @@ -57,7 +60,21 @@ const createRouterConfig = async () => { // Don't rewrite in production if it exists. Delete and restart to update the config return; } - // const rhaiPath = path.resolve(__dirname, 'rhai/main.rhai'); + + if ( + NODE_ENV === 'production' && + (INTROSPECTION || '').trim().toLowerCase() === 'true' + ) { + console.warn( + '----------------------------------------------------------------------------------------------' + ); + console.warn( + "Graphql introspection is enabled in production environment. Disable it, if it isn't required for front-end development. Hint: Check gateway config in configs.json" + ); + console.warn( + '----------------------------------------------------------------------------------------------' + ); + } const config = { include_subgraph_errors: { @@ -89,16 +106,17 @@ const createRouterConfig = async () => { } }, supergraph: { - listen: `127.0.0.1:${apolloRouterPort}` + listen: `127.0.0.1:${apolloRouterPort}`, + introspection: + NODE_ENV === 'development' || + (INTROSPECTION || '').trim().toLowerCase() === 'true' } }; fs.writeFileSync(routerConfigPath, yaml.stringify(config)); }; -export const startRouter = async ( - proxyTargets: ErxesProxyTarget[] -) => { +export const startRouter = async (proxyTargets: ErxesProxyTarget[]) => { await supergraphCompose(proxyTargets); await createRouterConfig(); await downloadRouter(); From e3f33fe17ba861df027ae36c7d4dcc45eaa93119 Mon Sep 17 00:00:00 2001 From: Dulguun Otgon Date: Mon, 23 Oct 2023 14:33:12 +0800 Subject: [PATCH 10/12] fix(cli): gateway - extra env --- cli/commands/dev.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/commands/dev.js b/cli/commands/dev.js index 67ba61a0a1..dfb11568fd 100644 --- a/cli/commands/dev.js +++ b/cli/commands/dev.js @@ -232,7 +232,7 @@ module.exports.devCmd = async program => { PORT: 4000, CLIENT_PORTAL_DOMAINS: configs.client_portal_domains || '', ...commonEnv, - ...((configs.gateway || {}).envs || {}) + ...((configs.gateway || {}).extra_env || {}) } }); From f6f1238563d30cfeaa1f7c6ecb67bb791c3ee170 Mon Sep 17 00:00:00 2001 From: Tuguldur Ch Date: Mon, 23 Oct 2023 21:28:54 +0800 Subject: [PATCH 11/12] update(contacts):add ability autocomplete usage type in contacts query --- .../src/graphql/contacts.ts | 3 +- .../src/graphql/resolvers/contactQueries.ts | 141 ++++++++++++------ 2 files changed, 94 insertions(+), 50 deletions(-) diff --git a/packages/plugin-contacts-api/src/graphql/contacts.ts b/packages/plugin-contacts-api/src/graphql/contacts.ts index a7f0d9fe08..567835f601 100644 --- a/packages/plugin-contacts-api/src/graphql/contacts.ts +++ b/packages/plugin-contacts-api/src/graphql/contacts.ts @@ -17,7 +17,8 @@ const queryParams = ` searchValue: String, fieldsMustExist:[String] sortField: String - sortDirection: String + sortDirection: String, + usageType: String `; export const queries = ` diff --git a/packages/plugin-contacts-api/src/graphql/resolvers/contactQueries.ts b/packages/plugin-contacts-api/src/graphql/resolvers/contactQueries.ts index a906c606f6..83032a5df9 100644 --- a/packages/plugin-contacts-api/src/graphql/resolvers/contactQueries.ts +++ b/packages/plugin-contacts-api/src/graphql/resolvers/contactQueries.ts @@ -12,7 +12,12 @@ type ContactType = { fullName: string; }; -const gneerateSort = ({ type, sortField, sortDirection, searchValue }) => { +const generateSort = async ({ + type, + sortField, + sortDirection, + searchValue +}) => { let sort = {}; const esTypes = getEsTypes(type); let fieldToSort = sortField || 'createdAt'; @@ -37,34 +42,24 @@ const generateQuery = async args => { const positiveList: any = []; const negativeList: any = []; - if (searchValue.includes('@')) { - positiveList.push({ - match_phrase: { - searchText: { - query: searchValue - } - } - }); - } else { - positiveList.push({ - bool: { - should: [ - { - match: { - searchText: { - query: searchValue - } - } - }, - { - wildcard: { - searchText: `*${searchValue.toLowerCase()}*` + positiveList.push({ + bool: { + should: [ + { + match: { + searchText: { + query: searchValue } } - ] - } - }); - } + }, + { + wildcard: { + searchText: `*${searchValue.toLowerCase()}*` + } + } + ] + } + }); if (!!fieldsMustExist?.length) { for (const field of fieldsMustExist) { @@ -91,6 +86,28 @@ const generateQuery = async args => { }; }; +const generateAutoCompleteQuery = args => { + if (args?.usageType !== 'autoComplete') { + return []; + } + + return args?.searchValue + ? args.searchValue + .split(',') + .filter(value => { + if (value.match(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/)) { + args.searchValue = args.searchValue.replace(`${value},`, ''); + return value; + } + }) + .map(value => ({ + match: { + primaryEmail: { query: value } + } + })) + : []; +}; + const generateContentType = (type, source) => { switch (type) { case 'companies': @@ -115,18 +132,59 @@ const generateFullName = (contentType, source) => { return null; }; +const generateList = (type, response) => { + return response.hits.hits.map(hit => { + const { primaryEmail, primaryPhone, avatar, createdAt, status } = + hit._source || {}; + + return { + _id: hit._id, + primaryEmail, + primaryPhone, + avatar, + createdAt, + status, + contentType: generateContentType(type, hit._source || {}), + fullName: generateFullName(type, hit._source || {}) + }; + }); +}; + const contactQueries = { async contacts(_root, args, { subdomain }: IContext) { const { perPage, page } = args; let list: ContactType[] = []; + let autoCompleteList: ContactType[] = []; const _page = Number(page || 1); const _limit = Number(perPage || 20); + const autoCompleteQuery = generateAutoCompleteQuery(args); + for (const type of ['customers', 'companies']) { - const customersQueryOptions = await generateQuery(args); - const customersSortOptions = await gneerateSort({ type, ...args }); + const contactsQueryOptions = await generateQuery(args); + const contactsSortOptions = await generateSort({ type, ...args }); + + if (!!autoCompleteQuery?.length) { + const response = await fetchEs({ + subdomain, + action: 'search', + index: type, + body: { + query: { + bool: { + should: autoCompleteQuery + } + } + } + }); + + autoCompleteList = [ + ...autoCompleteList, + ...generateList(type, response) + ]; + } if (list.length === _limit) { continue; @@ -139,30 +197,15 @@ const contactQueries = { body: { from: (_page - 1) * _limit, size: _limit - list.length, - ...customersQueryOptions, - sort: [customersSortOptions] + ...contactsQueryOptions, + sort: [contactsSortOptions] } }); - const responseList: ContactType[] = response.hits.hits.map(hit => { - const { primaryEmail, primaryPhone, avatar, createdAt, status } = - hit._source || {}; - - return { - _id: hit._id, - primaryEmail, - primaryPhone, - avatar, - createdAt, - status, - contentType: generateContentType(type, hit._source || {}), - fullName: generateFullName(type, hit._source || {}) - }; - }); - - list = [...list, ...responseList]; + list = [...list, ...generateList(type, response)]; } - return list; + + return [...autoCompleteList, ...list]; } }; From fb2e477bfc3ee946a2902d8e7c575622c0329ff8 Mon Sep 17 00:00:00 2001 From: soyombo Date: Tue, 24 Oct 2023 11:41:51 +0800 Subject: [PATCH 12/12] fix(widgets): Validate rules when displaying the form within Messenger. --- .../messenger/components/Integrations.tsx | 4 +- .../messenger/containers/lead/LeadConnect.tsx | 57 +++++++++++++------ widgets/client/utils.ts | 45 ++++++++------- 3 files changed, 68 insertions(+), 38 deletions(-) diff --git a/widgets/client/messenger/components/Integrations.tsx b/widgets/client/messenger/components/Integrations.tsx index ff00553121..7d9f069ff7 100644 --- a/widgets/client/messenger/components/Integrations.tsx +++ b/widgets/client/messenger/components/Integrations.tsx @@ -28,9 +28,7 @@ export default class Integrations extends React.PureComponent { } return formCodes.map((formCode: string) => ( - - - + )); } diff --git a/widgets/client/messenger/containers/lead/LeadConnect.tsx b/widgets/client/messenger/containers/lead/LeadConnect.tsx index eccfe7f2de..1655db56ab 100644 --- a/widgets/client/messenger/containers/lead/LeadConnect.tsx +++ b/widgets/client/messenger/containers/lead/LeadConnect.tsx @@ -1,14 +1,16 @@ -import gql from "graphql-tag"; -import * as React from "react"; -import client from "../../../apollo-client"; -import { formConnectMutation } from "../../../form/graphql"; -import { __, requestBrowserInfo } from "../../../utils"; -import { connection } from "../../connection"; -import LeadContent from "./LeadContent"; +import gql from 'graphql-tag'; +import * as React from 'react'; +import client from '../../../apollo-client'; +import { formConnectMutation } from '../../../form/graphql'; +import { __, checkRules, requestBrowserInfo } from '../../../utils'; +import { connection } from '../../connection'; +import LeadContent from './LeadContent'; +import IntegrationItem from '../../components/IntegrationItem'; interface IState { loading: boolean; hasError: boolean; + browserInfo?: any; } type Props = { @@ -25,10 +27,11 @@ class LeadConnect extends React.PureComponent { saveBrowserInfo() { requestBrowserInfo({ - source: "fromMessenger", - callback: browserInfo => { + source: 'fromMessenger', + callback: (browserInfo) => { connection.browserInfo = browserInfo; - } + this.setState({ browserInfo }); + }, }); } @@ -40,20 +43,20 @@ class LeadConnect extends React.PureComponent { mutation: gql(formConnectMutation), variables: { brandCode, - formCode - } + formCode, + }, }) .then(({ data = { widgetsLeadConnect: {} } }) => { if (!data) { this.setState({ hasError: true }); - return + return; } const response = data.widgetsLeadConnect; if (!response) { this.setState({ hasError: true }); - throw new Error("Integration not found"); + throw new Error('Integration not found'); } // save connection info @@ -72,14 +75,36 @@ class LeadConnect extends React.PureComponent { render() { if (this.state.hasError) { - return

{__("Failed")}

; + return

{__('Failed')}

; } if (this.state.loading) { return
; } - return ; + if (!this.state.browserInfo) { + return null; + } + + const leadData = connection.leadData[this.props.formCode]; + + const { integration } = leadData; + + // check rules ====== + const isPassedAllRules = checkRules( + integration.leadData.rules || [], + this.state.browserInfo + ); + + if (!isPassedAllRules) { + return null; + } + + return ( + + ; + + ); } } diff --git a/widgets/client/utils.ts b/widgets/client/utils.ts index 6759e1aa69..6386a7be99 100644 --- a/widgets/client/utils.ts +++ b/widgets/client/utils.ts @@ -15,7 +15,7 @@ export const postMessage = (source: string, message: string, postData = {}) => { fromErxes: true, source, message, - ...postData + ...postData, }, '*' ); @@ -39,7 +39,7 @@ export type LogicParams = { export const requestBrowserInfo = ({ source, postData = {}, - callback + callback, }: RequestBrowserInfoParams) => { postMessage(source, 'requestingBrowserInfo', postData); @@ -70,7 +70,7 @@ export const setLocale = (code?: string, callBack?: () => void) => { setDayjsLocale(code || 'en'); if (callBack) { - callBack() + callBack(); } }) .catch((e) => console.log(e)); // tslint:disable-line @@ -144,7 +144,7 @@ export const readFile = (value: string): string => { return `${API_URL}/read-file?key=${value}`; }; -export const checkRule = async (rule: IRule, browserInfo: IBrowserInfo) => { +export const checkRule = (rule: IRule, browserInfo: IBrowserInfo) => { const { language, url, city, country } = browserInfo || ({} as IBrowserInfo); const { value, kind, condition } = rule; const ruleValue: any = value; @@ -230,14 +230,14 @@ export const checkRule = async (rule: IRule, browserInfo: IBrowserInfo) => { return true; }; -export const checkRules = async ( +export const checkRules = ( rules: IRule[], browserInfo: IBrowserInfo -): Promise => { +): boolean => { let passedAllRules = true; for (const rule of rules) { - const result = await checkRule(rule, browserInfo); + const result = checkRule(rule, browserInfo); if (result === false) { passedAllRules = false; @@ -283,9 +283,16 @@ export const urlify = (text: string) => { export const checkLogicFulfilled = (logics: LogicParams[]) => { const values: { [key: string]: boolean } = {}; - + for (const logic of logics) { - const { fieldId, operator, logicValue, fieldValue, validation, type } = logic; + const { + fieldId, + operator, + logicValue, + fieldValue, + validation, + type, + } = logic; const key = `${fieldId}_${logicValue}`; values[key] = false; @@ -381,7 +388,6 @@ export const checkLogicFulfilled = (logics: LogicParams[]) => { values[key] = false; } } - } if (validation && validation.includes('date')) { @@ -438,25 +444,26 @@ export const checkLogicFulfilled = (logics: LogicParams[]) => { result.push(values[key]); } - if (result.filter(val => !val).length === 0) { + if (result.filter((val) => !val).length === 0) { return true; - } + } return false; -} +}; export const loadMapApi = (apiKey: string, locale?: string) => { if (!apiKey) { - return ""; + return ''; } - const mapsURL = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=geometry,places&language=${locale || 'en'}&v=quarterly`; + const mapsURL = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=geometry,places&language=${locale || + 'en'}&v=quarterly`; const scripts: any = document.getElementsByTagName('script'); // Go through existing script tags, and return google maps api tag when found. for (const script of scripts) { - if (script.src.indexOf(mapsURL) === 0) { - return script; - } + if (script.src.indexOf(mapsURL) === 0) { + return script; + } } const googleMapScript = document.createElement('script'); @@ -466,4 +473,4 @@ export const loadMapApi = (apiKey: string, locale?: string) => { window.document.body.appendChild(googleMapScript); return googleMapScript; -}; \ No newline at end of file +};