antd#FormProps TypeScript Examples

The following examples show how to use antd#FormProps. 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: FormWizardStore.ts    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
setValidateMessages(validateMessages: FormProps['validateMessages']) {
        this.validateMessages = validateMessages;
    }
Example #2
Source File: FormWizardStore.ts    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
validateMessages: FormProps['validateMessages'] = undefined;
Example #3
Source File: SourceEditorFormConfigurationConfigurableFields.tsx    From jitsu with MIT License 5 votes vote down vote up
SourceEditorFormConfigurationConfigurableFields: React.FC<Props> = memo(
  ({
    initialValues,
    configParameters,
    availableOauthBackendSecrets,
    hideFields,
    patchConfig,
    setValidator,
    setFormReference,
  }) => {
    const [form] = Form.useForm()

    // get form fields from source connector

    const handleFormValuesChange = useCallback<(values: PlainObjectWithPrimitiveValues) => void>(values => {
      patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { resetErrorsCount: true })
    }, [])

    const handleFormValuesChangeForm: FormProps<PlainObjectWithPrimitiveValues>["onValuesChange"] = (_, values) => {
      patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { resetErrorsCount: true })
    }

    const handleSetInitialFormValues = useCallback<(values: PlainObjectWithPrimitiveValues) => void>(values => {
      patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { doNotSetStateChanged: true })
    }, [])

    /**
     * set validator and form reference on first render
     */
    useEffect(() => {
      const validateGetErrorsCount: ValidateGetErrorsCount = async () => {
        let errorsCount = 0
        try {
          await form.validateFields()
        } catch (error) {
          errorsCount = +error?.errorFields?.length
        }
        return errorsCount
      }

      setValidator(() => validateGetErrorsCount)
      setFormReference(CONFIG_FORM_KEY, form, handleFormValuesChange)
    }, [])

    return (
      <Form
        id={"SourceEditorFormConfigurationConfigurableFields"}
        form={form}
        onValuesChange={handleFormValuesChangeForm}
      >
        <ConfigurableFieldsForm
          form={form}
          initialValues={initialValues}
          fieldsParamsList={configParameters}
          availableOauthBackendSecrets={availableOauthBackendSecrets}
          hideFields={hideFields}
          setFormValues={handleFormValuesChange}
          setInitialFormValues={handleSetInitialFormValues}
        />
      </Form>
    )
  }
)
Example #4
Source File: create-add-on.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
class CreateAddOn extends PureComponent<ICreateAddOnProps & FormProps, any> {
  formRef = React.createRef<FormInstance>();

  state = {
    groups: [],
    packUpTabs: new Set(),
    searchValue: undefined,
    editAddon: null,
    selectedAddon: null,
    // 是否重置过, 重置过的就不显示警告了
    isReset: false,
    selectedAddonVersions: [],
    versionMap: {},
    selectedAddonPlans: [],
  };

  static getDerivedStateFromProps(nextProps: Readonly<ICreateAddOnProps>, prevState: any): any {
    const result = getGroupData(nextProps);
    if (
      !isEqual(nextProps.groupedAddonList, prevState.propsGroupedAddonList) ||
      !isEqual(result, prevState.editAddon)
    ) {
      const { groupedAddonList, groups } = convertData(nextProps.groupedAddonList);
      let addonResource: any = null;
      if (nextProps.addOn && result) {
        addonResource = groupedAddonList.find((item: IAddon) => {
          const compareVal = (result.name === 'custom' ? result.alias : result.name).toLowerCase();
          // custom的addon不能编辑alias,用alais比对
          return item.name.toLowerCase() === compareVal;
        });
      }

      const packUpTabs = new Set();
      groups.forEach((i: IAddonGroup) => {
        packUpTabs.add(i.groupName);
      });

      return {
        groups,
        packUpTabs,
        selectedAddon: addonResource,
        editAddon: result,
        propsGroupedAddonList: nextProps.groupedAddonList,
        originGroupedAddonList: groupedAddonList,
      };
    }

    return prevState;
  }

  componentDidMount() {
    const { selectedAddon } = this.state;
    if (selectedAddon) {
      this.getAddonVersions();
    }
  }

  componentDidUpdate(prevProps, prevState) {
    const { selectedAddon } = this.state;
    if (selectedAddon && selectedAddon !== prevState.selectedAddon) {
      this.getAddonVersions();
    }
  }

  render() {
    const { editing, addOn } = this.props;

    const { selectedAddon, searchValue, isReset } = this.state;
    let alert;

    if (addOn && !selectedAddon && !isReset) {
      alert = (
        <ErdaAlert
          className="addon-error-tag"
          message={
            editing
              ? i18n.t('dop:the current instance does not exist, please add it again!')
              : i18n.t('dop:yml-addon-not-exist-tip')
          }
          type="error"
        />
      );
    }
    let content = (
      <React.Fragment>
        <Input.Search
          autoFocus
          disabled={!editing}
          onFocus={this.onFocus}
          onClick={this.openSelect}
          onChange={this.searchInputChange}
          value={searchValue}
          className="add-on-input"
          placeholder={`${i18n.t('dop:Please select')} Add-on`}
        />
      </React.Fragment>
    );

    if (selectedAddon) {
      content = (
        <React.Fragment>
          <AddOn reselect={editing} reselectFunc={this.clear} addon={selectedAddon} />
        </React.Fragment>
      );
    }
    const className = selectedAddon ? 'selected-addon' : null;

    const showContent = (
      <>
        <div className="add-on-head">{content}</div>
        {!selectedAddon ? this.renderSelectContent() : this.renderForm()}
      </>
    );

    return (
      <div className={classnames('add-on-select', className)}>
        {alert ? (
          editing ? (
            <>
              {alert}
              {showContent}
            </>
          ) : (
            alert
          )
        ) : (
          showContent
        )}
      </div>
    );
  }

  private getAddonVersions = () => {
    const { selectedAddon } = this.state;
    if (selectedAddon.addonName || selectedAddon.name) {
      this.props.getAddonVersions(selectedAddon.addonName || selectedAddon.name).then((data) => {
        this.setState({
          selectedAddonVersions: map(data, (item) => item.version),
          versionMap: keyBy(data, 'version'),
          selectedAddonPlans: Object.keys(data[0].spec.plan || { basic: {} }),
        });
      });
    }
  };

  private renderForm = () => {
    const { selectedAddon, editAddon, selectedAddonVersions, versionMap, selectedAddonPlans } = this.state;
    const { cancel, editing } = this.props;
    const form = this.formRef.current || {};
    const { getFieldValue, setFieldsValue } = form;
    if (!selectedAddon) {
      return null;
    }

    let nameValue;
    let versionValue;
    let planValue;

    if (editAddon) {
      if (editAddon.version) {
        versionValue = editAddon.version;
      }

      nameValue = editAddon.alias || editAddon.name;

      if (editAddon.config) {
        planValue = editAddon.config;
      }

      if (selectedAddon && selectedAddonPlans.length && !planValue) {
        planValue = selectedAddonPlans[0];
      }

      if (selectedAddon && selectedAddonVersions.length && !versionValue) {
        versionValue = selectedAddonVersions[0];
      }
    } else if (selectedAddon && selectedAddon.instanceId) {
      // 如果是 addon 实例,则只读
      nameValue = selectedAddon.name;
      versionValue = selectedAddon.version;
      planValue = selectedAddon.plan;
    }

    const name = (
      <Item
        name="alias"
        label={i18n.t('Name')}
        initialValue={nameValue}
        rules={[
          {
            required: true,
            message: i18n.t('dop:Please enter the name'),
          },
        ]}
      >
        <Input autoFocus disabled={this.isEditing()} placeholder={i18n.t('dop:Please enter the name')} />
      </Item>
    );

    const version = (
      <Item
        name="version"
        label={i18n.t('Version')}
        initialValue={versionValue}
        rules={[
          {
            required: true,
            message: i18n.t('dop:please select the version'),
          },
        ]}
      >
        <Select
          disabled={this.isEditing()}
          className="w-full"
          placeholder={i18n.t('dop:please select the version')}
          onSelect={() => setFieldsValue?.({ plan: undefined })}
        >
          {selectedAddonVersions.map((v: string) => (
            <Option key={v}>{v}</Option>
          ))}
        </Select>
      </Item>
    );

    // @ts-ignore
    let plans = [];
    if (selectedAddon.plan) {
      plans.push({
        plan: planValue,
        planCnName: PLAN_NAME[planValue],
      });
    } else if (getFieldValue?.('version') && !isEmpty(versionMap)) {
      plans = map(versionMap[getFieldValue?.('version')].spec.plan || { basic: {} }, (_, k) => ({
        plan: k,
        planCnName: PLAN_NAME[k],
      }));
    } else if (selectedAddonPlans?.length) {
      plans = map(selectedAddonPlans, (k) => ({ plan: k, planCnName: PLAN_NAME[k] }));
    } else {
      plans = map({ basic: {} }, (_, k) => ({
        plan: k,
        planCnName: PLAN_NAME[k],
      }));
    }
    const plan = (
      <Item
        name="plan"
        label={i18n.t('dop:Configuration')}
        initialValue={convertAddonPlan(planValue)}
        rules={[
          {
            required: true,
            message: i18n.t('dop:please select configuration'),
          },
        ]}
      >
        <RadioGroup disabled={this.isEditing()}>
          {plans.map((p: any) => (
            <RadioButton key={p.plan} value={p.plan}>
              {p.planCnName}
            </RadioButton>
          ))}
        </RadioGroup>
      </Item>
    );

    return (
      <Form ref={this.formRef} layout="vertical" className="add-on-form">
        {name}
        {version}
        {plan}
        {editing ? (
          <Item className="add-on-form-btn-group">
            <Button className="mr-2" onClick={cancel}>
              {i18n.t('Cancel')}
            </Button>
            <Button type="primary" onClick={this.submitAddon}>
              {i18n.t('Save')}
            </Button>
          </Item>
        ) : null}
      </Form>
    );
  };

  private isEditing() {
    const { editing } = this.props;
    const { editAddon, selectedAddon } = this.state;

    return !editing || (editAddon && editAddon.instanceId) || (selectedAddon && selectedAddon.instanceId);
  }

  private submitAddon = () => {
    const { selectedAddon, editAddon } = this.state;
    const { onSubmit, cancel } = this.props;
    const form = this.formRef.current;

    form
      ?.validateFields()
      .then((values: any) => {
        onSubmit({
          ...values,
          originName: editAddon ? editAddon.alias : null,
          plan: `${selectedAddon.addonName || selectedAddon.name}:${values.plan}`,
        });
        cancel();
      })
      .catch(({ errorFields }: { errorFields: Array<{ name: any[]; errors: any[] }> }) => {
        form?.scrollToField(errorFields[0].name);
      });
  };

  private openSelect = () => {
    const { editing } = this.props;
    if (!editing) {
      return;
    }
    this.setState({
      isSelected: !this.state.isSelected,
    });
  };

  private onFocus = (e: any) => {
    const { editing } = this.props;
    if (!editing) {
      return;
    }
    e.stopPropagation();
  };

  private clear = () => {
    this.setState({
      isReset: true,
      searchValue: null,
      isSelected: false,
      selectedAddon: null,
    });
  };

  private renderSelectContent = () => {
    const { editing } = this.props;
    const { packUpTabs, groups, selectedAddon, searchValue } = this.state;

    return groups.map((group: IAddonGroup) => {
      let addonsContent = [];
      let headClass = 'empty-content';
      if (packUpTabs.has(group.groupName)) {
        addonsContent = group.data.map((addon: IAddon) => {
          if (searchValue && !addon.name.includes(searchValue) && !addon.displayName.includes(searchValue)) {
            return null;
          }

          headClass = null;
          let activeClass = null;
          // @ts-ignore
          if (selectedAddon && selectedAddon.instanceId === addon.instanceId) {
            activeClass = 'add-on-selected';
          }
          return (
            <AddOn
              editing={editing}
              className={activeClass}
              addon={addon}
              key={addon.instanceId || addon.id}
              onClick={this.selectedAddonAction}
            />
          );
        });
      }

      const icon = packUpTabs.has(group.groupName) ? (
        <ErdaIcon type="down" className="head-icon" size="18px" />
      ) : (
        <ErdaIcon type="up" className="head-icon" size="18px" />
      );

      const content = packUpTabs.has(group.groupName) ? <div className="addon-group-body">{addonsContent}</div> : null;

      return (
        <div key={group.groupName} className="yml-addon-group">
          <div
            className={classnames('addon-group-head', headClass)}
            onClick={() => this.triggerGroupTab(group.groupName)}
          >
            {icon}
            <span className="group-name">{group.groupName}</span>
          </div>
          {content}
        </div>
      );
    });
  };

  private triggerGroupTab = (name: string) => {
    const { packUpTabs } = this.state;

    if (packUpTabs.has(name)) {
      packUpTabs.delete(name);
    } else {
      packUpTabs.add(name);
    }

    this.setState({
      packUpTabs: cloneDeep(packUpTabs),
    });
  };

  private searchInputChange = (e: any) => {
    const { editing } = this.props;
    if (!editing) {
      return;
    }
    this.setState({
      searchValue: e.target.value,
    });
  };

  private selectedAddonAction = (addon: IAddon) => {
    this.setState({
      selectedAddon: addon,
      searchValue: addon.name,
    });
  };
}
Example #5
Source File: edit-global-variable.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
class EditGlobalVariable extends PureComponent<IEditGlobalVariableProps & FormProps, any> {
  formRef = React.createRef<FormInstance>();

  state = {
    globalVariableList: [],
  };

  UNSAFE_componentWillMount(): void {
    const list = convertGlobalVariableList(this.props.globalVariable);
    this.setState({
      globalVariableList: list.map((i: any) => ({
        ...i,
        id: uniqueId('var-'),
      })),
    });
  }

  render() {
    const { globalVariableList } = this.state;
    const { editing } = this.props;

    const content = globalVariableList.map((item: any) => {
      const input = <VariableInputGroup lock={false} disabled={!editing} onDelete={this.deleteVariable} />;
      return (
        <Item
          className="mr-0"
          key={item.key}
          name={item.id}
          initialValue={item}
          rules={[
            {
              required: true,
              message: i18n.t('dop:environment variables cannot be empty'),
            },
          ]}
        >
          {input}
        </Item>
      );
    });

    return (
      <Form ref={this.formRef} className="global-input-form">
        <div className="global-input-form-title">
          {i18n.t('dop:global environment variable')}
          {editing ? (
            <ErdaIcon type="plus" className="variable-icon cursor-pointer" onClick={this.addNewVariable} />
          ) : null}
        </div>
        {content}
        <div className="mt-3">
          {editing ? (
            <Button type="primary" ghost onClick={this.onSubmit}>
              {i18n.t('Save')}
            </Button>
          ) : null}
        </div>
      </Form>
    );
  }

  private deleteVariable = (key: string) => {
    const { globalVariableList } = this.state;

    const index = findIndex(globalVariableList, (item: any) => item.key === key);
    globalVariableList.splice(index, 1);

    this.setState({
      globalVariableList: cloneDeep(globalVariableList),
    });
  };

  private onSubmit = () => {
    const { onSubmit } = this.props;
    const form = this.formRef.current;

    form
      ?.validateFields()
      .then((values: any) => {
        const object = {};
        forEach(values, (item: any, originKey: string) => {
          if (item.key !== '') {
            object[item.key] = item.value;
          } else {
            form?.setFields({
              [originKey]: {
                value: item,
                errors: [new Error(i18n.t('dop:environment variables cannot be empty'))],
              },
            });
          }
        });
        onSubmit(object);
      })
      .catch(({ errorFields }: { errorFields: Array<{ name: any[]; errors: any[] }> }) => {
        form?.scrollToField(errorFields[0].name);
      });
  };

  private addNewVariable = () => {
    const { globalVariableList } = this.state;

    globalVariableList.push({
      id: uniqueId('var-'),
      key: 'key',
      value: 'value',
    });
    this.setState({
      globalVariableList: cloneDeep(globalVariableList),
    });
  };
}
Example #6
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 #7
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>
  );
}
Example #8
Source File: SourceEditorFormConfigurationConfigurableLoadableFields.tsx    From jitsu with MIT License 4 votes vote down vote up
SourceEditorFormConfigurationConfigurableLoadableFields: React.FC<Props> = memo(
  ({
    editorMode,
    initialValues,
    sourceDataFromCatalog,
    hideFields: _hideFields,
    patchConfig,
    handleSetControlsDisabled,
    handleSetTabsDisabled,
    setValidator,
    setFormReference,
    handleResetOauth,
    handleReloadStreams,
  }) => {
    const [form] = Form.useForm()
    const [availableAirbyteImageVersions, setAvailableAirbyteImageVersions] = useState<string[]>([])
    const airbyteImageVersion = useRef<string>(initialValues?.config?.image_version ?? "")

    const {
      isLoading: isLoadingParameters,
      data: fieldsParameters,
      error: loadingParametersError,
      reload: reloadParameters,
    } = usePolling<Parameter[]>(
      {
        configure: () => {
          const controlsDisableRequestId = uniqueId("configurableLoadableFields-")
          const imageVersion: string = airbyteImageVersion.current
          let availableImageVersions: string[] = []
          return {
            onBeforePollingStart: async () => {
              handleSetControlsDisabled(true, controlsDisableRequestId)
              editorMode === "edit" && handleSetTabsDisabled(["streams"], "disable")
              availableImageVersions = (await pullAvailableAirbyteImageVersions(sourceDataFromCatalog.id)) || []
              setAvailableAirbyteImageVersions(availableImageVersions)
            },
            pollingCallback: (end, fail) => async () => {
              try {
                const response = await pullAirbyteSpec(
                  sourceDataFromCatalog.id,
                  imageVersion || availableImageVersions[0]
                )
                if (response?.message) throw new Error(response?.message)
                if (response?.status && response?.status !== "pending") {
                  const result = transformAirbyteSpecResponse(response)
                  end(result)
                }
              } catch (error) {
                fail(error)
              }
            },
            onAfterPollingEnd: () => {
              handleSetControlsDisabled(false, controlsDisableRequestId)
              editorMode === "edit" && handleSetTabsDisabled(["streams"], "enable")
            },
          }
        },
      },
      { interval_ms: 2000 }
    )

    const hideFields = useMemo<string[]>(() => {
      if (!fieldsParameters) return _hideFields
      const oauthFieldsParametersNames = fieldsParameters.reduce<string[]>((result, current) => {
        if (current.type.typeName === "oauthSecret") result.push(current.id)
        return result
      }, [])
      return [..._hideFields, ...oauthFieldsParametersNames]
    }, [_hideFields, fieldsParameters])

    const handleFormValuesChange = useCallback(
      (values: PlainObjectWithPrimitiveValues): void => {
        patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { resetErrorsCount: true })
      },
      [patchConfig]
    )

    const handleFormValuesChangeForm = useCallback<FormProps<PlainObjectWithPrimitiveValues>["onValuesChange"]>(
      (changedValues, allValues) => {
        patchConfig(CONFIG_INTERNAL_STATE_KEY, allValues, { resetErrorsCount: true })
        handleIfAirbyteVersionChanged(changedValues)
      },
      [patchConfig]
    )

    const handleIfAirbyteVersionChanged = async (changedFormValues: PlainObjectWithPrimitiveValues): Promise<void> => {
      const newImageVersion = changedFormValues[AIRBYTE_IMAGE_VERSION_FIELD_ID]
      if (newImageVersion && typeof newImageVersion === "string") {
        airbyteImageVersion.current = newImageVersion
        handleResetOauth()
        await reloadParameters()
        handleReloadStreams()
      }
    }

    const handleSetInitialFormValues = useCallback(
      (values: PlainObjectWithPrimitiveValues): void => {
        patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { doNotSetStateChanged: true })
      },
      [patchConfig]
    )

    /**
     * set validator and form reference to parent component after the first render
     */
    useEffect(() => {
      const validateGetErrorsCount: ValidateGetErrorsCount = async () => {
        let errorsCount = 0
        try {
          await form.validateFields()
        } catch (error) {
          errorsCount = +error?.errorFields?.length
        }
        return errorsCount
      }

      setValidator(() => validateGetErrorsCount)
      setFormReference(CONFIG_FORM_KEY, form, handleFormValuesChange)
    }, [])

    return loadingParametersError ? (
      <Row>
        <Col span={4} />
        <Col span={20}>
          <ErrorCard
            title={`Failed to load the source specification data`}
            descriptionWithContacts={null}
            stackTrace={loadingParametersError.stack}
            className={`form-fields-card`}
          />
        </Col>
      </Row>
    ) : isLoadingParameters ? (
      <Row>
        <Col span={4} />
        <Col span={20}>
          <LoadableFieldsLoadingMessageCard
            title="Loading the source configuration"
            longLoadingMessage="Loading the spec takes longer than usual. This might happen if you are configuring such source for the first time - Jitsu will need some time to pull a docker image with the connector code"
            showLongLoadingMessageAfterMs={5000}
            className={`form-fields-card`}
          />
        </Col>
      </Row>
    ) : (
      <Form form={form} onValuesChange={handleFormValuesChangeForm}>
        <AirbyteVersionSelection
          key={`Stream Version Selection`}
          defaultValue={airbyteImageVersion.current}
          options={availableAirbyteImageVersions}
        />
        <ConfigurableFieldsForm
          fieldsParamsList={fieldsParameters || []}
          form={form}
          initialValues={initialValues}
          availableOauthBackendSecrets={"all_from_config"}
          hideFields={hideFields}
          setFormValues={handleFormValuesChange}
          setInitialFormValues={handleSetInitialFormValues}
        />
      </Form>
    )
  }
)
Example #9
Source File: SourceEditorFormConfigurationStaticFields.tsx    From jitsu with MIT License 4 votes vote down vote up
SourceEditorFormConfigurationStaticFields: React.FC<Props> = ({
  editorMode,
  initialValues,
  patchConfig,
  setValidator,
  setFormReference,
}) => {
  const [form] = AntdForm.useForm<FormFields>()
  const services = useServices()
  const subscription = services.currentSubscription?.currentPlan
  const sourcesList = sourcesStore.list

  const validateUniqueSourceId = (_, value: string) =>
    editorMode === "add" && sourcesList?.find((source: SourceData) => source.sourceId === value)
      ? Promise.reject("SourceId must be unique!")
      : Promise.resolve()

  const validateSourceIdNoSpaces = (_, value: string) => {
    const re = /^[A-Za-z0-9_-]*$/
    if (!re.test(value)) {
      return Promise.reject("SourceId must contain only letters, numbers, hyphens or the underscore character")
    } else {
      return Promise.resolve()
    }
  }

  const sourceIdValidationRules = useMemo<AntdFormItemValidationRule[]>(
    () => [
      { required: true, message: "SourceId is required field" },
      { validator: validateUniqueSourceId },
      { validator: validateSourceIdNoSpaces },
    ],
    []
  )

  const handleFormValuesChange: FormProps<PlainObjectWithPrimitiveValues>["onValuesChange"] = (_, values) => {
    patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { resetErrorsCount: true })
  }

  /**
   * set initial state, validator and form reference on first render
   */
  useEffect(() => {
    const validateGetErrorsCount: ValidateGetErrorsCount = async () => {
      let errorsCount = 0
      try {
        await form.validateFields()
      } catch (error) {
        errorsCount = +error?.errorFields?.length
      }
      return errorsCount
    }

    patchConfig(CONFIG_INTERNAL_STATE_KEY, form.getFieldsValue(), { doNotSetStateChanged: true })
    setValidator(() => validateGetErrorsCount)
    setFormReference(CONFIG_FORM_KEY, form)
  }, [])

  return (
    <AntdForm name="source-config" form={form} autoComplete="off" onValuesChange={handleFormValuesChange}>
      <Row key="sourceId">
        <Col span={24}>
          <AntdForm.Item
            initialValue={initialValues?.sourceId}
            className={`form-field_fixed-label ${editorStyles.field}`}
            label={<span className="w-full">SourceId</span>}
            name="sourceId"
            rules={sourceIdValidationRules}
            labelCol={{ span: 4 }}
            wrapperCol={{ span: 20 }}
          >
            <Input disabled={editorMode === "edit"} autoComplete="off" />
          </AntdForm.Item>
        </Col>
      </Row>

      <Row key="schedule">
        <Col span={24}>
          <AntdForm.Item
            initialValue={initialValues?.schedule}
            name="schedule"
            className={`form-field_fixed-label ${editorStyles.field}`}
            label={<span className="w-full">Schedule</span>}
            labelCol={{ span: 4 }}
            wrapperCol={{ span: 20 }}
            rules={[{ required: true, message: "You have to choose schedule" }]}
          >
            <Select>
              {COLLECTIONS_SCHEDULES.map(option => {
                const available = subscription ? subscription.quota.allowedSchedules.includes(option.id) : true
                return (
                  <Select.Option value={option.value} key={option.value} disabled={!available}>
                    {option.label}
                    {!available && " - n/a, upgrade plan"}
                  </Select.Option>
                )
              })}
            </Select>
          </AntdForm.Item>
        </Col>
      </Row>
    </AntdForm>
  )
}