Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b723ecccc | ||
|
|
168938500f | ||
|
|
2bb7d1e4b2 | ||
|
|
c5c3b74728 |
+2
-1
@@ -89,6 +89,7 @@ import { filterAttributeGroups } from './advancedFilterItems';
|
|||||||
import filterMixin from 'shared/mixins/filterMixin';
|
import filterMixin from 'shared/mixins/filterMixin';
|
||||||
import * as OPERATORS from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js';
|
import * as OPERATORS from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js';
|
||||||
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||||
|
import cloneObject from 'dashboard/helpers/clone';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -351,7 +352,7 @@ export default {
|
|||||||
if (this.$v.$invalid) return;
|
if (this.$v.$invalid) return;
|
||||||
this.$store.dispatch(
|
this.$store.dispatch(
|
||||||
'setConversationFilters',
|
'setConversationFilters',
|
||||||
JSON.parse(JSON.stringify(this.appliedFilters))
|
cloneObject(this.appliedFilters)
|
||||||
);
|
);
|
||||||
this.$emit('applyFilter', this.appliedFilters);
|
this.$emit('applyFilter', this.appliedFilters);
|
||||||
this.$track(CONVERSATION_EVENTS.APPLY_FILTER, {
|
this.$track(CONVERSATION_EVENTS.APPLY_FILTER, {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import cloneObject from 'dashboard/helpers/clone';
|
||||||
|
|
||||||
const allElementsString = arr => {
|
const allElementsString = arr => {
|
||||||
return arr.every(elem => typeof elem === 'string');
|
return arr.every(elem => typeof elem === 'string');
|
||||||
};
|
};
|
||||||
@@ -27,7 +29,7 @@ const generatePayloadForObject = item => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const generatePayload = data => {
|
const generatePayload = data => {
|
||||||
const actions = JSON.parse(JSON.stringify(data));
|
const actions = cloneObject(data);
|
||||||
let payload = actions.map(item => {
|
let payload = actions.map(item => {
|
||||||
if (Array.isArray(item.action_params)) {
|
if (Array.isArray(item.action_params)) {
|
||||||
item.action_params = formatArray(item.action_params);
|
item.action_params = formatArray(item.action_params);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import {
|
|||||||
} from 'dashboard/routes/dashboard/settings/automation/operators';
|
} from 'dashboard/routes/dashboard/settings/automation/operators';
|
||||||
import filterQueryGenerator from './filterQueryGenerator';
|
import filterQueryGenerator from './filterQueryGenerator';
|
||||||
import actionQueryGenerator from './actionQueryGenerator';
|
import actionQueryGenerator from './actionQueryGenerator';
|
||||||
|
import cloneObject from 'dashboard/helpers/clone';
|
||||||
|
|
||||||
const MESSAGE_CONDITION_VALUES = [
|
const MESSAGE_CONDITION_VALUES = [
|
||||||
{
|
{
|
||||||
id: 'incoming',
|
id: 'incoming',
|
||||||
@@ -255,7 +257,7 @@ export const getStandardAttributeInputType = (automationTypes, event, key) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const generateAutomationPayload = payload => {
|
export const generateAutomationPayload = payload => {
|
||||||
const automation = JSON.parse(JSON.stringify(payload));
|
const automation = cloneObject(payload);
|
||||||
automation.conditions[automation.conditions.length - 1].query_operator = null;
|
automation.conditions[automation.conditions.length - 1].query_operator = null;
|
||||||
automation.conditions = filterQueryGenerator(automation.conditions).payload;
|
automation.conditions = filterQueryGenerator(automation.conditions).payload;
|
||||||
automation.actions = actionQueryGenerator(automation.actions);
|
automation.actions = actionQueryGenerator(automation.actions);
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Creates a deep clone of the provided object.
|
||||||
|
*
|
||||||
|
* This function attempts to use the `structuredClone` method if available.
|
||||||
|
* If `structuredClone` is not supported, it falls back to using
|
||||||
|
* `JSON.parse(JSON.stringify())`.
|
||||||
|
*
|
||||||
|
* @param {*} obj - The object to be cloned. Can be of any type.
|
||||||
|
* @returns {*} A deep clone of the input object.
|
||||||
|
*
|
||||||
|
* @throws {TypeError} If the object contains values that JSON cannot serialize
|
||||||
|
* (e.g., functions, undefined) when falling back to
|
||||||
|
* JSON methods.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const original = { a: 1, b: { c: 2 } };
|
||||||
|
* const clone = cloneObject(original);
|
||||||
|
* console.log(clone); // { a: 1, b: { c: 2 } }
|
||||||
|
* console.log(original === clone); // false
|
||||||
|
*/
|
||||||
|
export function cloneObject(obj) {
|
||||||
|
if (typeof structuredClone === 'function') {
|
||||||
|
return structuredClone(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The JSON method doesn't handle all JavaScript types correctly and may cause unexpected behavior.
|
||||||
|
// At the moment structuredClone has good adoption across browsers https://caniuse.com/mdn-api_structuredclone
|
||||||
|
// and is the preferred method for cloning objects.
|
||||||
|
//
|
||||||
|
// We can consider implementing a more robust fallback method in the future if we find users are running into issues.
|
||||||
|
// Ref: https://github.com/lukeed/klona
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import cloneObject from 'dashboard/helpers/clone';
|
||||||
|
|
||||||
const setArrayValues = item => {
|
const setArrayValues = item => {
|
||||||
return item.values[0]?.id ? item.values.map(val => val.id) : item.values;
|
return item.values[0]?.id ? item.values.map(val => val.id) : item.values;
|
||||||
};
|
};
|
||||||
@@ -21,7 +23,7 @@ const generateValues = item => {
|
|||||||
|
|
||||||
const generatePayload = data => {
|
const generatePayload = data => {
|
||||||
// Make a copy of data to avoid vue data reactivity issues
|
// Make a copy of data to avoid vue data reactivity issues
|
||||||
const filters = JSON.parse(JSON.stringify(data));
|
const filters = cloneObject(data);
|
||||||
let payload = filters.map(item => {
|
let payload = filters.map(item => {
|
||||||
// If item key is content, we will split it using comma and return as array
|
// If item key is content, we will split it using comma and return as array
|
||||||
// FIX ME: Make this generic option instead of using the key directly here
|
// FIX ME: Make this generic option instead of using the key directly here
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
|
import { cloneObject } from '../clone'; // Update this path
|
||||||
|
|
||||||
|
describe('cloneObject', () => {
|
||||||
|
it('should clone a simple object', () => {
|
||||||
|
const original = { a: 1, b: 'string', c: true };
|
||||||
|
const cloned = cloneObject(original);
|
||||||
|
expect(cloned).toEqual(original);
|
||||||
|
expect(cloned).not.toBe(original);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clone a nested object', () => {
|
||||||
|
const original = { a: 1, b: { c: 2, d: { e: 3 } } };
|
||||||
|
const cloned = cloneObject(original);
|
||||||
|
expect(cloned).toEqual(original);
|
||||||
|
expect(cloned.b).not.toBe(original.b);
|
||||||
|
expect(cloned.b.d).not.toBe(original.b.d);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clone an array', () => {
|
||||||
|
const original = [1, 2, [3, 4]];
|
||||||
|
const cloned = cloneObject(original);
|
||||||
|
expect(cloned).toEqual(original);
|
||||||
|
expect(cloned).not.toBe(original);
|
||||||
|
expect(cloned[2]).not.toBe(original[2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clone a Date object', () => {
|
||||||
|
const original = new Date();
|
||||||
|
const cloned = cloneObject(original);
|
||||||
|
expect(cloned).toEqual(original);
|
||||||
|
expect(cloned).not.toBe(original);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use structuredClone when available', () => {
|
||||||
|
const structuredCloneSpy = vi.fn(x => x);
|
||||||
|
global.structuredClone = structuredCloneSpy;
|
||||||
|
const obj = { a: 1 };
|
||||||
|
|
||||||
|
cloneObject(obj);
|
||||||
|
|
||||||
|
expect(structuredCloneSpy).toHaveBeenCalledWith(obj);
|
||||||
|
delete global.structuredClone;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fall back to JSON methods when structuredClone is not available', () => {
|
||||||
|
const jsonParseSpy = vi.spyOn(JSON, 'parse');
|
||||||
|
const jsonStringifySpy = vi.spyOn(JSON, 'stringify');
|
||||||
|
|
||||||
|
const original = { a: 1 };
|
||||||
|
global.structuredClone = undefined;
|
||||||
|
|
||||||
|
cloneObject(original);
|
||||||
|
|
||||||
|
expect(jsonStringifySpy).toHaveBeenCalledWith(original);
|
||||||
|
expect(jsonParseSpy).toHaveBeenCalled();
|
||||||
|
|
||||||
|
jsonParseSpy.mockRestore();
|
||||||
|
jsonStringifySpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
+3
-1
@@ -98,6 +98,8 @@ import { filterAttributeGroups } from '../contactFilterItems';
|
|||||||
import filterMixin from 'shared/mixins/filterMixin';
|
import filterMixin from 'shared/mixins/filterMixin';
|
||||||
import * as OPERATORS from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js';
|
import * as OPERATORS from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js';
|
||||||
import { CONTACTS_EVENTS } from '../../../../helper/AnalyticsHelper/events';
|
import { CONTACTS_EVENTS } from '../../../../helper/AnalyticsHelper/events';
|
||||||
|
import cloneObject from 'dashboard/helpers/clone';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
FilterInputBox,
|
FilterInputBox,
|
||||||
@@ -318,7 +320,7 @@ export default {
|
|||||||
if (this.$v.$invalid) return;
|
if (this.$v.$invalid) return;
|
||||||
this.$store.dispatch(
|
this.$store.dispatch(
|
||||||
'contacts/setContactFilters',
|
'contacts/setContactFilters',
|
||||||
JSON.parse(JSON.stringify(this.appliedFilters))
|
cloneObject(this.appliedFilters)
|
||||||
);
|
);
|
||||||
this.$emit('applyFilter', this.appliedFilters);
|
this.$emit('applyFilter', this.appliedFilters);
|
||||||
this.$track(CONTACTS_EVENTS.APPLY_FILTER, {
|
this.$track(CONTACTS_EVENTS.APPLY_FILTER, {
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ import automationMethodsMixin from 'dashboard/mixins/automations/methodsMixin';
|
|||||||
import automationValidationsMixin from 'dashboard/mixins/automations/validationsMixin';
|
import automationValidationsMixin from 'dashboard/mixins/automations/validationsMixin';
|
||||||
import filterInputBox from 'dashboard/components/widgets/FilterInput/Index.vue';
|
import filterInputBox from 'dashboard/components/widgets/FilterInput/Index.vue';
|
||||||
import automationActionInput from 'dashboard/components/widgets/AutomationActionInput.vue';
|
import automationActionInput from 'dashboard/components/widgets/AutomationActionInput.vue';
|
||||||
|
import cloneObject from 'dashboard/helpers/clone';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AUTOMATION_RULE_EVENTS,
|
AUTOMATION_RULE_EVENTS,
|
||||||
@@ -170,7 +171,7 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)),
|
automationTypes: cloneObject(AUTOMATIONS),
|
||||||
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
|
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
|
||||||
automationRuleEvents: AUTOMATION_RULE_EVENTS,
|
automationRuleEvents: AUTOMATION_RULE_EVENTS,
|
||||||
automationMutated: false,
|
automationMutated: false,
|
||||||
|
|||||||
+2
-1
@@ -147,6 +147,7 @@ import automationMethodsMixin from 'dashboard/mixins/automations/methodsMixin';
|
|||||||
import automationValidationsMixin from 'dashboard/mixins/automations/validationsMixin';
|
import automationValidationsMixin from 'dashboard/mixins/automations/validationsMixin';
|
||||||
import filterInputBox from 'dashboard/components/widgets/FilterInput/Index.vue';
|
import filterInputBox from 'dashboard/components/widgets/FilterInput/Index.vue';
|
||||||
import automationActionInput from 'dashboard/components/widgets/AutomationActionInput.vue';
|
import automationActionInput from 'dashboard/components/widgets/AutomationActionInput.vue';
|
||||||
|
import cloneObject from 'dashboard/helpers/clone';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AUTOMATION_RULE_EVENTS,
|
AUTOMATION_RULE_EVENTS,
|
||||||
@@ -172,7 +173,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)),
|
automationTypes: cloneObject(AUTOMATIONS),
|
||||||
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
|
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
|
||||||
automationRuleEvents: AUTOMATION_RULE_EVENTS,
|
automationRuleEvents: AUTOMATION_RULE_EVENTS,
|
||||||
automationMutated: false,
|
automationMutated: false,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { mapGetters } from 'vuex';
|
|||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import actionQueryGenerator from 'dashboard/helper/actionQueryGenerator.js';
|
import actionQueryGenerator from 'dashboard/helper/actionQueryGenerator.js';
|
||||||
import macrosMixin from 'dashboard/mixins/macrosMixin';
|
import macrosMixin from 'dashboard/mixins/macrosMixin';
|
||||||
|
import cloneObject from 'dashboard/helpers/clone';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -128,7 +129,7 @@ export default {
|
|||||||
this.mode === 'EDIT'
|
this.mode === 'EDIT'
|
||||||
? this.$t('MACROS.EDIT.API.SUCCESS_MESSAGE')
|
? this.$t('MACROS.EDIT.API.SUCCESS_MESSAGE')
|
||||||
: this.$t('MACROS.ADD.API.SUCCESS_MESSAGE');
|
: this.$t('MACROS.ADD.API.SUCCESS_MESSAGE');
|
||||||
let serializedMacro = JSON.parse(JSON.stringify(macro));
|
let serializedMacro = cloneObject(macro);
|
||||||
serializedMacro.actions = actionQueryGenerator(serializedMacro.actions);
|
serializedMacro.actions = actionQueryGenerator(serializedMacro.actions);
|
||||||
await this.$store.dispatch(action, serializedMacro);
|
await this.$store.dispatch(action, serializedMacro);
|
||||||
useAlert(successMessage);
|
useAlert(successMessage);
|
||||||
|
|||||||
Reference in New Issue
Block a user