Fix: some issue (#29)

* chore: change loading ui of DropdownList

* fix: remove index health in shard monitor overview

* chore: add error handling to monitor chart

* fix: lose table data in Overview

* chore: optimize ui of collect status

* fix: remove team in audit

---------

Co-authored-by: yaojiping <yaojiping@infini.ltd>
This commit is contained in:
yaojp123 2024-12-13 19:15:06 +08:00 committed by GitHub
parent 9cb54a3381
commit cfdc1870a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 119 additions and 86 deletions

View File

@ -1,13 +1,12 @@
import Loading from "./loading.svg";
import styles from "./Loading.less";
import { Spin } from "antd";
export default (props) => {
const { loading = true, currentLocales } = props;
if (!loading) return null;
return (
<div className={styles.loading}>
<img src={Loading}/>
<div className={styles.tips}>{currentLocales["dropdownlist.loading"]}</div>
<Spin tip={currentLocales["dropdownlist.loading"]}/>
</div>
)
}

View File

@ -1,9 +1,8 @@
import request from "@/utils/request"
import { firstUpperCase, formatToUniversalTime } from "@/utils/utils";
import { Descriptions, Icon, Tooltip } from "antd";
import { Descriptions, Icon, Spin, Tooltip } from "antd";
import moment from "moment";
import { useEffect, useMemo, useRef, useState } from "react";
import styles from "./index.less";
import { formatMessage } from "umi/locale";
const STATUS_ICONS = {
@ -48,14 +47,18 @@ export default (props) => {
const [data, setData] = useState();
const intervalRef = useRef()
const fetchData = async (fetchUrl) => {
const fetchData = async (fetchUrl, showLoading = true) => {
if (!fetchUrl) return
setLoading(true)
if (showLoading) {
setLoading(true)
}
const res = await request(fetchUrl)
if (res && !res?.error) {
setData(res)
}
setLoading(false)
if (showLoading) {
setLoading(false)
}
}
useEffect(() => {
@ -64,7 +67,7 @@ export default (props) => {
clearInterval(intervalRef.current)
}
intervalRef.current = setInterval(() => {
fetchData(fetchUrl)
fetchData(fetchUrl, false)
}, 300000)
return () => {
if (intervalRef.current) {
@ -91,24 +94,35 @@ export default (props) => {
<Tooltip
placement="bottomRight"
title={(
<Descriptions className={styles.content} title={formatMessage({ id: 'cluster.collect.last_active_at'})} column={1}>
{
stats.map((key) => (
<Descriptions.Item key={key} label={formatMessage({ id: `cluster.manage.monitor_configs.${key}`})}>
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
{STATUS_ICONS[data?.[key]?.status || 'unknown']}{formatToUniversalTime(data?.[key]?.last_active_at)}
<Spin spinning={loading}>
<div>
<div style={{ marginBottom: 12 }} >
<span style={{ fontWeight: 'bold' }}>
{formatMessage({ id: 'cluster.collect.last_active_at'})}
</span>
<a style={{ marginLeft: 8 }} onClick={() => !loading && fetchData(fetchUrl)} ><Icon type="reload"/></a>
</div>
{
stats.map((key, i) => (
<div key={key} style={{ display: 'flex', gap: 6, alignItems: 'center', marginBottom: i === stats.length - 1 ? 0 : 12 }}>
<div style={{ width: 130 }}>{formatMessage({ id: `cluster.manage.monitor_configs.${key}`})}</div>
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
{STATUS_ICONS[data?.[key]?.status || 'unknown']}{data?.[key]?.last_active_at ? moment.duration(data?.[key]?.last_active_at - new Date().valueOf()).humanize(true) : '-'}
</div>
</div>
</Descriptions.Item>
))
}
</Descriptions>
))
}
</div>
</Spin>
)}
overlayStyle={{ maxWidth: 360 }}
overlayStyle={{ maxWidth: 'none', width: 'auto' }}
>
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
{renderIcon()}
{firstUpperCase(data?.metric_collection_mode) || "Unknown"}
</div>
<Spin spinning={loading}>
<div style={{ display: "flex", alignItems: "center", gap: 6, cursor: 'pointer' }}>
{renderIcon()}
{firstUpperCase(data?.metric_collection_mode) || "Unknown"}
</div>
</Spin>
</Tooltip>
);
}

View File

@ -1,21 +0,0 @@
.content {
:global {
.ant-descriptions-item-label {
width: 110px;
}
.ant-descriptions-title, .ant-descriptions-item-label, .ant-descriptions-item-content {
color: #fff;
}
.ant-descriptions-title {
margin-bottom: 12px;
}
.ant-descriptions-item {
padding-bottom: 12px;
}
.ant-descriptions-row:last-child {
.ant-descriptions-item {
padding-bottom: 6px;
}
}
}
}

View File

@ -475,7 +475,6 @@ const Index = forwardRef((props, ref) => {
timeRange={queryParams?.timeRange || {}}
onTimeRangeChange={onTimeRangeChange}
isRefreshPaused={isRefreshPaused}
onRefresh={onRefresh}
recentlyUsedRangesKey={collectionName}
/>
) : null}

View File

@ -121,7 +121,7 @@ const Monitor = (props) => {
<Card bodyStyle={{ padding: 15 }}>
{
selectedCluster ? (
selectedCluster?.id ? (
<>
<div style={{ marginBottom: 5 }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>

View File

@ -103,7 +103,6 @@ export default forwardRef((props: IProps, ref: any) => {
const drawRef = useRef<IDrawerRef>(null);
const [searchField, setSearchField] = useState<string>();
const [infos, setInfos] = useState({});
const [selectedItem, setSelectedItem] = useState<IRecord>({});
const [dispalyTypeObj, setDispalyTypeObj] = useLocalStorage(
@ -187,20 +186,6 @@ export default forwardRef((props: IProps, ref: any) => {
});
};
const fetchListInfo = async () => {
const ids = hits?.map((hit: { _id: string }) => listItemConfig.getId(hit));
if (!ids || ids.length == 0) {
return;
}
const res = await request(infoAction, {
method: "POST",
body: ids,
});
if (res) {
setInfos(res);
}
};
const onFacetChange = (v: { value: string[]; field: string }) => {
const { filters = {}, ...restParams } = param;
if (!v.value || v.value.length === 0) {
@ -286,9 +271,6 @@ export default forwardRef((props: IProps, ref: any) => {
<listItemConfig.component
data={item}
id={infoField}
info={
infoField && infos[infoField] ? infos[infoField] : {}
}
isActive={selectedItem?._id == item?._id}
onSelect={() => {
setSelectedItem(item);
@ -303,8 +285,8 @@ export default forwardRef((props: IProps, ref: any) => {
/>
) : (
<tableConfig.component
infos={infos}
dataSource={hits}
infoAction={infoAction}
dataSource={hits.map((item) => ({...item, id: listItemConfig.getId(item)}))}
total={result?.total?.value || 0}
from={queryParams.from}
pageSize={queryParams.size}

View File

@ -16,6 +16,7 @@ import host from "./en-US/host";
import settings from "./en-US/settings";
import listview from "./en-US/listview";
import audit from "./en-US/audit";
import error from "./en-US/error";
export default {
"navBar.lang": "Languages",
@ -622,5 +623,6 @@ export default {
...host,
...settings,
...listview,
...audit
...audit,
...error
};

View File

@ -0,0 +1,5 @@
export default {
"error.split": ", ",
"error.unknown": "unknown error, please try again later or contact the support team!",
"error.request_timeout_error": "request timeout, please try again later or contact the support team!",
};

View File

@ -16,6 +16,7 @@ import host from "./zh-CN/host";
import settings from "./zh-CN/settings";
import listview from "./zh-CN/listview";
import audit from "./zh-CN/audit";
import error from "./zh-CN/error";
export default {
"navBar.lang": "语言",
@ -613,5 +614,6 @@ export default {
...host,
...settings,
...listview,
...audit
...audit,
...error
};

View File

@ -0,0 +1,5 @@
export default {
"error.split": "",
"error.unknown": "未知错误,请稍后重试或者联系支持团队!",
"error.request_timeout_error": "请求超时,请稍后重试或者联系支持团队!",
}

View File

@ -7,10 +7,11 @@ import { SearchEngineIcon } from "@/lib/search_engines";
import { HealthStatusView } from "@/components/infini/health_status_view";
import { StatusBlockGroup } from "@/components/infini/status_block";
import { Providers, ProviderIcon } from "@/lib/providers";
import request from "@/utils/request";
import styles from "./index.less"
export default (props) => {
const {
infos,
dataSource,
total,
from,
@ -19,8 +20,34 @@ export default (props) => {
onPageChange,
onPageSizeChange,
onRowClick,
infoAction
} = props;
const [infos, setInfos] = useState({});
const fetchListInfo = async (data) => {
const res = await Promise.all(data?.map((item) => request(infoAction, {
method: "POST",
body: [item.id],
}, false, false)));
if (res) {
let newInfos = {}
res.forEach((item) => {
if (item && !item.error) {
newInfos = {
...newInfos,
...item
}
}
})
setInfos(newInfos);
}
};
useEffect(() => {
fetchListInfo(dataSource);
}, [JSON.stringify(dataSource)])
const [tableData] = useMemo(() => {
let tableData = dataSource?.map((item) => {
const id = item?._id;
@ -269,6 +296,7 @@ export default (props) => {
},
};
}}
rowClassName={() => styles.rowPointer}
/>
</div>
);

View File

@ -0,0 +1,3 @@
.rowPointer {
cursor: pointer;
}

View File

@ -29,12 +29,12 @@ export default ({
timeout={timeout}
refresh={refresh}
metrics={[
"index_health",
isAgent && shardID ? 'shard_state' : "index_health",
"index_throughput",
"search_throughput",
"index_latency",
"search_latency",
isAgent ? "shard_state" : undefined,
isAgent && !shardID ? "shard_state" : undefined,
].filter((item) => !!item)}
/>
);

View File

@ -1,4 +1,4 @@
import request from "@/utils/request";
import request, { formatResponse } from "@/utils/request";
import { cloneDeep } from "lodash";
import { useEffect, useRef, useState } from "react";
import { formatMessage } from "umi/locale";
@ -71,8 +71,9 @@ export default (props) => {
},
ignoreTimeout: true
}, false, false)
if (res?.error?.reason) {
setError(res.error.reason)
if (res?.error) {
const error = formatResponse(res.error);
setError(error?.errorObject?.key ? formatMessage({ id: `${error?.errorObject?.key}` }) : res?.error?.reason)
} else if (res && !res.error) {
const { metrics = {} } = res || {};
const metric = metrics[metricKey]

View File

@ -56,13 +56,6 @@ export default (props) => {
searchable: true,
visible: false,
},
{
title: "TEAM",
key: "metadata.labels.team",
aggregable: true,
searchable: true,
visible: false,
},
{
title: "OPERATION",
key: "metadata.labels.operation",
@ -206,10 +199,6 @@ export default (props) => {
<div className={styles.label}>操作者</div>
<div className={styles.value}>{selectedItem.metadata.operator || '-'}</div>
</div>
<div className={styles.item}>
<div className={styles.label}>团队</div>
<div className={styles.value}>{selectedItem.metadata.labels.team || '-'}</div>
</div>
</div>
</div>
<div className={styles.drawerMsg}>

View File

@ -5,6 +5,31 @@ import hash from "hash.js";
import { isAntdPro } from "./utils";
import { formatMessage } from "umi/locale";
import { getAuthorizationHeader } from "./authority";
import * as uuid from 'uuid';
export const formatResponse = (response) => {
if (!response || !response.error) return response;
let key;
let msg;
if (response.error.reason === 'context deadline exceeded') {
key = 'error.timeout'
} else {
const errors = response.error.reason?.split(':');
const errorKey = errors[0]?.endsWith('_error') ? errors[0] : 'unknown';
const field = errors[1]
key = errorKey !== 'unknown' && errors[1] ? `error.${errorKey}.${errors[1]}` : `error.${errorKey}`;
msg = errors.slice(2).join(':')
}
return {
...response,
errorObject: {
id: uuid.v4(),
key,
msg
}
}
}
const checkStatus = async (response, noticeable, option={}) => {
const codeMessage = {