From 2a5722e45b82c63751b02d2530f0aee2883ed2d0 Mon Sep 17 00:00:00 2001 From: WindSpiritSR Date: Sun, 16 Feb 2025 22:01:25 +0800 Subject: [PATCH] feat: add detail sidebar for record list page, improve token list page (#3589) --- web/src/BaseListPage.js | 11 ++- web/src/RecordListPage.js | 165 +++++++++++++++++++++++++---------- web/src/TokenListPage.js | 6 +- web/src/common/Editor.js | 10 ++- web/src/locales/en/data.json | 1 + web/src/locales/zh/data.json | 1 + 6 files changed, 141 insertions(+), 53 deletions(-) diff --git a/web/src/BaseListPage.js b/web/src/BaseListPage.js index 4e7d84b1..7b554ea4 100644 --- a/web/src/BaseListPage.js +++ b/web/src/BaseListPage.js @@ -73,7 +73,7 @@ class BaseListPage extends React.Component { this.fetch({pagination}); } - getColumnSearchProps = dataIndex => ({ + getColumnSearchProps = (dataIndex, customRender = null) => ({ filterDropdown: ({setSelectedKeys, selectedKeys, confirm, clearFilters}) => (
this.searchInput.select(), 100); } }, - render: text => - this.state.searchedColumn === dataIndex ? ( + render: (text, record, index) => { + const highlightContent = this.state.searchedColumn === dataIndex ? ( ) : ( text - ), + ); + + return customRender ? customRender({text, record, index}, highlightContent) : highlightContent; + }, }); handleSearch = (selectedKeys, confirm, dataIndex) => { diff --git a/web/src/RecordListPage.js b/web/src/RecordListPage.js index ffa71225..f608128f 100644 --- a/web/src/RecordListPage.js +++ b/web/src/RecordListPage.js @@ -14,12 +14,12 @@ import React from "react"; import {Link} from "react-router-dom"; -import {Switch, Table} from "antd"; +import {Button, Descriptions, Drawer, Switch, Table, Tooltip} from "antd"; import * as Setting from "./Setting"; import * as RecordBackend from "./backend/RecordBackend"; import i18next from "i18next"; -import moment from "moment"; import BaseListPage from "./BaseListPage"; +import Editor from "./common/Editor"; class RecordListPage extends BaseListPage { UNSAFE_componentWillMount() { @@ -28,21 +28,6 @@ class RecordListPage extends BaseListPage { this.fetch({pagination}); } - newRecord() { - return { - owner: "built-in", - name: "1234", - id: "1234", - clientIp: "::1", - timestamp: moment().format(), - organization: "built-in", - username: "admin", - requestUri: "/api/get-account", - action: "login", - isTriggered: false, - }; - } - renderTable(records) { let columns = [ { @@ -65,16 +50,13 @@ class RecordListPage extends BaseListPage { title: i18next.t("general:Client IP"), dataIndex: "clientIp", key: "clientIp", - width: "100px", + width: "120px", sorter: true, - ...this.getColumnSearchProps("clientIp"), - render: (text, record, index) => { - return ( - - {text} - - ); - }, + ...this.getColumnSearchProps("clientIp", (row, highlightContent) => ( + + {highlightContent} + + )), }, { title: i18next.t("general:Timestamp"), @@ -120,28 +102,28 @@ class RecordListPage extends BaseListPage { title: i18next.t("general:Method"), dataIndex: "method", key: "method", - width: "110px", + width: "100px", sorter: true, filterMultiple: false, filters: [ - {text: "GET", value: "GET"}, - {text: "HEAD", value: "HEAD"}, - {text: "POST", value: "POST"}, - {text: "PUT", value: "PUT"}, - {text: "DELETE", value: "DELETE"}, - {text: "CONNECT", value: "CONNECT"}, - {text: "OPTIONS", value: "OPTIONS"}, - {text: "TRACE", value: "TRACE"}, - {text: "PATCH", value: "PATCH"}, - ], + "GET", "HEAD", "POST", "PUT", "DELETE", + "CONNECT", "OPTIONS", "TRACE", "PATCH", + ].map(el => ({text: el, value: el})), }, { title: i18next.t("general:Request URI"), dataIndex: "requestUri", key: "requestUri", - // width: "300px", + width: "200px", sorter: true, - ...this.getColumnSearchProps("requestUri"), + ellipsis: { + showTitle: false, + }, + ...this.getColumnSearchProps("requestUri", (row, highlightContent) => ( + + {highlightContent} + + )), }, { title: i18next.t("user:Language"), @@ -155,7 +137,7 @@ class RecordListPage extends BaseListPage { title: i18next.t("record:Status code"), dataIndex: "statusCode", key: "statusCode", - width: "90px", + width: "120px", sorter: true, ...this.getColumnSearchProps("statusCode"), }, @@ -163,16 +145,26 @@ class RecordListPage extends BaseListPage { title: i18next.t("record:Response"), dataIndex: "response", key: "response", - width: "90px", + width: "220px", sorter: true, - ...this.getColumnSearchProps("response"), + ellipsis: { + showTitle: false, + }, + ...this.getColumnSearchProps("response", (row, highlightContent) => ( + + {highlightContent} + + )), }, { title: i18next.t("record:Object"), dataIndex: "object", key: "object", - width: "90px", + width: "200px", sorter: true, + ellipsis: { + showTitle: false, + }, ...this.getColumnSearchProps("object"), }, { @@ -191,7 +183,7 @@ class RecordListPage extends BaseListPage { title: i18next.t("record:Is triggered"), dataIndex: "isTriggered", key: "isTriggered", - width: "140px", + width: "120px", sorter: true, fixed: (Setting.isMobile()) ? "false" : "right", render: (text, record, index) => { @@ -204,6 +196,24 @@ class RecordListPage extends BaseListPage { ); }, }, + { + title: i18next.t("general:Action"), + dataIndex: "action", + key: "action", + width: "80px", + sorter: true, + fixed: "right", + render: (text, record, index) => ( + + ), + }, ]; if (Setting.isLocalAdminUser(this.props.account)) { @@ -220,7 +230,7 @@ class RecordListPage extends BaseListPage { return (
- (
{i18next.t("general:Records")}     @@ -229,10 +239,73 @@ class RecordListPage extends BaseListPage { loading={this.state.loading} onChange={this.handleTableChange} /> + {/* TODO: Should be packaged as a component after confirm it run correctly.*/} + this.setState({detailShow: false})} + open={this.state.detailShow} + > + + {this.getDetailField("id")} + {this.getDetailField("clientIp")} + {this.getDetailField("createdTime")} + + + {this.getDetailField("organization")} + + + + + {this.getDetailField("user")} + + + {this.getDetailField("method")} + {this.getDetailField("requestUri")} + {this.getDetailField("language")} + {this.getDetailField("statusCode")} + {this.getDetailField("action")} + + + + + + + +
); } + jsonStrFormatter = str => { + try { + return JSON.stringify(JSON.parse(str), null, 2); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + return str; + } + }; + + getDetailField = dataIndex => { + return this.state.detailRecord ? this.state.detailRecord?.[dataIndex] ?? "" : ""; + }; + fetch = (params = {}) => { let field = params.searchedColumn, value = params.searchText; const sortField = params.sortField, sortOrder = params.sortOrder; @@ -255,6 +328,8 @@ class RecordListPage extends BaseListPage { }, searchText: params.searchText, searchedColumn: params.searchedColumn, + detailShow: false, + detailRecord: null, }); } else { if (res.data.includes("Please login first")) { diff --git a/web/src/TokenListPage.js b/web/src/TokenListPage.js index 48bde47a..efcb78e4 100644 --- a/web/src/TokenListPage.js +++ b/web/src/TokenListPage.js @@ -153,7 +153,7 @@ class TokenListPage extends BaseListPage { title: i18next.t("token:Authorization code"), dataIndex: "code", key: "code", - // width: '150px', + width: "180px", sorter: true, ...this.getColumnSearchProps("code"), render: (text, record, index) => { @@ -164,7 +164,7 @@ class TokenListPage extends BaseListPage { title: i18next.t("token:Access token"), dataIndex: "accessToken", key: "accessToken", - // width: '150px', + width: "220px", sorter: true, ellipsis: true, ...this.getColumnSearchProps("accessToken"), @@ -225,7 +225,7 @@ class TokenListPage extends BaseListPage { return (
-
`${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps} +
`${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps} title={() => (
{i18next.t("general:Tokens")}     diff --git a/web/src/common/Editor.js b/web/src/common/Editor.js index 0102af8f..c0043bfb 100644 --- a/web/src/common/Editor.js +++ b/web/src/common/Editor.js @@ -22,6 +22,10 @@ export const Editor = (props) => { height: "100%", style: {height: "100%"}, } : {}; + const fillWidth = props.fillWidth ? { + width: "100%", + style: {width: "100%"}, + } : {}; let extensions = []; switch (props.lang) { case "javascript": @@ -37,13 +41,17 @@ export const Editor = (props) => { case "xml": extensions = [langs.xml()]; break; + case "json": + extensions = [langs.json()]; + break; } return (