lodash#transform TypeScript Examples

The following examples show how to use lodash#transform. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Table.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
function removeDefaultValues(state: any, defaultState: any): any {
  return transform(state, (result, value, key) => {
    if (!isEqual(value, defaultState[key])) {
      result[key] = value;
    }
  });
}
Example #2
Source File: edit-stage.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
EditStage = (props: IEditStageProps & FormProps) => {
  const [form] = Form.useForm();
  const [state, updater] = useUpdate({
    task: {} as IStageTask | {},
    actionConfig: {} as DEPLOY.ActionConfig | {},
    resource: {},
    originType: null as null | string,
    originName: null as null | string,
  });

  const initialValue = React.useRef({});

  const { task, actionConfig, resource, originName, originType } = state;
  const { actions, otherTaskAlias, editing, isCreateTask, onSubmit: handleSubmit, task: PropsTask } = props;
  const { getFieldValue } = form;

  const actionConfigs = deployStore.useStore((s) => s.actionConfigs);
  const { getActionConfigs } = deployStore.effects;
  const [loading] = useLoading(deployStore, ['getActionConfigs']);
  React.useEffect(() => {
    if (!isEmpty(PropsTask)) {
      updater.originName(PropsTask.alias);
      updater.originType(PropsTask.type);
      updater.task(PropsTask);
    }
  }, [PropsTask, updater]);

  React.useEffect(() => {
    let config;
    if (actionConfigs.length > 0) {
      config = PropsTask.version
        ? actionConfigs.find((c) => c.version === PropsTask.version)
        : getDefaultVersionConfig(actionConfigs);
    }

    const newResource = getResource(PropsTask, config);
    updater.resource(newResource);
    updater.actionConfig(config as DEPLOY.ActionConfig);
  }, [actionConfigs, PropsTask, updater]);

  React.useEffect(() => {
    if (isCreateTask) {
      updater.actionConfig({});
    }
  }, [isCreateTask, updater]);

  if (!isCreateTask && isEmpty(actionConfig)) {
    return null;
  }

  const type = actionConfig.type || getFieldValue(['resource', 'type']);
  const taskInitName =
    originType === actionConfig.name
      ? originName
      : otherTaskAlias.includes(actionConfig.name)
      ? undefined
      : actionConfig.name;

  const changeResourceType = (value: string) => {
    const action = actions.find((a: any) => a.name === value);
    if (action) {
      getActionConfigs({ actionType: action.name }).then((result: DEPLOY.ActionConfig[]) => {
        const config = getDefaultVersionConfig(result);
        const mergedResource = mergeActionAndResource(config, {} as any);
        updater.resource({
          ...resource,
          ...mergedResource,
        });
      });
    }
  };

  const checkResourceName = (_rule: any, value: string, callback: any) => {
    const name = form.getFieldValue(['resource', 'alias']);

    if (!value) {
      return callback(i18n.t('dop:please enter the task name'));
    }
    if (otherTaskAlias.includes(name)) {
      return callback(i18n.t('dop:An Action with the same name exists.'));
    }
    callback();
  };

  const changeActionVersion = (version: string) => {
    const selectConfig = actionConfigs.find((config) => config.version === version) as DEPLOY.ActionConfig;
    updater.actionConfig(selectConfig);
    updater.resource(getResource(task, selectConfig));
  };

  const taskType = (
    <Item
      name="resource.type"
      initialValue={task.type}
      rules={[
        {
          required: true,
          message: `${i18n.t('dop:Please select')}Task Type`,
        },
      ]}
    >
      <ActionSelect
        disabled={!editing}
        label={i18n.t('Task type')}
        actions={actions}
        onChange={changeResourceType}
        placeholder={`${i18n.t('dop:Please choose the task type')}`}
      />
    </Item>
  );

  const actionVersion = (
    <Item
      label="version"
      name="resource.version"
      initialValue={task.version || actionConfig.version}
      rules={[
        {
          required: true,
          message: `${i18n.t('dop:Please select')}Task Version`,
        },
      ]}
    >
      <Select disabled={!editing} onChange={changeActionVersion} placeholder={`${i18n.t('dop:please choose version')}`}>
        {actionConfigs.map((config) => (
          <Option key={config.version} value={config.version}>
            {config.version}
          </Option>
        ))}
      </Select>
    </Item>
  );

  let alert;

  if (!isCreateTask && isEmpty(resource)) {
    return null;
  }

  if (!isCreateTask && !actionConfig.type) {
    alert = (
      <ErdaAlert
        className="addon-error-tag"
        message={i18n.t('dop:the current action does not exist, please re-select!')}
        type="error"
      />
    );
  }
  const taskName = (
    <Item
      label={i18n.t('dop:task name')}
      name="resource.alias"
      initialValue={taskInitName}
      rules={[
        {
          required: true,
          validator: checkResourceName,
        },
      ]}
    >
      <Input autoFocus={!type} disabled={!editing} placeholder={i18n.t('dop:please enter the task name')} />
    </Item>
  );

  const renderTaskTypeStructure = () => {
    if (isEmpty(resource)) {
      return null;
    }
    const { getFieldsValue } = form;
    const resourceForm = getFieldsValue(['resource.alias', 'resource.type']);
    if (!resourceForm.resource.type) {
      return null;
    }

    return renderResource(resource, 'resource');
  };

  const getDataValue = (dataSource: any, key: string) => {
    return dataSource ? dataSource[key] : null;
  };

  const renderResource = (resourceParam: any, parentKey?: string, dataSource?: any) => {
    if (resourceParam.data instanceof Array) {
      return resourceParam.data.map((item: any) => {
        const inputKey = parentKey ? `${parentKey}.${item.name}` : `${item.name}`;
        return renderObject(item, inputKey, getDataValue(dataSource, item.name));
      });
    }
    const { params, image, resources } = resourceParam.data;

    const parentObjectData = getDataValue(dataSource, 'params');
    const paramsContent = map(params, (value: any, itemKey: string) => {
      const inputKey = parentKey ? `${parentKey}.params.${itemKey}` : `params.${itemKey}`;
      return renderObject(value, inputKey, getDataValue(parentObjectData, itemKey));
    });

    return (
      <>
        {actionConfig.name === 'custom-script' ? (
          <div>{renderObject(image, 'resource.image', getDataValue(dataSource, 'image'))}</div>
        ) : null}
        <div>
          <div className="resource-input-group-title">params: </div>
          {paramsContent}
        </div>
        <div>{renderObject(resources, 'resource.resources', getDataValue(dataSource, 'resources'))}</div>
      </>
    );
  };

  const renderObject = (value: any, parentKey: string, dataSource?: any) => {
    if (!isObject(value.type)) {
      return renderPropertyValue(value, parentKey, dataSource);
    }

    if (value.type === 'string_array') {
      return renderStringArray(value, parentKey);
    }

    if (value.type === 'struct_array') {
      return renderStructArray(value, parentKey);
    }

    if (value.type === 'map') {
      return renderMap(value, parentKey, dataSource);
    }

    const content = renderResource({ data: value.struct }, parentKey, dataSource);
    if (!content || !Object.values(content).some((c) => c)) return null;

    return (
      <div key={parentKey}>
        <span className="resource-input-group-title">{value.name}: </span>
        <div>{content}</div>
      </div>
    );
  };

  const renderMap = (value: any, parentKey: string, dataSource?: any) => {
    let initialValue = isCreateTask ? value.default : value.value || value.default;

    if (dataSource) {
      initialValue = dataSource;
    }

    if (!editing && !initialValue) {
      return null;
    }

    const inputField = (
      <Item
        key={parentKey}
        name={parentKey}
        initialValue
        rules={[
          {
            required: value.required,
            message: i18n.t('dop:this item cannot be empty'),
          },
        ]}
      >
        {renderTooltip(value.desc, <VariableInput disabled={!editing} label={value.name} />)}
      </Item>
    );
    return inputField;
  };

  const renderStringArray = (value: any, parentKey: string) => {
    const inputField = (
      <Item
        key={parentKey}
        name={parentKey}
        initialValue={isCreateTask ? value.default : value.value || value.default}
        rules={[
          {
            required: value.required,
            message: i18n.t('dop:this item cannot be empty'),
          },
        ]}
      >
        {renderTooltip(value.desc, <ListInput disabled={!editing} label={value.name} />)}
      </Item>
    );
    return inputField;
  };

  const renderPropertyValue = (value: any, parentKey: string, dataSource?: any) => {
    let input;
    let initialValue = isCreateTask ? value.default : value.value || value.default;

    if (dataSource) {
      initialValue = dataSource;
    }

    if (!editing && !initialValue) {
      return null;
    }

    const unit = value.unit ? <span>{value.unit}</span> : null;

    switch (value.type) {
      case 'float':
      case 'int':
        input = (
          <InputNumber
            disabled={!editing || value.readOnly}
            className="w-full"
            placeholder={i18n.t('dop:please enter data')}
          />
        );
        break;
      default:
        input = (
          <Input
            disabled={!editing || value.readOnly}
            placeholder={i18n.t('dop:please enter data')}
            addonAfter={unit}
          />
        );
        break;
    }

    const inputField = (
      <Item
        key={parentKey}
        label={value.name}
        name={parentKey}
        initialValue
        rules={[
          {
            required: value.required,
            message: i18n.t('dop:this item cannot be empty'),
          },
        ]}
      >
        {renderTooltip(value.desc, input)}
      </Item>
    );
    return inputField;
  };

  const renderStructArray = (property: any, parentKey: string) => {
    if ((!editing && !property.value) || (!editing && property.value && !property.value.length)) {
      return null;
    }
    const addBtn = editing ? (
      <ErdaIcon
        type="plus"
        className="cursor-pointer"
        onClick={() => addNewItemToStructArray(property.value, property.struct[0])}
      />
    ) : null;
    initialValue.current = {
      [`${parentKey}-data`]: property.value || [],
    };
    const data = getFieldValue(`${parentKey}-data`);
    const content = data.map((item: any, index: number) => {
      const keys = Object.keys(item);
      const header = (
        <div>
          <span>{typeof item[keys[0]] === 'string' ? item[keys[0]] : 'module'}</span>
          {editing ? (
            <CustomIcon
              onClick={() => deleteItemFromStructArray(index, parentKey)}
              className="icon-delete"
              type="sc1"
            />
          ) : null}
        </div>
      );
      return (
        <Panel key={`${parentKey}.${item.name}`} header={header}>
          {renderResource({ data: property.struct }, `${parentKey}[${index}]`, item)}
        </Panel>
      );
    });

    return (
      <div key={parentKey}>
        <span className="resource-input-group-title">
          {property.name}:{addBtn}
        </span>
        {data.length ? (
          <Collapse className="collapse-field" accordion>
            {content}
          </Collapse>
        ) : null}
      </div>
    );
  };

  const deleteItemFromStructArray = (index: number, parentKey: string) => {
    const formDatas = form.getFieldValue(`${parentKey}-data`);
    formDatas.splice(index, 1);

    form.setFieldsValue({
      [parentKey]: formDatas,
    });
  };

  const addNewItemToStructArray = (list: any[], struct: any) => {
    list.push({
      [struct.name]: `module-${list.length + 1}`,
    });
    updater.resource(cloneDeep(resource));
  };

  const isObject = (inputType: string) => {
    return ['map', 'string_array', 'struct_array', 'struct'].includes(inputType);
  };

  const renderTooltip = (message: string, text: any) => {
    if (!message) {
      return text;
    }
    const msgComp = <pre className="prop-popover">{message}</pre>;
    return (
      <Popover placement="leftTop" trigger={['focus']} content={msgComp}>
        {text}
      </Popover>
    );
  };

  const onSubmit = () => {
    form
      .validateFields()
      .then((values: any) => {
        let data = cloneDeep(values);
        const resources = head(filter(state.resource.data, (item) => item.name === 'resources'));
        const originResource = transform(
          get(resources, 'struct'),
          (result, item: { name: string; default: string | number }) => {
            const { name, default: d } = item;
            // eslint-disable-next-line no-param-reassign
            result[name] = +d;
          },
          {},
        );
        const editedResources = get(data, 'resource.resources');
        forEach(Object.entries(editedResources), ([key, value]) => {
          editedResources[key] = +(value as string);
        });
        const isResourceDefault = isEqual(editedResources, originResource);

        if (isResourceDefault) {
          data = omit(data, ['resource.resources']);
        }
        const filledFieldsData = clearEmptyField(data);
        handleSubmit(filledFieldsData);
      })
      .catch(({ errorFields }: { errorFields: Array<{ name: any[]; errors: any[] }> }) => {
        form.scrollToField(errorFields[0].name);
      });
  };

  const clearEmptyField = (ObjData: any) => {
    const filledFields: string[] = [];
    const findData = (obj: any, parentArray: string[]) => {
      Object.keys(obj).forEach((key) => {
        const currentParent = [...parentArray, key];
        const value = get(obj, key);
        if (typeof value === 'object') {
          findData(value, currentParent);
        } else if (value || value === 0) {
          filledFields.push(currentParent.join('.'));
        }
      });
    };
    findData(ObjData, []);
    return pick(ObjData, filledFields);
  };

  return (
    <Spin spinning={loading}>
      <Form form={form} initialValues={initialValue.current} className="edit-service-container">
        {alert}
        {taskType}
        {type ? taskName : null}
        {actionVersion}
        {renderTaskTypeStructure()}
        {editing ? (
          <Button type="primary" ghost onClick={onSubmit}>
            {i18n.t('Save')}
          </Button>
        ) : null}
      </Form>
    </Spin>
  );
}
Example #3
Source File: pipeline-node-drawer.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
PurePipelineNodeForm = (props: IEditStageProps & FormProps) => {
  const [form] = Form.useForm();
  const {
    nodeData: propsNodeData,
    editing,
    isCreate,
    otherTaskAlias = [],
    onSubmit: handleSubmit = noop,
    chosenActionName,
    chosenAction,
    actionSpec,
  } = props;
  const [actionConfigs] = appDeployStore.useStore((s) => [s.actionConfigs]);

  const { getFieldValue } = form;
  const [{ actionConfig, resource, originType, originName, task, changeKey }, updater, update] = useUpdate({
    resource: {},
    actionConfig: {} as DEPLOY.ActionConfig,
    originType: null as null | string,
    originName: null as null | string,
    task: {} as IStageTask,
    changeKey: 0,
  });

  useEffectOnce(() => {
    handleActionSpec();
  });

  React.useEffect(() => {
    if (propsNodeData && !isEmpty(propsNodeData)) {
      update({
        originName: propsNodeData.alias,
        originType: propsNodeData.type,
        task: propsNodeData,
      });
    }
  }, [propsNodeData, update]);

  React.useEffect(() => {
    if (isCreate) {
      updater.actionConfig({} as DEPLOY.ActionConfig);
    }
  }, [isCreate, updater]);

  const taskInitName =
    originType === actionConfig.name
      ? originName
      : otherTaskAlias.includes(actionConfig.name)
      ? undefined
      : actionConfig.name;

  const taskInitVersion = task.version || actionConfig.version;
  useUpdateEffect(() => {
    const prevResource = form.getFieldValue('resource') || {};
    form.setFieldsValue({
      resource: {
        ...prevResource,
        type: chosenActionName,
        alias: taskInitName,
        version: taskInitVersion,
      },
    });
    updater.changeKey((prev: number) => prev + 1);
  }, [taskInitName, taskInitVersion, chosenActionName]);

  const handleActionSpec = () => {
    let _config;
    let _resource;
    if (propsNodeData && !isEmpty(propsNodeData)) {
      if (actionSpec.length > 0) {
        _config = propsNodeData.version
          ? actionSpec.find((c) => c.version === propsNodeData.version)
          : getDefaultVersionConfig(actionSpec);
      }
      _resource = getResource(propsNodeData, _config);
    } else {
      _config = getDefaultVersionConfig(actionSpec);
      const mergedResource = mergeActionAndResource(_config, {});
      _resource = { ...resource, ...mergedResource };
    }
    update({
      resource: _resource,
      actionConfig: _config || ({} as DEPLOY.ActionConfig),
    });
  };

  useUpdateEffect(() => {
    handleActionSpec();
  }, [actionSpec]);

  if (!isCreate && isEmpty(actionConfig)) {
    return null;
  }
  const type = actionConfig.type || getFieldValue(['resource', 'type']);

  const checkResourceName = (_rule: any, value: string, callback: any) => {
    const name = form.getFieldValue(['resource', 'alias']);

    if (!value) {
      return callback(i18n.t('dop:please enter the task name'));
    }
    if (otherTaskAlias.includes(name)) {
      return callback(i18n.t('dop:An Action with the same name exists.'));
    }
    callback();
  };

  const changeActionVersion = (version: any) => {
    const selectConfig = actionConfigs.find((config) => config.version === version) as DEPLOY.ActionConfig;
    updater.actionConfig(selectConfig);
    updater.resource(getResource(task as IStageTask, selectConfig));
  };

  const taskType = (
    <Item
      className="hidden"
      name={['resource', 'type']}
      initialValue={chosenActionName}
      rules={[
        {
          required: true,
          message: `${i18n.t('dop:Please select')}Task Type`,
        },
      ]}
    >
      <Input />
    </Item>
  );

  const loopData = (
    <Item className="hidden" name={['resource', 'loop']} initialValue={get(actionConfig, 'spec.loop')} />
  );

  const actionVersion = (
    <Item
      label={i18nMap.version}
      name={['resource', 'version']}
      initialValue={task.version || actionConfig.version}
      rules={[
        {
          required: true,
          message: `${i18n.t('dop:Please select')}Task Version`,
        },
      ]}
    >
      <Select disabled={!editing} onChange={changeActionVersion} placeholder={`${i18n.t('dop:please choose version')}`}>
        {actionConfigs.map((config) => (
          <Option key={config.version} value={config.version}>
            {config.version}
          </Option>
        ))}
      </Select>
    </Item>
  );

  let alert;
  if (!isCreate && !actionConfig.type) {
    alert = (
      <ErdaAlert
        className="addon-error-tag"
        showIcon
        message={i18n.t('dop:the current action does not exist, please re-select!')}
        type="error"
      />
    );
  }

  const taskName = (
    <Item
      label={i18n.t('dop:task name')}
      name={['resource', 'alias']}
      initialValue={taskInitName}
      rules={[
        {
          required: true,
          validator: checkResourceName,
        },
      ]}
    >
      <Input autoFocus={!type} disabled={!editing} placeholder={i18n.t('dop:please enter the task name')} />
    </Item>
  );

  const renderTaskTypeStructure = () => {
    if (isEmpty(resource)) {
      return null;
    }

    const { getFieldsValue } = form;
    const resourceForm = getFieldsValue([
      ['resource', 'alias'],
      ['resource', 'type'],
    ]);
    if (!get(resourceForm, 'resource.type')) {
      return null;
    }

    return renderResource(resource, 'resource');
  };

  const getDataValue = (dataSource: any, key: string) => {
    return dataSource ? dataSource[key] : null;
  };

  const renderResource = (resourceParam: any, parentKey?: string, dataSource?: any) => {
    if (resourceParam.data instanceof Array) {
      return resourceParam.data.map((item: any) => {
        const inputKey = parentKey ? `${parentKey}.${item.name}` : `${item.name}`;
        return renderObject(item, inputKey, getDataValue(dataSource, item.name));
      });
    }
    const { params, image, resources } = resourceParam.data;

    const parentObjectData = getDataValue(dataSource, 'params');
    const paramsContent = map(params, (value: any, itemKey: string) => {
      const inputKey = parentKey ? `${parentKey}.params.${itemKey}` : `params.${itemKey}`;
      return renderObject(value, inputKey, getDataValue(parentObjectData, itemKey));
    });

    return (
      <>
        {actionConfig.name === 'custom-script' ? (
          <div>{renderObject(image, 'resource.image', getDataValue(dataSource, 'image'))}</div>
        ) : null}
        <div>
          <div className="resource-input-group-title">{i18nMap.params}: </div>
          {paramsContent}
        </div>
        <div>{renderObject(resources, 'resource.resources', getDataValue(dataSource, 'resources'))}</div>
      </>
    );
  };

  const renderObject = (value: any, parentKey: string, dataSource?: any) => {
    if (!isObject(value.type)) {
      return renderPropertyValue(value, parentKey, dataSource);
    }

    if (value.type === 'string_array') {
      return renderStringArray(value, parentKey);
    }

    if (value.type === 'struct_array') {
      return renderStructArray(value, parentKey);
    }

    if (value.type === 'map') {
      return renderMap(value, parentKey, dataSource);
    }

    const content = renderResource({ data: value.struct }, parentKey, dataSource);
    if (!content || !Object.values(content).some((c) => c)) return null;

    return (
      <div key={parentKey}>
        <span className="resource-input-group-title">{i18nMap[value.name] || value.name}: </span>
        <div>{content}</div>
      </div>
    );
  };

  const renderMap = (value: any, parentKey: string, dataSource?: any) => {
    let initialValue = isCreate ? value.default : value.value || value.default;

    if (dataSource) {
      initialValue = dataSource;
    }

    if (!editing && !initialValue) {
      return null;
    }

    const inputField = (
      <Item
        key={parentKey}
        name={formKeyFormat(parentKey)}
        initialValue={initialValue}
        rules={[
          {
            required: value.required,
            message: i18n.t('dop:this item cannot be empty'),
          },
        ]}
      >
        <VariableInput disabled={!editing} label={getLabel(value.name, value.desc)} />
      </Item>
    );
    return inputField;
  };

  const renderStringArray = (value: any, parentKey: string) => {
    const inputField = (
      <Item
        key={parentKey}
        name={formKeyFormat(parentKey)}
        initialValue={isCreate ? value.default : value.value || value.default}
        rules={[
          {
            required: value.required,
            message: i18n.t('dop:this item cannot be empty'),
          },
        ]}
        getValueFromEvent={(val: Array<{ value: string }>) => {
          return val?.length ? val.map((v) => v.value) : val;
        }}
      >
        <ListInput disabled={!editing} label={getLabel(value.name, value.desc)} />
      </Item>
    );
    return inputField;
  };

  const renderPropertyValue = (value: any, parentKey: string, dataSource?: any) => {
    let input;
    let initialValue = isCreate ? value.default : value.value || value.default;

    if (dataSource) {
      initialValue = dataSource;
    }

    if (!editing && !initialValue) {
      return null;
    }

    const unit = value.unit ? <span>{value.unit}</span> : null;

    switch (value.type) {
      case 'float':
      case 'int':
        input = (
          <InputNumber
            disabled={!editing || value.readOnly}
            className="w-full"
            placeholder={i18n.t('dop:please enter data')}
          />
        );
        break;
      default:
        input = (
          <Input
            disabled={!editing || value.readOnly}
            placeholder={i18n.t('dop:please enter data')}
            addonAfter={unit}
          />
        );
        break;
    }

    const inputField = (
      <Item
        key={parentKey}
        label={getLabel(value.name, value.desc)}
        name={formKeyFormat(parentKey)}
        initialValue={initialValue}
        rules={[
          {
            required: value.required,
            message: i18n.t('dop:this item cannot be empty'),
          },
        ]}
      >
        {input}
      </Item>
    );
    return inputField;
  };

  const getLabel = (label: string, labelTip: string) => {
    let _label: any = label;
    if (labelTip) {
      _label = (
        <span>
          {_label}&nbsp;
          <Tooltip title={labelTip}>
            <ErdaIcon type="help" size="14" className="mr-1 align-middle text-icon" />
          </Tooltip>
        </span>
      );
    }
    return _label;
  };

  const renderStructArray = (property: any, parentKey: string) => {
    if ((!editing && !property.value) || (!editing && property.value && !property.value.length)) {
      return null;
    }

    const addBtn = editing ? (
      <ErdaIcon
        type="plus"
        className="cursor-pointer align-middle"
        onClick={() => addNewItemToStructArray(property, property.struct[0])}
      />
    ) : null;
    // getFieldDecorator(`${parentKey}-data`, { initialValue: property.value || [] });
    const data = property.value || []; // getFieldValue(`${parentKey}-data`);
    const _val = form.getFieldsValue();
    const realData = get(_val, `${parentKey}`) || [];
    const content = data.map((item: any, index: number) => {
      const keys = Object.keys(item);
      const curItem = realData[index] || item;
      const nameKey = get(property.struct, '[0].name');
      const headName = curItem[nameKey] || (typeof curItem[keys[0]] === 'string' ? curItem[keys[0]] : 'module');
      if (typeof headName === 'object') {
        return (
          <div className="p-2 text-black-4">
            {i18n.t(
              'dop:Rendering multi-layer nested structures is not supported at this time, please go to text mode.',
            )}
          </div>
        );
      }

      const header = (
        <div className="flex items-center justify-between">
          <span className="truncate" title={headName}>
            {headName}
          </span>
          {editing ? (
            <CustomIcon
              onClick={() => deleteItemFromStructArray(property, index, parentKey)}
              className="icon-delete"
              type="sc1"
            />
          ) : null}
        </div>
      );
      return (
        <Panel key={`${parentKey}.${item.key}-${String(index)}`} header={header} forceRender>
          {renderResource({ data: property.struct }, `${parentKey}.[${index}]`, item)}
        </Panel>
      );
    });

    return (
      <div key={`${parentKey}`}>
        <span className="resource-input-group-title">
          {property.name}:{addBtn}
        </span>
        {data.length ? (
          <Collapse className="collapse-field my-2" accordion>
            {content}
          </Collapse>
        ) : null}
      </div>
    );
  };

  const deleteItemFromStructArray = (property: any, index: number, parentKey: string) => {
    if (!property.value) {
      // eslint-disable-next-line no-param-reassign
      property.value = [];
    }
    property.value.splice(index, 1);
    updater.resource(cloneDeep(resource));

    const formDatas = form.getFieldValue(`${parentKey}`.split('.'));
    formDatas?.splice(index, 1);
    const curFormData = form.getFieldsValue();
    set(curFormData, parentKey, formDatas);
    form.setFieldsValue(curFormData);
  };

  const addNewItemToStructArray = (property: any, struct: any) => {
    if (!property.value) {
      // eslint-disable-next-line no-param-reassign
      property.value = [];
    }
    property.value.push({
      [struct.name]: `module-${property.value.length + 1}`,
    });
    updater.resource(cloneDeep(resource));
  };

  const isObject = (inputType: string) => {
    return ['map', 'string_array', 'struct_array', 'struct'].includes(inputType);
  };

  const onSubmit = () => {
    form
      .validateFields()
      .then((values: any) => {
        let data = cloneDeep(values);
        const resources = head(filter(resource.data, (item) => item.name === 'resources'));
        const originResource = transform(
          get(resources, 'struct'),
          (result, item: { name: string; default: string | number }) => {
            const { name, default: d } = item;
            // eslint-disable-next-line no-param-reassign
            result[name] = +d;
          },
          {},
        );
        const editedResources = get(data, 'resource.resources') || {};
        forEach(Object.entries(editedResources), ([key, value]) => {
          editedResources[key] = +(value as string);
        });
        const isResourceDefault = isEqual(editedResources, originResource);

        if (isResourceDefault) {
          data = omit(data, ['resource.resources']);
        }

        const filledFieldsData = clearEmptyField(data);
        const resData = { ...filledFieldsData, action: chosenAction } as any;
        if (data.executionCondition) resData.executionCondition = data.executionCondition;
        handleSubmit(resData);
      })
      .catch(({ errorFields }: { errorFields: Array<{ name: any[]; errors: any[] }> }) => {
        form.scrollToField(errorFields[0].name);
      });
  };

  const clearEmptyField = (ObjData: any) => {
    const filledFields: string[] = [];
    const findData = (obj: any, parentArray: string[]) => {
      Object.keys(obj).forEach((key) => {
        const currentParent = [...parentArray, key];
        const value = get(obj, key);
        if (typeof value === 'object' && value !== null) {
          findData(value, currentParent);
        } else if (value || value === 0) {
          filledFields.push(currentParent.join('.'));
        }
      });
    };
    findData(ObjData, []);
    return pick(ObjData, filledFields);
  };

  const executionCondition = (
    <Item
      label={i18n.t('common:execution conditions')}
      name={'executionCondition'}
      initialValue={get(propsNodeData, 'if') || undefined}
      rules={[
        {
          required: false,
        },
      ]}
    >
      <Input disabled={!editing} placeholder={i18n.t('common:configure execution conditions')} />
    </Item>
  );

  const onValuesChange = () => {
    // use changeKey to tigger a rerender,
    updater.changeKey((prev: number) => prev + 1);
  };

  return (
    <Form form={form} onValuesChange={onValuesChange} layout="vertical" className="edit-service-container">
      {alert}
      {taskType}
      {loopData}
      {type ? taskName : null}
      {actionVersion}
      {executionCondition}

      {renderTaskTypeStructure()}
      {editing ? (
        <Button type="primary" ghost onClick={onSubmit}>
          {i18n.t('Save')}
        </Button>
      ) : null}
    </Form>
  );
}