import {create} from 'zustand';
import {produce} from 'immer';
import * as burst_api from '../utils/api';
const fetchFields = () => {
return burst_api.getFields().then( ( response ) => {
let fields = response.fields;
let progress = response.progress;
return {fields, progress};
}).catch( ( error ) => {
console.error( error );
});
};
export const useFields = create( ( set, get ) => ({
fieldsLoaded: false,
fields: [],
changedFields: [],
progress: [],
nextButtonDisabled: false,
highLightField: '',
setHighLightField: ( highLightField ) => set( state => ({ highLightField }) ),
setChangedField: async( id, value ) => {
set(
produce( ( state ) => {
//remove current reference
const existingFieldIndex = state.changedFields.findIndex( field => { //find index of existing field
return field.id === id;
});
if ( -1 !== existingFieldIndex ) {
state.changedFields.splice( existingFieldIndex, 1 );
}
//add again, with new value
let field = {};
field.id = id;
field.value = value;
state.changedFields.push( field );
})
);
let conditionallyEnabledFields = updateFieldsListWithConditions( get().fields );
set( ( state ) => ({fields: conditionallyEnabledFields}) );
},
getFieldValue: ( id ) => {
let fields = get().fields;
for ( const fieldItem of fields ) {
if ( fieldItem.id === id ) {
return fieldItem.value;
}
}
return false;
},
updateField: ( id, value ) => {
set(
produce( ( state ) => {
let index = false;
state.fields.forEach( function( fieldItem, i ) {
if ( fieldItem.id === id ) {
index = i;
}
});
state.fields[index].value = value;
})
);
},
addHelpNotice: ( id, label, text, title, url ) => {
//create help object
let help = {};
help.label = label;
help.text = text;
if ( url ) {
help.url = url;
}
if ( title ) {
help.title = title;
}
set(
produce( ( state ) => {
const fieldIndex = state.fields.findIndex( field => {
return field.id === id;
});
if ( -1 !== fieldIndex ) {
state.fields[fieldIndex].help = help;
}
})
);
},
saveFields: async() => {
try {
let fields = get().fields;
let changedFields = get().changedFields;
let progress = get().progress;
let saveFields = [];
for ( const field of fields ) {
let fieldIsIncluded = 0 < changedFields.filter( changedField => changedField.id === field.id ).length;
if ( fieldIsIncluded ) {
saveFields.push( field );
}
}
if ( 0 === saveFields.length ) {
return Promise.resolve();
}
const response = await burst_api.setFields( saveFields );
progress = response.progress;
fields = updateFieldsListWithConditions( response.fields );
set( ( state ) => ({changedFields: [], fields: fields, progress: progress}) );
return Promise.resolve( response );
} catch ( error ) {
console.error( error );
return Promise.reject( error );
}
},
updateFieldsData: async( selectedSubMenuItem ) => {
let fields = get().fields;
fields = updateFieldsListWithConditions( fields );
const nextButtonDisabled = isNextButtonDisabled( fields, selectedSubMenuItem );
set(
produce( ( state ) => {
state.fields = fields;
state.nextButtonDisabled = nextButtonDisabled;
})
);
},
fetchFieldsData: async( selectedSubMenuItem ) => {
const { fields, progress } = await fetchFields();
//process pro field
if ( burst_settings.is_pro ) {
for ( const field of fields ) {
if ( field.pro ) {
if ( field.pro.default ) {
field.default = field.pro.default;
}
if ( field.pro.label ) {
field.label = field.pro.label;
}
if ( field.pro.comment ) {
field.comment = field.pro.comment;
}
if ( field.pro.tooltip ) {
field.tooltip = field.pro.tooltip;
}
if ( field.pro.react_conditions ) {
field.react_conditions = field.pro.react_conditions;
}
}
}
}
let conditionallyEnabledFields = updateFieldsListWithConditions( fields );
let selectedFields = conditionallyEnabledFields.filter( field => field.menu_id === selectedSubMenuItem );
set( ( state ) => ({fieldsLoaded: true, fields: conditionallyEnabledFields, selectedFields: selectedFields, progress: progress }) );
}
}) );
//check if all required fields have been enabled. If so, enable save/continue button
const isNextButtonDisabled = ( fields, selectedMenuItem ) => {
let fieldsOnPage = [];
//get all fields with group_id this.props.group_id
for ( const field of fields ) {
if ( field.menu_id === selectedMenuItem ) {
fieldsOnPage.push( field );
}
}
let requiredFields = fieldsOnPage.filter( field => field.required && ! field.conditionallyDisabled && ( 0 == field.value.length || ! field.value ) );
return 0 < requiredFields.length;
};
export const updateFieldsListWithConditions = ( fields ) => {
return fields.map( field => {
const enabled = ! ( field.hasOwnProperty( 'react_conditions' ) && ! validateConditions( field.react_conditions, fields, field.id ) );
const newField = {...field};
if ( 'disable' === newField.condition_action ) {
newField.disabled = ! enabled;
} else {
newField.conditionallyDisabled = ! enabled;
}
return newField;
});
};
export const validateConditions = ( conditions, fields, fieldId, isSub ) => {
let relation = 'OR' === conditions.relation ? 'OR' : 'AND';
let conditionApplies = 'AND' === relation;
for ( const key in conditions ) {
if ( conditions.hasOwnProperty( key ) ) {
let thisConditionApplies = 'AND' === relation;
let subConditionsArray = conditions[key];
//check if there's a subcondition
if ( subConditionsArray.hasOwnProperty( 'relation' ) ) {
thisConditionApplies = 1 === validateConditions( subConditionsArray, fields, fieldId, true );
if ( 'AND' === relation ) {
conditionApplies = conditionApplies && thisConditionApplies;
} else {
conditionApplies = conditionApplies || thisConditionApplies;
}
}
for ( let conditionField in subConditionsArray ) {
if ( 'hidden' === conditionField ) {
thisConditionApplies = false;
continue;
}
let invert = 0 === conditionField.indexOf( '!' );
if ( subConditionsArray.hasOwnProperty( conditionField ) ) {
let conditionValue = subConditionsArray[conditionField];
conditionField = conditionField.replace( '!', '' );
let conditionFields = fields.filter( field => field.id === conditionField );
if ( conditionFields.hasOwnProperty( 0 ) ) {
let field = conditionFields[0];
let actualValue = field.value;
if ( 'text_checkbox' === field.type ) {
thisConditionApplies = actualValue.hasOwnProperty( 'show' ) && actualValue.show == conditionValue;
} else if ( 'checkbox' === field.type ) {
thisConditionApplies = actualValue == conditionValue; //with == it can be either true or 1
} else if ( 'multicheckbox' === field.type ) {
//multicheckbox conditions
//loop through objects
thisConditionApplies = false;
let arrayValue = actualValue;
if ( ! Array.isArray( arrayValue ) ) {
arrayValue = '' !== arrayValue ? [] : [ arrayValue ];
}
if ( 0 === arrayValue.length ) {
thisConditionApplies = false;
} else {
for ( const key of Object.keys( arrayValue ) ) {
if ( ! Array.isArray( conditionValue ) ) {
conditionValue = [ conditionValue ];
}
if ( conditionValue.includes( arrayValue[key]) ) {
thisConditionApplies = true;
break;
}
}
}
} else if ( 'radio' === field.type || 'document' === field.type ) {
//as the regions field can be both radio and multicheckbox, an array is possible for a radio field
if ( Array.isArray( conditionValue ) ) {
thisConditionApplies = conditionValue.includes( actualValue );
} else {
thisConditionApplies = conditionValue === actualValue;
}
} else {
if ( true === conditionValue ) {
thisConditionApplies = 1 === actualValue || '1' === actualValue || true === actualValue;
} else if ( false === conditionValue ) {
thisConditionApplies = 0 === actualValue || '0' === actualValue || false === actualValue;
} else if ( -1 !== conditionValue.indexOf( 'EMPTY' ) ) {
thisConditionApplies = 0 === actualValue.length;
} else if ( Array.isArray( conditionValue ) ) {
thisConditionApplies = conditionValue.includes( actualValue );
} else {
thisConditionApplies = String( actualValue ).toLowerCase() === conditionValue.toLowerCase();
}
}
}
}
if ( invert ) {
thisConditionApplies = ! thisConditionApplies;
}
if ( 'AND' === relation ) {
conditionApplies = conditionApplies && thisConditionApplies;
} else {
conditionApplies = conditionApplies || thisConditionApplies;
}
}
if ( 'AND' === relation ) {
conditionApplies = conditionApplies && thisConditionApplies;
} else {
conditionApplies = conditionApplies || thisConditionApplies;
}
}
}
return conditionApplies ? 1 : 0;
};