@boarteam/boar-pack-users-frontend 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/package.json +38 -0
  2. package/src/.umi/core/EmptyRoute.tsx +9 -0
  3. package/src/.umi/core/defineApp.ts +17 -0
  4. package/src/.umi/core/helmet.ts +10 -0
  5. package/src/.umi/core/helmetContext.ts +4 -0
  6. package/src/.umi/core/history.ts +66 -0
  7. package/src/.umi/core/historyIntelli.ts +132 -0
  8. package/src/.umi/core/plugin.ts +55 -0
  9. package/src/.umi/core/pluginConfig.ts +299 -0
  10. package/src/.umi/core/pluginConfigJoi.d.ts +7 -0
  11. package/src/.umi/core/polyfill.ts +220 -0
  12. package/src/.umi/core/route.tsx +29 -0
  13. package/src/.umi/core/routeProps.ts +6 -0
  14. package/src/.umi/core/terminal.ts +37 -0
  15. package/src/.umi/exports.ts +23 -0
  16. package/src/.umi/plugin-access/context.ts +7 -0
  17. package/src/.umi/plugin-access/index.tsx +87 -0
  18. package/src/.umi/plugin-access/runtime.tsx +23 -0
  19. package/src/.umi/plugin-access/types.d.ts +5 -0
  20. package/src/.umi/plugin-locale/SelectLang.tsx +390 -0
  21. package/src/.umi/plugin-locale/index.ts +5 -0
  22. package/src/.umi/plugin-locale/locale.tsx +52 -0
  23. package/src/.umi/plugin-locale/localeExports.ts +279 -0
  24. package/src/.umi/plugin-locale/runtime.tsx +9 -0
  25. package/src/.umi/plugin-locale/runtimeConfig.d.ts +13 -0
  26. package/src/.umi/plugin-model/index.tsx +183 -0
  27. package/src/.umi/plugin-model/model.ts +6 -0
  28. package/src/.umi/plugin-model/runtime.tsx +20 -0
  29. package/src/.umi/testBrowser.tsx +87 -0
  30. package/src/.umi/tsconfig.json +44 -0
  31. package/src/.umi/typings.d.ts +136 -0
  32. package/src/.umi/umi.ts +76 -0
  33. package/src/access.ts +5 -0
  34. package/src/components/EventLogs/EventLogExplanation.tsx +166 -0
  35. package/src/components/EventLogs/EventLogsTable.tsx +146 -0
  36. package/src/components/EventLogs/EventLogsTimeline.tsx +109 -0
  37. package/src/components/EventLogs/UserAgentDisplay.tsx +21 -0
  38. package/src/components/EventLogs/eventLogsSearchableColumns.ts +87 -0
  39. package/src/components/EventLogs/getBrowserIcon.ts +27 -0
  40. package/src/components/EventLogs/useEventLogsColumns.tsx +375 -0
  41. package/src/components/index.tsx +7 -0
  42. package/src/index.tsx +1 -0
  43. package/src/pages/EventLogs/index.tsx +26 -0
  44. package/src/pages/getInitialState.tsx +114 -0
  45. package/src/tools/action.enum.ts +7 -0
  46. package/src/tools/api-client/apiClient.ts +6 -0
  47. package/src/tools/api-client/generated/ApiClient.ts +38 -0
  48. package/src/tools/api-client/generated/core/ApiError.ts +25 -0
  49. package/src/tools/api-client/generated/core/ApiRequestOptions.ts +17 -0
  50. package/src/tools/api-client/generated/core/ApiResult.ts +11 -0
  51. package/src/tools/api-client/generated/core/BaseHttpRequest.ts +14 -0
  52. package/src/tools/api-client/generated/core/CancelablePromise.ts +131 -0
  53. package/src/tools/api-client/generated/core/NodeHttpRequest.ts +26 -0
  54. package/src/tools/api-client/generated/core/OpenAPI.ts +32 -0
  55. package/src/tools/api-client/generated/core/request.ts +320 -0
  56. package/src/tools/api-client/generated/index.ts +26 -0
  57. package/src/tools/api-client/generated/models/EventLog.ts +56 -0
  58. package/src/tools/api-client/generated/models/EventLogCreateDto.ts +39 -0
  59. package/src/tools/api-client/generated/models/EventLogTimelineDto.ts +24 -0
  60. package/src/tools/api-client/generated/models/EventLogTimelineQueryDto.ts +11 -0
  61. package/src/tools/api-client/generated/models/EventLogUpdateDto.ts +39 -0
  62. package/src/tools/api-client/generated/models/GetManyEventLogResponseDto.ts +15 -0
  63. package/src/tools/api-client/generated/models/GetManyUserResponseDto.ts +15 -0
  64. package/src/tools/api-client/generated/models/PermissionDto.ts +9 -0
  65. package/src/tools/api-client/generated/models/User.ts +27 -0
  66. package/src/tools/api-client/generated/models/UserCreateDto.ts +23 -0
  67. package/src/tools/api-client/generated/models/UserUpdateDto.ts +23 -0
  68. package/src/tools/api-client/generated/services/EventLogsService.ts +178 -0
  69. package/src/tools/api-client/generated/services/UsersService.ts +226 -0
  70. package/src/tools/api-client/index.ts +2 -0
@@ -0,0 +1,136 @@
1
+ // This file is generated by Umi automatically
2
+ // DO NOT CHANGE IT MANUALLY!
3
+ type CSSModuleClasses = { readonly [key: string]: string }
4
+ declare module '*.css' {
5
+ const classes: CSSModuleClasses
6
+ export default classes
7
+ }
8
+ declare module '*.scss' {
9
+ const classes: CSSModuleClasses
10
+ export default classes
11
+ }
12
+ declare module '*.sass' {
13
+ const classes: CSSModuleClasses
14
+ export default classes
15
+ }
16
+ declare module '*.less' {
17
+ const classes: CSSModuleClasses
18
+ export default classes
19
+ }
20
+ declare module '*.styl' {
21
+ const classes: CSSModuleClasses
22
+ export default classes
23
+ }
24
+ declare module '*.stylus' {
25
+ const classes: CSSModuleClasses
26
+ export default classes
27
+ }
28
+
29
+ // images
30
+ declare module '*.jpg' {
31
+ const src: string
32
+ export default src
33
+ }
34
+ declare module '*.jpeg' {
35
+ const src: string
36
+ export default src
37
+ }
38
+ declare module '*.png' {
39
+ const src: string
40
+ export default src
41
+ }
42
+ declare module '*.gif' {
43
+ const src: string
44
+ export default src
45
+ }
46
+ declare module '*.svg' {
47
+ import * as React from 'react';
48
+ export const ReactComponent: React.FunctionComponent<React.SVGProps<
49
+ SVGSVGElement
50
+ > & { title?: string }>;
51
+
52
+ const src: string
53
+ export default src
54
+ }
55
+ declare module '*.ico' {
56
+ const src: string
57
+ export default src
58
+ }
59
+ declare module '*.webp' {
60
+ const src: string
61
+ export default src
62
+ }
63
+ declare module '*.avif' {
64
+ const src: string
65
+ export default src
66
+ }
67
+
68
+ // media
69
+ declare module '*.mp4' {
70
+ const src: string
71
+ export default src
72
+ }
73
+ declare module '*.webm' {
74
+ const src: string
75
+ export default src
76
+ }
77
+ declare module '*.ogg' {
78
+ const src: string
79
+ export default src
80
+ }
81
+ declare module '*.mp3' {
82
+ const src: string
83
+ export default src
84
+ }
85
+ declare module '*.wav' {
86
+ const src: string
87
+ export default src
88
+ }
89
+ declare module '*.flac' {
90
+ const src: string
91
+ export default src
92
+ }
93
+ declare module '*.aac' {
94
+ const src: string
95
+ export default src
96
+ }
97
+
98
+ // fonts
99
+ declare module '*.woff' {
100
+ const src: string
101
+ export default src
102
+ }
103
+ declare module '*.woff2' {
104
+ const src: string
105
+ export default src
106
+ }
107
+ declare module '*.eot' {
108
+ const src: string
109
+ export default src
110
+ }
111
+ declare module '*.ttf' {
112
+ const src: string
113
+ export default src
114
+ }
115
+ declare module '*.otf' {
116
+ const src: string
117
+ export default src
118
+ }
119
+
120
+ // other
121
+ declare module '*.wasm' {
122
+ const initWasm: (options: WebAssembly.Imports) => Promise<WebAssembly.Exports>
123
+ export default initWasm
124
+ }
125
+ declare module '*.webmanifest' {
126
+ const src: string
127
+ export default src
128
+ }
129
+ declare module '*.pdf' {
130
+ const src: string
131
+ export default src
132
+ }
133
+ declare module '*.txt' {
134
+ const src: string
135
+ export default src
136
+ }
@@ -0,0 +1,76 @@
1
+ // @ts-nocheck
2
+ // This file is generated by Umi automatically
3
+ // DO NOT CHANGE IT MANUALLY!
4
+ import './core/polyfill';
5
+
6
+ import { renderClient } from '/Users/boarteam/.config/yarn/global/node_modules/@umijs/renderer-react';
7
+ import { getRoutes } from './core/route';
8
+ import { createPluginManager } from './core/plugin';
9
+ import { createHistory } from './core/history';
10
+ import { ApplyPluginsType } from 'umi';
11
+
12
+
13
+ const publicPath = "/";
14
+ const runtimePublicPath = false;
15
+
16
+ async function render() {
17
+ const pluginManager = createPluginManager();
18
+ const { routes, routeComponents } = await getRoutes(pluginManager);
19
+
20
+ // allow user to extend routes
21
+ await pluginManager.applyPlugins({
22
+ key: 'patchRoutes',
23
+ type: ApplyPluginsType.event,
24
+ args: {
25
+ routes,
26
+ routeComponents,
27
+ },
28
+ });
29
+
30
+ const contextOpts = pluginManager.applyPlugins({
31
+ key: 'modifyContextOpts',
32
+ type: ApplyPluginsType.modify,
33
+ initialValue: {},
34
+ });
35
+
36
+ const basename = contextOpts.basename || '/';
37
+ const historyType = contextOpts.historyType || 'browser';
38
+
39
+ const history = createHistory({
40
+ type: historyType,
41
+ basename,
42
+ ...contextOpts.historyOpts,
43
+ });
44
+
45
+ return (pluginManager.applyPlugins({
46
+ key: 'render',
47
+ type: ApplyPluginsType.compose,
48
+ initialValue() {
49
+ const context = {
50
+ routes,
51
+ routeComponents,
52
+ pluginManager,
53
+ rootElement: contextOpts.rootElement || document.getElementById('root'),
54
+ publicPath,
55
+ runtimePublicPath,
56
+ history,
57
+ historyType,
58
+ basename,
59
+ callback: contextOpts.callback,
60
+ };
61
+ const modifiedContext = pluginManager.applyPlugins({
62
+ key: 'modifyClientRenderOpts',
63
+ type: ApplyPluginsType.modify,
64
+ initialValue: context,
65
+ });
66
+ return renderClient(modifiedContext);
67
+ },
68
+ }))();
69
+ }
70
+
71
+
72
+ render();
73
+
74
+ window.g_umi = {
75
+ version: '4.1.10',
76
+ };
package/src/access.ts ADDED
@@ -0,0 +1,5 @@
1
+ export default function () {
2
+ return {
3
+ canViewEventLogs: true,
4
+ };
5
+ }
@@ -0,0 +1,166 @@
1
+ import React from "react";
2
+ import { EventLog } from "@@api/generated";
3
+ import { Card, Descriptions, Tag, Typography } from "antd";
4
+ import {
5
+ ClockCircleOutlined,
6
+ CrownOutlined,
7
+ ExclamationCircleOutlined,
8
+ InfoCircleOutlined,
9
+ QuestionCircleOutlined,
10
+ SettingOutlined,
11
+ UserOutlined,
12
+ WarningOutlined
13
+ } from "@ant-design/icons";
14
+ import UserAgentDisplay from "./UserAgentDisplay";
15
+
16
+ const { Paragraph, Text, Title } = Typography;
17
+
18
+ function formatJson(s: string): string {
19
+ try {
20
+ return JSON.stringify(JSON.parse(s), null, 2);
21
+ } catch (e) {
22
+ return s;
23
+ }
24
+ }
25
+
26
+ export function getUserRoleIcon(role: string): React.ReactNode {
27
+ switch (role) {
28
+ case EventLog.userRole.ADMIN:
29
+ return <CrownOutlined />;
30
+ case EventLog.userRole.USER:
31
+ return <UserOutlined />;
32
+ case EventLog.userRole.GUEST:
33
+ return <QuestionCircleOutlined />;
34
+ case EventLog.userRole.SYSTEM:
35
+ return <SettingOutlined />;
36
+ default:
37
+ return <QuestionCircleOutlined />;
38
+ }
39
+ }
40
+
41
+ function getLogTypeTag(logType: string): React.ReactNode {
42
+ switch (logType) {
43
+ case EventLog.logType.AUDIT:
44
+ return <Tag icon={<UserOutlined />}>Audit Log</Tag>;
45
+ case EventLog.logType.OPERATIONAL:
46
+ return <Tag icon={<SettingOutlined />}>Operational Log</Tag>;
47
+ case EventLog.logType.APPLICATION:
48
+ return <Tag>Application Log</Tag>;
49
+ default:
50
+ return <Tag>Unknown</Tag>;
51
+ }
52
+ }
53
+
54
+ function renderUserAgentText(userAgent: string | null): React.ReactNode {
55
+ return userAgent
56
+ ? <>using <UserAgentDisplay userAgent={userAgent} /> with User-Agent header:
57
+ <pre>{userAgent}</pre>
58
+ </>
59
+ : 'without User-Agent header';
60
+
61
+ }
62
+
63
+ export const EventLogExplanation: React.FC<{ record: EventLog }> = ({ record }) => {
64
+ const duration = record.duration !== null ? <Tag icon={<ClockCircleOutlined />}>{record.duration} ms</Tag> : 'unknown time ';
65
+
66
+ let statusCode: React.ReactNode;
67
+ if (record.statusCode === null) {
68
+ statusCode = <Tag>Unknown</Tag>;
69
+ } else if (record.statusCode >= 500) {
70
+ statusCode = <Tag color='red'>{record.statusCode}</Tag>;
71
+ } else if (record.statusCode >= 300) {
72
+ statusCode = <Tag color='orange'>{record.statusCode}</Tag>;
73
+ } else if (record.statusCode >= 200) {
74
+ statusCode = <Tag color='green'>{record.statusCode}</Tag>;
75
+ }
76
+
77
+ // get query params form url
78
+ let queryParamsEl: React.ReactNode = <>N/A<br /></>;
79
+ if (record.url) {
80
+ let url: URL;
81
+ try {
82
+ url = new URL(window.location.origin + record.url);
83
+ } catch (e) {
84
+ console.error(e);
85
+ url = new URL(window.location.origin);
86
+ }
87
+ const queryParams = Array.from(url.searchParams.entries());
88
+ if (queryParams.length) {
89
+ queryParamsEl = <Descriptions column={1} size='small' bordered={true} items={
90
+ queryParams.map(([key, value], i) => {
91
+ return {
92
+ key: i,
93
+ label: key,
94
+ children: key !== 's' ? <Text code>{value}</Text> : <pre style={{
95
+ fontSize: '0.8em',
96
+ }}>{formatJson(value)}</pre>
97
+ };
98
+ })
99
+ } />
100
+ }
101
+ }
102
+ const query = <Paragraph>
103
+ <Title level={5}>Query Parameters</Title>
104
+ {queryParamsEl}
105
+ </Paragraph>;
106
+
107
+ let payloadEl: React.ReactNode = <>N/A<br /></>;
108
+ if (record.payload) {
109
+ payloadEl = <pre>{JSON.stringify(record.payload, null, 2).replace(/\n/g, '\n')}</pre>;
110
+ }
111
+ const payload = <Paragraph>
112
+ <Title level={5}>Action Payload</Title>
113
+ {payloadEl}
114
+ </Paragraph>;
115
+
116
+ let userEl = record.user
117
+ ? <>User <Tag>{record.user.name}</Tag>which had role <Tag
118
+ icon={getUserRoleIcon(record.userRole)}>{record.userRole}</Tag></>
119
+ : <>A <Tag icon={<QuestionCircleOutlined />}>Guest</Tag></>;
120
+
121
+ if (record.userRole === EventLog.userRole.SYSTEM) {
122
+ userEl = <Tag icon={<SettingOutlined />}>System</Tag>;
123
+ }
124
+
125
+ const actionEl = record.action === 'Access'
126
+ ? <>tried to access <Tag>{record.entity}</Tag>controller</>
127
+ : <>performed <Tag>{record.entity}.{record.action}</Tag>action</>;
128
+
129
+ let logLevelEl = <Tag color={'warning'} icon={<WarningOutlined />}>{record.logLevel}</Tag>;
130
+ if (record.logLevel === EventLog.logLevel.INFO) {
131
+ logLevelEl = <Tag color={'blue'} icon={<InfoCircleOutlined />}>{record.logLevel}</Tag>;
132
+ } else if (record.logLevel === EventLog.logLevel.ERROR) {
133
+ logLevelEl = <Tag color={'red'} icon={<ExclamationCircleOutlined />}>{record.logLevel}</Tag>;
134
+ }
135
+
136
+ return <Card
137
+ title={<>
138
+ {getLogTypeTag(record.logType)}captured at <Tag icon={<ClockCircleOutlined />}>{record.createdAt}</Tag>with log
139
+ level {logLevelEl}
140
+ </>}
141
+ >
142
+ {record.logType === EventLog.logType.AUDIT && <Paragraph>
143
+ {userEl}{actionEl} as result of the HTTP request<br />
144
+ <pre>
145
+ {record.method} {record.url}<br />
146
+ </pre>
147
+ {query}
148
+ {payload}
149
+ <Title level={5}>Client</Title>
150
+ Request was done from IP address <Tag>{record.ipAddress}</Tag><br />
151
+ {renderUserAgentText(record.userAgent)}
152
+ </Paragraph> || null}
153
+ {record.logType !== EventLog.logType.AUDIT && <><Paragraph>
154
+ {userEl}{actionEl}
155
+ </Paragraph>{payload}</>}
156
+ <Paragraph>
157
+ <Title level={5}>Service</Title>
158
+ The action was handled by service <Tag>{record.service}</Tag>{record.serviceId ? <>with ID <Text code
159
+ copyable>{record.serviceId}</Text></> : ''}
160
+ </Paragraph>
161
+ {(record.duration || record.statusCode) ? <Paragraph>
162
+ <Title level={5}>Response</Title>
163
+ The request took {duration}and returned status code {statusCode}<br />
164
+ </Paragraph> : null}
165
+ </Card>;
166
+ }
@@ -0,0 +1,146 @@
1
+ import apiClient from "@@api/apiClient";
2
+ import { EventLog, EventLogCreateDto, EventLogTimelineQueryDto, EventLogUpdateDto } from "@@api/generated";
3
+ import { useEventLogsColumns } from "./useEventLogsColumns";
4
+ import { Table, TColumnsSet } from "@boarteam/boar-pack-common-frontend";
5
+ import { eventLogsSearchableColumns } from "./eventLogsSearchableColumns";
6
+ import { EventLogExplanation } from "./EventLogExplanation";
7
+ import { createStyles } from "antd-style";
8
+ import { ConfigProvider } from "antd";
9
+
10
+ type TEventLogFilterParams = {}
11
+
12
+ const columnsSets: TColumnsSet<EventLog>[] = [
13
+ {
14
+ name: 'Audit',
15
+ columns: [
16
+ 'createdAt',
17
+ 'logType',
18
+ 'entity',
19
+ 'action',
20
+ 'entityId',
21
+ 'user',
22
+ 'logLevel',
23
+ ],
24
+ },
25
+ {
26
+ name: 'Operational',
27
+ columns: [
28
+ 'createdAt',
29
+ 'logType',
30
+ 'entity',
31
+ 'entityId',
32
+ 'service',
33
+ 'serviceId',
34
+ 'action',
35
+ 'userRole',
36
+ 'logLevel',
37
+ ],
38
+ },
39
+ {
40
+ name: 'Application',
41
+ columns: [
42
+ 'createdAt',
43
+ 'logType',
44
+ 'service',
45
+ 'serviceId',
46
+ 'logLevel',
47
+ 'payload',
48
+ ],
49
+ },
50
+ {
51
+ name: 'Requests',
52
+ columns: [
53
+ 'service',
54
+ 'createdAt',
55
+ 'user',
56
+ 'method',
57
+ 'url',
58
+ 'duration',
59
+ 'statusCode',
60
+ ],
61
+ },
62
+ {
63
+ name: 'HTTP',
64
+ columns: [
65
+ 'createdAt',
66
+ 'method',
67
+ 'url',
68
+ 'ipAddress',
69
+ 'userAgent',
70
+ ],
71
+ }
72
+ ];
73
+
74
+ const useStyles = createStyles(({ token }) => {
75
+ return {
76
+ table: {
77
+ '.ant-pro-table-list-toolbar-left': {
78
+ flex: '0 0 auto',
79
+ },
80
+ },
81
+ };
82
+ });
83
+
84
+ const EventLogsTable = ({
85
+ startTime,
86
+ endTime,
87
+ onDateRangeChange,
88
+ }: EventLogTimelineQueryDto & {
89
+ onDateRangeChange: (start: string | undefined, end: string | undefined) => void;
90
+ }) => {
91
+ const columns = useEventLogsColumns({
92
+ startTime,
93
+ endTime,
94
+ onDateRangeChange,
95
+ });
96
+ const { styles } = useStyles();
97
+
98
+ return (
99
+ <ConfigProvider
100
+ theme={{
101
+ components: {
102
+ Collapse: {
103
+ contentPadding: '0',
104
+ headerPadding: '0',
105
+ },
106
+ }
107
+ }}
108
+ >
109
+ <Table<EventLog, EventLogCreateDto, EventLogUpdateDto, TEventLogFilterParams>
110
+ className={styles.table}
111
+ getAll={params => apiClient.eventLogs.getManyBaseEventLogsControllerEventLog(params)}
112
+ columns={columns}
113
+ idColumnName='id'
114
+ pathParams={{}}
115
+ defaultSort={['createdAt', 'DESC']}
116
+ columnsSets={columnsSets}
117
+ params={{
118
+ join: [{
119
+ field: 'user',
120
+ select: ['id', 'name'],
121
+ }],
122
+ baseFilters: {
123
+ ...startTime && endTime && { createdAt: [startTime, endTime] },
124
+ }
125
+ }}
126
+ expandable={{
127
+ expandedRowRender: record => {
128
+ return <EventLogExplanation record={record} />;
129
+ },
130
+ fixed: 'left',
131
+ }}
132
+ viewOnly={true}
133
+ scroll={{
134
+ x: 'max-content',
135
+ }}
136
+ searchableColumns={eventLogsSearchableColumns}
137
+ search={{
138
+ filterType: 'light',
139
+ }}
140
+ ghost={true}
141
+ ></Table>
142
+ </ConfigProvider>
143
+ );
144
+ }
145
+
146
+ export default EventLogsTable;
@@ -0,0 +1,109 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { Column } from '@ant-design/plots';
3
+ import { ColumnConfig } from "@ant-design/plots/es/components/column";
4
+ import { EventLogTimelineDto, EventLogTimelineQueryDto } from "@@api/generated";
5
+ import apiClient from "@@api/apiClient";
6
+ import { Button } from "antd";
7
+ import { PageLoading } from "@ant-design/pro-layout";
8
+ import { useModel } from "umi";
9
+
10
+ type TEventLogsTimelineProps = EventLogTimelineQueryDto & {
11
+ onDateRangeChange: (start: string | undefined, end: string | undefined) => void;
12
+ }
13
+
14
+ export const EventLogsTimeline: React.FC<TEventLogsTimelineProps> = ({
15
+ startTime,
16
+ endTime,
17
+ onDateRangeChange,
18
+ }) => {
19
+ // @ts-ignore
20
+ const { initialState } = useModel('@@initialState');
21
+ // @ts-ignore
22
+ const { navTheme } = initialState?.settings || {};
23
+
24
+ const [data, setData] = useState<EventLogTimelineDto[] | null>(null);
25
+ const sliderPosition = useMemo(() => [0, 1], []);
26
+ const [showFilterButton, setShowFilterButton] = useState(false);
27
+
28
+ const chartRef = React.useRef(null);
29
+
30
+ useEffect(() => {
31
+ apiClient.eventLogs.getTimeline({
32
+ startTime,
33
+ endTime,
34
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
35
+ }).then(setData).then(() => {
36
+ sliderPosition[0] = 0;
37
+ sliderPosition[1] = 1;
38
+ setShowFilterButton(false);
39
+ });
40
+ }, [startTime, endTime]);
41
+
42
+ if (!data) {
43
+ return <PageLoading/>;
44
+ }
45
+
46
+ const applySliderDates = () => {
47
+ const [start, end] = sliderPosition;
48
+ const startDate = data?.[Math.floor(start * (data.length - 1))]?.startTime;
49
+ const endDate = data?.[Math.floor(end * (data.length - 1))]?.endTime;
50
+ onDateRangeChange(startDate, endDate);
51
+ }
52
+
53
+ const domain = ['Info', 'Warning', 'Error'];
54
+
55
+ const config: ColumnConfig = {
56
+ data,
57
+ xField: 'startTime',
58
+ yField: 'records',
59
+ colorField: 'logLevel',
60
+ stack: true,
61
+ height: 300,
62
+ theme: navTheme === 'realDark' ? 'dark' : 'light',
63
+ legend: {},
64
+ // slider: {
65
+ // x: {
66
+ // values: sliderPosition,
67
+ // onChange(values: [number, number]) {
68
+ // sliderPosition[0] = values[0];
69
+ // sliderPosition[1] = values[1];
70
+ // if (!showFilterButton) {
71
+ // setShowFilterButton(true);
72
+ // }
73
+ // }
74
+ // },
75
+ // },
76
+ axis: {
77
+ x: {
78
+ labelFormatter: (v: string, i: number) => {
79
+ return data[i * domain.length]?.time
80
+ },
81
+ },
82
+ y: {
83
+ gridLineWidth: 1,
84
+ }
85
+ },
86
+ scale: {
87
+ color: {
88
+ domain,
89
+ range: ['#1890ff', 'orange', 'red'],
90
+ },
91
+ },
92
+ onReady: ({chart}) => {
93
+ chart.on('interval:click', (event: any) => {
94
+ const {data} = event?.data || {};
95
+ onDateRangeChange(data?.startTime, data?.endTime);
96
+ });
97
+ }
98
+ };
99
+
100
+ return <>
101
+ <Column
102
+ {...config}
103
+ ref={chartRef}
104
+ />
105
+ {
106
+ showFilterButton && <Button onClick={applySliderDates}>Filter dates</Button> || null
107
+ }
108
+ </>
109
+ }
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import Bowser from 'bowser';
3
+ import { getBrowserIcon } from './getBrowserIcon';
4
+
5
+ type UserAgentProps = {
6
+ userAgent: string;
7
+ };
8
+
9
+ const UserAgentDisplay: React.FC<UserAgentProps> = ({ userAgent }) => {
10
+ const browserIconClass = getBrowserIcon(userAgent);
11
+ const browser = Bowser.getParser(userAgent).getBrowser();
12
+
13
+ return (
14
+ <span>
15
+ <i className={browserIconClass} style={{ fontSize: '14px', marginRight: '4px' }}/>
16
+ <span>{`${browser.name} ${browser.version}`}</span>
17
+ </span>
18
+ );
19
+ };
20
+
21
+ export default UserAgentDisplay;