lodash#every TypeScript Examples

The following examples show how to use lodash#every. 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: util.ts    From erda-ui with GNU Affero General Public License v3.0 7 votes vote down vote up
validSubnetMask = (subnet: string) => {
  const ipNum = getSubnetNum(subnet);
  if (ipNum.length !== 5) return false;
  const mask = +(ipNum.pop() as number); // 掩码
  if (isNaN(mask) || mask < 8 || mask > 24) return false;
  if (!validIp(ipNum)) return false;
  const len = Math.ceil(mask / 8); // 位数
  const emptyPos = ipNum.slice(len, ipNum.length);
  if (!every(emptyPos, (item) => item === 0)) return false;
  return true;
}
Example #2
Source File: index.tsx    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
// 对前端搜索数据进行表头筛选项过滤
  private handleFrontendFilters(
    dataSource: Record<string, any>[]
  ): Record<string, any>[] {
    let tempDataSource: Record<string, any>[] = dataSource || [];
    const filtersArray = map(this.filters, (item, k) => ({
      key: k,
      value: item,
    })).filter((item) => !isNil(item.value) && item.value.length !== 0);
    tempDataSource = tempDataSource.filter((item) => {
      return every(filtersArray, (filter) => {
        return filter.value?.includes(get(item, filter.key)) ?? true;
      });
    });
    return tempDataSource;
  }
Example #3
Source File: helpers.ts    From leda with MIT License 6 votes vote down vote up
getSuggestionFromValue = ({
  data,
  value,
  textField,
}: {
  data: Suggestion[],
  value: string | DataObject,
  textField?: string,
}): Suggestion => {
  const isEveryIsObject = every(data, isObject);

  const isValidTextField = isString(textField) && textField.length > 0;

  if (isEveryIsObject && !isValidTextField) {
    // todo: handle textField error
  }

  const suggestion: Suggestion | undefined = isEveryIsObject
    ? (data as DataObject[]).find((el: DataObject): boolean => (el[textField as string] === value))
    : (data as string[]).find((el: string): boolean => (el === value));

  return suggestion || null;
}
Example #4
Source File: util.ts    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
validIp = (ip: any[] | string) => {
  if (isEmpty(ip)) return false;
  const ipNum = isString(ip) ? ip.split('.') : ip;
  return every(ipNum, (item) => !isNaN(+item) && item <= 255 && item >= 0);
}
Example #5
Source File: use-hooks.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
convertFilterParamsToUrlFormat =
  (fullRange?: boolean, dateFormat?: string) =>
  (
    condition: { [prop: string]: any },
    fieldConvertor?: {
      [k: string]: (value: any, allQuery?: any) => string | string[] | undefined;
    },
  ) => {
    const formatCondition = {};
    forIn(condition, (v, k) => {
      const fieldConvertFunc = get(fieldConvertor, k);
      if (Array.isArray(v) && v.length === 2 && every(v, (item) => moment.isMoment(item))) {
        // handle date range
        const [start, end] = v as [Moment, Moment];
        const format = dateFormat || 'YYYY-MM-DD HH:mm:ss';
        let startName = `${k}From`;
        let endName = `${k}To`;
        const rangeNames = k.split(',');
        if (rangeNames.length === 2) {
          [startName, endName] = rangeNames;
        }
        const startMoment = fullRange ? start.startOf('day') : start;
        const endMoment = fullRange ? end.endOf('day') : end;
        set(formatCondition, startName, format === 'int' ? startMoment.valueOf() : startMoment.format(format));
        set(formatCondition, endName, format === 'int' ? endMoment.valueOf() : endMoment.format(format));
      } else if (fieldConvertFunc) {
        // handle custom field
        set(formatCondition, k, fieldConvertFunc(v, condition));
      } else {
        set(formatCondition, k, v);
      }
    });
    return formatCondition;
  }
Example #6
Source File: packet.utils.ts    From nestjs-jaeger-tracing with MIT License 6 votes vote down vote up
export function isRequestPacket<T>(
  packet: unknown,
): packet is RequestPacket<T> {
  const keys: Array<keyof RequestPacket<T>> = ['id', 'data', 'pattern'];
  return every(keys, partial(has, packet));
}
Example #7
Source File: packet.utils.ts    From nestjs-jaeger-tracing with MIT License 6 votes vote down vote up
export function isTracingContext(packet: unknown): packet is TracingContext {
  const keys: Array<keyof TracingContext> = ['payload', 'isSerialized'];
  return every(keys, partial(has, packet));
}
Example #8
Source File: number-set.ts    From dyngoose with ISC License 6 votes vote down vote up
toDynamo(values: Value): DynamoDB.AttributeValue {
    if (!isArray(values) || !every(values, isNumber)) {
      throw new ValidationError(`Expected ${this.propertyName} to be an array of numbers`)
    }

    // dynamodb does not allow sets to contain duplicate values, so ensure uniqueness here
    return {
      NS: uniq(values.map((value) => numberToString(value))),
    }
  }
Example #9
Source File: house.tsx    From S2 with MIT License 6 votes vote down vote up
Sheet = ({ data }) => {
  const [dataSource, setDataSource] = useState(data);

  const filterData = (filterInfo) => {
    const result = filter(data, (item) => {
      return every(filterInfo, (value, key) => {
        if (key === 'area') {
          return value[0] <= item.area && value[1] >= item.area;
        }
        if (key === 'level') {
          return value[0] <= item.level && value[1] >= item.level;
        }
        if(key === 'nearStreet') {
          console.log(item.nearStreet, 'item.nearStreet',  value, 'value');
          console.log(item.nearStreet === value, 'item.nearStreet === value');
        }
        return item[key] === value;
      });
    });
    setDataSource(result);
  };

  return (
    <div>
      <SelectList filterData={filterData} />
      <SheetComponent
        sheetType={'pivot'}
        dataCfg={{ ...dataConfig, data: dataSource }}
        options={s2Options}
        showPagination={true}
      />
    </div>
  );
}
Example #10
Source File: pivot-data-set.ts    From S2 with MIT License 6 votes vote down vote up
public getTotalStatus = (query: DataType) => {
    const { columns, rows } = this.fields;
    const isTotals = (dimensions: string[], isSubTotal?: boolean) => {
      if (isSubTotal) {
        const firstDimension = find(dimensions, (item) => !has(query, item));
        return firstDimension && firstDimension !== first(dimensions);
      }
      return every(dimensions, (item) => !has(query, item));
    };
    const getDimensions = (dimensions: string[], hasExtra: boolean) => {
      return hasExtra
        ? dimensions.filter((item) => item !== EXTRA_FIELD)
        : dimensions;
    };

    return {
      isRowTotal: isTotals(
        getDimensions(rows, !this.spreadsheet.isValueInCols()),
      ),
      isRowSubTotal: isTotals(rows, true),
      isColTotal: isTotals(
        getDimensions(columns, this.spreadsheet.isValueInCols()),
      ),
      isColSubTotal: isTotals(columns, true),
    };
  };
Example #11
Source File: split-total-spec.ts    From S2 with MIT License 6 votes vote down vote up
describe('DataSet splitTotal function test', () => {
  test('should return all total data.', () => {
    const fields = {
      rows: ['province', 'city'],
      columns: ['category', 'subCategory'],
    };
    const totals = splitTotal([].concat(data).concat(totalData), fields);
    totals.forEach((total) => {
      const dimensions = [].concat(fields.rows).concat(fields.columns);
      expect(every(dimensions, (dimension) => total[dimension])).toBe(false);
    });
  });
});
Example #12
Source File: tooltip.ts    From S2 with MIT License 5 votes vote down vote up
getSummaries = (params: SummaryParam): TooltipSummaryOptions[] => {
  const { spreadsheet, getShowValue, targetCell, options = {} } = params;
  const summaries: TooltipSummaryOptions[] = [];
  const summary: TooltipDataItem = {};
  const isTableMode = spreadsheet.isTableMode();

  if (isTableMode && options?.showSingleTips) {
    const selectedCellsData = spreadsheet.dataSet.getMultiData({});
    return [{ selectedData: selectedCellsData, name: '', value: '' }];
  }

  // 拿到选择的所有 dataCell的数据
  const selectedCellsData = getSelectedCellsData(
    spreadsheet,
    targetCell,
    options.showSingleTips,
  );

  forEach(selectedCellsData, (item) => {
    if (summary[item?.[EXTRA_FIELD]]) {
      summary[item?.[EXTRA_FIELD]]?.push(item);
    } else {
      summary[item?.[EXTRA_FIELD]] = [item];
    }
  });

  mapKeys(summary, (selected, field) => {
    const name = getSummaryName(spreadsheet, field, options?.isTotals);
    let value: number | string = getShowValue?.(selected, VALUE_FIELD);

    if (isTableMode) {
      value = '';
    } else if (every(selected, (item) => isNotNumber(get(item, VALUE_FIELD)))) {
      const { placeholder } = spreadsheet.options;
      const emptyPlaceholder = getEmptyPlaceholder(summary, placeholder);
      // 如果选中的单元格都无数据,则显示"-" 或 options 里配置的占位符
      value = emptyPlaceholder;
    } else {
      const currentFormatter = getFieldFormatter(spreadsheet, field);
      const dataSum = getDataSumByField(selected, VALUE_FIELD);
      value =
        currentFormatter?.(dataSum, selected) ??
        parseFloat(dataSum.toPrecision(PRECISION)); // solve accuracy problems;
    }
    summaries.push({
      selectedData: selected,
      name,
      value,
    });
  });

  return summaries;
}
Example #13
Source File: types.ts    From prism-frontend with MIT License 5 votes vote down vote up
isLayerKey = (layerKey: string | MenuGroup) => {
  if (typeof layerKey === 'string') {
    return layerKey in rawLayers;
  }
  // check every layer in group
  const layers = map(layerKey.layers, 'id');
  return every(layers, layer => layer in rawLayers);
}
Example #14
Source File: index.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
// istanbul ignore next
  private _handleRowSelectChange = (
    selectedRowKeys: string[],
    selectedRows: any[]
  ): void => {
    const rowKey =
      this.rowKey ?? this._fields.rowKey ?? this.configProps?.rowKey;
    this._selectedRows = selectedRows;
    if (this._selected) {
      const _selectedRows = [...selectedRows, ...this._allChildren];
      if (this.autoSelectParentWhenAllChildrenSelected && this._selectedRow) {
        const selectedRowKeySet = new Set(selectedRowKeys);
        const parent = this._findParentByChildKeyValue(
          this._selectedRow[rowKey] as string,
          rowKey,
          this._dataSource
        );

        if (
          parent &&
          (parent[this.childrenColumnName] as Record<string, unknown>[]).every(
            (item) => selectedRowKeySet.has(item[rowKey] as string)
          )
        ) {
          _selectedRows.push(parent);
        }
      }
      this._selectedRows = uniqBy(_selectedRows, rowKey);
    } else {
      let parent: Record<string, unknown>;

      if (this.autoSelectParentWhenAllChildrenSelected && this._selectedRow) {
        parent = this._findParentByChildKeyValue(
          this._selectedRow[rowKey] as string,
          rowKey,
          this._dataSource
        );
      }
      this._selectedRows = pullAllBy(
        selectedRows,
        this._allChildren.concat(parent),
        rowKey
      );
    }
    this._selectedRow = undefined;
    this.selectedRowKeys = map(this._selectedRows, rowKey);

    let detail = null;
    const data = isEmpty(this._selectUpdateEventDetailField)
      ? this._selectedRows
      : map(this._selectedRows, (row) =>
          get(row, this._selectUpdateEventDetailField)
        );
    detail =
      isEmpty(this._selectUpdateEventDetailKeys) || isEmpty(data)
        ? data
        : set({}, this._selectUpdateEventDetailKeys, data);
    if (!isEmpty(detail)) {
      detail = merge(detail, this._selectUpdateEventDetailExtra);
    }
    if (!this._selectUpdateEventName) {
      this.selectUpdate.emit(detail);
    } else {
      const eventName = this._selectUpdateEventName
        ? this._selectUpdateEventName
        : "select.update";
      this.dispatchEvent(new CustomEvent(eventName, { detail }));
    }
  };
Example #15
Source File: data-set-operate.ts    From S2 with MIT License 5 votes vote down vote up
isTotalData = (ids: string[], data: Data): boolean => {
  return !every(ids, (id) => data[id]);
}
Example #16
Source File: cluster-form.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
AddClusterModal = (props: IProps) => {
  const { initData, toggleModal, visible, onSubmit, clusterList, clusterType } = props;
  const handleSubmit = (values: any) => {
    const { scheduler, opsConfig, credential } = values;
    const postData = { ...values };
    if (every(opsConfig, (item) => isEmpty(item))) {
      postData.opsConfig = null;
    }
    const credentialContent = get(credential, 'content');
    const credentialAddress = get(credential, 'address');
    const cpuSubscribeRatio = get(scheduler, 'cpuSubscribeRatio');
    cpuSubscribeRatio && (postData.scheduler.cpuSubscribeRatio = `${cpuSubscribeRatio}`);
    credentialContent && (credential.content = `${credentialContent.trim()}`);
    credentialAddress && (credential.address = `${credentialAddress.trim()}`);
    onSubmit?.({ ...postData, type: clusterType });
    toggleModal();
  };

  const formData: FormData = (initData && { ...initData }) || ({} as FormData);

  if (TYPE_K8S_AND_EDAS.includes(clusterType) && initData) {
    const { manageConfig } = initData as Obj;
    const { credentialSource, address } = manageConfig || {};

    formData.credentialType = credentialSource;
    formData.credential = { ...formData.credential, address };
  }

  return (
    <FormModal
      width={800}
      name={i18n.t('cmp:{type} cluster', {
        type: get(find(flatten(clusterTypeMap), { type: clusterType }), 'name', ''),
      })}
      title={
        clusterType === 'k8s'
          ? formData
            ? i18n.t('dop:Edit cluster configuration')
            : i18n.t('cmp:import an existing Erda {type} cluster', { type: 'Kubernetes' })
          : undefined
      }
      visible={visible}
      onOk={handleSubmit}
      onCancel={() => toggleModal(true)}
      PureForm={ClusterAddForm}
      formData={formData}
      clusterList={clusterList}
      clusterType={clusterType}
      modalProps={{
        destroyOnClose: true,
        maskClosable: false,
      }}
    />
  );
}
Example #17
Source File: data-set-operate.ts    From S2 with MIT License 5 votes vote down vote up
isEveryUndefined = (data: string[] | undefined[]) => {
  return data?.every((item) => isUndefined(item));
}
Example #18
Source File: micro-service.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
generateMSMenu = (
  menuData: MS_INDEX.IMspMenu[],
  params: Record<string, any>,
  query: Record<string, any>,
  intros: IState['intro'],
) => {
  let queryStr = '';
  if (!isEmpty(query)) {
    queryStr = `?${qs.stringify(query)}`;
  }

  const intro = {
    ...intros,
  };

  const isZh = currentLocale.key === 'zh';
  const menuText = {};

  const newMenu = menuData
    .filter((m) => m.exists)
    .filter((m) => {
      if (!process.env.FOR_COMMUNITY) {
        return true;
      }
      return (
        !COMMUNITY_REMOVE_KEYS.includes(m.key) &&
        (!every(m.children, (c) => COMMUNITY_REMOVE_KEYS.includes(c.key)) || !m.children.length)
      );
    })
    .map((menu) => {
      const { key, cnName, enName, children } = menu;
      const href = getMSFrontPathByKey(key, { ...menu.params, ...params } as any);
      const IconComp = MSIconMap[key];
      const text = isZh ? cnName : enName;
      menuText[key] = text;
      const sideMenu = {
        key,
        icon: IconComp || 'zujian',
        text,
        subtitle: getMSPSubtitleByName(key)[currentLocale.key],
        href: `${href}${queryStr}`,
        prefix: `${href}`,
      };
      if (children.length) {
        sideMenu.subMenu = children
          .filter((m) => m.exists)
          .filter((m) => (process.env.FOR_COMMUNITY ? !COMMUNITY_REMOVE_KEYS.includes(m.key) : true))
          .map((child) => {
            if (child.key in intro) {
              intro[child.key] = !child.params._enabled;
            }
            const childHref = getMSFrontPathByKey(child.key, { ...child.params, ...params } as any);
            menuText[child.key] = isZh ? child.cnName : child.enName;
            return {
              key: child.key,
              text: isZh ? child.cnName : child.enName,
              jumpOut: !!child.href,
              href: child.href ? docUrlMap[child.href] || `${DOC_PREFIX}${child.href}` : `${childHref}${queryStr}`,
              prefix: `${childHref}`,
            };
          });
      }
      return sideMenu;
    });
  return [newMenu, intro, menuText];
}
Example #19
Source File: ResetPasswordModal.tsx    From hub with Apache License 2.0 4 votes vote down vote up
ResetPasswordModal = (props: Props) => {
  const [code, setCode] = useState(props.code);
  const [verifying, setVerifying] = useState<boolean | null>(null);
  const [validCode, setValidCode] = useState<boolean | null>(null);
  const form = useRef<HTMLFormElement>(null);
  const passwordInput = useRef<RefInputField>(null);
  const repeatPasswordInput = useRef<RefInputField>(null);
  const [password, setPassword] = useState<Password>({ value: '', isValid: false });
  const [displayResetPwd, setDisplayResetPwd] = useState<boolean>(false);
  const history = useHistory();
  const [isSending, setIsSending] = useState<boolean>(false);
  const [isValidated, setIsValidated] = useState<boolean>(false);
  const [apiError, setApiError] = useState<string | null>(null);
  const [apiPwdError, setApiPwdError] = useState<string | null>(null);
  const [isSuccess, setIsSuccess] = useState<boolean>(false);

  const onPasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
    setPassword({ value: e.target.value, isValid: e.currentTarget.checkValidity() });
  };

  // Clean API error when form is focused after validation
  const cleanApiError = () => {
    if (!isNull(apiError)) {
      setApiError(null);
    }
  };

  async function resetPassword(password: string) {
    try {
      await API.resetPassword(code!, password);
      setIsSuccess(true);
      setIsSending(false);
    } catch (err: any) {
      let error = compoundErrorMessage(err, 'An error occurred resetting the password');
      setApiError(error);
      setIsSending(false);
    }
  }

  const submitForm = () => {
    cleanApiError();
    setIsSending(true);
    if (form.current) {
      validateForm(form.current).then((validation: FormValidation) => {
        if (validation.isValid) {
          resetPassword(validation.password!);
          setIsValidated(true);
        } else {
          setIsSending(false);
        }
      });
    }
  };

  const validateForm = async (form: HTMLFormElement): Promise<FormValidation> => {
    let currentPassword: string | undefined;
    return validateAllFields().then((isValid: boolean) => {
      if (isValid) {
        const formData = new FormData(form);

        currentPassword = formData.get('password') as string;
      }
      setIsValidated(true);
      return { isValid, password: currentPassword };
    });
  };

  const validateAllFields = async (): Promise<boolean> => {
    return Promise.all([passwordInput.current!.checkIsValid(), repeatPasswordInput.current!.checkIsValid()]).then(
      (res: boolean[]) => {
        return every(res, (isValid: boolean) => isValid);
      }
    );
  };

  useEffect(() => {
    async function verifyPasswordResetCode() {
      setVerifying(true);
      try {
        await API.verifyPasswordResetCode(code!);
        setValidCode(true);
      } catch (err: any) {
        if (err.kind === ErrorKind.Gone) {
          setApiError('This password reset link is no longer valid, please get a new one.');
          setDisplayResetPwd(true);
        } else {
          let error = 'An error occurred with your password reset code.';
          if (!isUndefined(err.message)) {
            error = `Sorry, ${err.message}`;
          }
          setApiPwdError(error);
        }
        setValidCode(false);
      } finally {
        setVerifying(false);
      }
    }

    if (!isUndefined(code)) {
      history.replace({
        pathname: '/',
        search: '',
      });
      if (code !== props.code) {
        setCode(code);
      }
      verifyPasswordResetCode();
    }
  }, [code, history]); /* eslint-disable-line react-hooks/exhaustive-deps */

  if (isUndefined(code) || isNull(verifying)) return null;

  const closeButton = (
    <button
      className="btn btn-sm btn-outline-secondary"
      type="button"
      disabled={isSending}
      onClick={submitForm}
      aria-label="Reset password"
    >
      <div className="d-flex flex-row align-items-center">
        {isSending ? (
          <>
            <span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
            <span className="ms-2">Resetting password...</span>
          </>
        ) : (
          <>
            <CgLastpass className="me-2" />
            <span className="text-uppercase">Reset password</span>
          </>
        )}
      </div>
    </button>
  );

  return (
    <Modal
      data-testid="resetPwdModal"
      header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Reset password</div>}
      disabledClose={verifying}
      modalClassName={styles.modal}
      open={!isUndefined(code)}
      closeButton={validCode && !isSuccess ? closeButton : undefined}
      error={apiError}
      cleanError={cleanApiError}
    >
      <div
        className={`d-flex flex-column h-100 w-100 align-items-center justify-content-center text-center position-relative ${styles.content}`}
      >
        {verifying ? (
          <>
            <Loading className="position-relative" spinnerClassName="mt-0" />
            <small className="text-muted">We are verifying your code...</small>
          </>
        ) : (
          <div className="text-start w-100">
            {validCode ? (
              <>
                {isSuccess ? (
                  <div className="d-flex flex-column text-center h5">
                    <div className="display-4 text-success mb-4">
                      <MdDone />
                    </div>
                    Your password has been reset successfully. You can now log in using the new credentials.
                  </div>
                ) : (
                  <form
                    ref={form}
                    data-testid="resetPwdForm"
                    className={classnames(
                      'w-100',
                      { 'needs-validation': !isValidated },
                      { 'was-validated': isValidated }
                    )}
                    onFocus={cleanApiError}
                    autoComplete="off"
                    noValidate
                  >
                    <InputField
                      ref={passwordInput}
                      type="password"
                      label="Password"
                      name="password"
                      minLength={6}
                      invalidText={{
                        default: 'This field is required',
                        customError: 'Insecure password',
                      }}
                      onChange={onPasswordChange}
                      autoComplete="new-password"
                      checkPasswordStrength
                      validateOnChange
                      validateOnBlur
                      required
                    />

                    <InputField
                      ref={repeatPasswordInput}
                      type="password"
                      label="Confirm password"
                      labelLegend={<small className="ms-1 fst-italic">(Required)</small>}
                      name="confirmPassword"
                      pattern={password.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}
                      invalidText={{
                        default: 'This field is required',
                        patternMismatch: "Passwords don't match",
                      }}
                      autoComplete="new-password"
                      validateOnBlur={password.isValid}
                      required
                    />
                  </form>
                )}
              </>
            ) : (
              <>
                {displayResetPwd && <ResetPassword visibleTitle={false} onFinish={cleanApiError} />}
                {apiPwdError && (
                  <div className="d-flex flex-column text-center h5">
                    <div className="display-4 text-danger mb-4">
                      <MdClose />
                    </div>
                    {apiPwdError}
                  </div>
                )}
              </>
            )}
          </div>
        )}
      </div>
    </Modal>
  );
}
Example #20
Source File: index.tsx    From aqualink-app with MIT License 4 votes vote down vote up
InfoCard = ({ site, pointId, bgColor, classes }: InfoCardProps) => {
  const user = useSelector(userInfoSelector);
  const dispatch = useDispatch();
  const surveyPoint = site.surveyPoints.find((item) => item.id === pointId);
  const [editModeEnabled, setEditModeEnabled] = useState(false);
  const [editLoading, setEditLoading] = useState(false);
  const [editAlertOpen, setEditAlertOpen] = useState(false);
  const [editAlertSeverity, setEditAlertSeverity] = useState<
    "success" | "error"
  >();

  const [editPointName, setEditPointName] = useFormField(surveyPoint?.name, [
    "required",
    "maxLength",
  ]);
  const [editPointLatitude, setEditPointLatitude] = useFormField(
    surveyPoint?.polygon?.type === "Point"
      ? surveyPoint.polygon.coordinates[1].toString()
      : undefined,
    ["required", "isNumeric", "isLat"]
  );
  const [editPointLongitude, setEditPointLongitude] = useFormField(
    surveyPoint?.polygon?.type === "Point"
      ? surveyPoint.polygon.coordinates[0].toString()
      : undefined,
    ["required", "isNumeric", "isLong"]
  );

  const onFieldChange = (
    event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
  ) => {
    const { name: field, value: newValue } = event.target;
    switch (field) {
      case "pointName":
        setEditPointName(newValue);
        break;
      case "latitude":
        setEditPointLatitude(newValue);
        break;
      case "longitude":
        setEditPointLongitude(newValue);
        break;
      default:
        break;
    }
  };

  const onEditPointCoordinatesChange = (lat: string, lng: string) => {
    setEditPointLatitude(lat);
    setEditPointLongitude(lng);
  };

  const onSubmit = () => {
    if (
      user?.token &&
      every(
        [editPointName, editPointLatitude, editPointLongitude],
        (item) => item.value
      )
    ) {
      setEditLoading(true);
      surveyServices
        .updatePoi(
          pointId,
          {
            name: editPointName.value,
            latitude: parseFloat(editPointLatitude.value),
            longitude: parseFloat(editPointLongitude.value),
          },
          user.token
        )
        .then(({ data: newPoint }) => {
          dispatch(
            setSiteSurveyPoints(
              site.surveyPoints.map((point) => ({
                id: point.id,
                name: point.id === pointId ? newPoint.name : point.name,
                polygon:
                  point.id === pointId ? newPoint.polygon : point.polygon,
              }))
            )
          );
          setEditAlertSeverity("success");
        })
        .catch(() => setEditAlertSeverity("error"))
        .finally(() => {
          setEditLoading(false);
          setEditModeEnabled(false);
          setEditAlertOpen(true);
        });
    }
  };

  return (
    <Box bgcolor={bgColor}>
      <Container>
        <Collapse in={editAlertOpen}>
          <Alert
            severity={editAlertSeverity}
            action={
              <IconButton
                color="inherit"
                size="small"
                onClick={() => {
                  setEditAlertOpen(false);
                }}
              >
                <CloseIcon fontSize="inherit" />
              </IconButton>
            }
          >
            {editAlertSeverity === "success"
              ? "Successfully updated survey point information"
              : "Something went wrong"}
          </Alert>
        </Collapse>
        <Grid className={classes.cardWrapper} container justify="center">
          <Grid item xs={12} sm={12}>
            <Card elevation={3}>
              <Grid container justify="space-between">
                {editModeEnabled ? (
                  <EditForm
                    editLoading={editLoading}
                    editPointName={editPointName}
                    editPointLatitude={editPointLatitude}
                    editPointLongitude={editPointLongitude}
                    onFieldChange={onFieldChange}
                    onSaveButtonClick={onSubmit}
                    onCancelButtonClick={() => setEditModeEnabled(false)}
                  />
                ) : (
                  <Info
                    site={site}
                    pointId={pointId}
                    onEditButtonClick={() => setEditModeEnabled(true)}
                  />
                )}
                <Map
                  site={site}
                  selectedPointId={pointId}
                  editModeEnabled={editModeEnabled}
                  editPointLatitude={editPointLatitude}
                  editPointLongitude={editPointLongitude}
                  onEditPointCoordinatesChange={onEditPointCoordinatesChange}
                />
              </Grid>
            </Card>
          </Grid>
        </Grid>
      </Container>
    </Box>
  );
}
Example #21
Source File: use-hooks.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
useMultiFilter = (props: IMultiModeProps): IUseMultiFilterProps => {
  const {
    getData,
    excludeQuery = [],
    fieldConvertor,
    pageSize = PAGINATION.pageSize,
    multiGroupEnums,
    groupKey = 'type',
    extraQueryFunc = () => ({}),
    fullRange,
    dateFormat,
    shareQuery = false,
    activeKeyInParam = false,
    requiredKeys = [],
    checkParams = [],
  } = props;
  const wholeExcludeKeys = activeKeyInParam ? excludeQuery : excludeQuery.concat([groupKey]);
  const [query, params, currentRoute] = routeInfoStore.useStore((s) => [s.query, s.params, s.currentRoute]);
  const { pageNo: pNo, ...restQuery } = query;

  const pickQueryValue = React.useCallback(() => {
    return omit(restQuery, wholeExcludeKeys) || {};
  }, [restQuery, wholeExcludeKeys]);

  const activeType = (activeKeyInParam ? params[groupKey] : query[groupKey]) || multiGroupEnums[0];

  const [state, update] = useUpdate({
    groupSearchQuery: multiGroupEnums.reduce((acc, item) => {
      acc[item] = item === activeType || shareQuery ? pickQueryValue() : {};
      return acc;
    }, {}),
    groupPageNo: multiGroupEnums.reduce((acc, item) => {
      acc[item] = item === activeType ? Number(pNo || 1) : 1;
      return acc;
    }, {}),
    activeGroup: activeType,
    currentPath: currentRoute.path,
  });

  const { groupSearchQuery, groupPageNo, activeGroup, currentPath } = state;
  const extraQuery = extraQueryFunc(activeGroup);

  const pageNo = React.useMemo(() => {
    if (activeGroup) {
      return groupPageNo[activeGroup];
    }
    return 1;
  }, [activeGroup, groupPageNo]);

  useDeepCompareEffect(() => {
    // 因为multiGroupEnums随着渲染一直变化引用,所以使用useDeepCompareEffect
    if (activeType !== activeGroup) {
      update.activeGroup(activeType);
    }
  }, [multiGroupEnums, groupKey, activeKeyInParam, params, query, update]);
  useUpdateEffect(() => {
    // 当点击菜单时,href会把query覆盖,此时判断query是否为空并且路径没有改变的情况下重新初始化query
    if (isEmpty(query) && currentPath === currentRoute.path) {
      onReset();
      updateSearchQuery();
    }
  }, [query, currentPath, currentRoute]);

  const currentFetchEffect = getData.length === 1 ? getData[0] : getData[multiGroupEnums.indexOf(activeGroup)];

  const searchQuery = React.useMemo(() => {
    if (activeGroup) {
      return groupSearchQuery[activeGroup];
    }
    return {};
  }, [groupSearchQuery, activeGroup]);

  const updateSearchQuery = React.useCallback(() => {
    setTimeout(() => {
      setSearch({ ...searchQuery, pageNo }, wholeExcludeKeys, true);
    }, 0);
  }, [searchQuery, pageNo, wholeExcludeKeys]);

  const fetchData = (pageNum?: number) => {
    if (checkParams.length) {
      const checked = checkParams.every((key) => !isEmpty(extraQuery[key]));
      if (!checked) {
        return;
      }
    }
    currentFetchEffect({
      pageSize,
      ...extraQuery,
      ...searchQuery,
      pageNo: pageNum || pageNo,
    });
  };

  useDeepCompareEffect(() => {
    const payload = { pageSize, ...extraQuery, ...searchQuery, pageNo };
    const unableToSearch = some(requiredKeys, (key) => payload[key] === '' || payload[key] === undefined);
    if (unableToSearch) {
      return;
    }
    fetchData();
    updateSearchQuery();
  }, [pageNo, pageSize, searchQuery, extraQuery]);

  const fetchDataWithQuery = (pageNum?: number) => {
    if (pageNum && pageNum !== pageNo) {
      onPageChange(pageNum);
    } else {
      fetchData(pageNum);
    }
  };

  const onSubmit = (condition: { [prop: string]: any }) => {
    const formatCondition = convertFilterParamsToUrlFormat(fullRange, dateFormat)(condition, fieldConvertor);
    if (isEqual(formatCondition, searchQuery)) {
      // 如果查询条件没有变化,重复点击查询,还是要强制刷新
      fetchDataWithQuery(1);
    } else if (shareQuery) {
      update.groupSearchQuery(mapValues(groupSearchQuery, () => formatCondition));
      update.groupPageNo(mapValues(groupPageNo, () => 1));
    } else {
      update.groupSearchQuery({
        ...groupSearchQuery,
        [activeGroup]: formatCondition,
      });
      update.groupPageNo({ ...groupPageNo, [activeGroup]: 1 });
    }
  };

  const onReset = () => {
    if (isEmpty(searchQuery)) {
      fetchDataWithQuery(1);
    } else {
      update.groupSearchQuery({ ...groupSearchQuery, [activeGroup]: {} });
      update.groupPageNo({ ...groupPageNo, [activeGroup]: 1 });
    }
  };

  const onPageChange = (currentPageNo: number) => {
    update.groupPageNo({ ...groupPageNo, [activeGroup]: currentPageNo });
  };

  return {
    queryCondition: searchQuery,
    onSubmit, // 包装原始onSubmit, 当搜索时自动更新url
    onReset, // 包装原始onReset, 当重置时自动更新url
    onPageChange, // 当Table切换页码时记录PageNo并发起请求
    pageNo, // 返回当前pageNo,与paging的PageNo理论上相同
    fetchDataWithQuery, // 当页面表格发生操作(删除,启动,编辑)后,进行刷新页面,如不指定pageNum则使用当前页码
    activeType: activeGroup,
    onChangeType: (t: string | number) => setSearch({ [groupKey || 'type']: t }, wholeExcludeKeys, true),
    autoPagination: (paging: IPaging) => ({
      total: paging.total,
      current: paging.pageNo,
      // hideOnSinglePage: true,
      onChange: (n: number) => onPageChange(n),
    }),
  };
}
Example #22
Source File: resource-summary.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
ResourceSummary = React.memo((props: IProps) => {
  const [{ tagList, operationIdList }, updater] = useUpdate({
    tagList: [],
    operationIdList: [],
  });
  const { updateOpenApiDoc } = apiDesignStore;
  const [openApiDoc] = apiDesignStore.useStore((s) => [s.openApiDoc]);

  const { formData, onChange, isEditMode = true } = props;
  const formRef = React.useRef<IFormExtendType>(null);

  React.useEffect(() => {
    setTimeout(() => {
      formRef.current!.setFieldsValue({
        operationId: formData?.operationId,
        description: formData?.description || '',
        tags: get(formData, 'tags.0'),
      });
    });
  }, [formData]);

  const totalOperationIdList = React.useMemo(() => {
    const _operationIdList: string[] = [];
    const { paths } = openApiDoc;
    forEach(keys(paths), (pathName: string) => {
      const methodData = paths[pathName];
      forEach(keys(methodData), (item) => {
        methodData[item]?.operationId && _operationIdList.push(methodData[item]?.operationId);
      });
    });
    return _operationIdList;
  }, [openApiDoc]);

  React.useEffect(() => {
    const _operationIdList: string[] = totalOperationIdList;
    const tags = openApiDoc.tags || [];
    updater.tagList([...tags]);
    updater.operationIdList(_operationIdList);
  }, [openApiDoc, totalOperationIdList, updater]);

  const onCreateTag = React.useCallback(() => {
    const { tags } = formRef.current!.getFieldsValue();
    const newTagName = Array.isArray(tags) ? tags[0] : tags;
    const isNew = tagList?.length ? every(tagList, (item) => item.name !== newTagName) : true;
    if (isNew && newTagName && newTagName !== 'other') {
      const newTags = [...tagList, { name: newTagName }];
      onChange('summary', { propertyName: 'tags', propertyData: [newTagName], newTags });
    }
  }, [onChange, tagList]);

  const onDeleteTag = React.useCallback(
    (tagName: string) => {
      const newList = filter(tagList, (item) => item?.name !== tagName);
      const tempDocDetail = produce(openApiDoc, (draft) => {
        set(draft, 'tags', newList);
        forEach(keys(draft.paths), (apiName: string) => {
          const apiData = draft.paths[apiName];

          forEach(keys(apiData), (method) => {
            if (get(draft, ['paths', apiName, method, 'tags', '0']) === tagName) {
              draft.paths[apiName][method].tags = [DEFAULT_TAG];
            }
          });
        });
      });
      updateOpenApiDoc(tempDocDetail);
      const curTag = formRef.current!.getFieldsValue()?.tags;
      if (curTag === tagName) {
        formRef.current!.setFieldsValue({ tags: undefined });
      }
    },
    [openApiDoc, tagList, updateOpenApiDoc],
  );

  const setField = React.useCallback(
    (propertyName: string, val: string | string[]) => {
      const curFormData = formRef.current!.getFieldsValue();
      if (propertyName !== 'operationId' && !curFormData?.operationId) {
        let _operationId = 'operationId';
        const _operationIdList: string[] = totalOperationIdList;
        while (_operationIdList.includes(_operationId)) {
          _operationId += '1';
        }
        formRef.current!.setFieldsValue({ operationId: _operationId });
      }
      if (propertyName !== 'tags' && !curFormData?.tags) {
        formRef.current!.setFieldsValue({ tags: ['other'] });
      }
      onChange('summary', { propertyName, propertyData: val });
    },
    [onChange, totalOperationIdList],
  );

  const fieldList = React.useMemo(
    () => [
      {
        type: Input,
        label: i18n.t('Name'),
        name: 'operationId',
        colSpan: 24,
        required: false,
        customProps: {
          maxLength: INPUT_MAX_LENGTH,
          disabled: !isEditMode,
        },
        rules: [
          {
            validator: (_rule: any, value: string, callback: (msg?: string) => void) => {
              if (operationIdList.includes(value)) {
                callback(i18n.t('the same {key} exists', { key: i18n.t('Name') }));
              } else {
                setField('operationId', value);
                callback();
              }
            },
          },
        ],
      },
      {
        name: 'tags',
        label: i18n.t('dop:Group'),
        colSpan: 24,
        type: TagSelect,
        customProps: {
          disabled: !isEditMode,
          options: tagList,
          onChange: (e: string) => {
            setField('tags', [e]);
          },
          onDelete: onDeleteTag,
          onSubmit: onCreateTag,
          onInput: (e: string) => {
            formRef.current!.setFieldsValue({ tags: e });
          },
        },
      },
      {
        type: MarkdownEditor,
        label: i18n.t('Description'),
        name: 'description',
        colSpan: 24,
        required: false,
        customProps: {
          defaultMode: !isEditMode ? 'html' : 'md',
          readOnly: !isEditMode,
          maxLength: TEXTAREA_MAX_LENGTH,
          onChange: (val: string) => setField('description', val),
        },
      },
    ],
    [isEditMode, onCreateTag, onDeleteTag, operationIdList, setField, tagList],
  );

  return (
    <FormBuilder isMultiColumn ref={formRef}>
      <Fields fields={fieldList} fid="summaryFields" />
    </FormBuilder>
  );
})
Example #23
Source File: api-params-modal.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
ApiParamsModal = (props: IProps) => {
  const { visible, onClose, onImport, paramList } = props;
  const [{ selectedParams, isValidData, selectedParamsMap, disabledTip }, updater, update] = useUpdate({
    selectedParams: [],
    isValidData: false,
    selectedParamsMap: {},
    disabledTip: EMPTY_TIP,
  });

  const { inode } = routeInfoStore.useStore((s) => s.query);

  const schemaParams = apiDesignStore.useStore((s) => s.schemaParams);
  const { getSchemaParams, clearSchemaParams } = apiDesignStore;

  const [isFetching] = useLoading(apiDesignStore, ['getSchemaParams']);

  React.useEffect(() => {
    if (visible) {
      getSchemaParams({ inode });
    } else {
      update({
        selectedParams: [],
        isValidData: false,
        disabledTip: EMPTY_TIP,
      });
      clearSchemaParams();
    }
  }, [clearSchemaParams, getSchemaParams, inode, update, visible]);

  React.useEffect(() => {
    const tempData = {};
    forEach(schemaParams, ({ tableName }) => {
      tempData[tableName] = [];
    });
    updater.selectedParamsMap(tempData);
  }, [getSchemaParams, inode, schemaParams, update, updater, visible]);

  const columns = [
    {
      title: i18n.t('application'),
      dataIndex: API_FORM_KEY,
    },
    {
      title: i18n.t('Type'),
      dataIndex: 'type',
    },
  ];

  const onCancel = () => {
    onClose();
  };

  const onOk = () => {
    onImport(selectedParams);
  };

  const handleSelect = React.useCallback(
    (selectedRows: any[], tableName: string) => {
      const existNames = map(paramList, (item) => item[API_FORM_KEY]);

      const _tempData = produce(selectedParamsMap, (draft) => {
        draft[tableName] = selectedRows;
      });
      updater.selectedParamsMap(_tempData);

      const isNoSame = every(_tempData[tableName], (item) => !existNames.includes(item[API_FORM_KEY]));
      if (!isNoSame) {
        update({
          isValidData: false,
          disabledTip: SAME_TIP,
        });
      } else {
        const paramsMap = {};
        let isValid = true;

        forEach(values(_tempData), (pList) => {
          forEach(pList, (item) => {
            if (!paramsMap[item[API_FORM_KEY]]) {
              const tempItem = { ...item };
              tempItem[API_PROPERTY_REQUIRED] = true;
              paramsMap[tempItem[API_FORM_KEY]] = tempItem;
            } else {
              isValid = false;
            }
          });
        });

        if (!isValid) {
          update({
            isValidData: false,
            disabledTip: SAME_TIP,
          });
        } else {
          const validParams = values(paramsMap);
          const isEmptySelected = isEmpty(validParams);
          update({
            selectedParams: validParams,
            isValidData: !isEmptySelected,
          });

          if (isEmptySelected) {
            updater.disabledTip(EMPTY_TIP);
          }
        }
      }
    },
    [paramList, selectedParamsMap, update, updater],
  );

  return (
    <Modal
      title={i18n.t('dop:import parameters')}
      visible={visible}
      onCancel={onCancel}
      destroyOnClose
      maskClosable={false}
      className="api-params-modal"
      footer={
        schemaParams?.length
          ? [
              <Tooltip title={!isValidData ? disabledTip : undefined}>
                <Button type="primary" disabled={!isValidData} onClick={onOk}>
                  {i18n.t('OK')}
                </Button>
              </Tooltip>,
            ]
          : []
      }
    >
      <Spin spinning={isFetching}>
        {!schemaParams?.length ? (
          <EmptyHolder relative />
        ) : (
          <div className="schema-params-list">
            {map(schemaParams, ({ tableName, list }) => {
              return (
                <Collapse style={{ border: 'none' }} key={tableName}>
                  <Panel header={tableName} key={tableName}>
                    <Table
                      rowKey={API_FORM_KEY}
                      columns={columns}
                      dataSource={list}
                      rowSelection={{
                        hideDefaultSelections: true,
                        onChange: (_selectedKeys: string[], selectedRows: any[]) => {
                          handleSelect(selectedRows, tableName);
                        },
                      }}
                      pagination={false}
                      scroll={{ x: '100%' }}
                    />
                  </Panel>
                </Collapse>
              );
            })}
          </div>
        )}
      </Spin>
    </Modal>
  );
}
Example #24
Source File: strategy-form.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
StrategyForm = ({ scopeType, scopeId, commonPayload }: IProps) => {
  const memberStore = memberStoreMap[scopeType];
  const params = routeInfoStore.useStore((s) => s.params);
  const { id: strategyId, projectId = '', terminusKey = '', orgName = '' } = params;
  const [strategyForm] = Form.useForm();
  const { getRoleMap } = memberStore.effects;
  const alarmStrategyStore = alarmStrategyStoreMap[scopeType];
  const [alertTypes, alertTriggerConditions, alertTriggerConditionsContent] = alarmStrategyStore.useStore((s) => [
    s.alertTypes,
    s.alertTriggerConditions,
    s.alertTriggerConditionsContent,
  ]);
  const tableRef = React.useRef<HTMLDivElement>(null);
  const channelMethods = getNotifyChannelMethods.useData() as Obj<string>;
  const {
    getAlerts,
    createAlert,
    editAlert,
    getAlertDetail,
    getAlarmScopes,
    getAlertTypes,
    getAlertTriggerConditions,
    getAlertTriggerConditionsContent,
  } = alarmStrategyStore.effects;
  const { clearAlerts } = alarmStrategyStore.reducers;
  const { getNotifyGroups } = notifyGroupStore.effects;
  const notifyGroups = notifyGroupStore.useStore((s) => s.notifyGroups);
  const orgAddNotificationGroupAuth = usePerm((s) => s.org.cmp.alarms.addNotificationGroup.pass);

  // backend support the filterMap to match data
  const triggerConditionFilters = {
    org_name: orgName,
    project_id: projectId,
    terminus_key: terminusKey,
  };

  const addNotificationGroupAuth = scopeType === ScopeType.ORG ? orgAddNotificationGroupAuth : true; // 企业中心的添加通知组,需要验证权限,项目的暂无埋点

  const [state, updater, update] = useUpdate({
    editingRules: [] as any,
    editingFormRule: {},
    activeGroupId: undefined,
    triggerConditionValueOptions: [],
    triggerCondition: [] as COMMON_STRATEGY_NOTIFY.OperationTriggerCondition[],
    notifies: [],
    notifyLevel: null,
    allChannelMethods: notifyChannelOptionsMap,
  });

  useMount(() => {
    let payload = { scopeType, scopeId } as COMMON_NOTIFY.IGetNotifyGroupQuery;
    if (scopeType === ScopeType.MSP) {
      payload = {
        scopeType: commonPayload?.scopeType,
        scopeId: commonPayload?.scopeId,
      };
    }
    getAlerts();
    getAlarmScopes();
    getAlertTypes();
    getNotifyGroups({ ...payload, pageSize: 100 });
    getRoleMap({ scopeType, scopeId: scopeType === ScopeType.MSP ? commonPayload?.scopeId : scopeId });
    getAlertTriggerConditions(scopeType);
    getNotifyChannelMethods.fetch();
  });

  React.useEffect(() => {
    if (strategyId) {
      getAlertDetail(Number(strategyId)).then(
        ({ name, clusterNames, appIds, rules, notifies, triggerCondition }: COMMON_STRATEGY_NOTIFY.IAlertBody) => {
          updater.editingFormRule({
            id: strategyId,
            name,
            clusterName: clusterNames || [],
            appId: appIds || [],
            notifies,
          });
          strategyForm.setFieldsValue({
            name,
            silence: notifies ? `${notifies?.[0].silence.value}-${notifies?.[0].silence.unit}` : undefined,
            silencePolicy: notifies ? `${notifies?.[0].silence.policy}` : SilencePeriodType.FIXED,
          });
          updater.editingRules(
            map(rules, (rule) => ({
              key: uniqueId(),
              ...rule,
              level: rule?.level === 'WARNING' ? undefined : rule?.level,
            })),
          );
          updater.activeGroupId(notifies[0].groupId);

          updater.triggerCondition(
            (triggerCondition || []).map((x) => ({
              id: uniqueId(),
              condition: x.condition,
              operator: x.operator,
              values: x.values,
              valueOptions:
                alertTriggerConditionsContent
                  ?.find((item) => item.key === x.condition)
                  ?.options.map((y) => ({
                    key: y,
                    display: y,
                  })) ?? [],
            })),
          );

          updater.notifies(
            (notifies || []).map((x) => ({
              id: uniqueId(),
              groupId: x.groupId,
              level: x.level ? x.level?.split(',') : undefined,
              groupType: x.groupType?.split(','),
              groupTypeOptions:
                (state.allChannelMethods[x.notifyGroup.targets?.[0].type] || []).map(
                  (y: { value: string; name: string }) => ({
                    key: y.value,
                    display: y.name,
                  }),
                ) || [],
            })),
          );
        },
      );
    } else {
      updater.notifies([
        {
          id: uniqueId(),
          groupId: undefined,
          groupType: undefined,
          level: undefined,
          groupTypeOptions: [],
        },
      ]);
      updater.editingRules([
        {
          key: uniqueId(),
          name: undefined,
          window: undefined,
          functions: [],
          isRecover: true,
          level: 'Fatal',
        },
      ]);
    }
  }, [alertTriggerConditionsContent]);

  React.useEffect(() => {
    if (alertTriggerConditions?.length) {
      const query = [] as COMMON_STRATEGY_NOTIFY.IAlertTriggerConditionQueryItem[];
      forEach(alertTriggerConditions, (item) => {
        const { index, key, filters } = item;
        const filterMap = {};
        forEach(filters, (x) => {
          if (x in triggerConditionFilters) {
            filterMap[x] = triggerConditionFilters[x];
          }
        });
        query.push({ index, condition: key, filters: filterMap });
      });
      getAlertTriggerConditionsContent(query);
    }
  }, [alertTriggerConditions]);

  React.useEffect(() => {
    updater.allChannelMethods(getFinalNotifyChannelOptions(channelMethods, true));
  }, [channelMethods, updater]);

  useUnmount(() => {
    clearAlerts();
  });

  // 获取规则枚举
  const windows = React.useMemo(() => alertTypes.windows, [alertTypes.windows]);
  const silenceMap = React.useMemo(() => {
    const result = {};
    forEach(alertTypes.silence, (item) => {
      result[`${item.value}-${item.unit.key}`] = item.unit;
    });
    return result;
  }, [alertTypes.silence]);
  const operatorMap = React.useMemo(() => {
    const result = {};
    forEach(alertTypes.operators, (item) => {
      result[item.key] = item.display;
    });
    return result;
  }, [alertTypes.operators]);
  const aggregatorMap = React.useMemo(() => {
    const result = {};
    forEach(alertTypes.aggregator, (item) => {
      result[item.key] = item.display;
    });
    return result;
  }, [alertTypes.aggregator]);
  const [alertTypeRuleMap, allRuleFieldMap, allRuleMap, allRules] = React.useMemo(() => {
    const _alertTypeRuleMap = {};
    const _allRuleMap = {};
    const _allRuleFieldMap = {};
    let _allRules: any[] = [];
    forEach(alertTypes.alertTypeRules, ({ alertType, rules }) => {
      _alertTypeRuleMap[alertType.key] = rules;
      forEach(rules, (item) => {
        _allRuleMap[item.alertIndex.key] = item.alertIndex.display;
        forEach(item.functions, (subItem) => {
          _allRuleFieldMap[subItem.field.key] = subItem.field.display;
        });
      });
      _allRules = _allRules.concat(
        map(rules, ({ alertIndex, functions, ...rest }) => ({
          level: alertLevelOptions?.[0]?.key,
          alertIndex: alertIndex.key,
          functions: map(functions, ({ field, ...subRest }) => ({ field: field.key, ...subRest })),
          ...rest,
        })),
      );
    });
    return [_alertTypeRuleMap, _allRuleFieldMap, _allRuleMap, _allRules];
  }, [alertTypes.alertTypeRules]);

  const getFunctionsValueElement = (item: any, functionIndex: number, key: string) => {
    let functionsValueElement = null;
    switch (typeof item.value) {
      case 'boolean':
        functionsValueElement = (
          <Switch
            checkedChildren="true"
            unCheckedChildren="false"
            defaultChecked={item.value}
            onClick={(v: boolean) => {
              handleEditEditingRuleField(key, functionIndex, { key: 'value', value: v });
            }}
          />
        );
        break;
      case 'string':
        functionsValueElement = (
          <Input
            className="value"
            defaultValue={item.value}
            onChange={(e: any) => {
              handleEditEditingRuleField(key, functionIndex, { key: 'value', value: e.target.value });
            }}
          />
        );
        break;
      case 'number':
        functionsValueElement = (
          <InputNumber
            className="value"
            min={0}
            defaultValue={item.value}
            onChange={(v: string | number | undefined) => {
              handleEditEditingRuleField(key, functionIndex, { key: 'value', value: Number(v) });
            }}
          />
        );
        break;
      default:
        break;
    }
    return functionsValueElement;
  };

  const columns: Array<ColumnProps<COMMON_STRATEGY_NOTIFY.IFormRule>> = [
    {
      title: i18n.t('cmp:rule-name'),
      dataIndex: 'alertIndex',
      render: (value: string, { key }) => (
        <Select
          value={value}
          getPopupContainer={() => tableRef.current as HTMLElement}
          showSearch
          optionFilterProp="children"
          placeholder={i18n.t('Please Select')}
          onSelect={(alertIndex: any) => {
            const rules = cloneDeep(state.editingRules);
            const rule = find(allRules, { alertIndex });
            const index = findIndex(rules, { key });
            fill(rules, { key, ...rule }, index, index + 1);
            updater.editingRules(rules);
          }}
        >
          {map(allRules, ({ alertIndex }) => (
            <Select.Option key={alertIndex} value={alertIndex}>
              {allRuleMap[alertIndex]}
            </Select.Option>
          ))}
        </Select>
      ),
    },
    {
      title: `${i18n.t('cmp:Duration')}(min)`,
      dataIndex: 'window',
      render: (value: number, { key }: COMMON_STRATEGY_NOTIFY.IFormRule) => (
        <Select
          value={value}
          placeholder={i18n.t('Please Select')}
          getPopupContainer={() => tableRef.current as HTMLElement}
          onSelect={(window: any) => handleEditEditingRule(key, { key: 'window', value: Number(window) })}
        >
          {map(windows, (item) => (
            <Select.Option key={item} value={item}>
              {item}
            </Select.Option>
          ))}
        </Select>
      ),
    },
    {
      title: i18n.t('cmp:Aggregation rule'),
      dataIndex: 'functions',
      render: (functions: any[], { key }: COMMON_STRATEGY_NOTIFY.IFormRule) => (
        <div className="function-list">
          {functions?.length === 0 && <Input placeholder={i18n.t('cmp:Please enter here')} />}
          {map(functions, (item, index) => (
            <div className="function-item flex-div flex items-center" key={item.field}>
              <Tooltip title={allRuleFieldMap[item.field]}>
                <span className="field-name mr-2 nowrap">{allRuleFieldMap[item.field]}</span>
              </Tooltip>
              <span className="aggregator mr-2">{aggregatorMap[item.aggregator]}</span>
              {/* <Select
                  className="aggregator mr-2"
                  defaultValue={item.aggregator}
                  disabled
                >
                  {map(aggregatorMap, (name, _key) => (<Select.Option key={_key} value={_key}>{name}</Select.Option>))}
                </Select> */}
              <Select
                className="operator mr-2"
                defaultValue={item.operator}
                getPopupContainer={() => tableRef.current as HTMLElement}
                onSelect={(value: any) => {
                  handleEditEditingRuleField(key, index, { key: 'operator', value: String(value) });
                }}
              >
                {map(operatorMap, (name, _key) => (
                  <Select.Option key={_key} value={_key}>
                    {name}
                  </Select.Option>
                ))}
              </Select>
              {getFunctionsValueElement(item, index, key)}
            </div>
          ))}
        </div>
      ),
    },
    {
      title: i18n.t('cmp:Level'),
      dataIndex: 'level',
      render: (value: string, { key }) => (
        <Select
          className="operator mr-2"
          value={value}
          getPopupContainer={() => tableRef.current as HTMLElement}
          onSelect={(level: string) => {
            handleEditEditingRule(key, { key: 'level', value: level });
          }}
        >
          {map(alertLevelOptions, (item) => (
            <Option key={item.key} value={item.key}>
              {item.display}
            </Option>
          ))}
        </Select>
      ),
    },
    {
      title: i18n.t('cmp:Trigger recovery'),
      dataIndex: 'isRecover',
      render: (isRecover: boolean, { key }: COMMON_STRATEGY_NOTIFY.IFormRule) => (
        <>
          <Switch
            checked={isRecover}
            onChange={(checked) => handleEditEditingRule(key, { key: 'isRecover', value: checked })}
          />
        </>
      ),
    },
  ];

  const actions: IActions<COMMON_STRATEGY_NOTIFY.IFormRule> = {
    render: (record) => [
      {
        title: i18n.t('Delete'),
        onClick: () => {
          handleRemoveEditingRule(record.key);
        },
      },
    ],
  };

  const fieldsList = [
    {
      label: i18n.t('cmp:alert name'),
      name: 'name',
      itemProps: {
        placeholder: i18n.t('cmp:Please enter here'),
        disabled: !isEmpty(state.editingFormRule),
        maxLength: 50,
        style: { width: 480 },
      },
    },
    {
      label: i18n.t('cmp:Filter rule'),
      name: 'triggerCondition',
      required: false,
      getComp: () => (
        <>
          <Button className="flex items-center mb-2" type="primary" ghost onClick={handleAddTriggerConditions}>
            <ErdaIcon type="plus" size="16" />
            <span>{i18n.t('cmp:Add-rule')}</span>
          </Button>
          {state.triggerCondition?.length > 0 && (
            <div className="p-2 bg-cultured w-min">
              {state.triggerCondition?.map((item) => (
                <TriggerConditionSelect
                  keyOptions={alertTriggerConditions}
                  key={item.id}
                  id={item.id}
                  current={find(state.triggerCondition, (x) => x.id === item.id)}
                  handleEditTriggerConditions={handleEditTriggerConditions}
                  handleRemoveTriggerConditions={handleRemoveTriggerConditions}
                  operatorOptions={conditionOperatorOptions}
                  valueOptionsList={alertTriggerConditionsContent}
                />
              ))}
            </div>
          )}
        </>
      ),
    },
    {
      label: i18n.t('cmp:Alert rule'),
      name: 'expressions',
      required: false,
      getComp: () => (
        <div ref={tableRef}>
          <div className="opportunity-header flex mb-2">
            <Popover
              placement="bottomLeft"
              trigger="click"
              content={
                <div className="alarm-rule-collection">
                  {map(alertTypes.alertTypeRules, (item) => (
                    <div
                      className="collection-item hover-active-bg"
                      key={item.alertType.key}
                      onClick={() => {
                        handleClickAlertType(item.alertType.key);
                      }}
                    >
                      {item.alertType.display}
                    </div>
                  ))}
                </div>
              }
            >
              <Button className="mr-2 flex items-center" ghost type="primary">
                <ErdaIcon type="page-template" className="mr-1" size="14" />
                <span>{i18n.t('cmp:Type Template')}</span>
              </Button>
            </Popover>
            <Button type="primary" className="flex items-center" ghost onClick={handleAddEditingRule}>
              <ErdaIcon type="plus" size="16" />
              <span>{i18n.t('cmp:Add-rule')}</span>
            </Button>
          </div>
          <ErdaTable
            hideHeader
            rowKey="key"
            actions={actions}
            className="opportunity-table"
            dataSource={state.editingRules}
            columns={columns}
          />
        </div>
      ),
    },
    {
      label: i18n.t('cmp:Silence period'),
      name: 'silence',
      itemProps: {
        style: { width: 480 },
      },
      type: 'select',
      options: map(silenceMap, ({ display }, value) => ({ name: `${value.split('-')[0]} ${display}`, value })),
    },
    {
      label: i18n.t('Silence period policy'),
      name: 'silencePolicy',
      initialValue: state.editingFormRule.notifies
        ? `${state.editingFormRule.notifies[0].silence.policy}`
        : SilencePeriodType.FIXED,
      type: 'radioGroup',
      options: map(SILENCE_PERIOD_POLICY_MAP, (name, value) => ({ name, value })),
    },
    {
      label: i18n.t('dop:Notify'),
      required: false,
      name: 'notifies',
      getComp: () => (
        <>
          <Button type="primary" ghost className="flex items-center mb-2" onClick={handleAddNotifyStrategy}>
            <ErdaIcon type="plus" size="16" />
            <span>{i18n.t('cmp:Add Notification Target')}</span>
          </Button>
          {state.notifies?.length > 0 && (
            <div className="p-2 bg-cultured w-min">
              {state.notifies?.map((item) => (
                <NotifyStrategySelect
                  alertLevelOptions={alertLevelOptions}
                  goToNotifyGroup={() => {
                    goTo(notifyGroupPage[scopeType], { projectId: scopeId, ...params });
                  }}
                  notifyGroups={notifyGroups}
                  notifyChannelMap={state.allChannelMethods}
                  addNotificationGroupAuth={addNotificationGroupAuth}
                  key={item.id}
                  id={item.id}
                  updater={updater.activeGroupId}
                  current={state.notifies?.find((x) => x.id === item.id)}
                  handleEditNotifyStrategy={handleEditNotifyStrategy}
                  handleRemoveNotifyStrategy={handleRemoveNotifyStrategy}
                  valueOptions={item.groupTypeOptions}
                />
              ))}
            </div>
          )}
        </>
      ),
    },
    {
      label: '',
      getComp: ({ form }: { form: FormInstance }) => {
        return (
          <div className="text-right bg-white">
            <Button className="btn-save" type="primary" onClick={() => handleSave(form)}>
              {i18n.t('Save')}
            </Button>
            <Button className="ml-3" onClick={() => window.history.back()}>
              {i18n.t('Cancel')}
            </Button>
          </div>
        );
      },
    },
  ];

  // 添加集合的规则
  const handleClickAlertType = (val: string) => {
    const formRules: COMMON_STRATEGY_NOTIFY.IFormRule[] = map(
      alertTypeRuleMap[val],
      (rule: COMMON_STRATEGY_NOTIFY.IDataExpression) => ({
        key: uniqueId(),
        alertIndex: rule.alertIndex.key,
        window: rule.window,
        functions: map(rule.functions, ({ field, ...rest }) => ({
          field: field.key,
          ...rest,
        })),
        isRecover: rule.isRecover,
        level: alertLevelOptions?.[0]?.key,
      }),
    );
    updater.editingRules([...formRules, ...state.editingRules]);
  };

  // 添加单条规则
  const handleAddEditingRule = () => {
    updater.editingRules([
      {
        key: uniqueId(),
        name: undefined,
        window: undefined,
        functions: [],
        isRecover: true,
        level: 'Fatal',
      },
      ...state.editingRules,
    ]);
  };

  // 移除表格编辑中的规则
  const handleRemoveEditingRule = (key: string) => {
    updater.editingRules(filter(state.editingRules, (item) => item.key !== key));
  };

  // 编辑单条规则
  const handleEditEditingRule = (key: string, item: { key: string; value: any }) => {
    const rules = cloneDeep(state.editingRules);
    const rule = find(rules, { key });
    const index = findIndex(rules, { key });

    fill(rules, { key, ...rule, [item.key]: item.value }, index, index + 1);
    updater.editingRules(rules);
  };

  // 编辑单条规则下的指标
  const handleEditEditingRuleField = (key: string, index: number, item: { key: string; value: any }) => {
    const rules = cloneDeep(state.editingRules);
    const { functions } = find(rules, { key }) || {};
    const functionItem = functions[index];

    fill(functions, { ...functionItem, [item.key]: item.value }, index, index + 1);
    handleEditEditingRule(key, { key: 'functions', value: functions });
  };

  const handleSave = (form: FormInstance) => {
    form
      .validateFields()
      .then((values) => {
        const { name, silence = '', silencePolicy } = values;
        const [value, unit] = silence.split('-');
        const payload: COMMON_STRATEGY_NOTIFY.IAlertBody = {
          name,
          domain: location.origin,
          rules: map(state.editingRules, ({ key, ...rest }) => rest),
          notifies: state.notifies.map((item) => ({
            silence: {
              value: Number(value),
              unit,
              policy: silencePolicy,
            },
            groupId: item?.groupId,
            groupType: item?.groupType?.join(','),
            level: item?.level?.join(','),
          })),
          triggerCondition: state.triggerCondition.map((x) => ({
            condition: x.condition,
            operator: x.operator,
            values: x.values,
          })),
        };
        if (beforeSubmit(values)) {
          if (!isEmpty(state.editingFormRule)) {
            editAlert({ body: payload, id: state.editingFormRule.id });
          } else {
            createAlert(payload);
          }
          window.history.back();
        }
      })
      .catch(({ errorFields }) => {
        form.scrollToField(errorFields[0].name);
      });
  };

  // 添加单条触发条件
  const handleAddTriggerConditions = () => {
    // const currentTriggerValues =
    //   alertTriggerConditionsContent
    //     ?.find((item) => item.key === alertTriggerConditions?.[0]?.key)
    //     ?.options.map((item) => ({ key: item, display: item })) ?? [];

    updater.triggerCondition([
      {
        id: uniqueId(),
        condition: undefined,
        operator: conditionOperatorOptions?.[0].key,
        values: undefined,
        valueOptions: [],
      },
      ...(state.triggerCondition || []),
    ]);
  };

  // 添加单条通知策略
  const handleAddNotifyStrategy = () => {
    // const activeGroup = notifyGroups[0];
    // const groupTypeOptions =
    //   ((activeGroup && notifyChannelOptionsMap[activeGroup.targets[0].type]) || []).map((x) => ({
    //     key: x.value,
    //     display: x.name,
    //   })) || [];
    // updater.groupTypeOptions(groupTypeOptions);
    updater.notifies([
      {
        id: uniqueId(),
        groupId: undefined,
        level: undefined,
        groupType: undefined,
        groupTypeOptions: [],
      },
      ...(state.notifies || []),
    ]);
  };

  // 移除表格编辑中的规则
  const handleRemoveTriggerConditions = (id: string) => {
    updater.triggerCondition(filter(state.triggerCondition, (item) => item.id !== id));
  };

  // 移除策略
  const handleRemoveNotifyStrategy = (id: string) => {
    updater.notifies(filter(state.notifies, (item) => item.id !== id));
  };

  // 编辑单条触发条件
  const handleEditNotifyStrategy = (id: string, item: { key: string; value: string }) => {
    const rules = cloneDeep(state.notifies);
    const rule = find(rules, { id });
    const index = findIndex(rules, { id });

    fill(rules, { id, ...rule, [item.key]: item.value }, index, index + 1);
    updater.notifies(rules);
  };

  // 编辑单条触发条件
  const handleEditTriggerConditions = (id: string, item: { key: string; value: any }) => {
    const rules = cloneDeep(state.triggerCondition);

    const rule = find(rules, { id });
    const index = findIndex(rules, { id });
    if (item.key === 'operator' && item.value === 'all') {
      fill(
        rules,
        { id, ...rule, values: state.triggerCondition[index].valueOptions?.map((x) => x?.key)?.join(',') },
        index,
        index + 1,
      );
    }
    fill(rules, { id, ...rule, [item.key]: item.value }, index, index + 1);
    updater.triggerCondition(rules);
  };
  const beforeSubmit = (param: any) => {
    if (state.triggerCondition?.length > 0) {
      let isIncomplete = false;
      state.triggerCondition.forEach((item) => {
        for (const key in item) {
          // the third box is input when type is 'match' or 'notMatch', valueOptions is empty array
          if (
            (!item[key] && item.operator !== 'all') ||
            (!['match', 'notMatch'].includes(item.operator) && isArray(item[key]) && item[key].length === 0)
          ) {
            isIncomplete = true;
          }
        }
      });
      if (isIncomplete) {
        warning({
          title: i18n.t('cmp:content of filter rule is missing, please complete!'),
        });
        return null;
      }
    }

    if (isEmpty(state.editingRules)) {
      warning({
        title: i18n.t('cmp:create at least one rule'),
      });
      return null;
    } else {
      let isIncomplete = false;

      state.editingRules.forEach((item: { [x: string]: string | any[] }) => {
        for (const key in item) {
          if (['functions', 'level', 'name', 'window'].includes(key)) {
            if (!item[key] || (isArray(item[key]) && item[key].length === 0)) {
              isIncomplete = true;
            }
          }
        }
      });
      if (isIncomplete) {
        warning({
          title: i18n.t('cmp:content of alarm rule is missing, please complete!'),
        });
        return null;
      }
    }

    if (isEmpty(state.notifies)) {
      warning({
        title: i18n.t('cmp:create at least one notification object'),
      });
      return null;
    } else {
      let isIncomplete = false;
      state.notifies.forEach((item) => {
        for (const key in item) {
          if (!item[key] || (isArray(item[key]) && item[key].length === 0)) {
            isIncomplete = true;
          }
        }
      });
      if (isIncomplete) {
        warning({
          title: i18n.t('content of notification object is missing, please complete!'),
        });
        return null;
      }
    }

    const isLegalFunctions = every(state.editingRules, ({ functions }) => {
      return every(functions, ({ value }) => {
        return !(isNull(value) || value === '');
      });
    });

    if (!isLegalFunctions) {
      warning({
        title: i18n.t('cmp:rule value cannot be empty'),
      });
      return null;
    }
    return param;
  };

  return (
    <div>
      <RenderForm layout="vertical" form={strategyForm} list={fieldsList} className="w-full" />
    </div>
  );
}