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 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>
) )
} }

View File

@ -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>
); );
} }

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 || {}} timeRange={queryParams?.timeRange || {}}
onTimeRangeChange={onTimeRangeChange} onTimeRangeChange={onTimeRangeChange}
isRefreshPaused={isRefreshPaused} isRefreshPaused={isRefreshPaused}
onRefresh={onRefresh}
recentlyUsedRangesKey={collectionName} recentlyUsedRangesKey={collectionName}
/> />
) : null} ) : null}

View File

@ -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' }}>

View File

@ -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}

View File

@ -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
}; };

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 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
}; };

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 { 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>
); );

View File

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

View File

@ -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)}
/> />
); );

View File

@ -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]

View File

@ -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}>

View File

@ -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 = {