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 |
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 |
// 对前端搜索数据进行表头筛选项过滤
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
// 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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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>
);
}