@ant-design/icons#MinusCircleOutlined TypeScript Examples

The following examples show how to use @ant-design/icons#MinusCircleOutlined. 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: index.tsx    From drip-table with MIT License 6 votes vote down vote up
public renderFormItem(item: unknown, index: number) {
    return (
      <div className={styles['array-component-form-container']} key={index}>
        <div className={styles['array-component-left-container']}>
          { (this.props.schema['ui:props']?.items as DTGComponentPropertySchema[])
            .map((schema, i) => this.renderAttributeItem(schema, i, index)) }
        </div>
        <div className={styles['array-component-right-container']}>
          <Button
            icon={<PlusCircleOutlined />}
            shape="circle"
            size="small"
            onClick={() => {
              const currentValue = this.props.value?.slice() || [];
              currentValue.splice(index + 1, 0, { paramName: '', prefix: '', suffix: '' });
              this.props.onChange?.(currentValue);
            }}
          />
          <Button
            icon={<MinusCircleOutlined />}
            shape="circle"
            size="small"
            onClick={() => {
              const currentValue = this.props.value?.slice() || [];
              currentValue.splice(index, 1);
              this.props.onChange?.(currentValue);
            }}
          />
        </div>
      </div>
    );
  }
Example #2
Source File: index.tsx    From ant-simple-draw with MIT License 6 votes vote down vote up
TaskList: FC<{ keyName: string }> = memo(({ keyName }) => {
  return (
    <>
      <Form.List name={[keyName, 'list']}>
        {(fields, { add, remove }) => (
          <>
            {fields.map(({ key, name, ...restField }) => (
              <Space
                key={key}
                style={{ display: 'flex', marginBottom: 8, justifyContent: 'space-around' }}
                align="baseline"
              >
                <Form.Item
                  {...restField}
                  name={[name, 'check']}
                  style={{ marginBottom: '16px' }}
                  valuePropName="checked"
                >
                  <Checkbox></Checkbox>
                </Form.Item>
                <Form.Item {...restField} name={[name, 'text']} style={{ marginBottom: '16px' }}>
                  <Input />
                </Form.Item>
                <MinusCircleOutlined onClick={() => remove(name)} title="移除" />
              </Space>
            ))}
            <Form.Item style={{ marginBottom: '16px' }}>
              <Button onClick={() => add()} block icon={<PlusOutlined />}>
                添加数据
              </Button>
            </Form.Item>
          </>
        )}
      </Form.List>
    </>
  );
})
Example #3
Source File: tagItem.tsx    From fe-v5 with Apache License 2.0 5 votes vote down vote up
TagItem: React.FC<Itag> = ({ field, remove, form }) => {
  const { t } = useTranslation();
  const [valuePlaceholder, setValuePlaceholder] = useState<string>('');
  const [funcCur, setfuncCur] = useState('==');

  useEffect(() => {
    const tags = form.getFieldValue('tags');
    funcChange(tags[field.name].func);
  }, [field]);

  const funcChange = (val) => {
    let text = '';
    if (val === 'in') {
      text = '可以输入多个值,用回车分割';
    } else if (val === '=~') {
      text = '请输入正则表达式匹配标签value';
    }
    setfuncCur(val);
    setValuePlaceholder(text);
  };
  return (
    <>
      <Row gutter={[10, 10]} style={{ marginBottom: '10px' }}>
        <Col span={5}>
          <Form.Item style={{ marginBottom: 0 }} name={[field.name, 'key']} fieldKey={[field.name, 'key']} rules={[{ required: true, message: t('key不能为空') }]}>
            <Input placeholder={t('请输入屏蔽事件标签key')} />
          </Form.Item>
        </Col>
        <Col span={3}>
          <Form.Item style={{ marginBottom: 0 }} name={[field.name, 'func']} fieldKey={[field.name, 'func']} initialValue='=='>
            <Select suffixIcon={<CaretDownOutlined />} onChange={funcChange}>
              <Option value='=='>==</Option>
              <Option value='=~'>=~</Option>
              <Option value='in'>in</Option>
            </Select>
          </Form.Item>
        </Col>
        <Col span={15}>
          <Form.Item style={{ marginBottom: 0 }} name={[field.name, 'value']} fieldKey={[field.name, 'value']} rules={[{ required: true, message: t('value不能为空') }]}>
            {funcCur == 'in' ? (
              <Select mode='tags' open={false} style={{ width: '100%' }} placeholder={t(valuePlaceholder)} tokenSeparators={[' ']}></Select>
            ) : (
              <Input className='ant-input' placeholder={t(valuePlaceholder)} />
            )}
          </Form.Item>
        </Col>
        <Col>
          <MinusCircleOutlined style={{ marginTop: '8px' }} onClick={() => remove(field.name)} />
        </Col>
      </Row>
    </>
  );
}
Example #4
Source File: NotificationsSettings.tsx    From office-hours with GNU General Public License v3.0 5 votes vote down vote up
function DeviceNotifPanel() {
  const thisEndpoint = useThisDeviceEndpoint();
  const { data: profile, mutate } = useSWR(`api/v1/profile`, async () =>
    API.profile.index()
  );
  const thisDesktopNotif = profile?.desktopNotifs?.find(
    (dn) => dn.endpoint === thisEndpoint
  );
  return (
    <div>
      <DeviceAddHeader>
        <h3>Your Devices</h3>
        {!thisDesktopNotif && (
          <Tooltip
            title={
              getNotificationState() ===
                NotificationStates.browserUnsupported &&
              "Browser does not support notifications. Please use Chrome or Firefox, and not Incognito Mode."
            }
          >
            <Button
              onClick={async () => {
                const canNotify = await requestNotificationPermission();
                if (canNotify === NotificationStates.notAllowed) {
                  message.warning("Please allow notifications in this browser");
                }
                if (canNotify === NotificationStates.granted) {
                  await registerNotificationSubscription();
                  mutate();
                }
              }}
              disabled={
                getNotificationState() === NotificationStates.browserUnsupported
              }
              style={{ marginBottom: "4px" }}
            >
              Add This Device
            </Button>
          </Tooltip>
        )}
      </DeviceAddHeader>
      <List
        bordered
        dataSource={profile.desktopNotifs}
        locale={{ emptyText: "No Devices Registered To Receive Notifications" }}
        renderItem={(device: DesktopNotifPartial) => (
          <List.Item
            actions={[
              <MinusCircleOutlined
                style={{ fontSize: "20px" }}
                key={0}
                onClick={async () => {
                  await API.notif.desktop.unregister(device.id);
                  mutate();
                }}
              />,
            ]}
          >
            <List.Item.Meta
              title={renderDeviceInfo(device, device.endpoint === thisEndpoint)}
              description={`Registered ${device.createdAt.toLocaleDateString()}`}
            />
          </List.Item>
        )}
      />
    </div>
  );
}
Example #5
Source File: RelationTypeFilter.tsx    From datart with Apache License 2.0 5 votes vote down vote up
ConditionCol: FC<{
  condition: ChartFilterCondition;
  fields: ChartDataSectionField[];
  onRelationChange: (relation?: ChartFilterCondition) => void;
}> = ({ condition, fields, onRelationChange }) => {
  const onAddSubRelation = () => {
    const newCondition = new ConditionBuilder(condition)
      .setOperator(FilterRelationType.AND)
      .asRelation(condition.value, [
        new ChartFilterCondition(),
        new ChartFilterCondition(),
      ]);
    onRelationChange(newCondition);
  };

  const onDeleteRelation = () => {
    onRelationChange(undefined);
  };

  const handleOperatorChange = operator => {
    const newCondition = new ConditionBuilder(condition)
      .setOperator(operator)
      .asRelation();
    onRelationChange(newCondition);
  };

  const handleConditionChange = value => {
    const newCondition = new ConditionBuilder(condition)
      .setValue(value)
      .asRelation();
    onRelationChange(newCondition);
  };

  if (condition.type === FilterConditionType.Relation) {
    return (
      <StyledRelationCol>
        <Row>
          <OperatorCol
            operator={condition.operator}
            onOperatorChange={handleOperatorChange}
          />
          <MinusCircleOutlined onClick={onDeleteRelation} />
        </Row>
        <Row>
          <RelationTypeFilter
            condition={condition}
            fields={fields}
            showOperator={false}
            onRelationChange={onRelationChange}
          />
        </Row>
      </StyledRelationCol>
    );
  }

  return (
    <StyledRelationCol>
      <Row>
        <Select
          value={condition?.value as string}
          onChange={handleConditionChange}
        >
          {fields.map(c => (
            <Select.Option value={c.colName}>
              {getColumnRenderName(c)}
            </Select.Option>
          ))}
        </Select>
        <BranchesOutlined onClick={onAddSubRelation} />
      </Row>
    </StyledRelationCol>
  );
}
Example #6
Source File: index.tsx    From fe-v5 with Apache License 2.0 5 votes vote down vote up
export default function index({ targets }) {
  const namePrefix = ['overrides'];

  return (
    <Form.List name={namePrefix}>
      {(fields, { add, remove }) => (
        <>
          {fields.map(({ key, name, ...restField }) => {
            return (
              <Panel
                isInner
                header='override'
                extra={
                  <Space>
                    <PlusCircleOutlined
                      onClick={() => {
                        add({
                          type: 'special',
                        });
                      }}
                    />
                    {fields.length > 1 && (
                      <MinusCircleOutlined
                        onClick={() => {
                          remove(name);
                        }}
                      />
                    )}
                  </Space>
                }
              >
                <Form.Item label='查询条件名称' {...restField} name={[name, 'matcher', 'value']}>
                  <Select suffixIcon={<CaretDownOutlined />} allowClear>
                    {_.map(targets, (target) => {
                      return (
                        <Select.Option key={target.refId} value={target.refId}>
                          {target.refId}
                        </Select.Option>
                      );
                    })}
                  </Select>
                </Form.Item>
                <ValueMappings preNamePrefix={namePrefix} namePrefix={[name, 'properties', 'valueMappings']} />
                <StandardOptions preNamePrefix={namePrefix} namePrefix={[name, 'properties', 'standardOptions']} />
              </Panel>
            );
          })}
        </>
      )}
    </Form.List>
  );
}
Example #7
Source File: index.tsx    From imove with MIT License 5 votes vote down vote up
VisualFormItem: React.FC<IVisualFormItemProps> = (props) => {
  const { type, desc } = props;

  return (
    <React.Fragment>
      <div className={styles.itemHeader}>
        <span className={styles.itemTitleText}>{type}</span>
        <span className={styles.itemDescText}>{desc}</span>
      </div>
      <Form.List name={type}>
        {(fields, { add, remove }) => (
          <React.Fragment>
            {fields.map((field) => (
              <Space key={field.key} align={'baseline'}>
                <Form.Item
                  {...field}
                  label={'key'}
                  name={[field.name, 'key']}
                  fieldKey={[field.fieldKey, 'key']}
                >
                  <Input />
                </Form.Item>
                <Form.Item
                  {...field}
                  label={'value'}
                  name={[field.name, 'value']}
                  fieldKey={[field.fieldKey, 'value']}
                >
                  <Input />
                </Form.Item>
                <MinusCircleOutlined onClick={() => remove(field.name)} />
              </Space>
            ))}
            <Form.Item>
              <Button
                block={true}
                type={'dashed'}
                icon={<PlusOutlined />}
                onClick={() => add()}
              >
                新增
              </Button>
            </Form.Item>
          </React.Fragment>
        )}
      </Form.List>
    </React.Fragment>
  );
}
Example #8
Source File: index.tsx    From fe-v5 with Apache License 2.0 5 votes vote down vote up
export default function index({ targets }) {
  const namePrefix = ['overrides'];

  return (
    <Form.List name={namePrefix}>
      {(fields, { add, remove }) =>
        fields.map(({ key, name, ...restField }) => {
          return (
            <Panel
              key={key}
              isInner
              header='override'
              extra={
                <Space>
                  <PlusCircleOutlined
                    onClick={() => {
                      add({
                        type: 'special',
                      });
                    }}
                  />
                  {fields.length > 1 && (
                    <MinusCircleOutlined
                      onClick={() => {
                        remove(name);
                      }}
                    />
                  )}
                </Space>
              }
            >
              <Form.Item label='查询条件名称' {...restField} name={[name, 'matcher', 'value']}>
                <Select suffixIcon={<CaretDownOutlined />} allowClear>
                  {_.map(targets, (target) => {
                    return (
                      <Select.Option key={target.refId} value={target.refId}>
                        {target.refId}
                      </Select.Option>
                    );
                  })}
                </Select>
              </Form.Item>
              <ValueMappings preNamePrefix={namePrefix} namePrefix={[name, 'properties', 'valueMappings']} />
              <StandardOptions preNamePrefix={namePrefix} namePrefix={[name, 'properties', 'standardOptions']} />
            </Panel>
          );
        })
      }
    </Form.List>
  );
}
Example #9
Source File: tagItem.tsx    From fe-v5 with Apache License 2.0 5 votes vote down vote up
TagItem: React.FC<Itag> = ({ field, remove, form }) => {
  const { t } = useTranslation();
  const [valuePlaceholder, setValuePlaceholder] = useState<string>('');
  const [funcCur, setfuncCur] = useState('==');

  useEffect(() => {
    const tags = form.getFieldValue('tags');
    funcChange(tags[field.name].func);
  }, []);

  const funcChange = (val) => {
    let text = '';
    if (val === 'in') {
      text = '可以输入多个值,用回车分割';
    } else if (val === '=~') {
      text = '请输入正则表达式匹配标签value';
    }
    setfuncCur(val);
    setValuePlaceholder(text);
  };
  return (
    <>
      <Row gutter={[10, 10]} style={{ marginBottom: '10px' }}>
        <Col span={5}>
          <Form.Item style={{ marginBottom: 0 }} name={[field.name, 'key']} fieldKey={[field.name, 'key']} rules={[{ required: true, message: t('key不能为空') }]}>
            <Input placeholder={t('请输入订阅事件标签key')} />
          </Form.Item>
        </Col>
        <Col span={3}>
          <Form.Item style={{ marginBottom: 0 }} name={[field.name, 'func']} fieldKey={[field.name, 'func']} initialValue='=='>
            <Select suffixIcon={<CaretDownOutlined />} onChange={funcChange}>
              <Option value='=='>==</Option>
              <Option value='=~'>=~</Option>
              <Option value='in'>in</Option>
            </Select>
          </Form.Item>
        </Col>
        <Col span={15}>
          <Form.Item style={{ marginBottom: 0 }} name={[field.name, 'value']} fieldKey={[field.name, 'value']} rules={[{ required: true, message: t('value不能为空') }]}>
            {funcCur == 'in' ? (
              <Select mode='tags' open={false} style={{ width: '100%' }} placeholder={t(valuePlaceholder)} tokenSeparators={[' ']}></Select>
            ) : (
              <Input className='ant-input' placeholder={t(valuePlaceholder)} />
            )}
          </Form.Item>
        </Col>
        <Col>
          <MinusCircleOutlined style={{ marginTop: '8px' }} onClick={() => remove(field.name)} />
        </Col>
      </Row>
    </>
  );
}
Example #10
Source File: index.tsx    From ant-simple-draw with MIT License 5 votes vote down vote up
OlUl: FC<{ keyName: string; form: FormInstance<Store>; showEditPropsData: any }> = memo(
  ({ keyName, form, showEditPropsData }) => {
    const [type, setType] = useState<string | undefined>(undefined);
    const ulType = [
      { label: '小圆点', value: 'disc' },
      { label: '空心圆圈', value: 'circle' },
      { label: '小方块', value: 'square' },
    ];
    const olType = [
      { label: '1', value: '1' },
      { label: 'A', value: 'A' },
      { label: 'I', value: 'I' },
    ];

    useEffect(() => {
      if (showEditPropsData[keyName]) {
        // 将数据流的数据同步一下
        setType(showEditPropsData[keyName].type);
      }
    }, [showEditPropsData, keyName]);

    const selectData = useMemo(() => {
      return type === 'ol' ? olType : ulType;
    }, [type]);

    return (
      <>
        <Form.Item label={null} name={[keyName, 'type']} style={{ marginBottom: '16px' }}>
          <Radio.Group
            onChange={(val) => {
              setType(val.target.value);
              form.setFieldsValue({
                [keyName]: {
                  attrType: undefined,
                },
              });
            }}
          >
            <Radio value={'ol'}>有序列表</Radio>
            <Radio value={'ul'}>无序列表</Radio>
          </Radio.Group>
        </Form.Item>
        <Form.Item label={'序列'} name={[keyName, 'attrType']} style={{ marginBottom: '16px' }}>
          <Selects data={selectData} valKey="value" valName="label" />
        </Form.Item>
        <Form.List name={[keyName, 'list']}>
          {(fields, { add, remove }) => (
            <>
              {fields.map(({ key, name, ...restField }) => (
                <Space
                  key={key}
                  style={{ display: 'flex', marginBottom: 8, justifyContent: 'space-around' }}
                  align="baseline"
                >
                  <Form.Item {...restField} name={[name, 'text']} style={{ marginBottom: '16px' }}>
                    <Input />
                  </Form.Item>
                  <MinusCircleOutlined onClick={() => remove(name)} title="移除" />
                </Space>
              ))}
              <Form.Item style={{ marginBottom: '16px' }}>
                <Button onClick={() => add()} block icon={<PlusOutlined />}>
                  添加数据
                </Button>
              </Form.Item>
            </>
          )}
        </Form.List>
      </>
    );
  },
)
Example #11
Source File: index.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
UserForm = React.forwardRef<ReactNode, UserAndPasswordFormProps>((props, ref) => {
  const { t } = useTranslation();
  const { userId } = props;
  const [form] = Form.useForm();
  const [initialValues, setInitialValues] = useState<User>();
  const [loading, setLoading] = useState<boolean>(true);
  const [contactsList, setContactsList] = useState<ContactsItem[]>([]);
  const [roleList, setRoleList] = useState<{ name: string; note: string }[]>([]);

  useImperativeHandle(ref, () => ({
    form: form,
  }));
  useEffect(() => {
    if (userId) {
      getUserInfoDetail(userId);
    } else {
      setLoading(false);
    }

    getContacts();
    getRoles().then((res) => setRoleList(res));
  }, []);

  const getContacts = () => {
    getNotifyChannels().then((data: Array<ContactsItem>) => {
      setContactsList(data);
    });
  };

  const getUserInfoDetail = (id: string) => {
    getUserInfo(id).then((data: User) => {
      let contacts: Array<Contacts> = [];

      if (data.contacts) {
        Object.keys(data.contacts).forEach((item: string) => {
          let val: Contacts = {
            key: item,
            value: data.contacts[item],
          };
          contacts.push(val);
        });
      }

      setInitialValues(
        Object.assign({}, data, {
          contacts,
        }),
      );
      setLoading(false);
    });
  };

  return !loading ? (
    <Form {...layout} form={form} initialValues={initialValues} preserve={false}>
      {!userId && (
        <Form.Item
          label={t('用户名')}
          name='username'
          rules={[
            {
              required: true,
              message: t('用户名不能为空!'),
            },
          ]}
        >
          <Input />
        </Form.Item>
      )}
      <Form.Item label={t('显示名')} name='nickname'>
        <Input />
      </Form.Item>
      {!userId && (
        <>
          <Form.Item
            name='password'
            label={t('密码')}
            rules={[
              {
                required: true,
                message: t('请输入密码!'),
              },
            ]}
            hasFeedback
          >
            <Input.Password />
          </Form.Item>

          <Form.Item
            name='confirm'
            label={t('确认密码')}
            dependencies={['password']}
            hasFeedback
            rules={[
              {
                required: true,
                message: t('请确认密码!'),
              },
              ({ getFieldValue }) => ({
                validator(_, value) {
                  if (!value || getFieldValue('password') === value) {
                    return Promise.resolve();
                  }

                  return Promise.reject(new Error('密码不一致!'));
                },
              }),
            ]}
          >
            <Input.Password />
          </Form.Item>
        </>
      )}
      <Form.Item
        label={t('角色')}
        name='roles'
        rules={[
          {
            required: true,
            message: t('角色不能为空!'),
          },
        ]}
      >
        <Select mode='multiple'>
          {roleList.map((item, index) => (
            <Option value={item.name} key={index}>
              <div>
                <div>{item.name}</div>
                <div style={{ color: '#8c8c8c' }}>{item.note}</div>
              </div>
            </Option>
          ))}
        </Select>
      </Form.Item>
      <Form.Item label={t('邮箱')} name='email'>
        <Input />
      </Form.Item>
      <Form.Item label={t('手机')} name='phone'>
        <Input />
      </Form.Item>
      <Form.Item label={t('更多联系方式')}>
        <Form.List name='contacts'>
          {(fields, { add, remove }) => (
            <>
              {fields.map(({ key, name, fieldKey, ...restField }) => (
                <Space
                  key={key}
                  style={{
                    display: 'flex',
                  }}
                  align='baseline'
                >
                  <Form.Item
                    style={{
                      width: '180px',
                    }}
                    {...restField}
                    name={[name, 'key']}
                    fieldKey={[fieldKey, 'key']}
                    rules={[
                      {
                        required: true,
                        message: t('联系方式不能为空'),
                      },
                    ]}
                  >
                    <Select suffixIcon={<CaretDownOutlined />} placeholder={t('请选择联系方式')}>
                      {contactsList.map((item, index) => (
                        <Option value={item.key} key={index}>
                          {item.label}
                        </Option>
                      ))}
                    </Select>
                  </Form.Item>
                  <Form.Item
                    {...restField}
                    style={{
                      width: '170px',
                    }}
                    name={[name, 'value']}
                    fieldKey={[fieldKey, 'value']}
                    rules={[
                      {
                        required: true,
                        message: t('值不能为空'),
                      },
                    ]}
                  >
                    <Input placeholder={t('请输入值')} />
                  </Form.Item>
                  <MinusCircleOutlined className='control-icon-normal' onClick={() => remove(name)} />
                </Space>
              ))}
              <PlusCircleOutlined className='control-icon-normal' onClick={() => add()} />
            </>
          )}
        </Form.List>
      </Form.Item>
    </Form>
  ) : null;
})
Example #12
Source File: Form.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
function FormCpt(props: ModalWrapProps & IProps) {
  const { t } = useTranslation();
  const { action, visible, initialValues, destroy, range, onOk, admin } = props;
  const [form] = Form.useForm();
  const [labels, setLabels] = useState<string[]>([]);
  const [filteredLabels, setFilteredLabels] = useState<string[]>([]);
  const [previewVisible, setPreviewVisible] = useState(false);
  const [previewLoading, setPreviewLoading] = useState(false);
  const [previewData, setPreviewData] = useState([]);
  const [activeKey, setActiveKey] = useState('form');
  const getLablesOptions = (_labels) => {
    return _.map(_labels, (label) => {
      return (
        <Select.Option key={label} value={label}>
          {label}
        </Select.Option>
      );
    });
  };

  useEffect(() => {
    getLabels('', range).then((res) => {
      setLabels(res);
      setFilteredLabels(res);
    });
  }, [JSON.stringify(range)]);

  return (
    <Modal
      className='n9e-metric-views-modal'
      title={
        <Tabs className='custom-import-title' activeKey={activeKey} onChange={setActiveKey}>
          <TabPane tab={titleMap[action]} key='form' />
          {action === 'add' && <TabPane tab='导入快捷视图' key='import' />}
        </Tabs>
      }
      visible={visible}
      onCancel={() => {
        destroy();
      }}
      onOk={() => {
        form.validateFields().then((values) => {
          let _values = _.cloneDeep(values);
          if (activeKey === 'form') {
            _values.dynamicLabels = _.map(_values.dynamicLabels, (item) => {
              return {
                label: item,
                value: '',
              };
            });
            _values.dimensionLabels = _.map(_values.dimensionLabels, (item) => {
              return {
                label: item,
                value: '',
              };
            });
          }
          if (activeKey === 'import') {
            try {
              const config = JSON.parse(values.import);
              _values = {
                name: values.name,
                cate: values.cate,
                ...config,
              };
            } catch (e) {
              console.log(e);
              return;
            }
          }
          const { name, cate } = _values;
          const configs = JSON.stringify(_.omit(_values, ['name', 'cate']));
          const data: any = {
            name,
            cate: cate ? 0 : 1,
            configs,
          };
          if (action === 'add') {
            addMetricView(data).then((res) => {
              message.success('添加成功');
              onOk(res);
              destroy();
            });
          } else if (action === 'edit') {
            data.id = initialValues.id;
            updateMetricView(data).then(() => {
              message.success('修改成功');
              onOk();
              destroy();
            });
          }
        });
      }}
    >
      {activeKey === 'form' && (
        <Form
          layout='vertical'
          initialValues={
            initialValues || {
              cate: false,
            }
          }
          form={form}
          onValuesChange={(changedValues, allValues) => {
            if (changedValues.filters) {
              const filtersStr = getFiltersStr(allValues.filters);
              getLabels(`${filtersStr ? `{${filtersStr}}` : ''}`, range).then((res) => {
                setFilteredLabels(res);
              });
            }
          }}
        >
          <Form.Item label='视图名称' name='name' rules={[{ required: true }]}>
            <Input />
          </Form.Item>
          {admin && (
            <Form.Item label='是否公开' name='cate' rules={[{ required: true }]} valuePropName='checked'>
              <Switch />
            </Form.Item>
          )}
          <Form.List name='filters'>
            {(fields, { add, remove }) => (
              <>
                <div style={{ paddingBottom: 8 }}>
                  前置过滤条件{' '}
                  <PlusCircleOutlined
                    onClick={() => {
                      add({
                        oper: '=',
                      });
                    }}
                  />
                </div>
                {fields.map(({ key, name }) => {
                  return (
                    <Space key={key}>
                      <Form.Item name={[name, 'label']} rules={[{ required: true }]}>
                        <Select suffixIcon={<CaretDownOutlined />} allowClear showSearch style={{ width: 170 }}>
                          {getLablesOptions(labels)}
                        </Select>
                      </Form.Item>
                      <Form.Item name={[name, 'oper']} rules={[{ required: true }]}>
                        <Select suffixIcon={<CaretDownOutlined />} style={{ width: 60 }}>
                          <Select.Option value='='>=</Select.Option>
                          <Select.Option value='!='>!=</Select.Option>
                          <Select.Option value='=~'>=~</Select.Option>
                          <Select.Option value='!~'>!~</Select.Option>
                        </Select>
                      </Form.Item>
                      <Form.Item name={[name, 'value']} rules={[{ required: true }]}>
                        <Input style={{ width: 200 }} />
                      </Form.Item>
                      <Form.Item>
                        <MinusCircleOutlined
                          onClick={() => {
                            remove(name);
                          }}
                        />
                      </Form.Item>
                    </Space>
                  );
                })}
              </>
            )}
          </Form.List>
          <Form.Item label='动态过滤标签' name='dynamicLabels'>
            <Select allowClear showSearch mode='multiple'>
              {getLablesOptions(filteredLabels)}
            </Select>
          </Form.Item>
          <Form.Item label='展开维度标签' name='dimensionLabels' rules={[{ required: true }]}>
            <Select allowClear showSearch mode='multiple'>
              {getLablesOptions(filteredLabels)}
            </Select>
          </Form.Item>
          <div style={{ textAlign: 'right', marginBottom: 10 }}>
            <Button
              onClick={() => {
                const values = form.getFieldsValue();
                setPreviewVisible(true);
                setPreviewLoading(true);
                const filtersStr = getFiltersStr(values.filters);
                const _labels = _.compact(_.concat(values.dynamicLabels, values.dimensionLabels));
                const requests = _.map(_labels, (item) => {
                  return getLabelValues(item, range, filtersStr ? `{${filtersStr}}` : '');
                });
                Promise.all(requests).then((res) => {
                  const data = _.map(_labels, (item, idx) => {
                    return {
                      label: item,
                      values: res[idx],
                    };
                  });
                  setPreviewData(data);
                  setPreviewLoading(false);
                });
              }}
            >
              预览
            </Button>
          </div>
          {previewVisible && (
            <Table
              size='small'
              rowKey='label'
              columns={[
                {
                  title: 'Lable Key',
                  dataIndex: 'label',
                },
                {
                  title: 'Lable Value 数量',
                  dataIndex: 'values',
                  render: (text) => {
                    return text.length;
                  },
                },
                {
                  title: 'Lable Value 样例',
                  dataIndex: 'values',
                  render: (text) => {
                    return (
                      <Tooltip
                        placement='right'
                        title={
                          <div>
                            {_.map(text, (item) => {
                              return <div key={item}>{item}</div>;
                            })}
                          </div>
                        }
                      >{`${_.head(text)}...`}</Tooltip>
                    );
                  },
                },
              ]}
              dataSource={previewData}
              loading={previewLoading}
            />
          )}
        </Form>
      )}
      {activeKey === 'import' && (
        <Form
          form={form}
          preserve={false}
          layout='vertical'
          initialValues={
            initialValues || {
              cate: false,
            }
          }
        >
          <Form.Item label='视图名称' name='name' rules={[{ required: true }]}>
            <Input />
          </Form.Item>
          {admin && (
            <Form.Item label='是否公开' name='cate' rules={[{ required: true }]} valuePropName='checked'>
              <Switch />
            </Form.Item>
          )}
          <Form.Item
            label='配置JSON:'
            name='import'
            rules={[
              {
                required: true,
                message: t('请输入配置'),
                validateTrigger: 'trigger',
              },
            ]}
          >
            <Input.TextArea className='code-area' placeholder={t('请输入配置')} rows={4} />
          </Form.Item>
        </Form>
      )}
    </Modal>
  );
}
Example #13
Source File: editModal.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
editModal: React.FC<Props> = ({ isModalVisible, editModalFinish }) => {
  const { t, i18n } = useTranslation();

  const [form] = Form.useForm();
  const { clusters: clusterList } = useSelector<RootState, CommonStoreState>((state) => state.common);

  const [contactList, setInitContactList] = useState([]);
  const [notifyGroups, setNotifyGroups] = useState([]);

  const [field, setField] = useState<string>('cluster');
  const [refresh, setRefresh] = useState(true);

  useEffect(() => {
    getNotifyChannel();
    getGroups('');

    return () => {};
  }, []);

  const enableDaysOfWeekOptions = [t('周日'), t('周一'), t('周二'), t('周三'), t('周四'), t('周五'), t('周六')].map((v, i) => {
    return <Option value={String(i)} key={i}>{`${v}`}</Option>;
  });

  const contactListCheckboxes = contactList.map((c: { key: string; label: string }) => (
    <Checkbox value={c.key} key={c.label}>
      {c.label}
    </Checkbox>
  ));

  const notifyGroupsOptions = notifyGroups.map((ng: any) => (
    <Option value={ng.id} key={ng.id}>
      {ng.name}
    </Option>
  ));

  const getNotifyChannel = async () => {
    const res = await getNotifiesList();
    let contactList = res || [];
    setInitContactList(contactList);
  };

  const getGroups = async (str) => {
    const res = await getTeamInfoList({ query: str });
    const data = res.dat || res;
    setNotifyGroups(data || []);
  };

  const debounceFetcher = useCallback(debounce(getGroups, 800), []);

  const modelOk = () => {
    form.validateFields().then(async (values) => {
      const data = { ...values };
      switch (values.field) {
        case 'enable_time':
          data.enable_stime = values.enable_time[0].format('HH:mm');
          data.enable_etime = values.enable_time[1].format('HH:mm');
          delete data.enable_time;
          break;
        case 'disabled':
          data.disabled = !values.enable_status ? 1 : 0;
          delete data.enable_status;
          break;
        case 'enable_in_bg':
          data.enable_in_bg = values.enable_in_bg ? 1 : 0;
          break;
        case 'callbacks':
          data.callbacks = values.callbacks.map((item) => item.url);
          break;
        case 'notify_recovered':
          data.notify_recovered = values.notify_recovered ? 1 : 0;
          break;
        default:
          break;
      }
      delete data.field;
      Object.keys(data).forEach((key) => {
        // 因为功能上有清除备注的需求,需要支持传空
        if (data[key] === undefined) {
          data[key] = '';
        }
        if (Array.isArray(data[key])) {
          data[key] = data[key].join(' ');
        }
      });
      editModalFinish(true, data);
    });
  };

  const editModalClose = () => {
    editModalFinish(false);
  };

  const fieldChange = (val) => {
    setField(val);
  };

  return (
    <>
      <Modal
        title={t('批量更新')}
        visible={isModalVisible}
        onOk={modelOk}
        onCancel={() => {
          editModalClose();
        }}
      >
        <Form
          {...layout}
          form={form}
          className='strategy-form'
          layout={refresh ? 'horizontal' : 'horizontal'}
          initialValues={{
            prom_eval_interval: 15,
            disabled: 0, // 0:立即启用 1:禁用
            enable_status: true, // true:立即启用 false:禁用
            notify_recovered: 1, // 1:启用
            enable_time: [moment('00:00', 'HH:mm'), moment('23:59', 'HH:mm')],
            cluster: clusterList[0] || 'Default', // 生效集群
            enable_days_of_week: ['1', '2', '3', '4', '5', '6', '0'],
            field: 'cluster',
          }}
        >
          <Form.Item
            label={t('字段:')}
            name='field'
            rules={[
              {
                required: false,
              },
            ]}
          >
            <Select suffixIcon={<CaretDownOutlined />} style={{ width: '100%' }} onChange={fieldChange}>
              {fields.map((item) => (
                <Option key={item.id} value={item.field}>
                  {item.name}
                </Option>
              ))}
            </Select>
          </Form.Item>
          {(() => {
            switch (field) {
              case 'note':
                return (
                  <>
                    <Form.Item
                      label={t('改为:')}
                      name='note'
                      rules={[
                        {
                          required: false,
                        },
                      ]}
                    >
                      <Input placeholder={t('请输入规则备注')} />
                    </Form.Item>
                  </>
                );
              case 'runbook_url':
                return (
                  <>
                    <Form.Item label={t('改为:')} name='runbook_url'>
                      <Input />
                    </Form.Item>
                  </>
                );

              case 'cluster':
                return (
                  <>
                    <Form.Item
                      label={t('改为:')}
                      name='cluster'
                      rules={[
                        {
                          required: false,
                          message: t('生效集群不能为空'),
                        },
                      ]}
                    >
                      <Select suffixIcon={<CaretDownOutlined />}>
                        {clusterList?.map((item) => (
                          <Option value={item} key={item}>
                            {item}
                          </Option>
                        ))}
                      </Select>
                    </Form.Item>
                  </>
                );
              case 'severity':
                return (
                  <>
                    <Form.Item
                      label={t('改为:')}
                      name='severity'
                      initialValue={2}
                      rules={[
                        {
                          required: false,
                          message: t('告警级别不能为空'),
                        },
                      ]}
                    >
                      <Radio.Group>
                        <Radio value={1}>{t('一级报警')}</Radio>
                        <Radio value={2}>{t('二级报警')}</Radio>
                        <Radio value={3}>{t('三级报警')}</Radio>
                      </Radio.Group>
                    </Form.Item>
                  </>
                );
              case 'disabled':
                return (
                  <>
                    <Form.Item
                      label={t('改为:')}
                      name='enable_status'
                      rules={[
                        {
                          required: false,
                          message: t('立即启用不能为空'),
                        },
                      ]}
                      valuePropName='checked'
                    >
                      <Switch />
                    </Form.Item>
                  </>
                );
              case 'enable_in_bg':
                return (
                  <>
                    <Form.Item label={t('改为:')} name='enable_in_bg' valuePropName='checked'>
                      <SwitchWithLabel label='根据告警事件中的ident归属关系判断' />
                    </Form.Item>
                  </>
                );
              case 'prom_eval_interval':
                return (
                  <>
                    <Form.Item
                      label={t('改为:')}
                      rules={[
                        {
                          required: false,
                          message: t('执行频率不能为空'),
                        },
                      ]}
                    >
                      <Space>
                        <Form.Item style={{ marginBottom: 0 }} name='prom_eval_interval' initialValue={15} wrapperCol={{ span: 10 }}>
                          <InputNumber
                            min={1}
                            onChange={(val) => {
                              setRefresh(!refresh);
                            }}
                          />
                        </Form.Item>
                        秒
                        <Tooltip title={t(`每隔${form.getFieldValue('prom_eval_interval')}秒,把PromQL作为查询条件,去查询后端存储,如果查到了数据就表示当次有监控数据触发了规则`)}>
                          <QuestionCircleFilled />
                        </Tooltip>
                      </Space>
                    </Form.Item>
                  </>
                );
              case 'prom_for_duration':
                return (
                  <>
                    <Form.Item
                      label={t('改为:')}
                      rules={[
                        {
                          required: false,
                          message: t('持续时长不能为空'),
                        },
                      ]}
                    >
                      <Space>
                        <Form.Item style={{ marginBottom: 0 }} name='prom_for_duration' initialValue={60} wrapperCol={{ span: 10 }}>
                          <InputNumber min={0} />
                        </Form.Item>
                        秒
                        <Tooltip
                          title={t(
                            `通常持续时长大于执行频率,在持续时长内按照执行频率多次执行PromQL查询,每次都触发才生成告警;如果持续时长置为0,表示只要有一次PromQL查询触发阈值,就生成告警`,
                          )}
                        >
                          <QuestionCircleFilled />
                        </Tooltip>
                      </Space>
                    </Form.Item>
                  </>
                );
              case 'notify_channels':
                return (
                  <>
                    <Form.Item label={t('改为:')} name='notify_channels'>
                      <Checkbox.Group>{contactListCheckboxes}</Checkbox.Group>
                    </Form.Item>
                  </>
                );
              case 'notify_groups':
                return (
                  <>
                    <Form.Item label={t('改为:')} name='notify_groups'>
                      <Select mode='multiple' showSearch optionFilterProp='children' filterOption={false} onSearch={(e) => debounceFetcher(e)} onBlur={() => getGroups('')}>
                        {notifyGroupsOptions}
                      </Select>
                    </Form.Item>
                  </>
                );
              case 'notify_recovered':
                return (
                  <>
                    <Form.Item label={t('改为:')} name='notify_recovered' valuePropName='checked'>
                      <Switch />
                    </Form.Item>
                  </>
                );
              case 'recover_duration':
                return (
                  <>
                    <Form.Item label={t('改为:')}>
                      <Space>
                        <Form.Item
                          style={{ marginBottom: 0 }}
                          name='recover_duration'
                          initialValue={0}
                          wrapperCol={{ span: 10 }}
                          rules={[
                            {
                              required: false,
                              message: t('留观时长不能为空'),
                            },
                          ]}
                        >
                          <InputNumber
                            min={0}
                            onChange={(val) => {
                              setRefresh(!refresh);
                            }}
                          />
                        </Form.Item>
                        秒
                        <Tooltip title={t(`持续${form.getFieldValue('recover_duration') || 0}秒没有再次触发阈值才发送恢复通知`)}>
                          <QuestionCircleFilled />
                        </Tooltip>
                      </Space>
                    </Form.Item>
                  </>
                );
              case 'notify_repeat_step':
                return (
                  <>
                    <Form.Item label={t('改为:')}>
                      <Space>
                        <Form.Item
                          style={{ marginBottom: 0 }}
                          name='notify_repeat_step'
                          initialValue={60}
                          wrapperCol={{ span: 10 }}
                          rules={[
                            {
                              required: false,
                              message: t('重复发送频率不能为空'),
                            },
                          ]}
                        >
                          <InputNumber
                            min={0}
                            onChange={(val) => {
                              setRefresh(!refresh);
                            }}
                          />
                        </Form.Item>
                        分钟
                        <Tooltip title={t(`如果告警持续未恢复,间隔${form.getFieldValue('notify_repeat_step')}分钟之后重复提醒告警接收组的成员`)}>
                          <QuestionCircleFilled />
                        </Tooltip>
                      </Space>
                    </Form.Item>
                  </>
                );
              case 'callbacks':
                return (
                  <>
                    <Form.Item label={t('改为:')}>
                      <Form.List name='callbacks' initialValue={[{}]}>
                        {(fields, { add, remove }, { errors }) => (
                          <>
                            {fields.map((field, index) => (
                              <Row gutter={[10, 0]} key={field.key}>
                                <Col span={22}>
                                  <Form.Item name={[field.name, 'url']}>
                                    <Input />
                                  </Form.Item>
                                </Col>

                                <Col span={1}>
                                  <MinusCircleOutlined className='control-icon-normal' onClick={() => remove(field.name)} />
                                </Col>
                              </Row>
                            ))}
                            <PlusCircleOutlined className='control-icon-normal' onClick={() => add()} />
                          </>
                        )}
                      </Form.List>
                    </Form.Item>
                  </>
                );
              case 'append_tags':
                return (
                  <>
                    <Form.Item label='附加标签' name='append_tags' rules={[{ required: false, message: '请填写至少一项标签!' }, isValidFormat]}>
                      <Select mode='tags' tokenSeparators={[' ']} open={false} placeholder={'标签格式为 key=value ,使用回车或空格分隔'} tagRender={tagRender} />
                    </Form.Item>
                  </>
                );
              case 'enable_time':
                return (
                  <>
                    <Form.Item
                      label={t('改为:')}
                      name='enable_days_of_week'
                      rules={[
                        {
                          required: false,
                          message: t('生效时间不能为空'),
                        },
                      ]}
                    >
                      <Select mode='tags'>{enableDaysOfWeekOptions}</Select>
                    </Form.Item>
                    <Form.Item
                      name='enable_time'
                      {...tailLayout}
                      rules={[
                        {
                          required: false,
                          message: t('生效时间不能为空'),
                        },
                      ]}
                    >
                      <TimePicker.RangePicker
                        format='HH:mm'
                        onChange={(val, val2) => {
                          form.setFieldsValue({
                            enable_stime: val2[0],
                            enable_etime: val2[1],
                          });
                        }}
                      />
                    </Form.Item>
                  </>
                );
              default:
                return null;
            }
          })()}
        </Form>
      </Modal>
    </>
  );
}
Example #14
Source File: operateForm.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
operateForm: React.FC<Props> = ({ type, detail = {} }) => {
  const { t, i18n } = useTranslation();
  const history = useHistory(); // 创建的时候默认选中的值

  const [form] = Form.useForm();
  const { clusters: clusterList } = useSelector<RootState, CommonStoreState>((state) => state.common);
  const { curBusiItem } = useSelector<RootState, CommonStoreState>((state) => state.common);

  const [contactList, setInitContactList] = useState([]);
  const [notifyGroups, setNotifyGroups] = useState<any[]>([]);
  const [initVal, setInitVal] = useState<any>({});
  const [refresh, setRefresh] = useState(true);
  useEffect(() => {
    getNotifyChannel();
    getGroups('');

    return () => {};
  }, []);

  useEffect(() => {
    const data = {
      ...detail,
      enable_time: detail?.enable_stime ? [detail.enable_stime, detail.enable_etime] : [],
      enable_status: detail?.disabled === undefined ? true : !detail?.disabled,
    };
    setInitVal(data);
    if (type == 1) {
      const groups = (detail.notify_groups_obj ? detail.notify_groups_obj.filter((item) => !notifyGroups.find((i) => item.id === i.id)) : []).concat(notifyGroups);
      setNotifyGroups(groups);
    }
  }, [JSON.stringify(detail)]);

  const enableDaysOfWeekOptions = [t('周日'), t('周一'), t('周二'), t('周三'), t('周四'), t('周五'), t('周六')].map((v, i) => {
    return <Option value={String(i)} key={i}>{`${v}`}</Option>;
  });

  const contactListCheckboxes = contactList.map((c: { key: string; label: string }) => (
    <Checkbox value={c.key} key={c.label}>
      {c.label}
    </Checkbox>
  ));

  const notifyGroupsOptions = notifyGroups.map((ng: any) => (
    <Option value={String(ng.id)} key={ng.id}>
      {ng.name}
    </Option>
  ));

  const getNotifyChannel = async () => {
    const res = await getNotifiesList();
    let contactList = res || [];
    setInitContactList(contactList);
  };

  const getGroups = async (str) => {
    const res = await getTeamInfoList({ query: str });
    const data = res.dat || res;
    const combineData = (detail.notify_groups_obj ? detail.notify_groups_obj.filter((item) => !data.find((i) => item.id === i.id)) : []).concat(data);
    setNotifyGroups(combineData || []);
  };

  const addSubmit = () => {
    form.validateFields().then(async (values) => {
      const res = await prometheusQuery({ query: values.prom_ql }, values.cluster);
      if (res.error) {
        notification.error({
          message: res.error,
        });
        return false;
      }
      const callbacks = values.callbacks.map((item) => item.url);
      const data = {
        ...values,
        enable_stime: values.enable_time[0].format('HH:mm'),
        enable_etime: values.enable_time[1].format('HH:mm'),
        disabled: !values.enable_status ? 1 : 0,
        notify_recovered: values.notify_recovered ? 1 : 0,
        enable_in_bg: values.enable_in_bg ? 1 : 0,
        callbacks,
      };
      let reqBody,
        method = 'Post';
      if (type === 1) {
        reqBody = data;
        method = 'Put';
        const res = await EditStrategy(reqBody, curBusiItem.id, detail.id);
        if (res.err) {
          message.error(res.error);
        } else {
          message.success(t('编辑成功!'));
          history.push('/alert-rules');
        }
      } else {
        reqBody = [data];
        const { dat } = await addOrEditStrategy(reqBody, curBusiItem.id, method);
        let errorNum = 0;
        const msg = Object.keys(dat).map((key) => {
          dat[key] && errorNum++;
          return dat[key];
        });

        if (!errorNum) {
          message.success(`${type === 2 ? t('告警规则克隆成功') : t('告警规则创建成功')}`);
          history.push('/alert-rules');
        } else {
          message.error(t(msg));
        }
      }
    });
  };

  const debounceFetcher = useCallback(debounce(getGroups, 800), []);
  return (
    <div className='operate_con'>
      <Form
        {...layout}
        form={form}
        className='strategy-form'
        layout={refresh ? 'horizontal' : 'horizontal'}
        initialValues={{
          prom_eval_interval: 15,
          prom_for_duration: 60,
          severity: 2,
          disabled: 0, // 0:立即启用 1:禁用  待修改
          // notify_recovered: 1, // 1:启用
          cluster: clusterList[0] || 'Default', // 生效集群
          enable_days_of_week: ['1', '2', '3', '4', '5', '6', '0'],
          ...detail,
          enable_in_bg: detail?.enable_in_bg === 1,
          enable_time: detail?.enable_stime ? [moment(detail.enable_stime, 'HH:mm'), moment(detail.enable_etime, 'HH:mm')] : [moment('00:00', 'HH:mm'), moment('23:59', 'HH:mm')],
          enable_status: detail?.disabled === undefined ? true : !detail?.disabled,
          notify_recovered: detail?.notify_recovered === 1 || detail?.notify_recovered === undefined ? true : false, // 1:启用 0:禁用
          recover_duration: detail?.recover_duration || 0,
          callbacks: !!detail?.callbacks
            ? detail.callbacks.map((item) => ({
                url: item,
              }))
            : [{}],
        }}
      >
        <Space direction='vertical' style={{ width: '100%' }}>
          <Card title={t('基本配置')}>
            <Form.Item
              label={t('规则标题:')}
              name='name'
              rules={[
                {
                  required: true,
                  message: t('规则标题不能为空'),
                },
              ]}
            >
              <Input placeholder={t('请输入规则标题')} />
            </Form.Item>
            <Form.Item
              label={t('规则备注:')}
              name='note'
              rules={[
                {
                  required: false,
                },
              ]}
            >
              <Input placeholder={t('请输入规则备注')} />
            </Form.Item>
            <Form.Item
              label={t('告警级别')}
              name='severity'
              rules={[
                {
                  required: true,
                  message: t('告警级别不能为空'),
                },
              ]}
            >
              <Radio.Group>
                <Radio value={1}>{t('一级报警')}</Radio>
                <Radio value={2}>{t('二级报警')}</Radio>
                <Radio value={3}>{t('三级报警')}</Radio>
              </Radio.Group>
            </Form.Item>
            <Form.Item
              label={t('生效集群')}
              name='cluster'
              rules={[
                {
                  required: true,
                  message: t('生效集群不能为空'),
                },
              ]}
            >
              <Select suffixIcon={<CaretDownOutlined />}>
                {clusterList?.map((item) => (
                  <Option value={item} key={item}>
                    {item}
                  </Option>
                ))}
              </Select>
            </Form.Item>
            <AdvancedWrap>
              <AbnormalDetection form={form} />
            </AdvancedWrap>
            <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.cluster !== curValues.cluster}>
              {() => {
                return (
                  <Form.Item label='PromQL' className={'Promeql-content'} required>
                    <Form.Item name='prom_ql' validateTrigger={['onBlur']} trigger='onChange' rules={[{ required: true, message: t('请输入PromQL') }]}>
                      <PromQLInput
                        url='/api/n9e/prometheus'
                        headers={{
                          'X-Cluster': form.getFieldValue('cluster'),
                          Authorization: `Bearer ${localStorage.getItem('access_token') || ''}`,
                        }}
                      />
                    </Form.Item>
                  </Form.Item>
                );
              }}
            </Form.Item>
            <Form.Item required label={t('执行频率')}>
              <Space>
                <Form.Item
                  style={{ marginBottom: 0 }}
                  name='prom_eval_interval'
                  initialValue={15}
                  wrapperCol={{ span: 10 }}
                  rules={[
                    {
                      required: true,
                      message: t('执行频率不能为空'),
                    },
                  ]}
                >
                  <InputNumber
                    min={1}
                    onChange={(val) => {
                      setRefresh(!refresh);
                    }}
                  />
                </Form.Item>
                秒
                <Tooltip title={t(`每隔${form.getFieldValue('prom_eval_interval')}秒,把PromQL作为查询条件,去查询后端存储,如果查到了数据就表示当次有监控数据触发了规则`)}>
                  <QuestionCircleFilled />
                </Tooltip>
              </Space>
            </Form.Item>
            <Form.Item
              required
              label={t('持续时长')}
              rules={[
                {
                  required: true,
                  message: t('持续时长不能为空'),
                },
              ]}
            >
              <Space>
                <Form.Item style={{ marginBottom: 0 }} name='prom_for_duration' wrapperCol={{ span: 10 }}>
                  <InputNumber min={0} />
                </Form.Item>
                秒
                <Tooltip
                  title={t(
                    `通常持续时长大于执行频率,在持续时长内按照执行频率多次执行PromQL查询,每次都触发才生成告警;如果持续时长置为0,表示只要有一次PromQL查询触发阈值,就生成告警`,
                  )}
                >
                  <QuestionCircleFilled />
                </Tooltip>
              </Space>
            </Form.Item>
            <Form.Item label='附加标签' name='append_tags' rules={[{ required: false, message: '请填写至少一项标签!' }, isValidFormat]}>
              <Select mode='tags' tokenSeparators={[' ']} open={false} placeholder={'标签格式为 key=value ,使用回车或空格分隔'} tagRender={tagRender} />
            </Form.Item>
            <Form.Item label={t('预案链接')} name='runbook_url'>
              <Input />
            </Form.Item>
          </Card>
          <Card title={t('生效配置')}>
            <Form.Item
              label={t('立即启用')}
              name='enable_status'
              rules={[
                {
                  required: true,
                  message: t('立即启用不能为空'),
                },
              ]}
              valuePropName='checked'
            >
              <Switch />
            </Form.Item>
            <Form.Item
              label={t('生效时间')}
              name='enable_days_of_week'
              rules={[
                {
                  required: true,
                  message: t('生效时间不能为空'),
                },
              ]}
            >
              <Select mode='tags'>{enableDaysOfWeekOptions}</Select>
            </Form.Item>
            <Form.Item
              name='enable_time'
              {...tailLayout}
              rules={[
                {
                  required: true,
                  message: t('生效时间不能为空'),
                },
              ]}
            >
              <TimePicker.RangePicker
                format='HH:mm'
                onChange={(val, val2) => {
                  form.setFieldsValue({
                    enable_stime: val2[0],
                    enable_etime: val2[1],
                  });
                }}
              />
            </Form.Item>
            <Form.Item label={t('仅在本业务组生效')} name='enable_in_bg' valuePropName='checked'>
              <SwitchWithLabel label='根据告警事件中的ident归属关系判断' />
            </Form.Item>
          </Card>
          <Card title={t('通知配置')}>
            <Form.Item label={t('通知媒介')} name='notify_channels'>
              <Checkbox.Group>{contactListCheckboxes}</Checkbox.Group>
            </Form.Item>
            <Form.Item label={t('告警接收组')} name='notify_groups'>
              <Select mode='multiple' showSearch optionFilterProp='children' filterOption={false} onSearch={(e) => debounceFetcher(e)} onBlur={() => getGroups('')}>
                {notifyGroupsOptions}
              </Select>
            </Form.Item>
            <Form.Item label={t('启用恢复通知')}>
              <Space>
                <Form.Item name='notify_recovered' valuePropName='checked' style={{ marginBottom: 0 }}>
                  <Switch />
                </Form.Item>
                <Tooltip title={t(`告警恢复时也发送通知`)}>
                  <QuestionCircleFilled />
                </Tooltip>
              </Space>
            </Form.Item>
            <Form.Item label={t('留观时长')} required>
              <Space>
                <Form.Item style={{ marginBottom: 0 }} name='recover_duration' initialValue={0} wrapperCol={{ span: 10 }}>
                  <InputNumber
                    min={0}
                    onChange={(val) => {
                      setRefresh(!refresh);
                    }}
                  />
                </Form.Item>
                秒
                <Tooltip title={t(`持续${form.getFieldValue('recover_duration')}秒没有再次触发阈值才发送恢复通知`)}>
                  <QuestionCircleFilled />
                </Tooltip>
              </Space>
            </Form.Item>
            <Form.Item label={t('重复发送频率')} required>
              <Space>
                <Form.Item
                  style={{ marginBottom: 0 }}
                  name='notify_repeat_step'
                  initialValue={60}
                  wrapperCol={{ span: 10 }}
                  rules={[
                    {
                      required: true,
                      message: t('重复发送频率不能为空'),
                    },
                  ]}
                >
                  <InputNumber
                    min={0}
                    onChange={(val) => {
                      setRefresh(!refresh);
                    }}
                  />
                </Form.Item>
                分钟
                <Tooltip title={t(`如果告警持续未恢复,间隔${form.getFieldValue('notify_repeat_step')}分钟之后重复提醒告警接收组的成员`)}>
                  <QuestionCircleFilled />
                </Tooltip>
              </Space>
            </Form.Item>
            <Form.Item label={t('回调地址')}>
              <Form.List name='callbacks' initialValue={[{}]}>
                {(fields, { add, remove }) => (
                  <>
                    {fields.map((field) => (
                      <Row gutter={[10, 0]} key={field.key}>
                        <Col span={22}>
                          <Form.Item name={[field.name, 'url']} fieldKey={[field.fieldKey, 'url']}>
                            <Input />
                          </Form.Item>
                        </Col>

                        <Col span={1}>
                          <MinusCircleOutlined className='control-icon-normal' onClick={() => remove(field.name)} />
                        </Col>
                      </Row>
                    ))}
                    <PlusCircleOutlined className='control-icon-normal' onClick={() => add()} />
                  </>
                )}
              </Form.List>
            </Form.Item>
          </Card>
          <Form.Item
            // {...tailLayout}
            style={{
              marginTop: 20,
            }}
          >
            <Button type='primary' onClick={addSubmit} style={{ marginRight: '8px' }}>
              {type === 1 ? t('编辑') : type === 2 ? t('克隆') : t('创建')}
            </Button>
            {type === 1 && (
              <Button
                danger
                style={{ marginRight: '8px' }}
                onClick={() => {
                  Modal.confirm({
                    title: t('是否删除该告警规则?'),
                    onOk: () => {
                      deleteStrategy([detail.id], curBusiItem.id).then(() => {
                        message.success(t('删除成功'));
                        history.push('/alert-rules');
                      });
                    },

                    onCancel() {},
                  });
                }}
              >
                {t('删除')}
              </Button>
            )}

            <Button
              onClick={() => {
                history.push('/alert-rules');
              }}
            >
              {t('取消')}
            </Button>
          </Form.Item>
        </Space>
      </Form>
    </div>
  );
}
Example #15
Source File: ContactSyncedAutocompleteFields.tsx    From condo with MIT License 4 votes vote down vote up
ContactSyncedAutocompleteFields: React.FC<IContactSyncedAutocompleteFieldsProps> = ({ refetch, initialQuery, initialValue, onChange, onChecked, checked, contacts, displayMinusButton, onClickMinusButton }) => {
    const intl = useIntl()
    const NamePlaceholder = intl.formatMessage({ id: 'contact.Contact.ContactsEditor.Name.placeholder' })
    const [value, setValue] = useState(initialValue)

    const searchSuggestions = useCallback(
        async (query) => {
            return refetch({
                where: { ...initialQuery, ...query },
            })
        },
        [initialQuery, refetch],
    )

    const debouncedSearch = useMemo(
        () => {
            return debounce(searchSuggestions, DEBOUNCE_TIMEOUT_IN_MS)
        },
        [searchSuggestions],
    )

    const searchContactByPhone = useCallback(async (query) => {
        const contactName = get(value, 'name', undefined)

        await debouncedSearch({
            phone_contains_i: query,
            name_contains_i: contactName,
        })
    }, [debouncedSearch, value])

    const searchContactByName = useCallback(async (query) => {
        const contactPhone = get(value, 'phone', undefined)

        await debouncedSearch({
            name_contains_i: query,
            phone_contains_i: contactPhone,
        })
    }, [debouncedSearch, value])

    const handleSelectContact = (value: string, option: OptionProps) => {
        setValueAndTriggerOnChange(option.item)
    }

    const handleChangeContact = (field) => (fieldValue) => {
        const newValue = {
            ...value,
            [field]: fieldValue,
        }
        setValueAndTriggerOnChange(newValue)
    }

    const setValueAndTriggerOnChange = (contact) => {
        setValue(contact)
        onChange(contact)
    }

    const handleClearContact = () => {
        setValue(null)
    }

    const handleChecked = () => {
        onChecked && onChecked()
    }

    const renderOptionsBy = useCallback((prop) =>
        contacts.map(contact => ({
            value: contact[prop],
            item: contact,
        }))
    , [contacts])

    const phoneOptions = useMemo(() => renderOptionsBy('phone'), [renderOptionsBy])
    const nameOptions = useMemo(() => renderOptionsBy('name'), [renderOptionsBy])

    return (
        <>
            <Col span={10}>
                <AutoComplete
                    allowClear
                    value={get(value, 'phone')}
                    options={phoneOptions}
                    onSelect={handleSelectContact}
                    onSearch={searchContactByPhone}
                    onChange={handleChangeContact('phone')}
                    onClear={handleClearContact}
                    style={{ width: '100%' }}
                >
                    <PhoneInput
                        block
                        compatibilityWithAntAutoComplete={true}
                    />
                </AutoComplete>
            </Col>
            <Col span={10}>
                <AutoComplete
                    allowClear
                    placeholder={NamePlaceholder}
                    value={get(value, 'name')}
                    options={nameOptions}
                    onSelect={handleSelectContact}
                    onSearch={searchContactByName}
                    onChange={handleChangeContact('name')}
                    onClear={handleClearContact}
                    style={{ width: '100%' }}
                />
            </Col>
            <Col span={2}>
                {onChecked && (
                    <Radio
                        onClick={handleChecked}
                        checked={checked}
                        style={{ marginTop: '8px' }}
                    />
                )}
            </Col>
            <Col span={2}>
                {displayMinusButton && (
                    <MinusCircleOutlined
                        style={{
                            color: colors.black,
                            fontSize: '21px',
                            marginTop: '9px',
                            marginLeft: '-4px',
                        }}
                        onClick={onClickMinusButton}
                    />
                )}
            </Col>
        </>
    )
}
Example #16
Source File: index.tsx    From dashboard with Apache License 2.0 4 votes vote down vote up
AutoReply: React.FC<AutoReplyProps> = (props) => {
  const {welcomeMsg, setWelcomeMsg, isFetchDone} = props;
  const [modalVisible, setModalVisible] = useState(false);
  const [attachments, setAttachments] = useState<Attachment[]>([]);
  const [currentIndex, setCurrentIndex] = useState<number>(0);
  const [currentMode, setCurrentMode] = useState<MsgType>('image');
  const [linkFetching, setLinkFetching] = useState(false);
  const [content, setContent] = useState('');
  const contentRef = useRef<React.RefObject<HTMLElement>>();
  const imageModalFormRef = useRef<FormInstance>();
  const linkModalFormRef = useRef<FormInstance>();
  const miniAppModalFormRef = useRef<FormInstance>();

  const UploadFileFn = async (req: UploadRequestOption, ref: MutableRefObject<any | undefined>, inputName: string) => {
    const file = req.file as File;
    if (!file.name) {
      message.error('非法参数');
      return;
    }

    const hide = message.loading('上传中');
    try {
      const res = await GetSignedURL(file.name)
      const data = res.data as GetSignedURLResult
      if (res.code === 0) {
        const uploadRes = (await fetch(data.upload_url, {
          method: 'PUT',
          body: file
        }));
        hide();
        if (uploadRes.ok && ref) {
          ref.current?.setFieldsValue({[inputName]: data.download_url});
          return;
        }

        message.error('上传图片失败');

        return;
      }

      hide();
      message.error('获取上传地址失败');
      return;

    } catch (e) {
      message.error('上传图片失败');
      console.log(e);
    }
  };

  useEffect(() => {
    const formData = itemDataToFormData(welcomeMsg);
    setAttachments(formData.attachments || []);
    setContent(formData.text || '');
  }, [isFetchDone]);

  useEffect(() => {
    setWelcomeMsg({
      text: content || '',
      attachments: attachments || [],
    });
  }, [content, attachments]);

  return (
    <>
      <div className={styles.replyEditor}>
        <div className={'preview-container'}>
          <div className={styles.replyEditorPreview}>
            <img src={phoneImage} className='bg'/>
            <div className='content'>
              <ul className='reply-list'>
                {content && (
                  <li><img
                    src={avatarDefault}/>
                    <div className='msg text' dangerouslySetInnerHTML={{__html: content}}/>
                  </li>
                )}
                {attachments && attachments.length > 0 && (
                  attachments.map((attachment) => {
                    if (attachment.msgtype === 'image') {
                      return (
                        <li key={attachment.id}>
                          <img src={avatarDefault}/>
                          <div className={`msg image`}>
                            <img src={attachment.image?.pic_url}/>
                          </div>
                        </li>
                      );
                    }

                    if (attachment.msgtype === 'link') {
                      return (
                        <li key={attachment.id}>
                          <img src={avatarDefault}/>
                          <div className='msg link'><p className='title'>{attachment.link?.title}</p>
                            <div className='link-inner'><p
                              className='desc'>{attachment.link?.desc}</p>
                              <img src={attachment.link?.picurl}/>
                            </div>
                          </div>
                        </li>
                      );
                    }

                    if (attachment.msgtype === 'miniprogram') {
                      return (
                        <li key={attachment.id}>
                          <img src={avatarDefault}/>
                          <div className='msg miniprogram'>
                            <p className='m-title'>
                              <IconFont
                                type={'icon-weixin-mini-app'}
                                style={{marginRight: 4, fontSize: 14}}
                              />
                              {attachment.miniprogram?.title}
                            </p>
                            <img src={attachment.miniprogram?.pic_media_id}/>
                            <p className='l-title'>
                              <IconFont type={'icon-weixin-mini-app'} style={{marginRight: 4}}/>
                              小程序
                            </p>
                          </div>
                        </li>
                      );
                    }

                    return '';
                  })
                )}
              </ul>
            </div>
          </div>
        </div>

        <div className='text-area-container'>
          <div className={styles.msgTextareaContainer} style={{border: 'none'}}>
            {props.enableQuickInsert && (
              <div className='insert-btn '>
                    <span
                      className='clickable no-select'
                      onClick={() => {
                        setContent(`${content}[客户昵称]`);
                      }}
                    >[插入客户昵称]</span>
              </div>
            )}
            <div className='textarea-container '>
              <ContentEditable
                // @ts-ignore
                innerRef={contentRef}
                onKeyDown={(event) => {
                  if (event.key === 'Enter') {
                    document.execCommand('insertLineBreak');
                    event.preventDefault();
                  }
                }}
                className={'textarea'}
                html={content}
                onChange={(e) => {
                  setContent(e.target.value);
                }}/>
              <div className='flex-row align-side'>
                <p className='text-cnt'>{content.length}/600</p>
              </div>
            </div>
          </div>
        </div>
        <div className='option-area-container'>
          {attachments && attachments.length > 0 && (
            <ReactSortable handle={'.draggable-button'} tag='ul' className={'select-msg-options'} list={attachments} setList={setAttachments}>
              {attachments.map((attachment, index) => (
                <li key={attachment.id} className='flex-row'>
                      <span>
                        <MinusCircleOutlined
                          onClick={() => {
                            const items = [...attachments];
                            items.splice(index, 1);
                            setAttachments(items);
                          }}
                        />
                        【{msgTypes[attachment.msgtype]}】:
                        <span
                          className='col-1'>{attachment?.name}</span>
                      </span>
                  <span className='d-action-container'>
                      <EditOutlined
                        onClick={() => {
                          setCurrentMode(attachment.msgtype);
                          imageModalFormRef.current?.setFieldsValue(attachment.image);
                          linkModalFormRef.current?.setFieldsValue(attachment.link);
                          miniAppModalFormRef.current?.setFieldsValue(attachment.miniprogram);
                          setCurrentIndex(index);
                          setModalVisible(true);
                        }}
                      />
                      <DragOutlined
                        className={'draggable-button'}
                        style={{cursor: 'grabbing'}}
                      />
                    </span>
                </li>
              ))}
            </ReactSortable>
          )}
          <div className='option-container'>
            <Dropdown
              placement='topLeft'
              trigger={['click']}
              overlay={(
                <Menu style={{minWidth: 120}}>
                  <Menu.Item
                    key={'image'}
                    icon={<FileImageOutlined/>}
                    onClick={() => {
                      setCurrentMode('image');
                      setCurrentIndex(attachments.length);
                      imageModalFormRef.current?.resetFields();
                      setModalVisible(true);
                    }}
                  >
                    图片
                  </Menu.Item>
                  <Menu.Item
                    key={'link'}
                    icon={<LinkOutlined/>}
                    onClick={() => {
                      setCurrentMode('link');
                      setCurrentIndex(attachments.length);
                      setModalVisible(true);
                    }}
                  >
                    链接
                  </Menu.Item>
                  <Menu.Item
                    key={'miniApp'}
                    icon={<IconFont type={'icon-weixin-mini-app'}/>}
                    onClick={() => {
                      setCurrentMode('miniprogram');
                      setCurrentIndex(attachments.length);
                      setModalVisible(true);
                    }}
                  >
                    小程序
                  </Menu.Item>
                </Menu>
              )}
            >
              <a className='ant-dropdown-link' onClick={e => e.preventDefault()}>
                <PlusCircleOutlined/> 添加附件
              </a>
            </Dropdown>
          </div>
        </div>
      </div>

      <ModalForm
        formRef={imageModalFormRef}
        className={'dialog from-item-label-100w'}
        layout={'horizontal'}
        width={'560px'}
        visible={currentMode === 'image' && modalVisible}
        onVisibleChange={setModalVisible}
        onFinish={async (params: { title: string, pic_url: string, msgtype: MsgType }) => {
          attachments[currentIndex] = {
            id: new Date().getTime().toString(),
            msgtype: params.msgtype,
            name: params.title,
            image: {...params},
          };
          setAttachments(attachments);
          return true;
        }}
      >
        <h2 className='dialog-title'> 添加图片附件 </h2>
        <ProForm.Item initialValue={'image'} name={'msgtype'} noStyle={true}>
          <input type={'hidden'}/>
        </ProForm.Item>
        <ProFormText
          name='title'
          label='图片名称'
          placeholder={'请输入图片名称'}
          width='md'
          rules={[
            {
              required: true,
              message: '请输入图片名称!',
            },
          ]}
        />
        <Form.Item
          label='上传图片'
          name='pic_url'
          rules={[
            {
              required: true,
              message: '请上传图片!',
            },
          ]}
        >
          <ImageUploader
            customRequest={async (req) => {
              await UploadFileFn(req, imageModalFormRef, 'pic_url')
            }}
          />
        </Form.Item>
      </ModalForm>


      <ModalForm
        formRef={linkModalFormRef}
        className={'dialog from-item-label-100w'}
        layout={'horizontal'}
        width={'560px'}
        visible={currentMode === 'link' && modalVisible}
        onVisibleChange={setModalVisible}
        onFinish={async (params) => {
          attachments[currentIndex] = {
            id: new Date().getTime().toString(),
            msgtype: params.msgtype,
            name: params.title,
            // @ts-ignore
            link: {...params},
          };
          setAttachments(attachments);
          return true;
        }}
      >
        <Spin spinning={linkFetching}>
          <h2 className='dialog-title'> 添加链接附件 </h2>
          <ProForm.Item initialValue={'link'} name={'msgtype'} noStyle={true}>
            <input type={'hidden'}/>
          </ProForm.Item>
          <ProFormText
            name='url'
            label='链接地址'
            width='md'
            fieldProps={{
              disabled: linkFetching,
              addonAfter: (
                <Tooltip title="点击抓取远程链接,自动填充标题,描述,图片">
                  <div
                    onClick={async () => {
                      setLinkFetching(true);
                      const res = await ParseURL(linkModalFormRef.current?.getFieldValue('url'))
                      setLinkFetching(false);
                      if (res.code !== 0) {
                        message.error(res.message);
                      } else {
                        message.success('解析链接成功');
                        linkModalFormRef?.current?.setFieldsValue({
                          customer_link_enable: 1,
                          title: res.data.title,
                          desc: res.data.desc,
                          picurl: res.data.img_url,
                        })
                      }
                    }}
                    style={{
                      cursor: "pointer",
                      width: 32,
                      height: 30,
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center'
                    }}>
                    <SyncOutlined/>
                  </div>
                </Tooltip>
              )
            }}
            rules={[
              {
                required: true,
                message: '请输入链接地址',
              },
              {
                type: 'url',
                message: '请填写正确的的URL,必须是http或https开头',
              },
            ]}
          />
          <ProFormSwitch
            label={'高级设置'}
            checkedChildren='开启'
            unCheckedChildren='关闭'
            name='customer_link_enable'
            tooltip={'开启后可以自定义链接所有信息'}
          />
          <ProFormDependency name={['customer_link_enable']}>
            {({customer_link_enable}) => {
              if (customer_link_enable) {
                return (
                  <>
                    <ProFormText
                      name='title'
                      label='链接标题'
                      width='md'
                      rules={[
                        {
                          required: true,
                          message: '请输入链接标题',
                        },
                      ]}
                    />
                    <ProFormTextArea
                      name='desc'
                      label='链接描述'
                      width='md'
                    />
                    <Form.Item
                      label='链接封面'
                      name='picurl'
                      rules={[
                        {
                          required: true,
                          message: '请上传链接图片!',
                        },
                      ]}
                    >
                      <ImageUploader
                        customRequest={async (req) => {
                          await UploadFileFn(req, linkModalFormRef, 'picurl')
                        }}
                      />
                    </Form.Item>
                  </>
                );
              }
              return <></>;
            }}
          </ProFormDependency>
        </Spin>
      </ModalForm>


      <ModalForm
        formRef={miniAppModalFormRef}
        className={'dialog from-item-label-100w'}
        layout={'horizontal'}
        width={'560px'}
        labelCol={{
          md: 6,
        }}
        visible={currentMode === 'miniprogram' && modalVisible}
        onVisibleChange={setModalVisible}
        onFinish={async (params) => {
          attachments[currentIndex] = {
            id: new Date().getTime().toString(),
            msgtype: params.msgtype,
            name: params.title,
            // @ts-ignore
            miniprogram: {...params},
          };
          setAttachments(attachments);
          return true;
        }}
      >
        <h2 className='dialog-title'> 添加小程序附件 </h2>

        <Alert
          showIcon={true}
          type='info'
          message={
            '请填写企业微信后台绑定的小程序id和路径,否则会造成发送失败'
          }
          style={{marginBottom: 20}}
        />

        <ProForm.Item initialValue={'miniprogram'} name={'msgtype'} noStyle={true}>
          <input type={'hidden'}/>
        </ProForm.Item>

        <ProFormText
          name='title'
          label='小程序标题'
          width='md'
          rules={[
            {
              required: true,
              message: '请输入链接标题',
            },
          ]}
        />

        <ProFormText
          // 帮助指引
          name='app_id'
          label='小程序AppID'
          width='md'
          rules={[
            {
              required: true,
              message: '请输入小程序AppID',
            },
          ]}
        />

        <ProFormText
          name='page'
          label='小程序路径'
          width='md'
          rules={[
            {
              required: true,
              message: '请输入小程序路径',
            },
          ]}
        />

        <Form.Item
          label='小程序封面'
          name='pic_media_id'
          rules={[
            {
              required: true,
              message: '请小程序封面!',
            },
          ]}
        >
          <ImageUploader
            customRequest={async (req) => {
              await UploadFileFn(req, miniAppModalFormRef, 'pic_media_id')
            }}
          />
        </Form.Item>

      </ModalForm>

    </>
  );
}
Example #17
Source File: index.tsx    From surveyo with Apache License 2.0 4 votes vote down vote up
function SingleChoiceQuestionField({question, updateQuestion, options}: any) {
  return (
    <Form name="dynamic_form_item" {...formItemLayoutWithOutLabel}>
      <Form.List name="names">
        {(fields, {add, remove}) => {
          return (
            <div>
              {fields.map((field, index) => (
                <Form.Item
                  {...(index === 0
                    ? formItemLayout
                    : formItemLayoutWithOutLabel)}
                  label={index === 0 ? 'Options' : ''}
                  required={false}
                  key={field.key}
                >
                  <div>
                    <Row>
                      <Col span={16}>
                        <div>
                          <Radio style={radioStyle} value={1}>
                            <Form.Item
                              {...field}
                              validateTrigger={['onChange', 'onBlur']}
                              rules={[
                                {
                                  required: true,
                                  whitespace: true,
                                  message: 'Please input option',
                                },
                              ]}
                              noStyle
                            >
                              <Input
                                placeholder="Please input option"
                                style={{width: '60%'}}
                                value={options[index]}
                                onChange={e => {
                                  let newOptions = [...options];
                                  newOptions[index] = e.target.value;
                                  updateQuestion({
                                    ...question,
                                    options: newOptions,
                                  });
                                }}
                              />
                            </Form.Item>
                          </Radio>
                        </div>
                      </Col>
                      <Col span={8}>
                        <div>
                          {fields.length > 1 ? (
                            <MinusCircleOutlined
                              className="dynamic-delete-button"
                              style={{margin: '0 8px'}}
                              onClick={() => {
                                remove(field.name);
                                let newOptions = update(options, {
                                  $splice: [[field.name, 1]],
                                });
                                updateQuestion({
                                  ...question,
                                  options: newOptions,
                                });
                              }}
                            />
                          ) : null}
                        </div>
                      </Col>
                    </Row>
                  </div>
                </Form.Item>
              ))}
              <Form.Item>
                <Button
                  type="dashed"
                  onClick={() => {
                    add();
                    updateQuestion({
                      ...question,
                      options: [...options, ''],
                    });
                  }}
                  style={{width: '60%'}}
                >
                  <PlusOutlined /> Add option
                </Button>
              </Form.Item>
            </div>
          );
        }}
      </Form.List>
    </Form>
  );
}
Example #18
Source File: CreateNewUserForm.tsx    From wildduck-ui with MIT License 4 votes vote down vote up
CreateNewUserForm: React.FC = () => {
	const { mutate, isSuccess, data } = useCreateUser();

	const history = useHistory();

	if (isSuccess && !_.get(data, 'data.error')) {
		history.push(`${getBasePath()}/users`);
	}

	const [form] = Form.useForm();

	const onFinish = (values: any) => {
		const mailboxes = {};
		const metaData = {};

		const userDetails: any = _.reduce(
			values,
			(acc, value, index) => {
				if (!_.isEmpty(value) || value === false || value === true) {
					if (index === 'sent' || index === 'junk' || index === 'drafts' || index === 'trash') {
						_.set(mailboxes, `${index}`, value);
					} else if (index === 'metaData') {
						value.forEach((data: any) => {
							_.set(metaData, `${data.key}`, data.value);
						});
					} else {
						_.set(acc, `${index}`, value);
					}
				}

				return acc;
			},
			{},
		);

		_.set(userDetails, 'mailboxes', mailboxes);
		_.set(userDetails, 'metaData', metaData);

		mutate(userDetails);
	};

	const handleReset = () => {
		form.resetFields();
	};

	return (
		<Form
			form={form}
			name='basic'
			labelCol={{ span: 12 }}
			wrapperCol={{ span: 12 }}
			initialValues={{
				remember: true,
				emptyAddress: false,
				hashedPassword: false,
				allowUnsafe: false,
				requirePasswordChange: false,
				addTagsToAddress: false,
				uploadSentMessages: false,
				encryptMessages: false,
				encryptForwarded: false,
				spamLevel: 50,
				sent: 'Sent Mail',
				junk: 'Junk',
				drafts: 'Draft',
				trash: 'Trash',
			}}
			onFinish={onFinish}
		>
			<Row>
				<Col offset={20}>
					<Form.Item>
						<Space size='middle'>
							<Button type='primary' htmlType='submit'>
								Create
							</Button>
							<Button type='default' htmlType='button' onClick={handleReset}>
								Reset
							</Button>
						</Space>
					</Form.Item>
				</Col>
			</Row>
			<Row style={{ paddingTop: 20 }}>
				<Col span={10}>
					<Form.Item
						label='Username'
						name='username'
						rules={[
							{
								required: true,
								message: 'Please input your username!',
							},
						]}
						tooltip={userTooltip.username}
					>
						<Input />
					</Form.Item>
					<Form.Item
						label='Name'
						name='name'
						rules={[
							{
								required: true,
								message: 'Please input your name!',
							},
						]}
						tooltip={userTooltip.name}
					>
						<Input />
					</Form.Item>
					<Form.Item
						label='Email address'
						name='address'
						rules={[
							{
								required: false,
								message: 'Please input your address!',
							},
						]}
						tooltip={userTooltip.address}
					>
						<Input />
					</Form.Item>
					<Form.Item
						label='Password'
						name='password'
						rules={[
							{
								required: true,
								message: 'Please input your password!',
							},
						]}
					>
						<Input.Password minLength={6} />
					</Form.Item>
					<Form.Item
						label='Empty Address'
						name='emptyAddress'
						tooltip={userTooltip.emptyAddress}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item
						label='Hashed Password'
						name='hashedPassword'
						tooltip={userTooltip.hashedPassword}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item
						label='Required Password Change'
						name='requirePasswordChange'
						tooltip={userTooltip.requirePasswordChange}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item
						label='Allow Unsafe'
						name='allowUnsafe'
						tooltip={userTooltip.allowUnsafe}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item label='Tags' name='tags' tooltip={userTooltip.tags}>
						<Select mode='tags' style={{ width: '100%' }} placeholder='Enter Tags' />
					</Form.Item>
					<Form.Item
						label='Add Tags to Address'
						name='addTagsToAddress'
						tooltip={userTooltip.addTagsToAddress}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item label='Retention' name='retention' tooltip={userTooltip.retention}>
						<Input />
					</Form.Item>
					<Form.Item
						label='Upload sent messages'
						name='uploadSentMessages'
						tooltip={userTooltip.uploadSentMessages}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item label='Public PGP Key' name='pubKey' tooltip={userTooltip.pubKey}>
						<Input />
					</Form.Item>
					<Form.Item
						label='Encrypt Messages'
						name='encryptMessages'
						tooltip={userTooltip.encryptMessages}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item
						label='Encrypt Forwarded'
						name='encryptForwarded'
						tooltip={userTooltip.encryptForwarded}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item label='Meta Data' name='metaData' tooltip={userTooltip.metaData}>
						<Form.List name='metaData'>
							{(fields, { add, remove }) => (
								<>
									{fields.map((field) => (
										<Space
											key={field.key}
											style={{ display: 'flex', marginBottom: 8 }}
											align='baseline'
										>
											<Form.Item
												{...field}
												name={[field.name, 'key']}
												fieldKey={[field.fieldKey, 'key']}
												rules={[{ required: true, message: 'Missing Key' }]}
											>
												<Input placeholder='Key' />
											</Form.Item>
											<Form.Item
												{...field}
												name={[field.name, 'value']}
												fieldKey={[field.fieldKey, 'value']}
												rules={[{ required: true, message: 'Missing Value' }]}
											>
												<Input placeholder='Value' />
											</Form.Item>
											<MinusCircleOutlined onClick={() => remove(field.name)} />
										</Space>
									))}
									<Form.Item>
										<Button type='dashed' onClick={() => add()} block icon={<PlusOutlined />}>
											Add field
										</Button>
									</Form.Item>
								</>
							)}
						</Form.List>
					</Form.Item>
				</Col>
				<Col span={10} offset={1}>
					<Form.Item label='Targets' name='targets' tooltip={userTooltip.targets}>
						<Select mode='tags' style={{ width: '100%' }} placeholder='Enter Targets' />
					</Form.Item>
					<Form.Item label='Spam Level' name='spamLevel' tooltip={userTooltip.spamLevel}>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item label='Quota' name='quota' tooltip={userTooltip.quota}>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item label='Recipients' name='recipients' tooltip={userTooltip.recipients}>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item label='Forwards' name='forwards' tooltip={userTooltip.forwards}>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item label='Max Upload for imap ' name='imapMaxUpload' tooltip={userTooltip.imapMaxUpload}>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item
						label='Max Download for imap'
						name='imapMaxDownload'
						tooltip={userTooltip.imapMaxDownload}
					>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item
						label='Max Connections for imap'
						name='imapMaxConnections'
						tooltip={userTooltip.imapMaxConnections}
					>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item label='Max messages from MX' name='receivedMax' tooltip={userTooltip.receivedMax}>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item label='Mailboxes' name='mailboxes' tooltip={userTooltip.mailboxes}>
						<Form.Item label='Sent Mail' name='sent' tooltip={userTooltip.sent}>
							<Input />
						</Form.Item>
						<Form.Item label='Junk' name='junk' tooltip={userTooltip.junk}>
							<Input />
						</Form.Item>
						<Form.Item label='Drafts' name='drafts' tooltip={userTooltip.drafts}>
							<Input />
						</Form.Item>
						<Form.Item label='Trash' name='trash' tooltip={userTooltip.trash}>
							<Input />
						</Form.Item>
					</Form.Item>
					<Form.Item
						label='Disabled Scopes'
						name='disabledScopes'
						rules={[
							{
								required: false,
								message: 'List of scopes that are disabled for this user ("imap", "pop3", "smtp")',
							},
						]}
						tooltip={userTooltip.disabledScopes}
					>
						<Select mode='tags' style={{ width: '100%' }} placeholder='Select Scopes to disable'>
							{_.map(['imap', 'pop3', 'smtp'], (tag) => {
								return (
									<Option key={tag} value={tag}>
										{tag}
									</Option>
								);
							})}
						</Select>
					</Form.Item>
					<Form.Item label='Whitelist' name='fromWhitelist' tooltip={userTooltip.fromWhitelist}>
						<Select mode='tags' style={{ width: '100%' }} placeholder='Enter addresses to whitelist' />
					</Form.Item>
				</Col>
			</Row>
		</Form>
	);
}
Example #19
Source File: UserDetailsForm.tsx    From wildduck-ui with MIT License 4 votes vote down vote up
UserDetailsForm: React.FC<any> = (props: any) => {
	const { data } = props;

	const [form] = Form.useForm();

	const { mutate } = useUpdateUserDetails();

	const onFinish = (values: any) => {
		const metaData = {};
		const details: any = _.reduce(
			values,
			(acc, value, index) => {
				if (!_.isUndefined(value) || value === false || value === true) {
					if (index === 'metaData') {
						value.forEach((eachMetaData: any) => {
							_.set(metaData, `${eachMetaData.key}`, eachMetaData.value);
						});
					} else if (
						index === 'quota' ||
						index === 'imapDownload' ||
						index === 'pop3Download' ||
						index === 'imapUpload'
					) {
						_.set(acc, `${index}`, value * 1024000);
					} else {
						_.set(acc, `${index}`, value);
					}
				}
				return acc;
			},
			{},
		);

		_.set(details, 'metaData', metaData);

		mutate({
			userId: _.get(data, 'id'),
			userDetails: details,
		});
	};

	const handleReset = () => {
		form.resetFields();
	};

	return _.isUndefined(data) ? null : (
		<Form
			form={form}
			name='basic'
			labelCol={{ span: 12 }}
			wrapperCol={{ span: 12 }}
			initialValues={{
				remember: true,
				...data,
				allowUnsafe: false,
				hashedPassword: false,
				uploadSentMessages: false,
				disable2fa: false,
				retention: _.get(data, 'retention', false) || 0,
				quota: _.round(_.get(data, 'limits.quota.allowed', 0) / 1024000, 2),
				recipients: _.get(data, 'limits.recipients.allowed', 0),
				forwards: _.get(data, 'limits.forwards.allowed', 0),
				receivedMax: _.get(data, 'limits.received.allowed', 0),
				imapMaxDownload: _.round(_.get(data, 'limits.imapDownload.allowed', 0) / 1024000, 2),
				imapMaxConnections: _.get(data, 'limits.imapMaxConnections.allowed', 0),
				imapMaxUpload: _.round(_.get(data, 'limits.imapUpload.allowed', 0) / 1024000, 2),
				metaData: _.map(_.get(data, 'metaData', {}), (value, key) => {
					return { key: key, value: value };
				}),
			}}
			onFinish={onFinish}
		>
			<Row>
				<Col offset={20}>
					<Form.Item>
						<Space size='middle'>
							<Button type='primary' htmlType='submit'>
								Save
							</Button>
							<Button type='default' htmlType='button' onClick={handleReset}>
								Reset
							</Button>
						</Space>
					</Form.Item>
				</Col>
			</Row>
			<Row style={{ paddingTop: 20 }}>
				<Col span={10}>
					<Form.Item label='Name' name='name' tooltip={userTooltip.name}>
						<Input />
					</Form.Item>
					<Form.Item label='Password' name='password'>
						<Input.Password />
					</Form.Item>
					<Form.Item
						label='Hash the Password'
						name='hashedPassword'
						tooltip={userTooltip.hashedPassword}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item
						label='Allow Unsafe'
						name='allowUnsafe'
						tooltip={userTooltip.allowUnsafe}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item label='Tags' name='tags' tooltip={userTooltip.tags}>
						<Select mode='tags' style={{ width: '100%' }} placeholder='Enter Tags' />
					</Form.Item>
					<Form.Item label='Retention' name='retention' tooltip={userTooltip.retention}>
						<Input type='number' min={0} />
					</Form.Item>
					<Form.Item
						label='Upload sent messages'
						name='uploadSentMessages'
						tooltip={userTooltip.uploadSentMessages}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item label='Public PGP Key' name='pubKey' tooltip={userTooltip.pubKey}>
						<Input />
					</Form.Item>
					<Form.Item
						label='Encrypt Messages'
						name='encryptMessages'
						tooltip={userTooltip.encryptMessages}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item
						label='Encrypt Forwarded'
						name='encryptForwarded'
						tooltip={userTooltip.encryptForwarded}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item label='Meta Data' tooltip={userTooltip.metaData}>
						<Form.List name='metaData'>
							{(fields, { add, remove }) => (
								<>
									{fields.map((field) => (
										<Space
											key={field.key}
											style={{ display: 'flex', marginBottom: 8 }}
											align='baseline'
										>
											<Form.Item
												{...field}
												name={[field.name, 'key']}
												fieldKey={[field.fieldKey, 'key']}
												rules={[{ required: true, message: 'Missing Key' }]}
											>
												<Input placeholder='Key' />
											</Form.Item>
											<Form.Item
												{...field}
												name={[field.name, 'value']}
												fieldKey={[field.fieldKey, 'value']}
												rules={[{ required: true, message: 'Missing Value' }]}
											>
												<Input placeholder='Value' />
											</Form.Item>
											<MinusCircleOutlined onClick={() => remove(field.name)} />
										</Space>
									))}
									<Form.Item>
										<Button type='dashed' onClick={() => add()} block icon={<PlusOutlined />}>
											Add field
										</Button>
									</Form.Item>
								</>
							)}
						</Form.List>
					</Form.Item>
					<Form.Item label='Targets' name='targets' tooltip={userTooltip.targets}>
						<Select mode='tags' style={{ width: '100%' }} placeholder='Enter  Targets' />
					</Form.Item>
				</Col>
				<Col span={10} offset={1}>
					<Form.Item label='Spam Level' name='spamLevel' tooltip={userTooltip.spamLevel}>
						<Input type={'number'} max={100} min={0} />
					</Form.Item>
					<Form.Item label='Quota' name='quota' tooltip={userTooltip.quota}>
						<Input type={'number'} addonAfter='MB' />
					</Form.Item>
					<Form.Item label='Max Recipients / Day' name='recipients' tooltip={userTooltip.recipients}>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item label='Current Forwards / Day' name='forwards' tooltip={userTooltip.forwards}>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item
						label='Current Max Upload for imap '
						name='imapMaxUpload'
						tooltip={userTooltip.imapMaxUpload}
					>
						<Input type={'number'} addonAfter='MB' />
					</Form.Item>
					<Form.Item
						label='Current Max Download for imap '
						name='imapMaxDownload'
						tooltip={userTooltip.imapMaxDownload}
					>
						<Input type={'number'} addonAfter='MB' />
					</Form.Item>
					<Form.Item
						label='Current Max Connections for imap'
						name='imapMaxConnections'
						tooltip={userTooltip.imapMaxConnections}
					>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item
						label='Current Max messages from MX'
						name='receivedMax'
						tooltip={userTooltip.receivedMax}
					>
						<Input type={'number'} />
					</Form.Item>
					<Form.Item label='Disabled Scope' name='disabledScopes' tooltip={userTooltip.disabledScopes}>
						<Select mode='tags' style={{ width: '100%' }} placeholder='Select Scopes to disable'>
							{_.map(['imap', 'pop3', 'smtp'], (tag) => {
								return (
									<Option key={tag} value={tag}>
										{tag}
									</Option>
								);
							})}
						</Select>
					</Form.Item>
					<Form.Item
						label='Disable User'
						name='disabled'
						tooltip={userTooltip.disabled}
						valuePropName='checked'
					>
						<Switch />
					</Form.Item>
					<Form.Item label='Whitelist' name='fromWhitelist' tooltip={userTooltip.fromWhitelist}>
						<Select mode='tags' style={{ width: '100%' }} placeholder='Enter Whitelist' />
					</Form.Item>
					<Form.Item label='Suspend' name='suspended' tooltip={userTooltip.suspended} valuePropName='checked'>
						<Switch />
					</Form.Item>
				</Col>
			</Row>
		</Form>
	);
}
Example #20
Source File: index.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
TeamForm = React.forwardRef<ReactNode, TeamProps>((props, ref) => {
  const { t } = useTranslation();
  const { businessId, action } = props;
  const [form] = Form.useForm();
  const [userTeam, setUserTeam] = useState<Team[]>([]);
  const [initialValues, setInitialValues] = useState({
    label_enable: false,
    label_value: '',
    members: [{ perm_flag: true }],
    name: '',
  });
  const [loading, setLoading] = useState<boolean>(true);
  const [refresh, setRefresh] = useState(true);
  useImperativeHandle(ref, () => ({
    form: form,
  }));

  useEffect(() => {
    if (businessId && action === ActionType.EditBusiness) {
      getTeamInfoDetail(businessId);
    } else {
      setLoading(false);
    }
  }, []);

  const getTeamInfoDetail = (id: string) => {
    getBusinessTeamInfo(id).then((data: { name: string; label_enable: number; label_value: string; user_groups: { perm_flag: string; user_group: { id: number } }[] }) => {
      setInitialValues({
        name: data.name,
        label_enable: data.label_enable === 1,
        label_value: data.label_value,
        members: data.user_groups.map((item) => ({
          perm_flag: item.perm_flag === 'rw',
          user_group_id: item.user_group.id,
        })),
      });
      setLoading(false);
    });
  };

  useEffect(() => {
    getList('');
  }, []);

  const getList = (str: string) => {
    getTeamInfoList({ query: str }).then((res) => {
      setUserTeam(res.dat);
    });
  };

  const debounceFetcher = useCallback(debounce(getList, 800), []);

  return !loading ? (
    <Form {...layout} form={form} initialValues={initialValues} preserve={false} layout={refresh ? 'horizontal' : 'horizontal'}>
      {action !== ActionType.AddBusinessMember && (
        <>
          <Form.Item
            label={t('业务组名称')}
            name='name'
            rules={[
              {
                required: true,
                message: t('业务组名称不能为空!'),
              },
            ]}
          >
            <Input />
          </Form.Item>
          <Form.Item
            label={t('作为标签使用')}
            name='label_enable'
            valuePropName='checked'
            tooltip={{ title: '系统会自动把业务组的英文标识作为标签附到该业务组下辖监控对象的时序数据上', getPopupContainer: () => document.body }}
          >
            <Switch />
          </Form.Item>

          <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.label_enable !== curValues.label_enable}>
            {({ getFieldValue }) => {
              return (
                getFieldValue('label_enable') && (
                  <Form.Item
                    label={t('英文标识')}
                    name='label_value'
                    rules={[
                      {
                        required: true,
                      },
                    ]}
                    tooltip={{
                      title: (
                        <span>
                          尽量用英文,不能与其他业务组标识重复,系统会自动生成 <Tag color='blue'>busigroup={form.getFieldValue('label_value')}</Tag> 的标签
                        </span>
                      ),
                      getPopupContainer: () => document.body,
                    }}
                  >
                    <Input
                      onChange={(val) => {
                        setRefresh(!refresh);
                      }}
                    />
                  </Form.Item>
                )
              );
            }}
          </Form.Item>
        </>
      )}

      {(action === ActionType.CreateBusiness || action === ActionType.AddBusinessMember) && (
        <Form.Item
          label={t('团队')}
          required
          // tooltip={{
          //   title: '默认可读勾选可写',
          // }}
        >
          <Form.List name='members'>
            {(fields, { add, remove }) => (
              <>
                {fields.map(({ key, name, fieldKey, ...restField }) => (
                  <Space key={key} style={{ display: 'flex', marginBottom: 8 }} align='baseline'>
                    <Form.Item
                      style={{ width: 450 }}
                      {...restField}
                      name={[name, 'user_group_id']}
                      fieldKey={[fieldKey, 'user_group_id']}
                      rules={[{ required: true, message: t('业务组团队不能为空!') }]}
                    >
                      <Select
                        suffixIcon={<CaretDownOutlined />}
                        style={{ width: '100%' }}
                        filterOption={false}
                        onSearch={(e) => debounceFetcher(e)}
                        showSearch
                        onBlur={() => getList('')}
                      >
                        {userTeam.map((team) => (
                          <Option key={team.id} value={team.id}>
                            {team.name}
                          </Option>
                        ))}
                      </Select>
                    </Form.Item>
                    <Form.Item {...restField} name={[name, 'perm_flag']} fieldKey={[fieldKey, 'perm_flag']} valuePropName='checked'>
                      <Switch checkedChildren='读写' unCheckedChildren='只读' />
                    </Form.Item>
                    <MinusCircleOutlined onClick={() => remove(name)} />
                  </Space>
                ))}
                <Form.Item>
                  <Button type='dashed' onClick={() => add()} block icon={<PlusOutlined />}>
                    添加团队
                  </Button>
                </Form.Item>
              </>
            )}
          </Form.List>
        </Form.Item>
      )}
    </Form>
  ) : null;
})
Example #21
Source File: chartConfigModal.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
// 新增图表和编辑图表均在此组件

export default function ChartConfigModal(props: Props) {
  const { t } = useTranslation();
  const { busiId, groupId, show, onVisibleChange, initialValue, variableConfig, cluster, id } = props;
  const layout = initialValue?.configs.layout;
  const [innerVariableConfig, setInnerVariableConfig] = useState<VariableType | undefined>(variableConfig);
  const [chartForm] = Form.useForm();
  const [initialQL, setInitialQL] = useState([{ PromQL: '' }]);
  const [legend, setLegend] = useState<boolean>(initialValue?.configs.legend || false);
  const [step, setStep] = useState<number | null>(null);
  const [highLevelConfig, setHighLevelConfig] = useState<HighLevelConfigType>(
    initialValue?.configs.highLevelConfig || {
      shared: true,
      sharedSortDirection: 'desc',
      precision: 'short',
      formatUnit: 1000,
    },
  );
  const [range, setRange] = useState<Range>({
    start: 0,
    end: 0,
  });
  useEffect(() => {
    if (initialValue) {
      chartForm.setFieldsValue(initialValue.configs);
      setInitialQL(initialValue.configs.QL);
    }
  }, [initialValue]);

  const handleAddChart = async (e) => {
    try {
      await chartForm.validateFields();
      let formData: ChartConfig = Object.assign(
        chartForm.getFieldsValue(),
        { legend, highLevelConfig },
        {
          version: 1, // Temporarily, hardcode 1
          layout,
        },
      );
      if (initialValue && initialValue.id) {
        await updateCharts(busiId, [
          {
            configs: formData,
            weight: 0,
            group_id: groupId,
            id: initialValue.id,
          },
        ]);
      } else {
        await createChart(busiId, {
          configs: JSON.stringify(formData),
          weight: 0,
          group_id: groupId,
        });
      }

      onVisibleChange(true);
    } catch (errorInfo) {
      console.log('Failed:', errorInfo);
    }
  };

  const PromqlEditorField = ({ onChange = (e: any) => {}, value = '', fields, remove, add, index, name }) => {
    return (
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <PromqlEditor
          xCluster='Default'
          onChange={onChange}
          value={value}
          style={{
            width: '310px',
            // flex: 1,
          }}
        />
        {fields.length > 1 ? (
          <MinusCircleOutlined
            style={{ marginLeft: 10 }}
            onClick={() => {
              remove(name);
            }}
          />
        ) : null}
        {index === fields.length - 1 && (
          <PlusCircleOutlined
            style={{ marginLeft: 10 }}
            onClick={() => {
              add();
            }}
          />
        )}
      </div>
    );
  };

  const handleVariableChange = (value) => {
    setInnerVariableConfig(value);
  };

  const aggrFuncMenu = (
    <Menu
      onClick={(sort) => {
        setHighLevelConfig({ ...highLevelConfig, sharedSortDirection: (sort as { key: 'desc' | 'asc' }).key });
      }}
      selectedKeys={[highLevelConfig.sharedSortDirection]}
    >
      <Menu.Item key='desc'>desc</Menu.Item>
      <Menu.Item key='asc'>asc</Menu.Item>
    </Menu>
  );

  const precisionMenu = (
    <Menu
      onClick={(precision) => {
        const precisionKey = isNaN(Number(precision.key)) ? precision.key : Number(precision.key);
        setHighLevelConfig({ ...highLevelConfig, formatUnit: precisionKey as 1024 | 1000 | 'humantime' });
      }}
      selectedKeys={[String(highLevelConfig.formatUnit)]}
    >
      <Menu.Item key={'1000'}>Ki, Mi, Gi by 1000</Menu.Item>
      <Menu.Item key={'1024'}>Ki, Mi, Gi by 1024</Menu.Item>
      <Menu.Item key={'humantime'}>Human time duration</Menu.Item>
    </Menu>
  );

  const formatUnitInfoMap = {
    1024: 'Ki, Mi, Gi by 1024',
    1000: 'Ki, Mi, Gi by 1000',
    humantime: 'Human time duration',
  };

  const getContent = () => {
    const aggrFuncMenu = (
      <Menu
        onClick={(sort) => {
          setHighLevelConfig({ ...highLevelConfig, sharedSortDirection: (sort as { key: 'desc' | 'asc' }).key });
        }}
        selectedKeys={[highLevelConfig.sharedSortDirection]}
      >
        <Menu.Item key='desc'>desc</Menu.Item>
        <Menu.Item key='asc'>asc</Menu.Item>
      </Menu>
    );
    const precisionMenu = (
      <Menu
        onClick={(precision) => {
          const precisionKey = isNaN(Number(precision.key)) ? precision.key : Number(precision.key);
          setHighLevelConfig({ ...highLevelConfig, formatUnit: precisionKey as 1024 | 1000 | 'humantime' });
        }}
        selectedKeys={[String(highLevelConfig.formatUnit)]}
      >
        <Menu.Item key={'1000'}>Ki, Mi, Gi by 1000</Menu.Item>
        <Menu.Item key={'1024'}>Ki, Mi, Gi by 1024</Menu.Item>
        <Menu.Item key={'humantime'}>Human time duration</Menu.Item>
      </Menu>
    );
    return (
      <div>
        <Checkbox
          checked={highLevelConfig.shared}
          onChange={(e) => {
            setHighLevelConfig({ ...highLevelConfig, shared: e.target.checked });
          }}
        >
          Multi Series in Tooltip, order value
        </Checkbox>
        <Dropdown overlay={aggrFuncMenu}>
          <a className='ant-dropdown-link' onClick={(e) => e.preventDefault()}>
            {highLevelConfig.sharedSortDirection} <DownOutlined />
          </a>
        </Dropdown>
        <br />
        <Checkbox
          checked={legend}
          onChange={(e) => {
            setLegend(e.target.checked);
          }}
        >
          Show Legend
        </Checkbox>
        <br />
        <Checkbox
          checked={highLevelConfig.precision === 'short'}
          onChange={(e) => {
            setHighLevelConfig({ ...highLevelConfig, precision: e.target.checked ? 'short' : 'origin' });
          }}
        >
          Value format with:{' '}
        </Checkbox>
        <Dropdown overlay={precisionMenu}>
          <a className='ant-dropdown-link' onClick={(e) => e.preventDefault()}>
            {formatUnitInfoMap[highLevelConfig.formatUnit]} <DownOutlined />
          </a>
        </Dropdown>
      </div>
    );
  };

  return (
    <Modal
      title={
        <div style={{ display: 'flex', alignItems: 'center' }}>
          <div>{initialValue ? t('编辑图表') : t('新建图表')}</div>
          <div style={{ flex: 1, display: 'flex', justifyContent: 'flex-end', alignItems: 'center', fontSize: 12, lineHeight: '20px' }}>
            <DateRangePicker onChange={(e) => setRange(e)} />
            <Resolution onChange={(v) => setStep(v)} initialValue={step} />
            <CloseOutlined
              style={{ fontSize: 18 }}
              onClick={() => {
                onVisibleChange(false);
              }}
            />
          </div>
        </div>
      }
      width={900}
      visible={show}
      destroyOnClose={true}
      onOk={handleAddChart}
      closable={false}
      onCancel={() => {
        onVisibleChange(false);
      }}
    >
      <Form {...layout} form={chartForm} preserve={false}>
        <Row>
          <Col span={12}>
            <VariableConfig onChange={handleVariableChange} value={innerVariableConfig} editable={false} cluster={cluster} range={range} id={id} />
            <br />
            <Form.Item
              label={t('标题')}
              name='name'
              labelCol={{
                span: 4,
              }}
              wrapperCol={{
                span: 20,
              }}
              rules={[
                {
                  required: true,
                  message: t('图表名称'),
                },
              ]}
            >
              <Input />
            </Form.Item>
            <Form.Item label={t('下钻链接')} name='link' labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}>
              <Input />
            </Form.Item>

            <Form.Item
              wrapperCol={{
                span: 24,
              }}
              style={{
                marginBottom: '0px',
              }}
            >
              <Form.List name='QL' initialValue={initialQL}>
                {(fields, { add, remove }, { errors }) => {
                  return (
                    <>
                      {fields.length ? (
                        fields.map(({ key, name, fieldKey, ...restField }, index) => {
                          return (
                            <div key={name + fieldKey}>
                              <Form.Item
                                label='PromQL'
                                name={[name, 'PromQL']}
                                labelCol={{
                                  span: 4,
                                }}
                                wrapperCol={{
                                  span: 20,
                                }}
                                validateTrigger={['onBlur']}
                                rules={[
                                  {
                                    required: true,
                                    message: t('请输入PromQL'),
                                  },
                                ]}
                              >
                                <PromqlEditorField key={name + fieldKey} name={name} fields={fields} index={index} remove={remove} add={add} />
                              </Form.Item>
                              <Form.Item
                                label='Legend'
                                name={[name, 'Legend']}
                                tooltip={{
                                  getPopupContainer: () => document.body,
                                  title:
                                    'Controls the name of the time series, using name or pattern. For example {{hostname}} will be replaced with label value for the label hostname.',
                                }}
                                labelCol={{
                                  span: 4,
                                }}
                                wrapperCol={{
                                  span: 20,
                                }}
                              >
                                <Input />
                              </Form.Item>
                            </div>
                          );
                        })
                      ) : (
                        <PlusCircleOutlined
                          onClick={() => {
                            add();
                          }}
                        />
                      )}
                      <Form.ErrorList errors={errors} />
                    </>
                  );
                }}
              </Form.List>
            </Form.Item>
            <Row>
              <Col span={11}>
                <Form.Item label={t('预警值')} name='yplotline1' labelCol={{ span: 9 }} wrapperCol={{ span: 16 }}>
                  <InputNumber />
                </Form.Item>
              </Col>
              <Col span={12} offset={1}>
                <Form.Item label={t('危险值')} name='yplotline2' labelCol={{ span: 7 }} wrapperCol={{ span: 20 }}>
                  <InputNumber />
                </Form.Item>
              </Col>

              {/* <Col span={23} offset={1}>
                <Form.Item>
                  <Checkbox
                    checked={highLevelConfig.shared}
                    onChange={(e) => {
                      setHighLevelConfig({ ...highLevelConfig, shared: e.target.checked });
                    }}
                  >
                    Multi Series in Tooltip, order value
                  </Checkbox>
                  <Dropdown overlay={aggrFuncMenu}>
                    <a className='ant-dropdown-link' onClick={(e) => e.preventDefault()}>
                      {highLevelConfig.sharedSortDirection} <DownOutlined />
                    </a>
                  </Dropdown>
                </Form.Item>
              </Col>
              <Col span={23} offset={1}>
                <Form.Item>
                  <Checkbox
                    checked={legend}
                    onChange={(e) => {
                      setLegend(e.target.checked);
                    }}
                  >
                    Show Legend
                  </Checkbox>
                </Form.Item>
              </Col>
              <Col span={23} offset={1}>
                <Form.Item>
                  <Checkbox
                    checked={highLevelConfig.precision === 'short'}
                    onChange={(e) => {
                      setHighLevelConfig({ ...highLevelConfig, precision: e.target.checked ? 'short' : 'origin' });
                    }}
                  >
                    Value format with:{' '}
                  </Checkbox>
                  <Dropdown overlay={precisionMenu}>
                    <a className='ant-dropdown-link' onClick={(e) => e.preventDefault()}>
                      {formatUnitInfoMap[highLevelConfig.formatUnit]} <DownOutlined />
                    </a>
                  </Dropdown>
                </Form.Item>
              </Col> */}
            </Row>
          </Col>
          <Col span={12}>
            <Form.Item
              wrapperCol={{ span: 22, offset: 2 }}
              shouldUpdate={(prevValues, curValues) =>
                prevValues.QL !== curValues.QL || prevValues.multi === curValues.multi || prevValues.legend === curValues.legend || prevValues.format === curValues.format
              }
            >
              {({ getFieldsValue }) => {
                const { QL = [], yplotline1, yplotline2 } = getFieldsValue();
                const promqls = QL.filter((item) => item && item.PromQL).map((item) =>
                  innerVariableConfig ? replaceExpressionVars(item.PromQL, innerVariableConfig, innerVariableConfig.var.length, id) : item.PromQL,
                );
                const legendTitleFormats = QL.map((item) => item && item.Legend);
                return (
                  <div className={legend ? 'graph-container graph-container-hasLegend' : 'graph-container'}>
                    <div className='graph-header' style={{ height: '35px', lineHeight: '35px', display: 'flex', justifyContent: 'space-between' }}>
                      <div>预览图表</div>
                      <div className='graph-extra'>
                        <span className='graph-operationbar-item' key='info'>
                          <Popover placement='left' content={getContent()} trigger='click' autoAdjustOverflow={false} getPopupContainer={() => document.body}>
                            <Button className='' type='link' size='small' onClick={(e) => e.preventDefault()}>
                              <SettingOutlined />
                            </Button>
                          </Popover>
                        </span>
                      </div>
                    </div>
                    <Graph
                      showHeader={false}
                      graphConfigInnerVisible={false}
                      highLevelConfig={highLevelConfig}
                      data={{
                        yAxis: {
                          plotLines: [
                            {
                              value: yplotline1 ? yplotline1 : undefined,
                              color: 'orange',
                            },
                            {
                              value: yplotline2 ? yplotline2 : undefined,
                              color: 'red',
                            },
                          ],
                        },
                        legend: legend,
                        step,
                        range,
                        promqls,
                        legendTitleFormats,
                      }}
                    />
                  </div>
                );
                // ) : null;
              }}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    </Modal>
  );
}
Example #22
Source File: info.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
export default function Info() {
  const { t } = useTranslation();
  const [form] = Form.useForm();
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [contactsList, setContactsList] = useState<ContactsItem[]>([]);
  let { profile } = useSelector<RootState, accountStoreState>((state) => state.account);
  const [selectAvatar, setSelectAvatar] = useState<string>(profile.portrait || '/image/avatar1.png');
  const [customAvatar, setCustomAvatar] = useState('');
  const dispatch = useDispatch();
  useEffect(() => {
    const { id, nickname, email, phone, contacts, portrait } = profile;
    form.setFieldsValue({
      nickname,
      email,
      phone,
      contacts,
    });
    if (portrait.startsWith('http')) {
      setCustomAvatar(portrait);
    }
  }, [profile]);
  useEffect(() => {
    getNotifyChannels().then((data: Array<ContactsItem>) => {
      setContactsList(data);
    });
  }, []);

  const handleSubmit = async () => {
    try {
      await form.validateFields();
      updateProfile();
    } catch (err) {
      console.log(t('输入有误'), err);
    }
  };

  const handleOk = () => {
    if (customAvatar) {
      if (!customAvatar.startsWith('http')) {
        message.error(t('自定义头像需以http开头'));
        return;
      }

      fetch(customAvatar, { mode: 'no-cors' })
        .then((res) => {
          setIsModalVisible(false);
          handleSubmit();
        })
        .catch((err) => {
          message.error(t('自定义头像') + err);
        });
    } else {
      setIsModalVisible(false);
      handleSubmit();
    }
  };

  const handleCancel = () => {
    setIsModalVisible(false);
  };

  const updateProfile = () => {
    const { nickname, email, phone, moreContacts } = form.getFieldsValue();
    let { contacts } = form.getFieldsValue();

    if (moreContacts && moreContacts.length > 0) {
      moreContacts.forEach((item) => {
        const { key, value } = item;

        if (key && value) {
          if (contacts) {
            contacts[key] = value;
          } else {
            contacts = {
              [key]: value,
            };
          }
        }
      });
    }

    for (let key in contacts) {
      if (!contacts[key]) {
        delete contacts[key];
      }
    }

    dispatch({
      type: 'account/updateProfile',
      data: {
        ...profile,
        portrait: customAvatar || selectAvatar,
        nickname,
        email,
        phone,
        contacts,
      },
    });
    message.success(t('信息保存成功'));
  };

  const avatarList = new Array(8).fill(0).map((_, i) => i + 1);

  const handleImgClick = (i) => {
    setSelectAvatar(`/image/avatar${i}.png`);
  };

  return (
    <>
      <Form form={form} layout='vertical'>
        <Row
          gutter={16}
          style={{
            marginBottom: '24px',
          }}
        >
          <Col span={20}>
            <Row
              gutter={16}
              style={{
                marginBottom: '24px',
              }}
            >
              <Col span={4}>
                <div>
                  <label>{t('用户名')}:</label>
                  <span>{profile.username}</span>
                </div>
              </Col>
              <Col span={4}>
                <div>
                  <label>{t('角色')}:</label>
                  <span>{profile.roles.join(', ')}</span>
                </div>
              </Col>
            </Row>
            <Form.Item label={<span>{t('显示名')}:</span>} name='nickname'>
              <Input placeholder={t('请输入显示名')} />
            </Form.Item>
            <Form.Item label={<span>{t('邮箱')}:</span>} name='email'>
              <Input placeholder={t('请输入邮箱')} />
            </Form.Item>
            <Form.Item label={<span>{t('手机')}:</span>} name='phone'>
              <Input placeholder={t('请输入手机号')} />
            </Form.Item>

            {profile.contacts &&
              Object.keys(profile.contacts)
                .sort()
                .map((key, i) => {
                  let contact = contactsList.find((item) => item.key === key);
                  return (
                    <>
                      {contact ? (
                        <Form.Item label={contact.label + ':'} name={['contacts', key]} key={i}>
                          <Input placeholder={`${t('请输入')}${key}`} />
                        </Form.Item>
                      ) : null}
                    </>
                  );
                })}

            <Form.Item label={t('更多联系方式')}>
              <Form.List name='moreContacts'>
                {(fields, { add, remove }) => (
                  <>
                    {fields.map(({ key, name, fieldKey, ...restField }) => (
                      <Space
                        key={key}
                        style={{
                          display: 'flex',
                        }}
                        align='baseline'
                      >
                        <Form.Item
                          style={{
                            width: '180px',
                          }}
                          {...restField}
                          name={[name, 'key']}
                          fieldKey={[fieldKey, 'key']}
                          rules={[
                            {
                              required: true,
                              message: t('联系方式不能为空'),
                            },
                          ]}
                        >
                          <Select suffixIcon={<CaretDownOutlined />} placeholder={t('请选择联系方式')}>
                            {contactsList.map((item, index) => (
                              <Option value={item.key} key={index}>
                                {item.label}
                              </Option>
                            ))}
                          </Select>
                        </Form.Item>
                        <Form.Item
                          {...restField}
                          style={{
                            width: '330px',
                          }}
                          name={[name, 'value']}
                          fieldKey={[fieldKey, 'value']}
                          rules={[
                            {
                              required: true,
                              message: t('值不能为空'),
                            },
                          ]}
                        >
                          <Input placeholder={t('请输入值')} />
                        </Form.Item>
                        <MinusCircleOutlined className='control-icon-normal' onClick={() => remove(name)} />
                      </Space>
                    ))}
                    <PlusCircleOutlined className='control-icon-normal' onClick={() => add()} />
                  </>
                )}
              </Form.List>
            </Form.Item>

            <Form.Item>
              <Button type='primary' onClick={handleSubmit}>
                {t('确认修改')}
              </Button>
            </Form.Item>
          </Col>
          <Col span={4}>
            <div className='avatar'>
              <img src={profile.portrait || '/image/avatar1.png'} />
              <Button type='primary' className='update-avatar' onClick={() => setIsModalVisible(true)}>
                {t('更换头像')}
              </Button>
            </div>
          </Col>
        </Row>
      </Form>
      <Modal title={t('更换头像')} visible={isModalVisible} onOk={handleOk} onCancel={handleCancel} wrapClassName='avatar-modal'>
        <div className='avatar-content'>
          {avatarList.map((i) => {
            return (
              <div key={i} className={`/image/avatar${i}.png` === selectAvatar ? 'avatar active' : 'avatar'} onClick={() => handleImgClick(i)}>
                <img src={`/image/avatar${i}.png`} />
              </div>
            );
          })}
        </div>
        <Input addonBefore={<span>{t('头像URL')}:</span>} onChange={(e) => setCustomAvatar(e.target.value)} value={customAvatar} />
      </Modal>
    </>
  );
}
Example #23
Source File: index.tsx    From metaplex with Apache License 2.0 4 votes vote down vote up
InfoStep = (props: {
  attributes: IMetadataExtension;
  files: File[];
  isCollection: boolean;
  setIsCollection: (val: boolean) => void;
  setAttributes: (attr: IMetadataExtension) => void;
  confirm: () => void;
}) => {
  const { image } = useArtworkFiles(props.files, props.attributes);
  const [form] = Form.useForm();
  const { isCollection, setIsCollection } = props;
  const [selectedCollection, setSelectedCollection] = useState<
    Array<SafetyDepositDraft>
  >([]);

  const artistFilter = useCallback(
    (i: SafetyDepositDraft) =>
      !(i.metadata.info.data.creators || []).some((c: Creator) => !c.verified),
    [],
  );

  useEffect(() => {
    if (selectedCollection.length) {
      props.setAttributes({
        ...props.attributes,
        collection: selectedCollection[0].metadata.info.mint,
      });
    }
  }, [selectedCollection]);

  return (
    <>
      <Row className="call-to-action">
        <h2>Describe your item</h2>
        <p>
          Provide detailed description of your creative process to engage with
          your audience.
        </p>
      </Row>
      <Row className="content-action" justify="space-around">
        <Col>
          {props.attributes.image && (
            <ArtCard
              image={image}
              animationURL={props.attributes.animation_url}
              category={props.attributes.properties?.category}
              name={props.attributes.name}
              symbol={props.attributes.symbol}
              small={true}
              artView={!(props.files.length > 1)}
              className="art-create-card"
            />
          )}
        </Col>
        <Col className="section" style={{ minWidth: 300 }}>
          <label className="action-field">
            <span className="field-title">Title</span>
            <Input
              autoFocus
              className="input"
              placeholder="Max 50 characters"
              maxLength={50}
              allowClear
              value={props.attributes.name}
              onChange={info =>
                props.setAttributes({
                  ...props.attributes,
                  name: info.target.value,
                })
              }
            />
          </label>
          <label className="action-field">
            <span className="field-title">Symbol</span>
            <Input
              className="input"
              placeholder="Max 10 characters"
              maxLength={10}
              allowClear
              value={props.attributes.symbol}
              onChange={info =>
                props.setAttributes({
                  ...props.attributes,
                  symbol: info.target.value,
                })
              }
            />
          </label>
          <label className="action-field direction-row">
            <Checkbox
              checked={isCollection}
              onChange={val => {
                setIsCollection(val.target.checked);
              }}
            />
            <span className="field-title" style={{ marginLeft: '10px' }}>
              Is parent collection?
            </span>
          </label>
          {!isCollection && (
            <label className="action-field">
              <span className="field-title">Collection</span>
              <ArtSelector
                filter={artistFilter}
                selected={selectedCollection}
                setSelected={items => {
                  setSelectedCollection(items);
                }}
                allowMultiple={false}
              >
                Select NFT
              </ArtSelector>
            </label>
          )}
          <label className="action-field">
            <span className="field-title">Description</span>
            <Input.TextArea
              className="input textarea"
              placeholder="Max 500 characters"
              maxLength={500}
              value={props.attributes.description}
              onChange={info =>
                props.setAttributes({
                  ...props.attributes,
                  description: info.target.value,
                })
              }
              allowClear
            />
          </label>
          <label className="action-field">
            <span className="field-title">Maximum Supply</span>
            {!isCollection ? (
              <InputNumber
                placeholder="Quantity"
                value={props.attributes.properties.maxSupply}
                onChange={(val: number) => {
                  props.setAttributes({
                    ...props.attributes,
                    properties: {
                      ...props.attributes.properties,
                      maxSupply: val,
                    },
                  });
                }}
                className="royalties-input"
              />
            ) : (
              0
            )}
          </label>
          <label className="action-field">
            <span className="field-title">Attributes</span>
          </label>
          <Form name="dynamic_attributes" form={form} autoComplete="off">
            <Form.List name="attributes">
              {(fields, { add, remove }) => (
                <>
                  {fields.map(({ key, name }) => (
                    <Space key={key} align="baseline">
                      <Form.Item name={[name, 'trait_type']} hasFeedback>
                        <Input placeholder="trait_type (Optional)" />
                      </Form.Item>
                      <Form.Item
                        name={[name, 'value']}
                        rules={[{ required: true, message: 'Missing value' }]}
                        hasFeedback
                      >
                        <Input placeholder="value" />
                      </Form.Item>
                      <Form.Item name={[name, 'display_type']} hasFeedback>
                        <Input placeholder="display_type (Optional)" />
                      </Form.Item>
                      <MinusCircleOutlined onClick={() => remove(name)} />
                    </Space>
                  ))}
                  <Form.Item>
                    <Button
                      type="dashed"
                      onClick={() => add()}
                      block
                      icon={<PlusOutlined />}
                    >
                      Add attribute
                    </Button>
                  </Form.Item>
                </>
              )}
            </Form.List>
          </Form>
        </Col>
      </Row>

      <Row>
        <Button
          type="primary"
          size="large"
          onClick={() => {
            form.validateFields().then(values => {
              const nftAttributes = values.attributes;
              // value is number if possible
              for (const nftAttribute of nftAttributes || []) {
                const newValue = Number(nftAttribute.value);
                if (!isNaN(newValue)) {
                  nftAttribute.value = newValue;
                }
              }
              console.log('Adding NFT attributes:', nftAttributes);
              props.setAttributes({
                ...props.attributes,
                attributes: nftAttributes,
              });

              props.confirm();
            });
          }}
          className="action-btn"
        >
          Continue to royalties
        </Button>
      </Row>
    </>
  );
}
Example #24
Source File: EventsList.tsx    From jitsu with MIT License 4 votes vote down vote up
EventsList: React.FC<{
  type: EventType
  filterOptions: FilterOption[]
}> = ({ type, filterOptions }) => {
  const statusOptions = [
    { label: "All", value: null },
    { label: "Error", value: "error" },
  ]

  const listInnerRef = useRef()
  const location = useLocation()
  const params = new URLSearchParams(location.search)
  const [autoReload, setAutoReload] = useState(true)
  const [selectedEvent, setSelectedEvent] = useState(null)
  const [events, setEvents] = useState<Event[]>([])
  const [filteredEvents, setFilteredEvents] = useState<Event[]>([])
  const [term, setTerm] = useState(params.get("q"))
  const [idFilter, setIdFilter] = useState(
    filterOptions.find(f => f.value === params.get("id"))?.value ?? filterOptions[0]?.value
  )
  const [statusFilter, setStatusFilter] = useState(
    statusOptions.find(f => f.value === params.get("status"))?.value ?? statusOptions[0]?.value
  )
  const [reloadCount, setReloadCount] = useState(0)
  const services = useServices()
  const history = useHistory()

  const destinationsMap: Record<string, DestinationData> = destinationsStore.listIncludeHidden.reduce((index, dst) => {
    index[dst._uid] = dst
    return index
  }, {})

  useEffect(() => {
    if (!idFilter) {
      history.push({ search: null })
      return
    }
    let queryParams = omitBy({ type, id: idFilter, status: statusFilter }, isNull)
    if (term) {
      queryParams["q"] = term
    }
    history.push({ search: new URLSearchParams(queryParams).toString() })
  }, [idFilter, statusFilter, term])

  const { data, error } = useLoaderAsObject(() => {
    if (!idFilter) {
      return null
    }

    const ids = type === EventType.Destination ? `${services.activeProject.id}.${idFilter}` : idFilter
    setSelectedEvent(null)
    return services.backendApiClient
      .get(
        `/events/cache?project_id=${services.activeProject.id}&limit=500&namespace=${type}&ids=${ids}&status=${
          statusFilter ?? ""
        }`,
        { proxy: true }
      )
      .then(events => {
        return { events, id: idFilter }
      })
  }, [idFilter, statusFilter, reloadCount])

  useEffect(() => {
    const interval = setInterval(() => {
      if (!autoReload || selectedEvent) {
        return
      }
      setReloadCount(reloadCount + 1)
    }, 15000)
    return () => clearInterval(interval)
  }, [autoReload, selectedEvent, reloadCount])

  const filterByTerm = (events, term) => {
    return term ? events.filter(i => JSON.stringify(i.rawJson).indexOf(term) !== -1) : events
  }

  const search = term => {
    setTerm(term)
    setFilteredEvents(filterByTerm(events, term))
  }

  useEffect(() => {
    const initialEvents = error || !data ? [] : processEvents(type, data)
    setEvents(initialEvents)
    setFilteredEvents(filterByTerm(initialEvents, term))
  }, [error, data])

  if (!filterOptions.length) {
    return <NoDataFlowing showHint={true} />
  }

  const filters = (
    <>
      <div className={`mb-6 flex ${styles.filters}`}>
        <SelectFilter
          className="mr-5"
          label={type === EventType.Token ? "API Key" : "Destination"}
          initialValue={idFilter}
          options={filterOptions}
          onChange={option => {
            setIdFilter(option.value)
          }}
        />
        <SelectFilter
          className="mr-5"
          label="Status"
          initialValue={statusFilter}
          options={statusOptions}
          onChange={option => {
            setStatusFilter(option.value)
          }}
        />
        <Button
          size="large"
          type="primary"
          className={styles.reloadBtn}
          onClick={() => {
            setReloadCount(count => count + 1)
          }}
        >
          <ReloadOutlined /> Reload
        </Button>
      </div>
      <Input className="w-full" placeholder="Filter" value={term} onChange={e => search(e.target.value)} />
    </>
  )

  const eventStatusMessage = event => {
    const error = event.status === EventStatus.Error
    const skip = event.status === EventStatus.Skip

    if (type === EventType.Token) {
      if (skip) {
        return `Skip`
      }
      return error ? event.rawJson.error ?? "Error" : "Success"
    }

    return error
      ? "Failed - at least one destination load is failed"
      : skip
      ? "Skipped - event was not sent to destination"
      : "Success - successfully sent to destination"
  }

  if (error) {
    return (
      <div className="w-full">
        {filters}
        <CenteredError error={error} />
      </div>
    )
  } else if (!data) {
    return (
      <div className="w-full">
        {filters}
        <CenteredSpin />
      </div>
    )
  }

  const { last_minute_limited, cache_capacity_per_interval, interval_seconds } = data?.events
  const alert =
    last_minute_limited > 0 ? (
      <div className="mt-4">
        <Alert
          message={`This isn't a full list of all events. Jitsu doesn't cache all events, but the only ${cache_capacity_per_interval} event per ${interval_seconds} seconds. Other ${last_minute_limited} events from the last minute have been being processed and stored to the destinations but haven't been saved into the cache.`}
          type="warning"
        />
      </div>
    ) : null

  const onScroll = () => {
    if (!listInnerRef.current) {
      return
    }
    const { scrollTop } = listInnerRef.current
    const startAutoReload = scrollTop === 0
    if (startAutoReload === autoReload) {
      return
    }
    setAutoReload(startAutoReload)
  }

  return (
    <>
      {filters}
      {alert}
      <div
        className={`mt-3 transition-all duration-300 ${styles.autoReloadInfo} ${
          autoReload && !selectedEvent ? "" : "opacity-0"
        }`}
      >
        <ReloadOutlined spin={true} /> Auto reload is enabled. <a onClick={() => setAutoReload(false)}>Disable</a>
      </div>
      <div className={styles.eventsList} ref={listInnerRef} onScroll={onScroll}>
        {!filteredEvents.length ? <NoDataFlowing showHint={false} /> : null}
        {filteredEvents.map(event => {
          const active = event.eventId === selectedEvent
          return (
            <div key={event.eventId}>
              <div
                className={`overflow-hidden w-full flex flex-row border-b border-secondaryText border-opacity-50 items-center cursor-pointer h-12 ${
                  selectedEvent === event.eventId ? "bg-bgSecondary" : "hover:bg-bgComponent"
                }`}
                key="header"
                onClick={() => setSelectedEvent(active ? null : event.eventId)}
              >
                <div className="w-6 flex items-center justify-center px-3 text-lg" key="icon">
                  <Tooltip title={eventStatusMessage(event)}>
                    {event.status === EventStatus.Error ? (
                      <ExclamationCircleOutlined className="text-error" />
                    ) : event.status === EventStatus.Pending || event.status === EventStatus.Skip ? (
                      <MinusCircleOutlined className="text-warning" />
                    ) : (
                      <CheckCircleOutlined className="text-success" />
                    )}
                  </Tooltip>
                </div>
                <div
                  className={`text-xxs whitespace-nowrap text-secondaryText px-1 ${styles.timestampColumn}`}
                  key="time"
                >
                  <div>{event.timestamp.format("YYYY-MM-DD HH:mm:ss")} UTC</div>
                  <div className="text-xxs">{event.timestamp.fromNow()}</div>
                </div>
                <div
                  className="pl-4 text-3xs text-secondaryText font-monospace overflow-hidden overflow-ellipsis h-12 leading-4 flex-shrink"
                  key="json"
                >
                  {event.rawJson.malformed ? event.rawJson.malformed : JSON.stringify(event.rawJson, null, 2)}
                </div>
                <div
                  className={cn(
                    "w-12 text-testPale flex items-center justify-center px-2 text-xl transition-transform duration-500",
                    styles.expandBtn,
                    active && "transform rotate-90"
                  )}
                  key="expand"
                >
                  <RightCircleOutlined />
                </div>
              </div>
              <div key="details">
                {active && <EventsView event={event} allDestinations={destinationsMap} className="pb-6" />}
              </div>
            </div>
          )
        })}
      </div>
    </>
  )
}
Example #25
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Save: React.FC<Props> = props => {

    const { form: { getFieldDecorator }, form } = props;

    const [bridgeConfigs, setBridgeConfigs] = useState<any>([]);
    const [accessConfig, setAccessConfig] = useState({});
    const [productList, setProductList] = useState([]);
    const [protocolSupport, setProtocolSupport] = useState([]);
    const [productKeyList, setProductKeyList] = useState([]);
    const [deviceList, setDeviceList] = useState<any>([]);
    const [serveIdList, setServeIdList] = useState([]);
    const [regionIdList] = useState(['cn-qingdao', 'cn-beijing', 'cn-zhangjiakou', 'cn-huhehaote', 'cn-wulanchabu', 'cn-hangzhou', 'cn-shanghai', 'cn-shenzhen', 'cn-heyuan', 'cn-guangzhou', 'cn-chengdu']);

    useEffect(() => {
        setBridgeConfigs(props.data.bridgeConfigs || [
            {
                serverId: "",
                bridgeProductKey: "",
                bridgeDeviceName: "",
                bridgeDeviceSecret: "",
                http2Endpoint: ""
            }
        ]);
        setAccessConfig(props.data?.accessConfig || {
            regionId: "",
            apiEndpoint: "",
            authEndpoint: "",
            accessKeyId: "",
            accessSecret: "",
            productKey: ""
        });
        apis.aliyun.getNodesList().then(res => {
            if (res.status === 200) {
                setServeIdList(res.result)
            }
        });
        apis.aliyun.productList({}).then(res => {
            if (res.status === 200) {
                setProductList(res.result)
            }
        });

        apis.aliyun.protocolSupport().then(res => {
            if (res.status === 200) {
                setProtocolSupport(res.result)
            }
        });
        if (props.data.accessConfig) {
            getBridge(props.data?.accessConfig);
            let item = props.data?.accessConfig;
            props.data.bridgeConfigs.map((i: any, index: number) => {
                let param = {
                    regionId: item.regionId,
                    accessSecret: item.accessSecret,
                    apiEndpoint: item.apiEndpoint,
                    authEndpoint: item.authEndpoint,
                    accessKeyId: item.accessKeyId,
                    productKey: i.bridgeProductKey
                };
                apis.aliyun.getDevices(param).then(res => {
                    if (res.status === 200) {
                        deviceList[index] = res.result?.data || [];
                        setDeviceList([...deviceList])
                    }
                })
            })
        }
    }, []);

    const saveData = () => {
        form.validateFields((err, fileValue) => {
            if (err) return;
            apis.aliyun.save(fileValue).then(res => {
                if (res.status === 200) {
                    props.save();
                }
            })
        })
    };
    const getBridge = (params: any) => {
        if (params.regionId !== '' && params.accessSecret !== '' && params.apiEndpoint !== '' && params.authEndpoint !== '' && params.accessKeyId !== '') {
            let param = {
                regionId: params.regionId,
                accessSecret: params.accessSecret,
                apiEndpoint: params.apiEndpoint,
                authEndpoint: params.authEndpoint,
                accessKeyId: params.accessKeyId,
            };
            apis.aliyun.getProducts(param).then(res => {
                if (res.status === 200) {
                    setProductKeyList(res.result?.data || [])
                }
            })
        }
    };

    return (
        <Modal
            width='60VW'
            title={props.data.id ? "编辑产品" : "添加产品"}
            visible
            okText="确定"
            cancelText="取消"
            onOk={() => { saveData() }}
            onCancel={() => props.close()}
        >
            <div>
                <Form layout="horizontal" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}>
                    <Row justify="space-around" gutter={24}>
                        <Col span={12}>
                            <Form.Item label="产品ID" >
                                {getFieldDecorator('id', {
                                    initialValue: props.data?.id,
                                    rules: [{ required: false, message: '请选择' }],
                                })(
                                    <Select placeholder="请选择" allowClear showSearch
                                    optionFilterProp="value"
                                    >
                                        {productList && productList.map((i: any, index: number) => {
                                            return <Select.Option key={index} value={i.id}>{i.id}</Select.Option>
                                        })}
                                    </Select>
                                )}
                            </Form.Item>
                        </Col>
                        <Col span={12}>
                            <Form.Item label="产品名称">
                                {getFieldDecorator('name', {
                                    initialValue: props.data?.name,
                                    rules: [{ required: true, message: '请输入名称' }],
                                })(<Input placeholder="请输入名称" />)}
                            </Form.Item>
                        </Col>
                        <Col span={12}>
                            <Form.Item label="编解码协议">
                                {getFieldDecorator('codecProtocol', {
                                    initialValue: props.data?.codecProtocol,
                                    rules: [{ required: true, message: '请选择' }],
                                })(<Select placeholder="请选择" showSearch>
                                    {protocolSupport && protocolSupport.map((i: any, index: number) => {
                                        return <Select.Option key={index} value={i.id}>{i.name}</Select.Option>
                                    })}
                                </Select>)}
                            </Form.Item>
                        </Col>
                        <Col span={12}>
                            <Form.Item label="说明">
                                {getFieldDecorator('description', {
                                    initialValue: props.data?.description,
                                    rules: [{ required: false, message: '请输入' }],
                                })(<Input placeholder="请输入" />)}
                            </Form.Item>
                        </Col>
                    </Row>
                    <Divider orientation="left" dashed><div style={{ fontWeight: 'bold' }}>认证信息配置</div></Divider>
                    <Row justify="start" gutter={24}>
                        <Col span={12}>
                            <Form.Item label={
                                <span>
                                    区域ID&nbsp; <Tooltip title="地域和可用区">
                                        <QuestionCircleOutlined onClick={() => {
                                            window.open('https://help.aliyun.com/document_detail/40654.html')
                                        }} />
                                    </Tooltip>
                                </span>
                            } >
                                {getFieldDecorator('accessConfig.regionId', {
                                    initialValue: accessConfig?.regionId,
                                    rules: [{ required: true, message: '请选择' }],
                                })(
                                    <AutoComplete placeholder="本地服务ID"
                                        dataSource={regionIdList}
                                        filterOption={(inputValue, option) =>
                                            option?.props?.children?.toUpperCase()?.indexOf(inputValue.toUpperCase()) !== -1
                                        }
                                        onBlur={(value) => {
                                            if (value) {
                                                let temp = form.getFieldValue('accessConfig.productKey');
                                                form.setFieldsValue({
                                                    accessConfig: {
                                                        apiEndpoint: `https://iot.${value}.aliyuncs.com`,
                                                        authEndpoint: `https://iot-auth.${value}.aliyuncs.com/auth/bridge`,
                                                        http2Endpoint: `https://${temp}.iot-as-http2.${value}.aliyuncs.com`,
                                                    }
                                                });
                                                let params = form.getFieldValue('accessConfig');
                                                getBridge({
                                                    regionId: value,
                                                    accessSecret: params.accessSecret,
                                                    apiEndpoint: params.apiEndpoint,
                                                    authEndpoint: params.authEndpoint,
                                                    accessKeyId: params.accessKeyId,
                                                })
                                            }
                                        }}
                                    >
                                    </AutoComplete>
                                )}
                            </Form.Item>
                        </Col>
                        <Col span={12}>
                            <Form.Item label="API接口地址">
                                {getFieldDecorator('accessConfig.apiEndpoint', { //https://iot.cn-shanghai.aliyuncs.com
                                    initialValue: accessConfig?.apiEndpoint,
                                    rules: [{ required: true, message: '请输入' }],
                                })(<Input placeholder="请输入" />)}
                            </Form.Item>
                        </Col>
                        <Col span={12}>
                            <Form.Item label="认证接口地址">
                                {getFieldDecorator('accessConfig.authEndpoint', { //https://iot-auth.cn-shanghai.aliyuncs.com/auth/bridge
                                    initialValue: accessConfig?.authEndpoint,
                                    rules: [{ required: true, message: '请输入' }],
                                })(<Input placeholder="请输入" />)}
                            </Form.Item>
                        </Col>
                        <Col span={12}>
                            <Form.Item label="accessKey">
                                {getFieldDecorator('accessConfig.accessKeyId', {
                                    initialValue: accessConfig?.accessKeyId,
                                    rules: [{ required: true, message: '请输入' }],
                                })(<Input placeholder="请输入" onBlur={(e) => {
                                    let params = form.getFieldValue('accessConfig');
                                    getBridge({
                                        regionId: params.regionId,
                                        accessSecret: params.accessSecret,
                                        apiEndpoint: params.apiEndpoint,
                                        authEndpoint: params.authEndpoint,
                                        accessKeyId: e.target.value,
                                    })
                                }} />)}
                            </Form.Item>
                        </Col>
                        <Col span={12}>
                            <Form.Item label="accessSecret">
                                {getFieldDecorator('accessConfig.accessSecret', {
                                    initialValue: accessConfig?.accessSecret,
                                    rules: [{ required: true, message: '请输入' }],
                                })(<Input placeholder="请输入" onBlur={(e) => {
                                    let params = form.getFieldValue('accessConfig');
                                    getBridge({
                                        regionId: params.regionId,
                                        accessSecret: e.target.value,
                                        apiEndpoint: params.apiEndpoint,
                                        authEndpoint: params.authEndpoint,
                                        accessKeyId: params.accessKeyId,
                                    })
                                }} />)}
                            </Form.Item>
                        </Col>
                        <Col span={12}>
                            <Form.Item label="ProductKey">
                                {getFieldDecorator('accessConfig.productKey', {
                                    initialValue: accessConfig?.productKey,
                                    rules: [{ required: true, message: '请输入' }],
                                })(
                                    <AutoComplete placeholder="请选择" allowClear>
                                        {productKeyList && productKeyList.map((i: any, index: number) => {
                                            return <AutoComplete.Option key={index} value={i.productKey}>{`${i.productKey}(${i.productName})`}</AutoComplete.Option>
                                        })}
                                    </AutoComplete>
                                    // <Select placeholder="请选择" allowClear>
                                    //     {productKeyList && productKeyList.map((i: any, index: number) => {
                                    //         return <Select.Option key={index} value={i.productKey}>{`${i.productKey}(${i.productName})`}</Select.Option>
                                    //     })}
                                    // </Select>
                                )}
                            </Form.Item>
                        </Col>
                    </Row>
                    <Divider orientation="left" dashed><div style={{ fontWeight: 'bold' }}>网桥配置</div></Divider>
                    {
                        bridgeConfigs.map((item: any, index: number) => {
                            return (
                                <div key={index} style={{ backgroundColor: 'rgba(192,192,192,0.1)', marginBottom: '10px', paddingTop: '20px' }}>
                                    <div style={{ width: "90%", marginLeft: '5%' }}>网桥: {index + 1}</div>
                                    <div style={{ display: 'flex', justifyContent: 'center' }}>
                                        <div style={{ width: "90%" }}>
                                            <Row gutter={0} justify="start">
                                                <Col span={12}>
                                                    <Form.Item label="本地服务ID">
                                                        {getFieldDecorator(`bridgeConfigs[${index}].serverId`, {
                                                            initialValue: item.serverId || undefined,
                                                            rules: [{ required: true, message: '本地服务ID' }],
                                                        })(<AutoComplete placeholder="本地服务ID">
                                                            {serveIdList && serveIdList.map((i: any, index: number) => {
                                                                return <AutoComplete.Option key={index} value={i.id}>{i.id}</AutoComplete.Option>
                                                            })}
                                                        </AutoComplete>)}
                                                    </Form.Item>
                                                </Col>
                                                <Col span={12}>
                                                    <Form.Item label="ProductKey">
                                                        {getFieldDecorator(`bridgeConfigs[${index}].bridgeProductKey`, {
                                                            initialValue: item.bridgeProductKey || undefined,
                                                            rules: [{ required: true, message: '网桥ProductKey' }],
                                                        })(
                                                            <AutoComplete placeholder="请选择" allowClear
                                                                onBlur={(value: SelectValue) => {
                                                                    let temp = form.getFieldValue('accessConfig.regionId');
                                                                    let bridge = form.getFieldValue('bridgeConfigs');
                                                                    bridge[index].http2Endpoint = `https://${value}.iot-as-http2.${temp}.aliyuncs.com`;
                                                                    form.setFieldsValue({
                                                                        bridgeConfigs: bridge
                                                                    });
                                                                    let config = form.getFieldValue('accessConfig');
                                                                    if (config.regionId !== '' && config.apiEndpoint !== '' &&
                                                                        config.authEndpoint !== '' && config.accessKeyId !== '' && config.accessSecret !== '' && value !== '') {
                                                                        apis.aliyun.getDevices({
                                                                            regionId: config.regionId,
                                                                            accessSecret: config.accessSecret,
                                                                            apiEndpoint: config.apiEndpoint,
                                                                            productKey: value,
                                                                            authEndpoint: config.authEndpoint,
                                                                            accessKeyId: config.accessKeyId,
                                                                        }).then(res => {
                                                                            if (res.status === 200) {
                                                                                deviceList[index] = res.result?.data || [];
                                                                                setDeviceList([...deviceList])
                                                                            }
                                                                        })
                                                                    }
                                                                }}>
                                                                {productKeyList && productKeyList.map((i: any, index: number) => {
                                                                    return <AutoComplete.Option key={index} value={i.productKey}>{`${i.productKey}(${i.productName})`}</AutoComplete.Option>
                                                                })}
                                                            </AutoComplete>
                                                        )}
                                                    </Form.Item>
                                                </Col>
                                                <Col span={12}>
                                                    <Form.Item label="DeviceName">
                                                        {getFieldDecorator(`bridgeConfigs[${index}].bridgeDeviceName`, {
                                                            initialValue: item.bridgeDeviceName || undefined,
                                                            rules: [{ required: true, message: '网桥DeviceName' }],
                                                        })(
                                                            <AutoComplete placeholder="网桥DeviceName" allowClear onBlur={(value: SelectValue) => {
                                                                let secret = '';
                                                                if (value !== '' && value !== undefined) {
                                                                    let data: any[] = deviceList[index].filter((i: any) => {
                                                                        return i.deviceName === value
                                                                    });
                                                                    if (data.length > 0) {
                                                                        secret = data[0].deviceSecret
                                                                    }
                                                                }
                                                                let bridge = form.getFieldValue('bridgeConfigs');
                                                                bridge[index].bridgeDeviceSecret = secret;
                                                                form.setFieldsValue({
                                                                    bridgeConfigs: bridge
                                                                })
                                                            }}>
                                                                {deviceList && deviceList.length > 0 && deviceList[index] && deviceList[index].length > 0 && deviceList[index].map((i: any, index: number) => {
                                                                    return <AutoComplete.Option key={index} value={i.deviceName}>{i.deviceName}</AutoComplete.Option>
                                                                })}
                                                            </AutoComplete>
                                                        )}
                                                    </Form.Item>
                                                </Col>
                                                <Col span={12}>
                                                    <Form.Item label="DeviceSecret">
                                                        {getFieldDecorator(`bridgeConfigs[${index}].bridgeDeviceSecret`, {
                                                            initialValue: item.bridgeDeviceSecret || undefined,
                                                            rules: [{ required: true, message: '网桥DeviceSecret' }],
                                                        })(
                                                            <Input placeholder="请输入" readOnly />
                                                        )}
                                                    </Form.Item>
                                                </Col>
                                                <Col span={12}>
                                                    <Form.Item label="HTTP2接口地址">
                                                        {getFieldDecorator(`bridgeConfigs[${index}].http2Endpoint`, { //https://a1WEHOY5PU7.iot-as-http2.cn-shanghai.aliyuncs.com
                                                            initialValue: item.http2Endpoint || undefined,
                                                            rules: [{ required: true, message: '请输入' }],
                                                        })(<Input placeholder="请输入" />)}
                                                    </Form.Item>
                                                </Col>
                                            </Row>
                                        </div>
                                        <div style={{ width: "10%", display: 'flex', justifyContent: 'center', marginTop: '45px' }}>
                                            <Tooltip title="删除">
                                                <MinusCircleOutlined
                                                    onClick={() => {
                                                        bridgeConfigs.splice(index, 1);
                                                        setBridgeConfigs([...bridgeConfigs]);
                                                    }}
                                                />
                                            </Tooltip>
                                        </div>
                                    </div>
                                </div>
                            )
                        })
                    }
                    <Button icon="plus" type="link"
                        onClick={() => {
                            setBridgeConfigs([...bridgeConfigs, {
                                serverId: "",
                                bridgeProductKey: "",
                                bridgeDeviceName: "",
                                bridgeDeviceSecret: "",
                                http2Endpoint: ""
                            }]);
                            setDeviceList([...deviceList, {}])
                        }}
                    >添加</Button>
                </Form>
            </div>
        </Modal>
    )
}
Example #26
Source File: DynamicFormItemV2.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
LegacyDynamicFormItemV2 = forwardRef(
  (
    props: LegacyDynamicFormItemV2Props,
    ref: Ref<LegacyDynamicFormItemV2Ref>
  ): React.ReactElement => {
    const {
      value,
      columns,
      onChange,
      onAdd,
      onRemove,
      hideRemoveButton,
      disabledRemoveButton,
      hideAddButton,
      disabledAddButton,
    } = props;
    const { t } = useTranslation(NS_FORMS);
    const [form] = Form.useForm();

    useImperativeHandle(ref, () => ({
      validateFields: form.validateFields,
    }));

    useEffect(() => {
      form.setFieldsValue({ [FORM_LIST_NAME]: value });
    }, [value]);

    const handleValuesChange = (
      changedValues: DynamicFormValue,
      allValues: DynamicFormValue
    ): void => {
      onChange?.(allValues?.[FORM_LIST_NAME]);
    };

    const hasLabel = useMemo(
      () => columns.some((column) => column.label),
      [columns]
    );

    const defaultValues = useMemo(
      () =>
        columns.reduce(
          (pre, cur) => ({ ...pre, [cur.name]: cur.defaultValue }),
          {}
        ),
      [columns]
    );

    return (
      <div className={style.dynamicForm}>
        <Form
          form={form}
          layout={"vertical"}
          initialValues={value}
          onValuesChange={handleValuesChange}
        >
          <Form.List name={FORM_LIST_NAME}>
            {(fields, { add, remove }) => (
              <>
                {fields.map(({ key, name, ...restField }) => {
                  const rowValue = value?.[name];

                  return (
                    <Row key={key} className={style.row} gutter={12}>
                      {columns?.map((column) => (
                        <Col
                          key={column.name}
                          style={{ flex: column.flex ?? "1", minWidth: 0 }}
                        >
                          <ColumnComponent
                            hasLabel={hasLabel}
                            rowIndex={name}
                            column={column}
                            formValue={value}
                            field={{ key, name, ...restField }}
                          />
                        </Col>
                      ))}
                      <Col>
                        <Button
                          type="link"
                          className={classNames(style.removeRowBtn, [
                            {
                              [style.hidden]: getRealValue(hideRemoveButton, [
                                rowValue,
                                name,
                              ]),
                            },
                          ])}
                          disabled={getRealValue(disabledRemoveButton, [
                            rowValue,
                            name,
                          ])}
                          onClick={() => {
                            const index = name;
                            const curValue =
                              form.getFieldValue(FORM_LIST_NAME)?.[index];
                            remove(index);
                            onRemove?.({ detail: curValue, index });
                          }}
                        >
                          <MinusCircleOutlined />
                        </Button>
                      </Col>
                    </Row>
                  );
                })}
                <Button
                  className={classNames(style.addRowBtn, [
                    {
                      [style.displayNone]: getRealValue(hideAddButton, [value]),
                    },
                  ])}
                  disabled={getRealValue(disabledAddButton, [value])}
                  type="dashed"
                  onClick={() => {
                    const index = fields.length;
                    add(defaultValues);
                    onAdd?.({ detail: defaultValues, index });
                  }}
                  icon={<PlusOutlined />}
                >
                  {t(`${NS_FORMS}:${K.ADD}`)}
                </Button>
              </>
            )}
          </Form.List>
        </Form>
      </div>
    );
  }
)
Example #27
Source File: DynamicCommonItem.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function CommonItem(
  props: PropsWithChildren<CommonItemProps>,
  ref: any
): React.ReactElement {
  const { getFieldValue, setFieldsValue, validateFields } =
    props.formElement.formUtils;
  const [, forceUpdate] = useState();
  const defaultRow = props.columns.reduce((obj: any, column) => {
    obj[column.name] = column.defaultValue;
    return obj;
  }, {});

  const encrypted = React.useRef([]);

  const [rows, setRows] = useState<RowProps[]>(() => {
    const value = getFieldValue(props.name) ?? props.value;

    if (!value) {
      return [];
    }

    encrypted.current = cloneDeep(value);

    const columnMap = keyBy(props.columns, "name");
    for (const row of value) {
      for (const [k, v] of Object.entries(row)) {
        if (columnMap[k]?.encrypt) {
          row[k] = decrypt(v as string);
        }
      }
    }

    return value;
  });

  const [trackRows, setTrackRows] = useState(() => {
    return Array(rows.length)
      .fill(0)
      .map(() => uniqueId());
  });

  const handleAdd = () => {
    setTrackRows((trackRows) => [...trackRows, uniqueId()]);
    setRows([...rows, defaultRow]);

    // important! notify form to detect changes
    setFieldsValue({
      [props.name]: [...rows, defaultRow],
    });

    props.onAdd?.();
  };

  const handleRemove = (data: RowProps, index: number) => {
    const filter = rows.filter((row, i) => index !== i);

    // important! notify form to detect changes
    setFieldsValue({
      [props.name]: filter,
    });

    setRows(filter);
    setTrackRows((trackRows) => trackRows.filter((_, i) => index !== i));
    props.onRemove?.(data);
    encrypted.current = filter;
    props.onChange?.(encrypted.current);
  };

  const batchChange = (row, index) => {
    const newRows = cloneDeep(rows);
    newRows[index] = row;
    setRows(newRows);
    props.onChange?.(newRows);
    setFieldsValue({
      [props.name]: [...newRows],
    });
  };
  const handleChange = (value: string, name: string, index: number) => {
    const newRows = cloneDeep(rows);
    newRows[index][name] = value;
    setRows(newRows);

    // todo(ice): notify with encrypted data
    const rawData = cloneDeep(newRows);
    const cols = props.columns.filter((col) => col.encrypt);
    rawData.forEach((row) => {
      cols.forEach((col) => {
        if (row[col.name]) {
          row[col.name] = encrypt(row[col.name]);
        }
      });
    });
    encrypted.current = rawData;
    props.onChange?.(rawData);
  };

  const headers = (
    <Row gutter={14} type="flex" className={style.headRow}>
      {props.columns.map((item, index) => (
        <Col key={index} style={{ flex: item.flex ?? 1 }}>
          {item.label}
        </Col>
      ))}
    </Row>
  );

  useEffect(() => {
    props.emitChangeOnInit && props.onChange?.(encrypted.current);
  }, []);

  useEffect(() => {
    props.columns.forEach((column) => {
      const triggerValidate = column.rules?.some(
        (rule: BuiltInValidatorRule) => rule.uniq
      );
      if (triggerValidate) {
        rows.forEach((item, rowIndex) => {
          // 有值的时候才做唯一性校验
          item[column.name] &&
            validateFields([`${props.name}[${rowIndex}].${column.name}`]);
        });
      }
    });
  }, [rows]);

  useEffect(() => {
    const editedValue = props.manualEditedValue;
    if (editedValue) {
      setRows(editedValue);
      setTrackRows(
        Array(editedValue.length)
          .fill(0)
          .map(() => uniqueId())
      );
      setFieldsValue({
        [props.name]: editedValue,
      });
    }
  }, [props.manualEditedValue]);

  const uniqValidator = (
    rule: any,
    value: string,
    callback: (v?: boolean) => void
  ) => {
    const reg = /\[(\d+)\]\.(\w+)/;
    const [, rowIndex, name] = reg.exec(rule.field);
    const valueList = rows
      .filter((_, index) => index !== Number(rowIndex))
      .map((item) => item[name]);
    if (value && valueList.includes(value)) {
      callback(false);
    } else {
      callback();
    }
    forceUpdate({});
  };

  const columns = props.columns.map((item) => {
    const rules = (item.rules ?? []).map((rule: BuiltInValidatorRule) => {
      if (rule.uniq) {
        return {
          validator: uniqValidator,
          message: rule.message,
        };
      }
      return rule;
    });

    return {
      ...item,
      rules,
    };
  });

  const hiddenDeleteBtnAtOneRow = props.oneRowRequired && rows.length === 1;

  return (
    <div
      ref={ref}
      className={classNames(style.dynamicForm, style.showBackground)}
    >
      {headers}
      {rows.map((row, index) => {
        return (
          <Row type="flex" gutter={12} key={trackRows[index]}>
            {React.cloneElement(props.children as React.ReactElement, {
              rowIndex: index,
              columns,
              srcObjectId: props?.srcObjectId,
              row: row,
              prefixId: `${props.name}[${index}]`,
              form: props.formElement.formUtils,
              batchChange: (row) => {
                batchChange(row, index);
              },
              onChange: (value: string, name: string) =>
                handleChange(value, name, index),
            })}
            <Col className={style.removeColumn}>
              {!props.hideDeleteButton && !hiddenDeleteBtnAtOneRow && (
                <Button
                  type="link"
                  disabled={
                    props.disabledDeleteButton ||
                    props.rowDisabledhandler?.(row, index)
                  }
                  className={style.operatorBtn}
                  onClick={() => {
                    handleRemove(row, index);
                  }}
                >
                  <MinusCircleOutlined />
                </Button>
              )}
            </Col>
          </Row>
        );
      })}
      <Row gutter={12} className={style.addButton}>
        <Col span={24}>
          {!props.hideAddButton && (
            <Button
              style={{ width: "100%" }}
              type="dashed"
              onClick={() => handleAdd()}
              disabled={props.disabledAddButton}
            >
              <PlusOutlined />
              {i18n.t(`${NS_FORMS}:${K.ADD}`)}
            </Button>
          )}
        </Col>
      </Row>
    </div>
  );
}
Example #28
Source File: DynamicCommonItem.spec.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
describe("DynamicCommonItem", () => {
  const TestInput = (props: any) => {
    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
      const value = e.target.value;
      props.onChange?.(value, "name");
    };

    return <Input onChange={handleChange} />;
  };

  it("should render empty row default", () => {
    const props = {
      name: "dynamic",
      columns: [
        {
          name: "name",
          label: "名称",
          rules: [{ required: true, message: "这个是必填项" }],
        },
        { name: "port", label: "端口" },
        { name: "type", label: "类型" },
      ],
      formElement: {
        formUtils: {
          getFieldValue: jest.fn(),
        },
      } as any,
    };
    const wrapper = shallow(
      <DynamicCommonItem {...props}>
        <TestInput />
      </DynamicCommonItem>
    );
    expect(wrapper.find(TestInput).length).toEqual(0);
  });

  it("should work with add/remove item", () => {
    const setFieldsValueMock = jest.fn();
    const props = {
      name: "dynamic",
      columns: [
        {
          name: "name",
          label: "名称",
          rules: [{ required: true, message: "这个是必填项" }],
        },
        { name: "port", label: "端口" },
        { name: "type", label: "类型" },
      ],
      formElement: {
        formUtils: {
          getFieldValue: () => [
            { name: "jack", port: "80", type: "tcp" },
            { name: "lucy", port: "50", type: "udp" },
          ],
          setFieldsValue: setFieldsValueMock,
        },
      } as any,
      onRemove: jest.fn(),
      onChange: jest.fn(),
    };
    const wrapper = shallow(
      <DynamicCommonItem {...props}>
        <TestInput />
      </DynamicCommonItem>
    );

    wrapper.find(TestInput).at(0).invoke("onChange")("abc", "name");

    expect(props.onChange).toBeCalledWith([
      { name: "abc", port: "80", type: "tcp" },
      { name: "lucy", port: "50", type: "udp" },
    ]);

    wrapper.find({ type: "link" }).at(1).simulate("click");

    expect(setFieldsValueMock).toHaveBeenCalledWith({
      dynamic: [{ name: "abc", port: "80", type: "tcp" }],
    });

    wrapper.find({ type: "dashed" }).simulate("click");

    expect(setFieldsValueMock).toHaveBeenCalledWith({
      dynamic: [
        { name: "abc", port: "80", type: "tcp" },
        { name: undefined, port: undefined, type: undefined },
      ],
    });

    expect(wrapper.find(MinusCircleOutlined)).toHaveLength(2);
    expect(wrapper.find(PlusOutlined)).toHaveLength(1);

    wrapper.setProps({
      hideAddButton: true,
      hideDeleteButton: true,
    });

    expect(wrapper.find(MinusCircleOutlined)).toHaveLength(0);
    expect(wrapper.find(PlusOutlined)).toHaveLength(0);
  });

  it("custom Validator", async () => {
    const props = {
      columns: [
        {
          name: "name",
          label: "名称",
          rules: [
            { required: true, message: "这个是必填项" },
            { uniq: true, message: "名称必须唯一" },
          ],
        },
        { name: "port", label: "端口" },
        { name: "type", label: "类型" },
      ],
      formElement: {
        formUtils: {
          getFieldValue: () => [
            { name: "jack", port: "80", type: "tcp" },
            { name: "jack", port: "90", type: "udp" },
          ],
          setFieldsValue: jest.fn(),
          getFieldDecorator: () => (comp: React.Component) => comp,
          validateFields: jest.fn(),
        },
      } as any,
      onRemove: jest.fn(),
      onChange: jest.fn(),
    };

    const wrapper = mount(
      <DynamicCommonItem {...props}>
        <TestInput />
      </DynamicCommonItem>
    );
    wrapper.find(Input).at(0).simulate("change", {
      target: "lucy",
    });
    const ValidatorFn = jest.fn();

    const customValidator = wrapper.find(TestInput).at(0).prop("columns")[0]
      .rules[1].validator;

    act(() => {
      customValidator({ field: "dynamic[0].name" }, "jack", ValidatorFn);
    });

    expect(ValidatorFn).toHaveBeenCalled();
  });

  it("should set dynamic value", () => {
    const setFieldsValueMock = jest.fn();
    const props = {
      name: "dynamic",
      columns: [
        {
          name: "name",
          label: "名称",
          rules: [{ required: true, message: "这个是必填项" }],
        },
        { name: "port", label: "端口" },
        { name: "type", label: "类型" },
      ],
      formElement: {
        formUtils: {
          getFieldValue: jest.fn(),
          setFieldsValue: setFieldsValueMock,
        },
      } as any,
    };
    const wrapper = mount(
      <DynamicCommonItem {...props}>
        <TestInput />
      </DynamicCommonItem>
    );
    wrapper.setProps({
      manualEditedValue: [{ name: "test", port: "70", type: "udp" }],
    });

    expect(setFieldsValueMock).toHaveBeenCalledWith({
      dynamic: [{ name: "test", port: "70", type: "udp" }],
    });
  });
  it("hidden delete button if the dynamic value only one element", () => {
    const props = {
      name: "dynamic",
      oneRowRequired: true,
      columns: [
        {
          name: "name",
          label: "名称",
          rules: [{ required: true, message: "这个是必填项" }],
        },
        { name: "port", label: "端口" },
        { name: "type", label: "类型" },
      ],
      formElement: {
        formUtils: {
          getFieldValue: () => [
            { name: "jack", port: "80", type: "tcp" },
            { name: "lucy", port: "50", type: "udp" },
          ],
          setFieldsValue: jest.fn(),
        },
      } as any,
    };

    const wrapper = shallow(
      <DynamicCommonItem {...props}>
        <TestInput />
      </DynamicCommonItem>
    );

    expect(wrapper.find(MinusCircleOutlined)).toHaveLength(2);

    wrapper.find({ type: "link" }).at(0).simulate("click");

    wrapper.update();

    expect(wrapper.find(MinusCircleOutlined)).toHaveLength(0);
  });

  it("disabled removed button of the specified row ", () => {
    const props = {
      name: "dynamic",
      rowDisabledhandler: (row: any, index: number) => index === 0,
      columns: [
        {
          name: "name",
          label: "名称",
          rules: [{ required: true, message: "这个是必填项" }],
        },
        { name: "port", label: "端口" },
        { name: "type", label: "类型" },
      ],
      formElement: {
        formUtils: {
          getFieldValue: () => [
            { name: "jack", port: "80", type: "tcp" },
            { name: "lucy", port: "50", type: "udp" },
          ],
          setFieldsValue: jest.fn(),
        },
      } as any,
    };

    const wrapper = shallow(
      <DynamicCommonItem {...props}>
        <TestInput />
      </DynamicCommonItem>
    );
    expect(wrapper.find({ type: "link" }).at(0).prop("disabled")).toEqual(true);
  });
});
Example #29
Source File: Icon.tsx    From html2sketch with MIT License 4 votes vote down vote up
IconSymbol: FC = () => {
  return (
    <Row>
      {/*<CaretUpOutlined*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/1.CaretUpOutlined'}*/}
      {/*/>*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/2.MailOutlined'}*/}
      {/*/>*/}
      {/*<StepBackwardOutlined*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
      {/*/>*/}
      {/*<StepForwardOutlined*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
      {/*/>*/}
      <StepForwardOutlined />
      <ShrinkOutlined />
      <ArrowsAltOutlined />
      <DownOutlined />
      <UpOutlined />
      <LeftOutlined />
      <RightOutlined />
      <CaretUpOutlined />
      <CaretDownOutlined />
      <CaretLeftOutlined />
      <CaretRightOutlined />
      <VerticalAlignTopOutlined />
      <RollbackOutlined />
      <FastBackwardOutlined />
      <FastForwardOutlined />
      <DoubleRightOutlined />
      <DoubleLeftOutlined />
      <VerticalLeftOutlined />
      <VerticalRightOutlined />
      <VerticalAlignMiddleOutlined />
      <VerticalAlignBottomOutlined />
      <ForwardOutlined />
      <BackwardOutlined />
      <EnterOutlined />
      <RetweetOutlined />
      <SwapOutlined />
      <SwapLeftOutlined />
      <SwapRightOutlined />
      <ArrowUpOutlined />
      <ArrowDownOutlined />
      <ArrowLeftOutlined />
      <ArrowRightOutlined />
      <LoginOutlined />
      <LogoutOutlined />
      <MenuFoldOutlined />
      <MenuUnfoldOutlined />
      <BorderBottomOutlined />
      <BorderHorizontalOutlined />
      <BorderInnerOutlined />
      <BorderOuterOutlined />
      <BorderLeftOutlined />
      <BorderRightOutlined />
      <BorderTopOutlined />
      <BorderVerticleOutlined />
      <PicCenterOutlined />
      <PicLeftOutlined />
      <PicRightOutlined />
      <RadiusBottomleftOutlined />
      <RadiusBottomrightOutlined />
      <RadiusUpleftOutlined />
      <RadiusUprightOutlined />
      <FullscreenOutlined />
      <FullscreenExitOutlined />
      <QuestionOutlined />
      <PauseOutlined />
      <MinusOutlined />
      <PauseCircleOutlined />
      <InfoOutlined />
      <CloseOutlined />
      <ExclamationOutlined />
      <CheckOutlined />
      <WarningOutlined />
      <IssuesCloseOutlined />
      <StopOutlined />
      <EditOutlined />
      <CopyOutlined />
      <ScissorOutlined />
      <DeleteOutlined />
      <SnippetsOutlined />
      <DiffOutlined />
      <HighlightOutlined />
      <AlignCenterOutlined />
      <AlignLeftOutlined />
      <AlignRightOutlined />
      <BgColorsOutlined />
      <BoldOutlined />
      <ItalicOutlined />
      <UnderlineOutlined />
      <StrikethroughOutlined />
      <RedoOutlined />
      <UndoOutlined />
      <ZoomInOutlined />
      <ZoomOutOutlined />
      <FontColorsOutlined />
      <FontSizeOutlined />
      <LineHeightOutlined />
      <SortAscendingOutlined />
      <SortDescendingOutlined />
      <DragOutlined />
      <OrderedListOutlined />
      <UnorderedListOutlined />
      <RadiusSettingOutlined />
      <ColumnWidthOutlined />
      <ColumnHeightOutlined />
      <AreaChartOutlined />
      <PieChartOutlined />
      <BarChartOutlined />
      <DotChartOutlined />
      <LineChartOutlined />
      <RadarChartOutlined />
      <HeatMapOutlined />
      <FallOutlined />
      <RiseOutlined />
      <StockOutlined />
      <BoxPlotOutlined />
      <FundOutlined />
      <SlidersOutlined />
      <AndroidOutlined />
      <AppleOutlined />
      <WindowsOutlined />
      <IeOutlined />
      <ChromeOutlined />
      <GithubOutlined />
      <AliwangwangOutlined />
      <DingdingOutlined />
      <WeiboSquareOutlined />
      <WeiboCircleOutlined />
      <TaobaoCircleOutlined />
      <Html5Outlined />
      <WeiboOutlined />
      <TwitterOutlined />
      <WechatOutlined />
      <AlipayCircleOutlined />
      <TaobaoOutlined />
      <SkypeOutlined />
      <FacebookOutlined />
      <CodepenOutlined />
      <CodeSandboxOutlined />
      <AmazonOutlined />
      <GoogleOutlined />
      <AlipayOutlined />
      <AntDesignOutlined />
      <AntCloudOutlined />
      <ZhihuOutlined />
      <SlackOutlined />
      <SlackSquareOutlined />
      <BehanceSquareOutlined />
      <DribbbleOutlined />
      <DribbbleSquareOutlined />
      <InstagramOutlined />
      <YuqueOutlined />
      <AlibabaOutlined />
      <YahooOutlined />
      <RedditOutlined />
      <SketchOutlined />
      <AccountBookOutlined />
      <AlertOutlined />
      <ApartmentOutlined />
      <ApiOutlined />
      <QqOutlined />
      <MediumWorkmarkOutlined />
      <GitlabOutlined />
      <MediumOutlined />
      <GooglePlusOutlined />
      <AppstoreAddOutlined />
      <AppstoreOutlined />
      <AudioOutlined />
      <AudioMutedOutlined />
      <AuditOutlined />
      <BankOutlined />
      <BarcodeOutlined />
      <BarsOutlined />
      <BellOutlined />
      <BlockOutlined />
      <BookOutlined />
      <BorderOutlined />
      <BranchesOutlined />
      <BuildOutlined />
      <BulbOutlined />
      <CalculatorOutlined />
      <CalendarOutlined />
      <CameraOutlined />
      <CarOutlined />
      <CarryOutOutlined />
      <CiCircleOutlined />
      <CiOutlined />
      <CloudOutlined />
      <ClearOutlined />
      <ClusterOutlined />
      <CodeOutlined />
      <CoffeeOutlined />
      <CompassOutlined />
      <CompressOutlined />
      <ContactsOutlined />
      <ContainerOutlined />
      <ControlOutlined />
      <CopyrightCircleOutlined />
      <CopyrightOutlined />
      <CreditCardOutlined />
      <CrownOutlined />
      <CustomerServiceOutlined />
      <DashboardOutlined />
      <DatabaseOutlined />
      <DeleteColumnOutlined />
      <DeleteRowOutlined />
      <DisconnectOutlined />
      <DislikeOutlined />
      <DollarCircleOutlined />
      <DollarOutlined />
      <DownloadOutlined />
      <EllipsisOutlined />
      <EnvironmentOutlined />
      <EuroCircleOutlined />
      <EuroOutlined />
      <ExceptionOutlined />
      <ExpandAltOutlined />
      <ExpandOutlined />
      <ExperimentOutlined />
      <ExportOutlined />
      <EyeOutlined />
      <FieldBinaryOutlined />
      <FieldNumberOutlined />
      <FieldStringOutlined />
      <DesktopOutlined />
      <DingtalkOutlined />
      <FileAddOutlined />
      <FileDoneOutlined />
      <FileExcelOutlined />
      <FileExclamationOutlined />
      <FileOutlined />
      <FileImageOutlined />
      <FileJpgOutlined />
      <FileMarkdownOutlined />
      <FilePdfOutlined />
      <FilePptOutlined />
      <FileProtectOutlined />
      <FileSearchOutlined />
      <FileSyncOutlined />
      <FileTextOutlined />
      <FileUnknownOutlined />
      <FileWordOutlined />
      <FilterOutlined />
      <FireOutlined />
      <FlagOutlined />
      <FolderAddOutlined />
      <FolderOutlined />
      <FolderOpenOutlined />
      <ForkOutlined />
      <FormatPainterOutlined />
      <FrownOutlined />
      <FunctionOutlined />
      <FunnelPlotOutlined />
      <GatewayOutlined />
      <GifOutlined />
      <GiftOutlined />
      <GlobalOutlined />
      <GoldOutlined />
      <GroupOutlined />
      <HddOutlined />
      <HeartOutlined />
      <HistoryOutlined />
      <HomeOutlined />
      <HourglassOutlined />
      <IdcardOutlined />
      <ImportOutlined />
      <InboxOutlined />
      <InsertRowAboveOutlined />
      <InsertRowBelowOutlined />
      <InsertRowLeftOutlined />
      <InsertRowRightOutlined />
      <InsuranceOutlined />
      <InteractionOutlined />
      <KeyOutlined />
      <LaptopOutlined />
      <LayoutOutlined />
      <LikeOutlined />
      <LineOutlined />
      <LinkOutlined />
      <Loading3QuartersOutlined />
      <LoadingOutlined />
      <LockOutlined />
      <MailOutlined />
      <ManOutlined />
      <MedicineBoxOutlined />
      <MehOutlined />
      <MenuOutlined />
      <MergeCellsOutlined />
      <MessageOutlined />
      <MobileOutlined />
      <MoneyCollectOutlined />
      <MonitorOutlined />
      <MoreOutlined />
      <NodeCollapseOutlined />
      <NodeExpandOutlined />
      <NodeIndexOutlined />
      <NotificationOutlined />
      <NumberOutlined />
      <PaperClipOutlined />
      <PartitionOutlined />
      <PayCircleOutlined />
      <PercentageOutlined />
      <PhoneOutlined />
      <PictureOutlined />
      <PoundCircleOutlined />
      <PoundOutlined />
      <PoweroffOutlined />
      <PrinterOutlined />
      <ProfileOutlined />
      <ProjectOutlined />
      <PropertySafetyOutlined />
      <PullRequestOutlined />
      <PushpinOutlined />
      <QrcodeOutlined />
      <ReadOutlined />
      <ReconciliationOutlined />
      <RedEnvelopeOutlined />
      <ReloadOutlined />
      <RestOutlined />
      <RobotOutlined />
      <RocketOutlined />
      <SafetyCertificateOutlined />
      <SafetyOutlined />
      <ScanOutlined />
      <ScheduleOutlined />
      <SearchOutlined />
      <SecurityScanOutlined />
      <SelectOutlined />
      <SendOutlined />
      <SettingOutlined />
      <ShakeOutlined />
      <ShareAltOutlined />
      <ShopOutlined />
      <ShoppingCartOutlined />
      <ShoppingOutlined />
      <SisternodeOutlined />
      <SkinOutlined />
      <SmileOutlined />
      <SolutionOutlined />
      <SoundOutlined />
      <SplitCellsOutlined />
      <StarOutlined />
      <SubnodeOutlined />
      <SyncOutlined />
      <TableOutlined />
      <TabletOutlined />
      <TagOutlined />
      <TagsOutlined />
      <TeamOutlined />
      <ThunderboltOutlined />
      <ToTopOutlined />
      <ToolOutlined />
      <TrademarkCircleOutlined />
      <TrademarkOutlined />
      <TransactionOutlined />
      <TrophyOutlined />
      <UngroupOutlined />
      <UnlockOutlined />
      <UploadOutlined />
      <UsbOutlined />
      <UserAddOutlined />
      <UserDeleteOutlined />
      <UserOutlined />
      <UserSwitchOutlined />
      <UsergroupAddOutlined />
      <UsergroupDeleteOutlined />
      <VideoCameraOutlined />
      <WalletOutlined />
      <WifiOutlined />
      <BorderlessTableOutlined />
      <WomanOutlined />
      <BehanceOutlined />
      <DropboxOutlined />
      <DeploymentUnitOutlined />
      <UpCircleOutlined />
      <DownCircleOutlined />
      <LeftCircleOutlined />
      <RightCircleOutlined />
      <UpSquareOutlined />
      <DownSquareOutlined />
      <LeftSquareOutlined />
      <RightSquareOutlined />
      <PlayCircleOutlined />
      <QuestionCircleOutlined />
      <PlusCircleOutlined />
      <PlusSquareOutlined />
      <MinusSquareOutlined />
      <MinusCircleOutlined />
      <InfoCircleOutlined />
      <ExclamationCircleOutlined />
      <CloseCircleOutlined />
      <CloseSquareOutlined />
      <CheckCircleOutlined />
      <CheckSquareOutlined />
      <ClockCircleOutlined />
      <FormOutlined />
      <DashOutlined />
      <SmallDashOutlined />
      <YoutubeOutlined />
      <CodepenCircleOutlined />
      <AliyunOutlined />
      <PlusOutlined />
      <LinkedinOutlined />
      <AimOutlined />
      <BugOutlined />
      <CloudDownloadOutlined />
      <CloudServerOutlined />
      <CloudSyncOutlined />
      <CloudUploadOutlined />
      <CommentOutlined />
      <ConsoleSqlOutlined />
      <EyeInvisibleOutlined />
      <FileGifOutlined />
      <DeliveredProcedureOutlined />
      <FieldTimeOutlined />
      <FileZipOutlined />
      <FolderViewOutlined />
      <FundProjectionScreenOutlined />
      <FundViewOutlined />
      <MacCommandOutlined />
      <PlaySquareOutlined />
      <OneToOneOutlined />
      <RotateLeftOutlined />
      <RotateRightOutlined />
      <SaveOutlined />
      <SwitcherOutlined />
      <TranslationOutlined />
      <VerifiedOutlined />
      <VideoCameraAddOutlined />
      <WhatsAppOutlined />

      {/*</Col>*/}
    </Row>
  );
}