-
Notifications
You must be signed in to change notification settings - Fork 92
feat(new-webui): Add search results table. #866
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dab2aef
db6fd70
abf99d1
ea55f6b
dc828f3
5ea59fc
f194823
3146005
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,4 @@ | ||
| .searchControlsContainer { | ||
| margin-top: 20px; | ||
| padding: 0 20px; | ||
| display: flex; | ||
| gap: 10px; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import {LinkOutlined} from "@ant-design/icons"; | ||
| import { | ||
| Tooltip, | ||
| Typography, | ||
| } from "antd"; | ||
|
|
||
| import styles from "./index.module.css"; | ||
|
|
||
|
|
||
| const {Link} = Typography; | ||
|
|
||
| // eslint-disable-next-line no-warning-comments | ||
| // TODO: Fix link to connect to package log viewer when log viewer setup finished. Also pass | ||
| // proper args to package log viewer. | ||
| const LOG_VIEWER_URL = "https://y-scope.github.io/yscope-log-viewer/"; | ||
|
|
||
| interface LogViewerLinkProps { | ||
| filePath: string; | ||
| } | ||
|
|
||
| /** | ||
| * Render a link to the log viewer with open file. | ||
| * | ||
| * @param props | ||
| * @param props.filePath | ||
| * @return | ||
| */ | ||
| const LogViewerLink = ({filePath}: LogViewerLinkProps) => ( | ||
| <Tooltip title={"Open file"}> | ||
| <Link | ||
| href={LOG_VIEWER_URL} | ||
| target={"_blank"} | ||
| type={"secondary"} | ||
| > | ||
| <LinkOutlined className={styles["linkIcon"] || ""}/> | ||
| {filePath} | ||
| </Link> | ||
| </Tooltip> | ||
| ); | ||
|
|
||
| export default LogViewerLink; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| .linkIcon { | ||
| margin-right: 4px; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import SyntaxHighlighter from "react-syntax-highlighter"; | ||
|
|
||
| import {Typography} from "antd"; | ||
|
|
||
| import LogViewerLink from "./LogViewerLink"; | ||
| import {highlighterCustomStyles} from "./utils"; | ||
|
|
||
| import "highlight.js/styles/intellij-light.css"; | ||
|
|
||
|
|
||
| const {Text} = Typography; | ||
|
|
||
| interface MessageProps { | ||
| message: string; | ||
| filePath: string; | ||
| } | ||
|
|
||
| /** | ||
| * Renders a message with syntax highlighting and a file path link. | ||
| * | ||
| * @param props | ||
| * @param props.message | ||
| * @param props.filePath | ||
| * @return | ||
| */ | ||
| const Message = ({message, filePath}: MessageProps) => { | ||
| return ( | ||
| <> | ||
| {/* Parent `Text` component allows syntax highlighter to inherit AntD fonts. */} | ||
| <Text> | ||
| <SyntaxHighlighter | ||
| customStyle={highlighterCustomStyles} | ||
| language={"armasm"} | ||
| useInlineStyles={false} | ||
| > | ||
| {message} | ||
| </SyntaxHighlighter> | ||
| </Text> | ||
| <LogViewerLink filePath={filePath}/> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aesthetics: it might look cleaner to display the file link in a separate column. Users could also filter or sort based on the file name. If horizontal space is a concern, we could consider adding buttons to show/hide columns later. (I don't have full context of the PR, so feel push back)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For context, this is what the old webui did. I do agree the columns are better, but I think this will be later once we setup dynamic columns for all fields in the query results(clp-s only). for now i think better/simpler to match old ui.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the future, I believe we can add a right pop-up panel (a.k.a. a "drawer") for displaying metadata of a specific log event when selected. For now, since we intend to make the log viewer links visible (as a feature highlight), it makes sense to leave the links directly in the table. Adding an extra column for file name display would work, but as you mention we could be leaving a lot of gap in the log viewer link cells when the message is too long - it could be a waste of space. Therefore, for now I believe it is fine to leave the links at the bottom of the message cells. |
||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default Message; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| /** | ||
| * Style overrides for the syntax highlighter. "react-syntax-highlighter" lib will not accept | ||
| * styles directly from CSS modules. | ||
| */ | ||
| export const highlighterCustomStyles: React.CSSProperties = { | ||
| background: "none", | ||
| border: "none", | ||
| fontFamily: "inherit", | ||
| margin: "0", | ||
| padding: "0", | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,54 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import {Table} from "antd"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SearchResult, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| searchResultsTableColumns, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "./typings"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line no-warning-comments | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Replace with values from database once api implemented. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const DUMMY_RESULTS: SearchResult[] = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timestamp: "2023-01-01 12:00:00", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "INFO: User login successful for user 'john.doe'.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| filePath: "/var/logs/auth.log", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: 2, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timestamp: "2023-01-01 12:01:00", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "ERROR: Failed to connect to database 'logs_db'.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| filePath: "/var/logs/db.log", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: 3, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timestamp: "2023-01-01 12:02:00", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "WARN: Disk space running low on volume '/var/logs'.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| filePath: "/var/logs/system.log", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: 4, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timestamp: "2023-01-01 12:03:00", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "DEBUG: Processing request ID 12345.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| filePath: "/var/logs/app.log", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+9
to
+36
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAdd pagination controls for future implementation. The dummy data is well structured and provides a good representation of expected search results. While the TODO comment correctly indicates these will be replaced with database values, consider planning for server-side pagination in the component design. 🌐 Web query: 💡 Result: Best Practices for Implementing Server-Side Pagination with Ant Design TableImplementing server-side pagination in Ant Design's Table component ensures efficiency and scalability, especially when working with large datasets. Here are the best practices supported by recent examples and the official Ant Design approach: 1. Maintain Pagination State in React
2. Fetch Data Based on Table Pagination Events
3. Configure the Table's Pagination Prop Properly
4. Efficient Data Fetching and Loading Feedback
5. Always Update the Total Count
6. Handle Sorting and Filtering
7. Robust Error Handling
Example Pattern const [data, setData] = useState([]);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 0
});
const [loading, setLoading] = useState(false);
const fetchData = async (params) => {
setLoading(true);
const response = await api.get('/items', {
params: {
page: params.pagination.current,
size: params.pagination.pageSize
}
});
setData(response.data.items);
setPagination({
...params.pagination,
total: response.data.totalCount
});
setLoading(false);
};
useEffect(() => {
fetchData({ pagination });
}, []);
const handleTableChange = (newPagination, filters, sorter) => {
fetchData({
pagination: newPagination,
filters,
sorter,
});
};
<Table
columns={columns}
dataSource={data}
pagination={pagination}
loading={loading}
onChange={handleTableChange}
/>[5][8] Summary Table: Key Practices
By following these best practices, you can efficiently implement server-side pagination in Ant Design Table components, resulting in responsive UIs and scalable performance for large data sets[5][8][4]. Citations:
Plan for server-side pagination in SearchResultsTable Once the real API is in place, extend the component to support server-side pagination by:
File to update: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Renders search results in a table. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const SearchResultsTable = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Table<SearchResult> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| columns={searchResultsTableColumns} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dataSource={DUMMY_RESULTS} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pagination={false} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rowKey={(record) => record.id.toString()} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| virtual={true}/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+38
to
+52
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance component flexibility with props. The component is currently hardcoded to use dummy data. Consider enhancing it with props to make it more flexible and reusable: -const SearchResultsTable = () => {
+interface SearchResultsTableProps {
+ dataSource?: SearchResult[];
+ loading?: boolean;
+ onSortChange?: (field: string, order: 'ascend' | 'descend' | undefined) => void;
+}
+
+const SearchResultsTable = ({
+ dataSource = DUMMY_RESULTS,
+ loading = false,
+ onSortChange,
+}: SearchResultsTableProps) => {
return (
<Table<SearchResult>
columns={searchResultsTableColumns}
- dataSource={DUMMY_RESULTS}
+ dataSource={dataSource}
+ loading={loading}
+ onChange={(pagination, filters, sorter) => {
+ if (onSortChange && !Array.isArray(sorter)) {
+ onSortChange(sorter.field as string, sorter.order);
+ }
+ }}
pagination={false}
rowKey={(record) => record.id.toString()}
virtual={true}/>
);
};📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default SearchResultsTable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import {TableProps} from "antd"; | ||
|
|
||
| import Message from "./Message"; | ||
|
|
||
|
|
||
| /** | ||
| * Structure of search results data displayed in the table. | ||
| */ | ||
| interface SearchResult { | ||
| id: number; | ||
| timestamp: string; | ||
| message: string; | ||
| filePath: string; | ||
| } | ||
|
|
||
| /** | ||
| * Columns configuration for the seach results table. | ||
| */ | ||
| const searchResultsTableColumns: NonNullable<TableProps<SearchResult>["columns"]> = [ | ||
| { | ||
| dataIndex: "timestamp", | ||
| key: "timestamp", | ||
| sorter: true, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might be missing something, sorting doesn't seem to work. According to the docs, it looks like
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The sorter will be server side, like it will need to launch a new query to the backend, so this is just a dummy sorter until database api is implemented
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It makes sense to set the (I haven't run the code locally yet. Do the sorting indicators (asc / desc) change as we click the table headers?) We can register the handler and print a log if the sorter changes, though this will be just boilerplate code until we submit the API integration PR. @davemarco I'll let you decide whether you want to add the handler in this PR.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make sense
The indicator changes. |
||
| title: "Timestamp", | ||
| width: 15, | ||
| }, | ||
| { | ||
| dataIndex: "message", | ||
| key: "message", | ||
| render: (_, record) => ( | ||
| <Message | ||
| filePath={record.filePath} | ||
| message={record.message}/> | ||
| ), | ||
| title: "Message", | ||
| width: 85, | ||
| }, | ||
| ]; | ||
|
|
||
| export type {SearchResult}; | ||
| export {searchResultsTableColumns}; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| .searchPageContainer { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 16px; | ||
| padding: 24px 16px 16px; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Consider using more appropriate language for log syntax highlighting.
The current implementation uses "armasm" as the language for syntax highlighting, which seems unusual for log messages. Consider using a more appropriate language like "log", "text" or "plaintext".
🌐 Web query:
💡 Result:
React Syntax Highlighter offers a wide range of language support for syntax highlighting in React applications, but there isn't a specific language explicitly designed for log files. However, several options can be effective for highlighting log files depending on your specific log format.
Best Options for Log File Syntax Highlighting
For log files in react-syntax-highlighter, these languages work particularly well:
Plain Text
The simplest approach is to use "text" as your language option, which will render your logs in monospaced font without specific highlighting[1][7]. This works well for simple logs where you don't need complex syntax highlighting.
Shell/Bash
For command-line or terminal-style logs, the "shell" or "bash" language options provide useful highlighting that can make timestamps, errors, and command outputs more distinguishable[10]. These languages highlight typical patterns found in terminal outputs.
HTTP
If your logs contain HTTP requests and responses, the "http" language option can be particularly useful as it will highlight status codes, headers, and request methods[10].
JavaScript Console
For logs that originate from browser consoles or JavaScript applications, using "javascript" as the language can provide good highlighting for objects, errors, and various data types[5][6].
Customization Options
Regardless of which language you choose, react-syntax-highlighter offers several customization features that can enhance log file readability:
Line Numbers
Adding line numbers helps with referencing specific parts of lengthy logs[5]:
Highlighting Specific Lines
You can highlight important lines (like errors) in your logs using the
wrapLinesandlinePropsoptions[5]:Custom Styling
You can further customize the appearance using the
customStyleprop to match your application's theme[1][5].If your log files have a specific format or structure, you might need to experiment with different language options to find the one that best highlights the important parts of your logs. In some cases, you might even consider creating a custom language definition if your logs have a very specific structure that isn't well-served by the existing options.
Citations:
Switch to Plain Text Highlighting for Logs
To improve readability of log output, use the plain text highlighter instead of “armasm”. For example:
• File:
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/Message/index.tsx
Line: 33
If your logs include shell commands, HTTP messages or JavaScript console output, consider using
"bash","http"or"javascript"respectively.📝 Committable suggestion