Smart action hook previousValue wrongly set

Expected behavior

changedField.previousValue should be properly set on the ‘Number of values’ field whatever the position of that field is (See context it will be more clear)

Actual behavior

If my ‘Number of values’ number input field is not at the 4th position (index = 3) the hook previous value is always null and it seems that whatever the field that is at the 4th position in the array has it’s previous value set instead of my number input

Failure Logs

None

Context

I also opened an issue on github: Smart action hook previousValue wrongly set · Issue #1010 · ForestAdmin/forest-express-sequelize · GitHub
Please help this makes no sense to me. Also note that all fields before the prblematic one is not added or removed dynamically so that can’t really change the position on ‘Number of values’ field

Smart action:

export enum EnterpriseCollectionFields {
  Name = 'Name',
  OfferCard = 'Offer mobility card',
  Type = 'Spending rules type',
  ShortDescription = 'Short Description (Summary)',
  Description = 'Description',
  Value = 'Value',
  DailyLimit = 'Daily limit',
  TransactionLimit = 'Transaction limit',
  MonthlyAllocation = 'Monthly allocation'
}

export declare enum SpendingRuleType {
    StripeMerchant = "stripeMerchant",
    StripeCategory = "stripeCategory",
    CommutifiPlan = "commutifiPlan",
    Wallet = "wallet"
}

{
      name: 'Create Wallet',
      type: 'single',
      fields: [
        {
          field: EnterpriseCollectionFields.OfferCard,
          description:
            'Weather this wallet will setup a Commutifi card or not. Then rules like Stripe categories can be applied',
          type: 'Boolean',
          hook: 'onOfferCardChanged',
          isRequired: true,
          defaultValue: false
        },
        {
          field: EnterpriseCollectionFields.Type,
          description: 'Type of spending rule restrictions you want to add',
          type: 'Enum',
          enums: Object.values(SpendingRuleType),
          hook: 'onTypeChanged',
          isRequired: true
        },
        {
          field: 'Number of values',
          description: 'How many plans or stripe categories you want to add ?',
          type: 'Number',
          defaultValue: 1,
          hook: 'onNbValueChange'
        },
        {
          field: EnterpriseCollectionFields.Value,
          description:
            'Commutifi Plan ids, Stripe categories or leave empty if you are making a global rule for the wallet (Ex. monthly limit only or days limit)',
          type: 'String',
          reference: 'plans.id'
        },
        {
          field: EnterpriseCollectionFields.Name,
          description: 'Wallet Name that employee will see',
          type: 'String',
          isRequired: true
        },
        {
          field: EnterpriseCollectionFields.Description,
          description:
            'Long Wallet description. Markdown available in wallet detail view but we can only use a simple text here ...',
          type: 'String',
          isRequired: true
        },
        {
          field: EnterpriseCollectionFields.ShortDescription,
          description: 'Quick 50 max characters description of the wallet',
          type: 'String',
          isRequired: true,
          hook: 'onDescriptionChange'
        },

        {
          field: EnterpriseCollectionFields.DailyLimit,
          description: 'Daily max amount spent (in cents)',
          type: 'Number'
        },
        {
          field: EnterpriseCollectionFields.TransactionLimit,
          description: 'Max amount PER transaction (in cents)',
          type: 'Number'
        }
      ],
      hooks: {
        change: {
          onOfferCardChanged: ({ fields, changedField }) => {
            if (changedField.value === true) {
              fields.push({
                previousValue: undefined,
                value: undefined,
                field: EnterpriseCollectionFields.MonthlyAllocation,
                type: 'Number',
                isRequired: true
              })
            } else {
              const index = fields.findIndex(
                (f) => f.field === EnterpriseCollectionFields.MonthlyAllocation
              )
              fields.splice(index, 1)
            }
            return fields
          },
          onTypeChanged: ({ fields, changedField }) => {
            const fieldsIndexToMutate = []
            fields.forEach((field, i) =>
              field.field.startsWith(EnterpriseCollectionFields.Value)
                ? fieldsIndexToMutate.push(i)
                : null
            )

            switch (changedField.value) {
              case SpendingRuleType.CommutifiPlan:
                fieldsIndexToMutate.forEach((index) => {
                  const field = fields[index]
                  field.isRequired = true
                  field.type = 'String'
                  field.reference = 'plans.id'
                  field.enums = null
                  // @ts-ignore
                  field.widgetEdit = {
                    name: 'belongsto typeahead',
                    parameters: {}
                  }
                })
                break
              case SpendingRuleType.StripeMerchant:
                fieldsIndexToMutate.forEach((index) => {
                  const field = fields[index]
                  field.isRequired = true
                  field.type = 'String'
                  field.reference = null
                  field.enums = null
                  // @ts-ignore
                  field.widgetEdit = {
                    name: 'text editor',
                    parameters: {}
                  }
                })
                break
              case SpendingRuleType.Wallet:
                fieldsIndexToMutate.forEach((index) => {
                  const field = fields[index]
                  field.isRequired = false
                  field.type = 'String'
                  field.reference = null
                  field.enums = null
                  // @ts-ignore
                  field.widgetEdit = {
                    name: 'text editor',
                    parameters: {}
                  }
                })
                break

              default:
                break
            }
            return fields
          },
          onNbValueChange: ({ changedField, fields }) => {
            if (changedField.value < 0) {
              return fields
            }
            const previousValue = changedField.previousValue ?? 1
            const value = changedField.value ?? 1
            const valueFieldIndex = fields.findIndex((field) =>
              field.field.startsWith(EnterpriseCollectionFields.Value)
            )
            const index =
              valueFieldIndex >= 0
                ? valueFieldIndex
                : fields.findIndex(
                    (field) => field.field === 'Number of values'
                  ) + 1
            if (previousValue < value) {
              const newFieldAmount = value - previousValue
              fields.splice(
                index + previousValue,
                0,
                // times function is from lodash
                ...times(newFieldAmount, (i) => ({
                  previousValue: undefined,
                  value: undefined,
                  ...walletPlanValueField,
                  field: `${EnterpriseCollectionFields.Value}${
                    previousValue + i
                  }`
                }))
              )
            } else if (previousValue > value) {
              const deleteFieldAmount = previousValue - value
              fields.splice(index + value, deleteFieldAmount)
            }

            return fields
          },
          onDescriptionChange: ({ fields, changedField }) => {
            if (
              typeof changedField.value === 'string' &&
              changedField.value.length > 50
            ) {
              changedField.value.substring(0, 50)
            }
            return fields
          }
        }
      }
    }

In that configuration :arrow_up: I can’t get the number input to work with adding value fields properly because the previous value is set on the wrong item in the fields array

But the position in the schema is right (removed the image to show cause I can’t embed more than 3 images :frowning: )

Screen record of bad behavior (Number of values is 3rd in array → index 2)

bad-previous-value

Screen record of good behavior (Number of values is 4th in array → index 3)

good-previous-value

  • Package Version: “forest-express-sequelize”: “~8.5.10”,
  • Express Version: “express”: “~4.17.1”
  • Sequelize Version: “sequelize”: “~5.15.1”,
  • Database Dialect: Postgres
  • Database Version: “pg”: “~8.2.2”,
  • Project name: Commutifi
  • Team name: Commutifi
  • Environment name: Staging
  • Agent type & version: Google Chrome 94.0.4606.61
  • Recent changes made on your end if any: See code snippets above

Hello @FrancisRoc,

Thanks for making this detailed report. It helps us a lot. :pray:

We are able to reproduce the issue on our side. It will be fixed very soon. :handshake:

The issue is on the front side of the solution. To add some context and transparency, some computation was badly handled ending in mismatching of previousValue between fields…

I will inform you once the fix will be deployed (it should be a matter of hours).

Kind regards,
Morgan

1 Like

Hello @FrancisRoc,

We released a new version of our front application.

You should now be able to use the field property previousValue with the right values.

Let me know if the issue is solved on your side.

Kind regards,
Morgan

Yes updating to 8.5.13 fixed it on my side too :slight_smile: Thanks!