This is an automated email from the ASF dual-hosted git repository. kxiao pushed a commit to branch branch-2.0 in repository https://gitbox.apache.org/repos/asf/doris.git
commit 0f338a82a92882d3cc6f57aa3e59da899ad957bc Author: Jeffrey <color.d...@gmail.com> AuthorDate: Mon Jul 31 20:35:52 2023 +0800 [feature](ui) add profile download button in the list page (#22409) --- ui/prettier.config.js | 51 ++++++++++ ui/public/locales/en-us.json | 3 +- ui/public/locales/zh-cn.json | 3 +- ui/src/pages/query-profile/index.tsx | 182 ++++++++++++++++++++++++----------- 4 files changed, 180 insertions(+), 59 deletions(-) diff --git a/ui/prettier.config.js b/ui/prettier.config.js new file mode 100644 index 0000000000..839c4cccd9 --- /dev/null +++ b/ui/prettier.config.js @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** @type {import('prettier').Config} */ +module.exports = { + endOfLine: "lf", + semi: true, + singleQuote: true, + tabWidth: 4, + trailingComma: "es5", + importOrder: [ + "^Licensed to the Apache Software Foundation (ASF)", + "^(react/(.*)$)|^(react$)", + "^(next/(.*)$)|^(next$)", + "<THIRD_PARTY_MODULES>", + "", + "^types$", + "^@/types/(.*)$", + "^@/config/(.*)$", + "^@/lib/(.*)$", + "^@/hooks/(.*)$", + "^@/components/ui/(.*)$", + "^@/components/(.*)$", + "^@/styles/(.*)$", + "^@/app/(.*)$", + "", + "^[./]", + ], + importOrderSeparation: false, + importOrderSortSpecifiers: true, + importOrderBuiltinModulesToTop: true, + importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], + importOrderMergeDuplicateImports: true, + importOrderCombineTypeAndValueImports: true, +} diff --git a/ui/public/locales/en-us.json b/ui/public/locales/en-us.json index bbcc98fe6c..8690d71fec 100644 --- a/ui/public/locales/en-us.json +++ b/ui/public/locales/en-us.json @@ -50,5 +50,6 @@ "executionTime": "Execution Time", "search":"Search", "executionFailed":"Execution failed", - "loading":"loading" + "loading":"loading", + "Download": "Download" } diff --git a/ui/public/locales/zh-cn.json b/ui/public/locales/zh-cn.json index 90dfdd340a..f593f707b7 100644 --- a/ui/public/locales/zh-cn.json +++ b/ui/public/locales/zh-cn.json @@ -50,5 +50,6 @@ "executionTime": "执行时间", "search":"查询", "executionFailed": "执行失败", - "loading":"加载中" + "loading":"加载中", + "Download": "下载" } diff --git a/ui/src/pages/query-profile/index.tsx b/ui/src/pages/query-profile/index.tsx index 5590ce9085..a5bb13b204 100644 --- a/ui/src/pages/query-profile/index.tsx +++ b/ui/src/pages/query-profile/index.tsx @@ -17,45 +17,94 @@ * under the License. */ -import React, {useEffect, useRef, useState} from 'react'; -import {Button, Col, Row, Typography, Space} from 'antd'; -import {queryProfile} from 'Src/api/api'; +import React, { useEffect, useRef, useState } from 'react'; +import { Button, Col, Row, Typography, Space } from 'antd'; +import { queryProfile } from 'Src/api/api'; import Table from 'Src/components/table'; -import {useHistory} from 'react-router-dom'; -import {Result} from '@src/interfaces/http.interface'; -import {replaceToTxt} from 'Src/utils/utils'; +import { useHistory } from 'react-router-dom'; +import { Result } from '@src/interfaces/http.interface'; +import { replaceToTxt } from 'Src/utils/utils'; +import { useTranslation } from 'react-i18next'; +import SyntaxHighlighter from 'react-syntax-highlighter'; +import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs'; -const {Text, Title} = Typography; +const { Text, Title } = Typography; export default function QueryProfile(params: any) { // const [parentUrl, setParentUrl] = useState(''); const container = useRef<HTMLDivElement>(null); - const [allTableData, setAllTableData] = useState({column_names: [], rows: []}); + let { t } = useTranslation(); + const [allTableData, setAllTableData] = useState({ + column_names: [], + rows: [], + }); const [profile, setProfile] = useState<any>(); const history = useHistory(); const doQueryProfile = function (ac?: AbortController) { const param = { path: getLastPath(), - signal: ac?.signal + signal: ac?.signal, }; - queryProfile(param).then((res: Result<any>) => { - if (res && res.msg === 'success') { - if (!res.data.column_names) { - setProfile(res.data); - if (container.current !== null) { - container.current.innerHTML = res.data; + queryProfile(param) + .then((res: Result<any>) => { + if (res && res.msg === 'success') { + if (!res.data.column_names) { + setProfile(res.data); + if (container.current !== null) { + container.current.innerHTML = res.data; + } + } else { + setProfile(''); + res.data.column_names.push('Action'); + res.data.rows = res.data.rows.map((row) => { + row['Sql Statement'] = ( + <div style={{ maxWidth: 700 }}> + <SyntaxHighlighter + language="sql" + style={docco} + > + {row['Sql Statement']} + </SyntaxHighlighter> + </div> + ); + row.Action = ( + <Button + size="small" + onClick={() => { + queryProfile<any>({ + path: row['Profile ID'], + }).then((profileDetailRes) => { + if ( + profileDetailRes && + profileDetailRes.msg === + 'success' + ) { + if ( + !profileDetailRes.data + .column_names + ) { + download( + profileDetailRes.data + ); + } + } + }); + }} + > + {t('Download')} + </Button> + ); + return row; + }); + setAllTableData(res.data); } } else { - setProfile(''); - setAllTableData(res.data); + setAllTableData({ + column_names: [], + rows: [], + }); } - } else { - setAllTableData({ - column_names: [], - rows: [], - }); - } - }).catch(err => { - }); + }) + .catch((err) => {}); }; useEffect(() => { const ac = new AbortController(); @@ -79,11 +128,11 @@ export default function QueryProfile(params: any) { function download(profile: string) { const profileTxt = replaceToTxt(profile); const blob = new Blob([profileTxt], { - type: "text/plain", + type: 'text/plain', }); - const tagA = document.createElement("a"); + const tagA = document.createElement('a'); tagA.download = `profile_${new Date().valueOf()}.txt`; - tagA.style.display = "none"; + tagA.style.display = 'none'; tagA.href = URL.createObjectURL(blob); document.body.appendChild(tagA); tagA.click(); @@ -92,7 +141,7 @@ export default function QueryProfile(params: any) { } function copyToClipboard(profile: string) { const profileTxt = replaceToTxt(profile); - const textarea = document.createElement("textarea"); + const textarea = document.createElement('textarea'); textarea.value = profileTxt; document.body.appendChild(textarea); textarea.select(); @@ -101,38 +150,57 @@ export default function QueryProfile(params: any) { } return ( - <Typography style={{padding: '30px'}}> + <Typography style={{ padding: '30px' }}> <Title>Finished Queries</Title> - <Row style={{paddingBottom: '15px'}}> - <Col span={12}><Text strong={true}>This table lists the latest 100 queries</Text></Col> - <Col span={12} style={{textAlign: 'right'}}> - {profile ? <Space> - <Button type="primary" onClick={goPrev}>back</Button> - <Button onClick={() => { - download(profile) - }}>download</Button> - <Button onClick={() => { - copyToClipboard(profile) - }}>copy profile</Button> - </Space> : ''} + <Row style={{ paddingBottom: '15px' }}> + <Col span={12}> + <Text strong={true}> + This table lists the latest 100 queries + </Text> + </Col> + <Col span={12} style={{ textAlign: 'right' }}> + {profile ? ( + <Space> + <Button type="primary" onClick={goPrev}> + Back + </Button> + <Button + onClick={() => { + download(profile); + }} + > + Download + </Button> + <Button + onClick={() => { + copyToClipboard(profile); + }} + > + Copy Profile + </Button> + </Space> + ) : ( + '' + )} </Col> </Row> - { - profile - ? <div ref={container} style={{background: '#f9f9f9', padding: '20px'}}> - {/* {profile} */} - </div> - : <Table - rowKey={(record) => record['Query ID']} - isSort={true} - isFilter={true} - isInner={'/QueryProfile'} - allTableData={allTableData} - /> - } - + {profile ? ( + <div + ref={container} + style={{ background: '#f9f9f9', padding: '20px' }} + > + {/* {profile} */} + </div> + ) : ( + <Table + rowKey={(record) => record['Profile ID']} + isSort={true} + isFilter={true} + isInner={'/QueryProfile'} + allTableData={allTableData} + /> + )} </Typography> ); } - --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org