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:
parent
9cb54a3381
commit
cfdc1870a9
|
@ -1,13 +1,12 @@
|
||||||
import Loading from "./loading.svg";
|
|
||||||
import styles from "./Loading.less";
|
import styles from "./Loading.less";
|
||||||
|
import { Spin } from "antd";
|
||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
const { loading = true, currentLocales } = props;
|
const { loading = true, currentLocales } = props;
|
||||||
if (!loading) return null;
|
if (!loading) return null;
|
||||||
return (
|
return (
|
||||||
<div className={styles.loading}>
|
<div className={styles.loading}>
|
||||||
<img src={Loading}/>
|
<Spin tip={currentLocales["dropdownlist.loading"]}/>
|
||||||
<div className={styles.tips}>{currentLocales["dropdownlist.loading"]}</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
import request from "@/utils/request"
|
import request from "@/utils/request"
|
||||||
import { firstUpperCase, formatToUniversalTime } from "@/utils/utils";
|
import { firstUpperCase, formatToUniversalTime } from "@/utils/utils";
|
||||||
import { Descriptions, Icon, Tooltip } from "antd";
|
import { Descriptions, Icon, Spin, Tooltip } from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import styles from "./index.less";
|
|
||||||
import { formatMessage } from "umi/locale";
|
import { formatMessage } from "umi/locale";
|
||||||
|
|
||||||
const STATUS_ICONS = {
|
const STATUS_ICONS = {
|
||||||
|
@ -48,14 +47,18 @@ export default (props) => {
|
||||||
const [data, setData] = useState();
|
const [data, setData] = useState();
|
||||||
const intervalRef = useRef()
|
const intervalRef = useRef()
|
||||||
|
|
||||||
const fetchData = async (fetchUrl) => {
|
const fetchData = async (fetchUrl, showLoading = true) => {
|
||||||
if (!fetchUrl) return
|
if (!fetchUrl) return
|
||||||
setLoading(true)
|
if (showLoading) {
|
||||||
|
setLoading(true)
|
||||||
|
}
|
||||||
const res = await request(fetchUrl)
|
const res = await request(fetchUrl)
|
||||||
if (res && !res?.error) {
|
if (res && !res?.error) {
|
||||||
setData(res)
|
setData(res)
|
||||||
}
|
}
|
||||||
setLoading(false)
|
if (showLoading) {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -64,7 +67,7 @@ export default (props) => {
|
||||||
clearInterval(intervalRef.current)
|
clearInterval(intervalRef.current)
|
||||||
}
|
}
|
||||||
intervalRef.current = setInterval(() => {
|
intervalRef.current = setInterval(() => {
|
||||||
fetchData(fetchUrl)
|
fetchData(fetchUrl, false)
|
||||||
}, 300000)
|
}, 300000)
|
||||||
return () => {
|
return () => {
|
||||||
if (intervalRef.current) {
|
if (intervalRef.current) {
|
||||||
|
@ -91,24 +94,35 @@ export default (props) => {
|
||||||
<Tooltip
|
<Tooltip
|
||||||
placement="bottomRight"
|
placement="bottomRight"
|
||||||
title={(
|
title={(
|
||||||
<Descriptions className={styles.content} title={formatMessage({ id: 'cluster.collect.last_active_at'})} column={1}>
|
<Spin spinning={loading}>
|
||||||
{
|
<div>
|
||||||
stats.map((key) => (
|
<div style={{ marginBottom: 12 }} >
|
||||||
<Descriptions.Item key={key} label={formatMessage({ id: `cluster.manage.monitor_configs.${key}`})}>
|
<span style={{ fontWeight: 'bold' }}>
|
||||||
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
|
{formatMessage({ id: 'cluster.collect.last_active_at'})}
|
||||||
{STATUS_ICONS[data?.[key]?.status || 'unknown']}{formatToUniversalTime(data?.[key]?.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>
|
</div>
|
||||||
</Descriptions.Item>
|
))
|
||||||
))
|
}
|
||||||
}
|
</div>
|
||||||
</Descriptions>
|
</Spin>
|
||||||
)}
|
)}
|
||||||
overlayStyle={{ maxWidth: 360 }}
|
overlayStyle={{ maxWidth: 'none', width: 'auto' }}
|
||||||
>
|
>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
<Spin spinning={loading}>
|
||||||
{renderIcon()}
|
<div style={{ display: "flex", alignItems: "center", gap: 6, cursor: 'pointer' }}>
|
||||||
{firstUpperCase(data?.metric_collection_mode) || "Unknown"}
|
{renderIcon()}
|
||||||
</div>
|
{firstUpperCase(data?.metric_collection_mode) || "Unknown"}
|
||||||
|
</div>
|
||||||
|
</Spin>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -475,7 +475,6 @@ const Index = forwardRef((props, ref) => {
|
||||||
timeRange={queryParams?.timeRange || {}}
|
timeRange={queryParams?.timeRange || {}}
|
||||||
onTimeRangeChange={onTimeRangeChange}
|
onTimeRangeChange={onTimeRangeChange}
|
||||||
isRefreshPaused={isRefreshPaused}
|
isRefreshPaused={isRefreshPaused}
|
||||||
onRefresh={onRefresh}
|
|
||||||
recentlyUsedRangesKey={collectionName}
|
recentlyUsedRangesKey={collectionName}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -121,7 +121,7 @@ const Monitor = (props) => {
|
||||||
|
|
||||||
<Card bodyStyle={{ padding: 15 }}>
|
<Card bodyStyle={{ padding: 15 }}>
|
||||||
{
|
{
|
||||||
selectedCluster ? (
|
selectedCluster?.id ? (
|
||||||
<>
|
<>
|
||||||
<div style={{ marginBottom: 5 }}>
|
<div style={{ marginBottom: 5 }}>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
|
|
@ -103,7 +103,6 @@ export default forwardRef((props: IProps, ref: any) => {
|
||||||
const drawRef = useRef<IDrawerRef>(null);
|
const drawRef = useRef<IDrawerRef>(null);
|
||||||
|
|
||||||
const [searchField, setSearchField] = useState<string>();
|
const [searchField, setSearchField] = useState<string>();
|
||||||
const [infos, setInfos] = useState({});
|
|
||||||
const [selectedItem, setSelectedItem] = useState<IRecord>({});
|
const [selectedItem, setSelectedItem] = useState<IRecord>({});
|
||||||
|
|
||||||
const [dispalyTypeObj, setDispalyTypeObj] = useLocalStorage(
|
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 onFacetChange = (v: { value: string[]; field: string }) => {
|
||||||
const { filters = {}, ...restParams } = param;
|
const { filters = {}, ...restParams } = param;
|
||||||
if (!v.value || v.value.length === 0) {
|
if (!v.value || v.value.length === 0) {
|
||||||
|
@ -286,9 +271,6 @@ export default forwardRef((props: IProps, ref: any) => {
|
||||||
<listItemConfig.component
|
<listItemConfig.component
|
||||||
data={item}
|
data={item}
|
||||||
id={infoField}
|
id={infoField}
|
||||||
info={
|
|
||||||
infoField && infos[infoField] ? infos[infoField] : {}
|
|
||||||
}
|
|
||||||
isActive={selectedItem?._id == item?._id}
|
isActive={selectedItem?._id == item?._id}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
setSelectedItem(item);
|
setSelectedItem(item);
|
||||||
|
@ -303,8 +285,8 @@ export default forwardRef((props: IProps, ref: any) => {
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<tableConfig.component
|
<tableConfig.component
|
||||||
infos={infos}
|
infoAction={infoAction}
|
||||||
dataSource={hits}
|
dataSource={hits.map((item) => ({...item, id: listItemConfig.getId(item)}))}
|
||||||
total={result?.total?.value || 0}
|
total={result?.total?.value || 0}
|
||||||
from={queryParams.from}
|
from={queryParams.from}
|
||||||
pageSize={queryParams.size}
|
pageSize={queryParams.size}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import host from "./en-US/host";
|
||||||
import settings from "./en-US/settings";
|
import settings from "./en-US/settings";
|
||||||
import listview from "./en-US/listview";
|
import listview from "./en-US/listview";
|
||||||
import audit from "./en-US/audit";
|
import audit from "./en-US/audit";
|
||||||
|
import error from "./en-US/error";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
"navBar.lang": "Languages",
|
"navBar.lang": "Languages",
|
||||||
|
@ -622,5 +623,6 @@ export default {
|
||||||
...host,
|
...host,
|
||||||
...settings,
|
...settings,
|
||||||
...listview,
|
...listview,
|
||||||
...audit
|
...audit,
|
||||||
|
...error
|
||||||
};
|
};
|
||||||
|
|
|
@ -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!",
|
||||||
|
};
|
|
@ -16,6 +16,7 @@ import host from "./zh-CN/host";
|
||||||
import settings from "./zh-CN/settings";
|
import settings from "./zh-CN/settings";
|
||||||
import listview from "./zh-CN/listview";
|
import listview from "./zh-CN/listview";
|
||||||
import audit from "./zh-CN/audit";
|
import audit from "./zh-CN/audit";
|
||||||
|
import error from "./zh-CN/error";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
"navBar.lang": "语言",
|
"navBar.lang": "语言",
|
||||||
|
@ -613,5 +614,6 @@ export default {
|
||||||
...host,
|
...host,
|
||||||
...settings,
|
...settings,
|
||||||
...listview,
|
...listview,
|
||||||
...audit
|
...audit,
|
||||||
|
...error
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
export default {
|
||||||
|
"error.split": ",",
|
||||||
|
"error.unknown": "未知错误,请稍后重试或者联系支持团队!",
|
||||||
|
"error.request_timeout_error": "请求超时,请稍后重试或者联系支持团队!",
|
||||||
|
}
|
|
@ -7,10 +7,11 @@ import { SearchEngineIcon } from "@/lib/search_engines";
|
||||||
import { HealthStatusView } from "@/components/infini/health_status_view";
|
import { HealthStatusView } from "@/components/infini/health_status_view";
|
||||||
import { StatusBlockGroup } from "@/components/infini/status_block";
|
import { StatusBlockGroup } from "@/components/infini/status_block";
|
||||||
import { Providers, ProviderIcon } from "@/lib/providers";
|
import { Providers, ProviderIcon } from "@/lib/providers";
|
||||||
|
import request from "@/utils/request";
|
||||||
|
import styles from "./index.less"
|
||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
const {
|
const {
|
||||||
infos,
|
|
||||||
dataSource,
|
dataSource,
|
||||||
total,
|
total,
|
||||||
from,
|
from,
|
||||||
|
@ -19,8 +20,34 @@ export default (props) => {
|
||||||
onPageChange,
|
onPageChange,
|
||||||
onPageSizeChange,
|
onPageSizeChange,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
|
infoAction
|
||||||
} = props;
|
} = 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(() => {
|
const [tableData] = useMemo(() => {
|
||||||
let tableData = dataSource?.map((item) => {
|
let tableData = dataSource?.map((item) => {
|
||||||
const id = item?._id;
|
const id = item?._id;
|
||||||
|
@ -269,6 +296,7 @@ export default (props) => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
|
rowClassName={() => styles.rowPointer}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
.rowPointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
|
@ -29,12 +29,12 @@ export default ({
|
||||||
timeout={timeout}
|
timeout={timeout}
|
||||||
refresh={refresh}
|
refresh={refresh}
|
||||||
metrics={[
|
metrics={[
|
||||||
"index_health",
|
isAgent && shardID ? 'shard_state' : "index_health",
|
||||||
"index_throughput",
|
"index_throughput",
|
||||||
"search_throughput",
|
"search_throughput",
|
||||||
"index_latency",
|
"index_latency",
|
||||||
"search_latency",
|
"search_latency",
|
||||||
isAgent ? "shard_state" : undefined,
|
isAgent && !shardID ? "shard_state" : undefined,
|
||||||
].filter((item) => !!item)}
|
].filter((item) => !!item)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import request from "@/utils/request";
|
import request, { formatResponse } from "@/utils/request";
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { formatMessage } from "umi/locale";
|
import { formatMessage } from "umi/locale";
|
||||||
|
@ -71,8 +71,9 @@ export default (props) => {
|
||||||
},
|
},
|
||||||
ignoreTimeout: true
|
ignoreTimeout: true
|
||||||
}, false, false)
|
}, false, false)
|
||||||
if (res?.error?.reason) {
|
if (res?.error) {
|
||||||
setError(res.error.reason)
|
const error = formatResponse(res.error);
|
||||||
|
setError(error?.errorObject?.key ? formatMessage({ id: `${error?.errorObject?.key}` }) : res?.error?.reason)
|
||||||
} else if (res && !res.error) {
|
} else if (res && !res.error) {
|
||||||
const { metrics = {} } = res || {};
|
const { metrics = {} } = res || {};
|
||||||
const metric = metrics[metricKey]
|
const metric = metrics[metricKey]
|
||||||
|
|
|
@ -56,13 +56,6 @@ export default (props) => {
|
||||||
searchable: true,
|
searchable: true,
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: "TEAM",
|
|
||||||
key: "metadata.labels.team",
|
|
||||||
aggregable: true,
|
|
||||||
searchable: true,
|
|
||||||
visible: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: "OPERATION",
|
title: "OPERATION",
|
||||||
key: "metadata.labels.operation",
|
key: "metadata.labels.operation",
|
||||||
|
@ -206,10 +199,6 @@ export default (props) => {
|
||||||
<div className={styles.label}>操作者</div>
|
<div className={styles.label}>操作者</div>
|
||||||
<div className={styles.value}>{selectedItem.metadata.operator || '-'}</div>
|
<div className={styles.value}>{selectedItem.metadata.operator || '-'}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.item}>
|
|
||||||
<div className={styles.label}>团队</div>
|
|
||||||
<div className={styles.value}>{selectedItem.metadata.labels.team || '-'}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.drawerMsg}>
|
<div className={styles.drawerMsg}>
|
||||||
|
|
|
@ -5,6 +5,31 @@ import hash from "hash.js";
|
||||||
import { isAntdPro } from "./utils";
|
import { isAntdPro } from "./utils";
|
||||||
import { formatMessage } from "umi/locale";
|
import { formatMessage } from "umi/locale";
|
||||||
import { getAuthorizationHeader } from "./authority";
|
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 checkStatus = async (response, noticeable, option={}) => {
|
||||||
const codeMessage = {
|
const codeMessage = {
|
||||||
|
|
Loading…
Reference in New Issue