lodash#toString TypeScript Examples

The following examples show how to use lodash#toString. 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: ObjectAttrBool.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export function ObjectAttrBool(props: ObjectAttrBoolProps): React.ReactElement {
  const [value, setValue] = React.useState<BoolValueType>({
    default: undefined,
  });
  const { t } = useTranslation(NS_FORMS);
  React.useEffect(() => {
    !isNil(props.value) && setValue(props.value);
  }, [props.value]);

  const handleValueChange = (value: BoolValueType) => {
    setValue(value);
    props.onChange && props.onChange(value);
  };

  return (
    <>
      <div>
        {t(K.ATTRIBUTE_DEFAULT_VALUE)}
        <Row>
          <Select
            allowClear
            value={toString(value?.default)}
            style={{ width: "100%" }}
            onChange={(e) =>
              handleValueChange({
                ...value,
                default: e === "true" ? true : e === "false" ? false : null,
              })
            }
          >
            <Select.Option key={0} value="true">
              true
            </Select.Option>
            <Select.Option key={1} value="false">
              false
            </Select.Option>
          </Select>
        </Row>
      </div>
    </>
  );
}
Example #2
Source File: Args.tsx    From subscan-multisig-react with Apache License 2.0 5 votes vote down vote up
// eslint-disable-next-line complexity
function formatAddressValue(value: string | string[], chain: Chain) {
  const encodeAddr = (addr: string) => {
    try {
      // eslint-disable-next-line no-magic-numbers
      const formatVal = addr.padStart(66, '0x');

      return encodeAddress(formatVal, +chain.ss58Format);
    } catch (err) {
      console.error('? ~ file: Args.tsx ~ line 57 ~ formatAddressValue ~ err', err);

      return addr;
    }
  };

  if (isString(value)) {
    // eslint-disable-next-line no-magic-numbers
    if (value.length < 12 && /^\d+$/.test(value)) {
      const registry = new TypeRegistry();

      registry.setChainProperties(
        registry.createType('ChainProperties', { ss58Format: +chain.ss58Format }) as ChainProperties
      );

      const ss58 = registry.createType('AccountIndex', +value).toString();

      return <SubscanLink address={ss58} copyable />;
    }
    // eslint-disable-next-line no-magic-numbers
    if (value.length > 60) {
      return <SubscanLink address={encodeAddr(value)} copyable />;
    }

    return <SubscanLink address={value} copyable />;
  }

  if (isArray(value)) {
    return (
      <>
        {value.map((item: string) => (
          <SubscanLink address={encodeAddr(item)} copyable key={item} />
        ))}
      </>
    );
  }

  return null;
}
Example #3
Source File: Args.tsx    From subscan-multisig-react with Apache License 2.0 5 votes vote down vote up
export function Args({ args, className, section, method }: ArgsProps) {
  const { t } = useTranslation();
  const { chain } = useApi();
  const columns: ColumnsType<ArgObj> = [
    {
      key: 'name',
      dataIndex: 'name',
      render(name: string, record) {
        return name || record.type;
      },
    },
    {
      key: 'value',
      dataIndex: 'value',
      className: 'value-column',
      // eslint-disable-next-line complexity
      render(value, record) {
        const { type, name } = record;
        const isAddr = type ? isAddressType(type) : isSS58Address(value);

        if (isObject(value)) {
          return (
            <Args
              args={Object.entries(value).map(([prop, propValue]) => ({ name: prop, value: propValue }))}
              section={section}
              method={method}
            />
          );
          // return JSON.stringify(value);
        }

        if (isAddr) {
          return formatAddressValue(value, chain);
        }

        // balances(transfer) kton(transfer)
        if (isBalanceType(type || name) || isCrabValue(name) || section === 'balances' || method === 'transfer') {
          const formatValue = toString(value).replaceAll(',', '');
          return formatBalance(formatValue, +chain.tokens[0].decimal, {
            noDecimal: false,
            withThousandSplit: true,
          }); // FIXME: decimal issue;
        }

        if (isDownloadType(value)) {
          return (
            <a href={value}>
              {t('download')} <DownloadOutlined />
            </a>
          );
        }

        if (isValueType(name)) {
          return value;
        }

        return <div style={{ wordBreak: 'break-all' }}>{value}</div>;
      },
    },
  ];

  return (
    <Table
      columns={columns}
      /* antd form data source require object array */
      dataSource={args.map((arg) => (isObject(arg) ? arg : { value: arg }))}
      pagination={false}
      bordered
      rowKey="name"
      showHeader={false}
      className={className}
    />
  );
}
Example #4
Source File: text.ts    From S2 with MIT License 4 votes vote down vote up
getEllipsisTextInner = (
  text: any,
  maxWidth: number,
  font: CSSStyleDeclaration,
) => {
  const STEP = 16; // 每次 16,调参工程师
  const DOT_WIDTH = measureTextWidth('...', font);

  let leftText;

  if (!isString(text)) {
    leftText = toString(text);
  } else {
    leftText = text;
  }

  let leftWidth = maxWidth;

  const r = []; // 最终的分段字符串
  let currentText;
  let currentWidth;

  if (measureTextWidth(text, font) <= maxWidth) {
    return text;
  }

  let runningStep1 = true;
  // 首先通过 step 计算,找出最大的未超出长度的
  while (runningStep1) {
    // 更新字符串
    currentText = leftText.substr(0, STEP);

    // 计算宽度
    currentWidth = measureTextWidth(currentText, font);

    // 超出剩余宽度,则停止
    if (currentWidth + DOT_WIDTH > leftWidth) {
      if (currentWidth > leftWidth) {
        runningStep1 = false;
        break;
      }
    }

    r.push(currentText);

    // 没有超出,则计算剩余宽度
    leftWidth -= currentWidth;
    leftText = leftText.substr(STEP);

    // 字符串整体没有超出
    if (!leftText) {
      return r.join('');
    }
  }

  let runningStep2 = true;
  // 最下的最后一个 STEP,使用 1 递增(用二分效果更高)
  while (runningStep2) {
    // 更新字符串
    currentText = leftText.substr(0, 1);

    // 计算宽度
    currentWidth = measureTextWidth(currentText, font);

    // 超出剩余宽度,则停止
    if (currentWidth + DOT_WIDTH > leftWidth) {
      runningStep2 = false;
      break;
    }

    r.push(currentText);
    // 没有超出,则计算剩余宽度
    leftWidth -= currentWidth;
    leftText = leftText.substr(1);

    if (!leftText) {
      return r.join('');
    }
  }

  return `${r.join('')}...`;
}
Example #5
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
CustomAlarm = ({ scopeType }: { scopeType: string }) => {
  const customAlarmStore = customAlarmStoreMap[scopeType];
  const monitorMetaDataStore = monitorMetaDataStoreMap[scopeType];
  const [switchCustomAlarmLoading, getPreviewMetaDataLoading, getCustomAlarmsLoading, getCustomAlarmDetailLoading] =
    useLoading(customAlarmStore, [
      'switchCustomAlarm',
      'getPreviewMetaData',
      'getCustomAlarms',
      'getCustomAlarmDetail',
    ]);
  const [extraLoading] = useLoading(monitorMetaDataStore, ['getMetaData']);
  const [metaGroups, metaConstantMap, metaMetrics] = monitorMetaDataStore.useStore((s: any) => [
    s.metaGroups,
    s.metaConstantMap,
    s.metaMetrics,
  ]);
  const { getMetaGroups, getMetaData } = monitorMetaDataStore.effects;
  const {
    fields,
    tags,
    metric,
    filters: defaultFilters,
  } = React.useMemo(() => (metaMetrics || [])[0] || {}, [metaMetrics]);
  const { types, filters } = React.useMemo(() => metaConstantMap, [metaConstantMap]);
  const fieldsMap = React.useMemo(() => keyBy(fields, 'key'), [fields]);

  const [customAlarms, customAlarmPaging, customMetricMap, customAlarmDetail, customAlarmTargets] =
    customAlarmStore.useStore((s: any) => [
      s.customAlarms,
      s.customAlarmPaging,
      s.customMetricMap,
      s.customAlarmDetail,
      s.customAlarmTargets,
    ]);
  const {
    getCustomAlarms,
    switchCustomAlarm,
    deleteCustomAlarm,
    getCustomMetrics,
    getCustomAlarmDetail,
    getCustomAlarmTargets,
    createCustomAlarm,
    editCustomAlarm,
  } = customAlarmStore.effects;
  const { clearCustomAlarmDetail } = customAlarmStore.reducers;
  const { total, pageSize, pageNo } = customAlarmPaging;

  useMount(() => {
    getMetaGroups();
    getCustomMetrics();
    getCustomAlarmTargets();
  });

  const [
    { modalVisible, editingFilters, editingFields, selectedMetric, activedFormData, previewerKey, layout, searchValue },
    updater,
    update,
  ] = useUpdate({
    layout: [],
    modalVisible: false,
    editingFilters: [],
    editingFields: [],
    selectedMetric: undefined as any,
    activedFormData: {},
    previewerKey: undefined,
    searchValue: '',
  });

  React.useEffect(() => {
    updater.selectedMetric(metric);
  }, [metric, updater]);

  React.useEffect(() => {
    if (isEmpty(customAlarmDetail)) return;
    const { rules } = customAlarmDetail;
    const { activedMetricGroups } = rules[0];
    getMetaData({ groupId: activedMetricGroups[activedMetricGroups.length - 1] });
  }, [customAlarmDetail, getMetaData]);

  React.useEffect(() => {
    const { rules, notifies } = customAlarmDetail;
    if (isEmpty(rules) || isEmpty(notifies)) return;

    const { functions } = rules[0];
    update({
      editingFields: map(functions, (item) => {
        const aggregations = get(types[get(fieldsMap[item.field], 'type')], 'aggregations');
        return {
          ...item,
          uniKey: uniqueId(),
          aggregations,
          aggregatorType: get(find(aggregations, { aggregation: item.aggregator }), 'result_type'),
        };
      }),
    });
  }, [customAlarmDetail, fieldsMap, types, update]);

  React.useEffect(() => {
    const { name, rules, notifies, id } = customAlarmDetail;
    if (isEmpty(rules) || isEmpty(notifies)) return;

    const { window, metric: _metric, filters: _filters, group, activedMetricGroups } = rules[0];
    const { title, content, targets } = notifies[0];
    update({
      editingFilters: map(_filters, (item) => ({ ...item, uniKey: uniqueId() })),
      activedFormData: {
        id,
        name,
        rule: {
          activedMetricGroups,
          window,
          metric: _metric,
          group,
        },
        notify: {
          title,
          content,
          targets: filter(targets, (target) => target !== 'ticket'),
        },
      },
      selectedMetric: _metric,
    });
  }, [customAlarmDetail, update]);

  React.useEffect(() => {
    getCustomAlarms({ name: searchValue, pageNo: 1 });
  }, [searchValue]);

  const handlePageChange: TableProps<COMMON_CUSTOM_ALARM.CustomAlarms>['onChange'] = (paging) => {
    const { current, pageSize: size } = paging;
    getCustomAlarms({ pageNo: current, pageSize: size, name: searchValue });
  };
  const handleDeleteAlarm = (id: number) => {
    confirm({
      title: i18n.t('are you sure you want to delete this item?'),
      content: i18n.t('the item will be permanently deleted!'),
      onOk() {
        deleteCustomAlarm(id);
      },
    });
  };

  const handleEnableRule = (enable: string, record: COMMON_CUSTOM_ALARM.CustomAlarms) => {
    switchCustomAlarm({
      enable: enable === 'enable',
      id: record.id,
    }).then(() => {
      getCustomAlarms({ pageNo, pageSize, name: searchValue });
    });
  };

  const columns: Array<ColumnProps<COMMON_CUSTOM_ALARM.CustomAlarms>> = [
    {
      title: i18n.t('Name'),
      dataIndex: 'name',
      key: 'name',
    },
    {
      title: i18n.t('Status'),
      dataIndex: 'enable',
      onCell: () => ({ style: { minWidth: 100, maxWidth: 300 } }),
      render: (enable: boolean, record) => (
        <Dropdown
          trigger={['click']}
          overlay={
            <Menu
              onClick={(e) => {
                handleEnableRule(e.key, record);
              }}
            >
              <Menu.Item key="enable">
                <Badge text={i18n.t('Enable')} status="success" />
              </Menu.Item>
              <Menu.Item key="unable">
                <Badge text={i18n.t('unable')} status="default" />
              </Menu.Item>
            </Menu>
          }
        >
          <div
            onClick={(e) => e.stopPropagation()}
            className="group flex items-center justify-between px-2 cursor-pointer absolute top-0 left-0 bottom-0 right-0 hover:bg-default-04"
          >
            <Badge text={enable ? i18n.t('Enable') : i18n.t('unable')} status={enable ? 'success' : 'default'} />
            <ErdaIcon type="caret-down" size={20} fill="black-3" className="opacity-0 group-hover:opacity-100" />
          </div>
        </Dropdown>
      ),
    },
    {
      title: i18n.t('Indicator'),
      dataIndex: 'metric',
      key: 'metric',
    },
    {
      title: i18n.t('Period'),
      dataIndex: 'window',
      key: 'window',
      render: (value: number) => `${value} ${i18n.t('min')}`,
    },
    {
      title: i18n.t('Notification method'),
      dataIndex: 'notifyTargets',
      key: 'notifyTargets',
      render: (value: string[]) => `${value.join('、')}`,
    },
    {
      title: i18n.t('Creator'),
      dataIndex: 'creator',
      render: (text: string) => <UserInfo id={text} />,
    },
  ];

  const filterColumns = [
    {
      title: i18n.t('label'),
      dataIndex: 'tag',
      render: (value: string, { uniKey }: COMMON_CUSTOM_ALARM.Filter) => (
        <Select
          dropdownMatchSelectWidth={false}
          defaultValue={value}
          className="w-full"
          onSelect={(tag) => {
            handleEditEditingFilters(uniKey, [
              { key: 'tag', value: tag },
              { key: 'value', value: undefined },
            ]);
          }}
          getPopupContainer={() => document.body}
        >
          {map(tags, ({ key, name }) => (
            <Select.Option key={key} value={key}>
              {name}
            </Select.Option>
          ))}
        </Select>
      ),
    },
    {
      title: i18n.t('operation'),
      dataIndex: 'operator',
      render: (value: string, { uniKey }: COMMON_CUSTOM_ALARM.Filter) => (
        <Select
          dropdownMatchSelectWidth={false}
          defaultValue={value}
          className="w-full"
          onSelect={(operator) => {
            handleEditEditingFilters(uniKey, [{ key: 'operator', value: operator }]);
          }}
          getPopupContainer={() => document.body}
        >
          {map(filters, ({ operation, name }) => (
            <Select.Option key={operation}>{name}</Select.Option>
          ))}
        </Select>
      ),
    },
    {
      title: i18n.t('cmp:Expected value'),
      dataIndex: 'value',
      render: (value: any, { uniKey }: COMMON_CUSTOM_ALARM.Filter) => {
        let expectedValEle = (
          <Input
            defaultValue={value}
            onBlur={(e: any) => {
              handleEditEditingFilters(uniKey, [{ key: 'value', value: e.target.value }]);
            }}
          />
        );
        const selectedFilter = find(editingFilters, { uniKey }) || ({} as any);
        const { values: _values } = find(tags, { key: selectedFilter.tag }) || ({} as any);
        if (!isEmpty(_values)) {
          expectedValEle = (
            <Select
              dropdownMatchSelectWidth={false}
              showSearch
              className="w-full"
              value={value}
              onSelect={(v: any) => {
                handleEditEditingFilters(uniKey, [{ key: 'value', value: v }]);
              }}
              getPopupContainer={() => document.body}
            >
              {map(_values, ({ value: v, name }) => (
                <Select.Option key={v} value={v}>
                  {name}
                </Select.Option>
              ))}
            </Select>
          );
        }
        return expectedValEle;
      },
    },
  ];

  const filteredTableActions: IActions<COMMON_CUSTOM_ALARM.Filter> = {
    render: (record) => [
      {
        title: i18n.t('Delete'),
        onClick: () => {
          handleRemoveEditingFilter(record.uniKey);
        },
      },
    ],
  };

  const getFieldColumns = (form: FormInstance) => [
    {
      title: i18n.t('Field'),
      dataIndex: 'field',
      render: (value: string, { uniKey }: COMMON_CUSTOM_ALARM.Field) => (
        <Select
          dropdownMatchSelectWidth={false}
          defaultValue={value}
          className="w-full"
          onSelect={(field: any) => {
            handleEditEditingFields(uniKey, [
              { key: 'field', value: field },
              { key: 'aggregations', value: get(types[get(fieldsMap[field], 'type')], 'aggregations') },
            ]);
          }}
          getPopupContainer={() => document.body}
        >
          {map(fields, ({ key, name }) => (
            <Select.Option key={key} value={key}>
              <Tooltip title={name}>{name}</Tooltip>
            </Select.Option>
          ))}
        </Select>
      ),
    },
    {
      title: i18n.t('cmp:Alias'),
      dataIndex: 'alias',
      render: (value: string, { uniKey }: COMMON_CUSTOM_ALARM.Field) => (
        <Input
          defaultValue={value}
          onBlur={(e: any) => {
            handleEditEditingFields(uniKey, [{ key: 'alias', value: e.target.value }]);
          }}
        />
      ),
    },
    {
      title: i18n.t('cmp:Aggregation'),
      dataIndex: 'aggregator',
      render: (value: string, { uniKey, aggregations }: COMMON_CUSTOM_ALARM.Field) => (
        <Select
          dropdownMatchSelectWidth={false}
          defaultValue={value}
          className="w-full"
          onSelect={(aggregator: any) => {
            handleEditEditingFields(uniKey, [
              { key: 'aggregator', value: aggregator },
              { key: 'aggregatorType', value: get(find(aggregations, { aggregation: aggregator }), 'result_type') },
            ]);
          }}
          getPopupContainer={() => document.body}
        >
          {map(aggregations, ({ aggregation, name }) => (
            <Select.Option key={aggregation}>{name}</Select.Option>
          ))}
        </Select>
      ),
    },
    {
      title: i18n.t('Operations'),
      dataIndex: 'operator',
      render: (value: string, { uniKey, aggregatorType }: COMMON_CUSTOM_ALARM.Field) => (
        <Select
          dropdownMatchSelectWidth={false}
          defaultValue={value}
          className="w-full"
          onSelect={(operator) => {
            handleEditEditingFields(uniKey, [{ key: 'operator', value: operator }]);
          }}
          getPopupContainer={() => document.body}
        >
          {map(get(types[aggregatorType], 'operations'), ({ operation, name }) => (
            <Select.Option key={operation}>{name}</Select.Option>
          ))}
        </Select>
      ),
    },
    {
      title: i18n.t('cmp:Default threshold'),
      dataIndex: 'value',
      fixed: 'right',
      render: (value: any, { uniKey, aggregatorType }: COMMON_CUSTOM_ALARM.Field) => {
        let valueEle = null;
        switch (aggregatorType) {
          case DataType.STRING:
          case DataType.STRING_ARRAY:
            valueEle = (
              <Input
                defaultValue={value}
                onBlur={(e: any) => {
                  handleEditEditingFields(uniKey, [{ key: 'value', value: e.target.value }]);
                }}
              />
            );
            break;
          case DataType.NUMBER:
          case DataType.NUMBER_ARRAY:
            valueEle = (
              <InputNumber
                min={0}
                defaultValue={value}
                onChange={(v: any) => {
                  debounceEditEditingFields(uniKey, [{ key: 'value', value: v }]);
                }}
              />
            );
            break;
          case DataType.BOOL:
          case DataType.BOOL_ARRAY:
            valueEle = (
              <Switch
                checkedChildren="true"
                unCheckedChildren="false"
                defaultChecked={value}
                onClick={(v: boolean) => {
                  handleEditEditingFields(uniKey, [{ key: 'value', value: v }]);
                }}
              />
            );
            break;
          default:
            break;
        }
        return valueEle;
      },
    },
  ];

  const fieldsTableActions: IActions<COMMON_CUSTOM_ALARM.Field> = {
    render: (record) => [
      {
        title: i18n.t('Delete'),
        onClick: () => {
          handleRemoveEditingField(record.uniKey);
        },
      },
    ],
  };

  const handleAddEditingFilters = () => {
    updater.editingFilters([
      {
        uniKey: uniqueId(),
        // tag: customMetricMap.metricMap[selectedMetric].tags[0].tag.key,
        tag: undefined,
        // operator: keys(customMetricMap.filterOperatorMap)[0],
        operator: undefined,
      },
      ...editingFilters,
    ]);
  };

  const handleAddEditingFields = () => {
    updater.editingFields([
      {
        uniKey: uniqueId(),
        field: undefined,
        alias: undefined,
        aggregator: undefined,
        operator: undefined,
      },
      ...editingFields,
    ]);
  };

  const editRule = (rules: any, uniKey: any, items: Array<{ key: string; value: any }>) => {
    if (!uniKey) return;
    const _rules = cloneDeep(rules);
    const rule = find(_rules, { uniKey });
    const index = findIndex(_rules, { uniKey });
    const rest = reduce(items, (acc, { key, value }) => ({ ...acc, [key]: value }), {});
    const newRule = {
      uniKey,
      ...rule,
      ...rest,
    } as any;

    // // 标签、字段对应不同的 value 类型,改变标签或字段就重置 value
    // if (['tag', 'field'].includes(item.key)) {
    //   newRule = { ...newRule, value: undefined };
    // }

    fill(_rules, newRule, index, index + 1);

    return _rules;
  };

  const handleShowNotifySample = () => {
    Modal.info({
      title: i18n.t('cmp:Template Sample'),
      content: <span className="prewrap">{customMetricMap.notifySample}</span>,
    });
  };

  const handleEditEditingFilters = (uniKey: any, items: Array<{ key: string; value: any }>) => {
    updater.editingFilters(editRule(editingFilters, uniKey, items));
  };

  const handleEditEditingFields = (uniKey: any, items: Array<{ key: string; value: any }>) => {
    updater.editingFields(editRule(editingFields, uniKey, items));
  };

  const debounceEditEditingFields = debounce(handleEditEditingFields, 500);

  const handleRemoveEditingFilter = (uniKey: string | undefined) => {
    updater.editingFilters(filter(editingFilters, (item) => item.uniKey !== uniKey));
  };

  const handleRemoveEditingField = (uniKey: string | undefined) => {
    updater.editingFields(filter(editingFields, (item) => item.uniKey !== uniKey));
  };

  const extraKeys = ['uniKey', 'aggregations', 'aggregatorType'];
  const openModal = (id?: number) => {
    id && getCustomAlarmDetail(id);
    updater.modalVisible(true);
  };

  const closeModal = () => {
    updater.editingFields([]);
    updater.editingFilters([]);
    updater.activedFormData({});
    updater.modalVisible(false);
    updater.previewerKey(undefined);
    clearCustomAlarmDetail();
  };

  const someValueEmpty = (data: any[], key: string) => {
    return some(data, (item) => isEmpty(toString(item[key])));
  };

  const beforeSubmit = (data: any) => {
    return new Promise((resolve, reject) => {
      if (isEmpty(editingFields)) {
        message.warning(i18n.t('cmp:field rules are required'));
        return reject();
      }
      if (someValueEmpty(editingFilters, 'value')) {
        message.warning(i18n.t('cmp:The expected value of filter rule is required.'));
        return reject();
      }
      if (someValueEmpty(editingFields, 'alias')) {
        message.warning(i18n.t('cmp:field rule alias is required'));
        return reject();
      }
      if (uniqBy(editingFields, 'alias').length !== editingFields.length) {
        message.warning(i18n.t('cmp:field rule alias cannot be repeated'));
        return reject();
      }
      if (someValueEmpty(editingFields, 'value')) {
        message.warning(i18n.t('cmp:field rule threshold is required'));
        return reject();
      }
      resolve(data);
    });
  };

  const handleUpdateCustomAlarm = (value: { name: string; rule: any; notify: any }) => {
    const _notify = merge({}, value.notify, { targets: [...(value.notify.targets || []), 'ticket'] });
    const payload = {
      name: value.name,
      rules: [
        {
          ...value.rule,
          metric: selectedMetric,
          functions: map(editingFields, (item) => omit(item, extraKeys)),
          filters: map(editingFilters, (item) => omit(item, extraKeys)),
        },
      ],
      notifies: [_notify],
    };
    if (isEmpty(activedFormData)) {
      createCustomAlarm(payload);
    } else {
      editCustomAlarm({ id: activedFormData.id, ...payload });
    }
    closeModal();
  };

  const BasicForm = ({ form }: { form: FormInstance }) => {
    const fieldsList = [
      {
        label: i18n.t('Name'),
        name: 'name',
        itemProps: {
          maxLength: 50,
        },
      },
    ];
    return <RenderPureForm list={fieldsList} form={form} formItemLayout={formItemLayout} />;
  };

  const RuleForm = ({ form }: { form: FormInstance }) => {
    let fieldsList = [
      {
        label: `${i18n.t('Period')} (${i18n.t('common:minutes')})`,
        name: ['rule', 'window'],
        type: 'inputNumber',
        itemProps: {
          min: 0,
          precision: 0,
          className: 'w-full',
        },
      },
      {
        label: i18n.t('Indicator'),
        name: ['rule', 'activedMetricGroups'],
        type: 'cascader',
        options: metaGroups,
        itemProps: {
          className: 'w-full',
          showSearch: true,
          placeholder: i18n.t('cmp:Please select the index group'),
          onChange: (v: any) => {
            getMetaData({ groupId: v[v.length - 1] }).then(() => {
              form.setFieldsValue({
                rule: {
                  group: undefined,
                },
              });
              update({
                editingFilters: [],
                editingFields: [],
                previewerKey: undefined,
              });
            });
          },
        },
      },
    ];
    if (selectedMetric) {
      fieldsList = concat(
        fieldsList,
        {
          label: i18n.t('cmp:Filter rule'),
          name: ['rule', 'filters'],
          required: false,
          getComp: () => (
            <>
              <Button
                ghost
                className="mb-2"
                type="primary"
                disabled={someValueEmpty(editingFilters, 'value')}
                onClick={handleAddEditingFilters}
              >
                {i18n.t('cmp:Add-filter-rules')}
              </Button>
              <ErdaTable
                hideHeader
                className="filter-rule-table"
                rowKey="uniKey"
                dataSource={editingFilters}
                columns={filterColumns}
                actions={filteredTableActions}
                scroll={undefined}
              />
            </>
          ),
        },
        {
          label: i18n.t('cmp:Grouping rule'),
          name: ['rule', 'group'],
          required: true,
          type: 'select',
          options: map(tags, ({ key, name }) => ({ value: key, name })),
          itemProps: {
            mode: 'multiple',
            allowClear: true,
            className: 'w-full',
          },
        },
        {
          label: i18n.t('cmp:Field rule'),
          name: ['rule', 'functions'],
          required: false,
          getComp: () => (
            <>
              <Button
                className="mb-2"
                type="primary"
                ghost
                disabled={someValueEmpty(editingFields, 'value')}
                onClick={handleAddEditingFields}
              >
                {i18n.t('cmp:Add-field-rules')}
              </Button>
              <ErdaTable
                hideHeader
                className="field-rule-table"
                rowKey="uniKey"
                dataSource={editingFields}
                actions={fieldsTableActions}
                columns={getFieldColumns(form)}
                scroll={undefined}
              />
            </>
          ),
        },
      );
    }
    return <RenderPureForm list={fieldsList} form={form} formItemLayout={formItemLayout} />;
  };

  const NotifyForm = ({ form }: { form: FormInstance }) => {
    const Comp = () => (
      <>
        <Button
          className="mb-2"
          type="primary"
          ghost
          disabled={isEmpty(customMetricMap.notifySample)}
          onClick={handleShowNotifySample}
        >
          {i18n.t('cmp:Template Sample')}
        </Button>
        <MarkdownEditor
          value={form.getFieldValue(['notify', 'content'])}
          onBlur={(value) => {
            form.setFieldsValue({
              notify: {
                ...(form.getFieldValue('notify') || {}),
                content: value,
              },
            });
          }}
          placeholder={i18n.t('cmp:Refer to the sample to input content')}
          maxLength={512}
        />
      </>
    );

    const fieldsList = [
      {
        label: i18n.t('cmp:Notification method'),
        name: ['notify', 'targets'],
        type: 'select',
        required: false,
        options: map(
          filter(customAlarmTargets, ({ key }) => key !== 'ticket'),
          ({ key, display }) => ({ value: key, name: display }),
        ),
        itemProps: {
          mode: 'multiple',
          allowClear: true,
          className: 'w-full',
        },
      },
      {
        label: i18n.t('cmp:Message title'),
        name: ['notify', 'title'],
        itemProps: {
          maxLength: 128,
          placeholder: i18n.t('cmp:message title rules template', { interpolation: { suffix: '>', prefix: '<' } }),
        },
      },
      {
        label: i18n.t('cmp:Message content'),
        name: ['notify', 'content'],
        getComp: () => <Comp />,
      },
    ];
    return <RenderPureForm list={fieldsList} form={form} formItemLayout={formItemLayout} />;
  };

  const CustomAlarmForm = ({ form }: any) => {
    if (isEmpty(customMetricMap) || isEmpty(customAlarmTargets)) return null;
    return (
      <div className="custom-alarm-form">
        <BasicForm form={form} />
        <div className="title font-bold text-base">{i18n.t('cmp:Trigger rule')}</div>
        <RuleForm form={form} />
        <div className="title font-bold text-base">{i18n.t('cmp:Message template')}</div>
        <NotifyForm form={form} />
      </div>
    );
  };

  const customRender = (content: JSX.Element) => (
    <div className="flex justify-between items-center">
      <div className="flex-1">{content}</div>
      <IF check={!!previewerKey}>
        <div className="custom-alarm-previewer px-4">
          <Spin spinning={getPreviewMetaDataLoading}>
            <BoardGrid.Pure layout={layout} />
          </Spin>
        </div>
      </IF>
    </div>
  );

  const actions: IActions<COMMON_CUSTOM_ALARM.CustomAlarms> = {
    render: (record: COMMON_CUSTOM_ALARM.CustomAlarms) => renderMenu(record),
  };

  const renderMenu = (record: COMMON_CUSTOM_ALARM.CustomAlarms) => {
    const { editAlarmRule, deleteAlarmRule } = {
      editAlarmRule: {
        title: i18n.t('Edit'),
        onClick: () => openModal(record.id),
      },
      deleteAlarmRule: {
        title: i18n.t('Delete'),
        onClick: () => handleDeleteAlarm(record.id),
      },
    };

    return [editAlarmRule, deleteAlarmRule];
  };

  const handleChange = React.useCallback(
    debounce((value) => {
      updater.searchValue(value);
    }, 1000),
    [],
  );

  return (
    <div className="custom-alarm">
      <TopButtonGroup>
        <Button type="primary" onClick={() => openModal()}>
          {i18n.t('cmp:Add Custom Rule')}
        </Button>
      </TopButtonGroup>
      <ErdaTable
        slot={
          <Input
            size="small"
            className="w-[200px] bg-black-06 border-none ml-0.5"
            allowClear
            prefix={<ErdaIcon size="16" fill={'default-3'} type="search" />}
            onChange={(e) => {
              handleChange(e.target.value);
            }}
            placeholder={i18n.t('search by {name}', { name: i18n.t('Name').toLowerCase() })}
          />
        }
        loading={getCustomAlarmsLoading || switchCustomAlarmLoading}
        dataSource={customAlarms}
        columns={columns}
        rowKey="id"
        onChange={handlePageChange}
        pagination={{ current: pageNo, pageSize, total }}
        actions={actions}
      />
      <FormModal
        name={i18n.t('cmp:custom rule')}
        loading={getCustomAlarmDetailLoading || extraLoading}
        visible={modalVisible}
        width={1200}
        modalProps={{ bodyStyle: { height: '550px', overflow: 'auto' } }}
        PureForm={CustomAlarmForm}
        formData={activedFormData}
        customRender={customRender}
        onOk={handleUpdateCustomAlarm}
        beforeSubmit={beforeSubmit}
        onCancel={closeModal}
      />
    </div>
  );
}