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; };