From 595cde154377e502fa58ca30a86bc4ea9d6571a3 Mon Sep 17 00:00:00 2001 From: Valentin Kirilov Date: Fri, 7 Nov 2025 10:08:57 +0200 Subject: [PATCH 1/9] feat(ui): update the layout of the slow log page - update the welcome screen - update the placement of the action buttons re #RI-7437 --- .../src/pages/slow-log/SlowLogPage.styles.ts | 6 + .../ui/src/pages/slow-log/SlowLogPage.tsx | 96 +++++----- .../components/Actions/Actions.styles.ts | 7 + .../slow-log/components/Actions/Actions.tsx | 27 ++- .../components/Actions/styles.module.scss | 20 -- .../EmptySlowLog/EmptySlowLog.styles.ts | 5 + .../components/EmptySlowLog/EmptySlowLog.tsx | 43 +++-- .../SlowLogConfig/SlowLogConfig.styles.ts | 14 ++ .../SlowLogConfig/SlowLogConfig.tsx | 177 +++++++++--------- .../SlowLogConfig/styles.module.scss | 112 ----------- 10 files changed, 206 insertions(+), 301 deletions(-) create mode 100644 redisinsight/ui/src/pages/slow-log/SlowLogPage.styles.ts create mode 100644 redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts create mode 100644 redisinsight/ui/src/pages/slow-log/components/EmptySlowLog/EmptySlowLog.styles.ts create mode 100644 redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.styles.ts delete mode 100644 redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/styles.module.scss diff --git a/redisinsight/ui/src/pages/slow-log/SlowLogPage.styles.ts b/redisinsight/ui/src/pages/slow-log/SlowLogPage.styles.ts new file mode 100644 index 0000000000..01c9be7811 --- /dev/null +++ b/redisinsight/ui/src/pages/slow-log/SlowLogPage.styles.ts @@ -0,0 +1,6 @@ +import styled from 'styled-components' +import { RiSelect } from 'uiSrc/components/base/forms/select/RiSelect' + +export const StyledSelect = styled(RiSelect)` + border: none; +` diff --git a/redisinsight/ui/src/pages/slow-log/SlowLogPage.tsx b/redisinsight/ui/src/pages/slow-log/SlowLogPage.tsx index b39621eb3a..49ec63e57e 100644 --- a/redisinsight/ui/src/pages/slow-log/SlowLogPage.tsx +++ b/redisinsight/ui/src/pages/slow-log/SlowLogPage.tsx @@ -32,17 +32,15 @@ import { import { AnalyticsViewTab } from 'uiSrc/slices/interfaces/analytics' import { FormatedDate } from 'uiSrc/components' -import { FlexItem, Row } from 'uiSrc/components/base/layout/flex' -import { Text } from 'uiSrc/components/base/text' -import { - defaultValueRender, - RiSelect, -} from 'uiSrc/components/base/forms/select/RiSelect' +import { Col, FlexItem, Row } from 'uiSrc/components/base/layout/flex' +import { Text, Title } from 'uiSrc/components/base/text' +import { defaultValueRender } from 'uiSrc/components/base/forms/select/RiSelect' import { SlowLog } from 'apiSrc/modules/slow-log/models' import { Actions, EmptySlowLog, SlowLogTable } from './components' import styles from './styles.module.scss' +import { StyledSelect } from './SlowLogPage.styles' const HIDE_TIMESTAMP_FROM_WIDTH = 850 const DEFAULT_COUNT_VALUE = '50' @@ -144,61 +142,78 @@ const SlowLogPage = () => { const isEmptySlowLog = !data.length return ( -
- - - - - - - {connectionType !== ConnectionType.Cluster && config && ( - - Execution time:{' '} - {numberWithSpaces( - convertNumberByUnits(slowlogLogSlowerThan, durationUnit), - )} -   - {durationUnit === DurationUnits.milliSeconds - ? DurationUnits.mSeconds - : DurationUnits.microSeconds} - , Max length: {numberWithSpaces(slowlogMaxLen)} - - )} - - - + {({ width }) => (
+ + + + + + + + {connectionType !== ConnectionType.Cluster && config && ( + + Execution time:{' '} + {numberWithSpaces( + convertNumberByUnits( + slowlogLogSlowerThan, + durationUnit, + ), + )} +   + {durationUnit === DurationUnits.milliSeconds + ? DurationUnits.mSeconds + : DurationUnits.microSeconds} + , Max length: {numberWithSpaces(slowlogMaxLen)} + + )} + + + + + + - + + Slow Log + + + + - + {connectionType === ConnectionType.Cluster ? 'Display per node:' : 'Display up to:'} - setCount(value)} - className={styles.countSelect} data-testid="count-select" /> {width > HIDE_TIMESTAMP_FROM_WIDTH && ( ({data.length} entries @@ -214,15 +229,6 @@ const SlowLogPage = () => { )} - - -
)} @@ -239,7 +245,7 @@ const SlowLogPage = () => { durationUnit={durationUnit} /> )} -
+ ) } diff --git a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts new file mode 100644 index 0000000000..ec6bf5081f --- /dev/null +++ b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts @@ -0,0 +1,7 @@ +import styled from 'styled-components' + +export const StyledInfoIcon = styled.span` + display: flex; + align-self: center; + cursor: pointer; +` diff --git a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx index 1a27d903bc..e2365db3c1 100644 --- a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react' import { useSelector } from 'react-redux' -import cx from 'classnames' import { useParams } from 'react-router-dom' import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances' import { DurationUnits } from 'uiSrc/constants' @@ -22,6 +21,7 @@ import { RiIcon } from 'uiSrc/components/base/icons/RiIcon' import SlowLogConfig from '../SlowLogConfig' import styles from './styles.module.scss' +import { StyledInfoIcon } from './Actions.styles' export interface Props { width: number @@ -133,8 +133,8 @@ const Actions = (props: Props) => { ) return ( - - + + { testid="slowlog" /> - + + {}} - panelClassName={cx('popover-without-top-tail', styles.configWrapper)} button={ showConfigPopover()} @@ -173,8 +174,9 @@ const Actions = (props: Props) => { /> + {!isEmptySlowLog && ( - + { )} - + + Slow Log is a list of slow operations for your Redis instance. @@ -218,12 +220,9 @@ const Actions = (props: Props) => { } > - + + + diff --git a/redisinsight/ui/src/pages/slow-log/components/Actions/styles.module.scss b/redisinsight/ui/src/pages/slow-log/components/Actions/styles.module.scss index 65c3be58f2..720f4dabb2 100644 --- a/redisinsight/ui/src/pages/slow-log/components/Actions/styles.module.scss +++ b/redisinsight/ui/src/pages/slow-log/components/Actions/styles.module.scss @@ -18,20 +18,6 @@ display: flex; align-items: center; justify-content: center; - - &:hover { - color: var(--iconsDefaultHoverColor); - } - - :global(.euiIcon) { - width: 18px; - height: 18px; - } - - .infoIcon { - width: 20px; - height: 20px; - } } } @@ -60,9 +46,3 @@ color: var(--htmlColor); } } - -.configWrapper { - padding: 24px !important; - box-shadow: 0px 3px 15px #00000099; - background-color: var(--euiColorLightestShade) !important; -} diff --git a/redisinsight/ui/src/pages/slow-log/components/EmptySlowLog/EmptySlowLog.styles.ts b/redisinsight/ui/src/pages/slow-log/components/EmptySlowLog/EmptySlowLog.styles.ts new file mode 100644 index 0000000000..181255de19 --- /dev/null +++ b/redisinsight/ui/src/pages/slow-log/components/EmptySlowLog/EmptySlowLog.styles.ts @@ -0,0 +1,5 @@ +import styled from 'styled-components' + +export const StyledImage = styled.img` + max-width: 120px; +` diff --git a/redisinsight/ui/src/pages/slow-log/components/EmptySlowLog/EmptySlowLog.tsx b/redisinsight/ui/src/pages/slow-log/components/EmptySlowLog/EmptySlowLog.tsx index 5b3818ca98..1ccdf2b4aa 100644 --- a/redisinsight/ui/src/pages/slow-log/components/EmptySlowLog/EmptySlowLog.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/EmptySlowLog/EmptySlowLog.tsx @@ -4,8 +4,10 @@ import { Title } from 'uiSrc/components/base/text/Title' import { convertNumberByUnits } from 'uiSrc/pages/slow-log/utils' import { numberWithSpaces } from 'uiSrc/utils/numbers' import { Text } from 'uiSrc/components/base/text' +import { Col } from 'uiSrc/components/base/layout/flex' +import NoQueryResultsIcon from 'uiSrc/assets/img/vector-search/no-query-results.svg' -import styles from '../styles.module.scss' +import { StyledImage } from './EmptySlowLog.styles' export interface Props { durationUnit: DurationUnits @@ -16,24 +18,27 @@ const EmptySlowLog = (props: Props) => { const { durationUnit, slowlogLogSlowerThan } = props return ( -
-
- - No Slow Logs found - - - Either no commands exceeding  - {numberWithSpaces( - convertNumberByUnits(slowlogLogSlowerThan, durationUnit), - )} -   - {durationUnit === DurationUnits.milliSeconds - ? DurationUnits.mSeconds - : DurationUnits.microSeconds} -  were found or Slow Log is disabled on the server. - -
-
+ + + + + + No Slow Logs found + + + Either no commands exceeding  + {numberWithSpaces( + convertNumberByUnits(slowlogLogSlowerThan, durationUnit), + )} +   + {durationUnit === DurationUnits.milliSeconds + ? DurationUnits.mSeconds + : DurationUnits.microSeconds} +  were found or Slow Log is disabled on the server. + + + + ) } diff --git a/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.styles.ts b/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.styles.ts new file mode 100644 index 0000000000..d407763fb1 --- /dev/null +++ b/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.styles.ts @@ -0,0 +1,14 @@ +import styled from 'styled-components' +import { Col } from 'uiSrc/components/base/layout/flex' +import { TextInput } from 'uiSrc/components/base/inputs' +import { theme } from '@redis-ui/styles' + +export const StyledContainer = styled(Col)<{ $isCluster?: boolean }>` + width: ${({ $isCluster }) => ($isCluster ? '394px' : '550px')}; + padding: ${theme.core.space.space200}; + border-radius: 4px; +` + +export const StyledInput = styled(TextInput)` + width: 160px; +` diff --git a/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.tsx b/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.tsx index abafa624e7..ab09c74192 100644 --- a/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.tsx @@ -2,7 +2,6 @@ import { toNumber } from 'lodash' import React, { useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { useParams } from 'react-router-dom' -import cx from 'classnames' import { DEFAULT_SLOWLOG_DURATION_UNIT, DEFAULT_SLOWLOG_MAX_LEN, @@ -34,9 +33,8 @@ import { defaultValueRender, RiSelect, } from 'uiSrc/components/base/forms/select/RiSelect' -import { TextInput } from 'uiSrc/components/base/inputs' import { convertNumberByUnits } from '../../utils' -import styles from './styles.module.scss' +import { StyledContainer, StyledInput } from './SlowLogConfig.styles' export interface Props { closePopover: () => void @@ -121,7 +119,7 @@ const SlowLogConfig = ({ closePopover, onRefresh }: Props) => { const clusterContent = () => ( <> - + Each node can have different Slow Log configuration in a clustered database. @@ -131,15 +129,14 @@ const SlowLogConfig = ({ closePopover, onRefresh }: Props) => { CONFIG SET slowlog-max-len {' for a specific node in redis-cli to configure it.'} - - - - Ok - + + + Ok + + ) @@ -167,101 +164,99 @@ const SlowLogConfig = ({ closePopover, onRefresh }: Props) => { } return ( - {connectionType === ConnectionType.Cluster && clusterContent()} {connectionType !== ConnectionType.Cluster && ( <>
- slowlog-log-slower-than - } - additionalText={ -
-
{unitConverter()}
-
- Execution time to exceed in order to log the command. -
- -1 disables Slow Log. 0 logs each command. -
-
- } - > - + slowlog-log-slower-than} + additionalText={ + + + {unitConverter()} + + + Execution time to exceed in order to log the command. +
+ -1 disables Slow Log. 0 logs each command. +
+
+ } > -
- + { - setSlowerThan( - validateNumber(value.trim(), -1, Infinity), - ) + onChange={(value) => { + setSlowerThan(validateNumber(value.trim(), -1, Infinity)) }} placeholder={`${convertNumberByUnits(DEFAULT_SLOWLOG_SLOWER_THAN, durationUnit)}`} autoComplete="off" data-testid="slower-than-input" /> -
- -
-
- slowlog-max-len} - additionalText={ -
- The length of the Slow Log. When a new command is logged the - oldest -
- one is removed from the queue of logged commands. -
- } - > - <> -
- { - setMaxLen(validateNumber(value.trim())) - }} - autoComplete="off" - data-testid="max-len-input" + -
- -
- + + + slowlog-max-len} + additionalText={ + + The length of the Slow Log. When a new command is logged the + oldest +
+ one is removed from the queue of logged commands. +
+ } + > + <> +
+ { + setMaxLen(validateNumber(value.trim())) + }} + autoComplete="off" + data-testid="max-len-input" + /> +
+ +
+ - - - NOTE: This is server configuration + + + + NOTE: This is server configuration + - + { )} - + ) } diff --git a/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/styles.module.scss b/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/styles.module.scss deleted file mode 100644 index 829dae177b..0000000000 --- a/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/styles.module.scss +++ /dev/null @@ -1,112 +0,0 @@ -.container { - width: 550px; - height: 280px; - background-color: var(--euiColorLightestShade); - border-radius: 4px; -} - -.containerCluster { - width: 394px; - height: 150px; -} - -.selectWrapper { - display: inline-block !important; - width: 60px !important; - margin-top: -4px; - - :global(.euiFormControlLayout__childrenWrapper) { - height: 42px !important; - width: 74px; - } -} - -.input { - width: 120px !important; -} - -.formRow { - width: 600px !important; - max-width: 600px !important; - padding-bottom: 2px; -} - -.rowFields { - width: 400px; -} - -.rowLabel { - min-width: 156px; - font-size: 13px; -} - -.helpText { - display: inline-block; - font-size: 12px; - line-height: 18px; - letter-spacing: -0.12px; - color: var(--euiColorMediumShade); - padding-top: 6px; - - div { - display: inline-block; - padding-bottom: 6px; - width: 100%; - } -} - -.footer { - padding-top: 6px; - - .helpText { - padding-top: 12px; - } -} - -.actions { - display: inline-block; - right: 24px; - position: absolute; - - :global { - .euiButton, - .euiButtonEmpty { - margin-right: 12px !important; - width: 100px; - height: 42px; - - .euiButtonEmpty__text { - color: var(--euiColorFullShade) !important; - } - } - } -} - -.clusterText :global(.euiTextColor) { - font-size: 13px !important; - line-height: 18px !important; - letter-spacing: -0.13px !important; - - a { - color: var(--euiColorFullShade) !important; - } - - code { - font-size: 13px; - line-height: 18px; - padding-left: 4px; - padding-right: 4px; - color: var(--inputTextColor); - - &:last-of-type { - padding-left: 0 !important; - } - } -} - -.clusterBtn { - margin-top: 8px; - position: absolute; - right: 24px; - bottom: 18px; -} From 497fb8fca659ccfc22e3aeb198617644f06b345b Mon Sep 17 00:00:00 2001 From: Valentin Kirilov Date: Fri, 7 Nov 2025 10:20:16 +0200 Subject: [PATCH 2/9] feat(ui): update the slow log table - replace the virtual table with Redis UI re #RI-7437 --- .../SlowLogTable/SlowLogTable.styles.ts | 11 ++ .../components/SlowLogTable/SlowLogTable.tsx | 138 ++++++++---------- 2 files changed, 72 insertions(+), 77 deletions(-) create mode 100644 redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.styles.ts diff --git a/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.styles.ts b/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.styles.ts new file mode 100644 index 0000000000..f582295568 --- /dev/null +++ b/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.styles.ts @@ -0,0 +1,11 @@ +import { Table } from '@redis-ui/table' +import styled from 'styled-components' +import { Col } from 'uiSrc/components/base/layout/flex' + +export const StyledTableWrapper = styled(Col)` + height: calc(100% - 100px); +` + +export const StyledTable = styled(Table)` + max-height: 100%; +` diff --git a/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.tsx b/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.tsx index 22bda3f182..ff5e3201f2 100644 --- a/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.tsx @@ -1,102 +1,88 @@ -import React, { useEffect, useState } from 'react' +import React from 'react' import { useParams } from 'react-router-dom' -import { ITableColumn } from 'uiSrc/components/virtual-table/interfaces' -import VirtualTable from 'uiSrc/components/virtual-table/VirtualTable' -import { - DURATION_UNITS, - DurationUnits, - SortOrder, - TableCellAlignment, - TableCellTextAlignment, -} from 'uiSrc/constants' +import { DURATION_UNITS, DurationUnits, SortOrder } from 'uiSrc/constants' import { convertNumberByUnits } from 'uiSrc/pages/slow-log/utils' import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry' import { numberWithSpaces } from 'uiSrc/utils/numbers' import { Text } from 'uiSrc/components/base/text' +import { + Table, + ColumnDef, + SortingState, +} from 'uiSrc/components/base/layout/table' import { FormatedDate, RiTooltip } from 'uiSrc/components' import styles from '../styles.module.scss' +import { SlowLog } from 'apiSrc/modules/slow-log/models' +import { StyledTableWrapper } from './SlowLogTable.styles' + export const DATE_FORMAT = 'HH:mm:ss d LLL yyyy' export interface Props { - items: any + items: SlowLog[] loading: boolean durationUnit: DurationUnits } -const sortByTimeStamp = (items = [], order: SortOrder) => - [...items].sort((a: any, b: any) => - order === SortOrder.DESC ? b.time - a.time : a.time - b.time, - ) - const SlowLogTable = (props: Props) => { - const { items = [], loading = false, durationUnit } = props - const [table, setTable] = useState([]) - const [sortedColumnName, setSortedColumnName] = useState('time') - const [sortedColumnOrder, setSortedColumnOrder] = useState(SortOrder.DESC) + const { items = [], durationUnit } = props const { instanceId } = useParams<{ instanceId: string }>() - const sortedColumn = { - column: sortedColumnName, - order: sortedColumnOrder, - } - - useEffect(() => { - setTable(sortByTimeStamp(items, sortedColumnOrder)) - }, [items, sortedColumnOrder]) - const columns: ITableColumn[] = [ + const columns: ColumnDef[] = [ { id: 'time', - label: 'Timestamp', - absoluteWidth: 190, - minWidth: 190, - isSortable: true, - render: (timestamp) => ( - - - - ), + header: 'Timestamp', + accessorKey: 'time', + size: 15, + cell: ({ getValue }) => { + const date = (getValue() as number) * 1000 + + return + }, }, { id: 'durationUs', - label: `Duration, ${DURATION_UNITS.find(({ value }) => value === durationUnit)?.inputDisplay}`, - minWidth: 110, - absoluteWidth: 'auto', - textAlignment: TableCellTextAlignment.Right, - alignment: TableCellAlignment.Right, - render: (duration) => ( - - {numberWithSpaces(convertNumberByUnits(duration, durationUnit))} - - ), + header: `Duration, ${DURATION_UNITS.find(({ value }) => value === durationUnit)?.inputDisplay}`, + accessorKey: 'durationUs', + size: 15, + cell: ({ getValue }) => { + const duration = getValue() as number + + return ( + + {numberWithSpaces(convertNumberByUnits(duration, durationUnit))} + + ) + }, }, { id: 'args', - label: 'Command', - render: (command) => ( - - - {command} - - - ), + header: 'Command', + accessorKey: 'args', + cell: ({ getValue }) => { + const command = getValue() as string + + return ( + + + {command} + + + ) + }, }, ] - const onChangeSorting = (column: any, order: SortOrder) => { - setSortedColumnName(column) - setSortedColumnOrder(order) + const handleSortingChange = (state: SortingState) => { + const { desc } = state[0] || { desc: true } + const order = desc ? SortOrder.DESC : SortOrder.ASC + sendEventTelemetry({ event: TelemetryEvent.SLOWLOG_SORTED, eventData: { @@ -107,17 +93,15 @@ const SlowLogTable = (props: Props) => { } return ( -
- + - + ) } From d448f27531f8ecb8a14d95cc0633fe47970035bb Mon Sep 17 00:00:00 2001 From: Valentin Kirilov Date: Fri, 7 Nov 2025 14:47:59 +0200 Subject: [PATCH 3/9] feat(ui): update the visual of the clear slow log popover re #RI-7437 --- .../components/Actions/Actions.styles.ts | 4 ++ .../slow-log/components/Actions/Actions.tsx | 70 +++++++++---------- .../components/Actions/styles.module.scss | 48 ------------- 3 files changed, 36 insertions(+), 86 deletions(-) delete mode 100644 redisinsight/ui/src/pages/slow-log/components/Actions/styles.module.scss diff --git a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts index ec6bf5081f..777d747f15 100644 --- a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts +++ b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts @@ -5,3 +5,7 @@ export const StyledInfoIcon = styled.span` align-self: center; cursor: pointer; ` + +export const StyledDatabaseName = styled.span` + color: ${({ theme }) => theme.semantic.color.text.primary500}; +` diff --git a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx index e2365db3c1..3d2941467d 100644 --- a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx @@ -8,7 +8,12 @@ import { AutoRefresh } from 'uiSrc/components' import { RiPopover, RiTooltip } from 'uiSrc/components/base' import { Nullable } from 'uiSrc/utils' import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry' -import { FlexItem, Row } from 'uiSrc/components/base/layout/flex' +import { + Col, + FlexGroup, + FlexItem, + Row, +} from 'uiSrc/components/base/layout/flex' import { Spacer } from 'uiSrc/components/base/layout/spacer' import { EraserIcon, SettingsIcon } from 'uiSrc/components/base/icons' import { @@ -16,12 +21,11 @@ import { IconButton, SecondaryButton, } from 'uiSrc/components/base/forms/buttons' -import { Text } from 'uiSrc/components/base/text' +import { Text, Title } from 'uiSrc/components/base/text' import { RiIcon } from 'uiSrc/components/base/icons/RiIcon' import SlowLogConfig from '../SlowLogConfig' -import styles from './styles.module.scss' -import { StyledInfoIcon } from './Actions.styles' +import { StyledDatabaseName, StyledInfoIcon } from './Actions.styles' export interface Props { width: number @@ -99,37 +103,32 @@ const Actions = (props: Props) => { } const ToolTipContent = ( -
- -
- -

- Clear Slow Log? -

- + +
+ + Clear Slow Log? + + + Slow Log will be cleared for  - {name} -
+ {name} +
+ NOTE: This is server configuration - -
- handleClearClick()} - className={styles.popoverDeleteBtn} - data-testid="reset-confirm-btn" - > - Clear - -
- - + + + + handleClearClick()} + data-testid="reset-confirm-btn" + > + Clear + + + ) return ( @@ -140,7 +139,6 @@ const Actions = (props: Props) => { loading={loading} displayText={width > HIDE_REFRESH_LABEL_WIDTH} lastRefreshTime={lastRefreshTime} - containerClassName={styles.refreshContainer} onRefresh={() => onRefresh()} onEnableAutoRefresh={handleEnableAutoRefresh} onChangeAutoRefreshRate={handleChangeAutoRefreshRate} @@ -184,11 +182,7 @@ const Actions = (props: Props) => { closePopover={closePopoverClear} panelPaddingSize="m" button={ - + Date: Fri, 7 Nov 2025 15:05:39 +0200 Subject: [PATCH 4/9] test(ui): update slow log tests to use new redis ui table re #RI-7437 --- .../components/SlowLogTable/SlowLogTable.spec.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.spec.tsx b/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.spec.tsx index f78d09b476..2d908abfb1 100644 --- a/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.spec.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.spec.tsx @@ -31,9 +31,13 @@ describe('SlowLogTable', () => { }) it('should render data', () => { - expect( - render(), - ).toBeTruthy() - expect(screen.getAllByLabelText(/^row$/)).toHaveLength(mockedData.length) + const { container } = render( + , + ) + + expect(container).toBeTruthy() + + const rows = container.querySelectorAll('[data-row-type="regular"]') + expect(rows).toHaveLength(mockedData.length) }) }) From 2ffa2ce516d78dc7bd36d62aed803dc3f2bd461f Mon Sep 17 00:00:00 2001 From: Valentin Kirilov Date: Fri, 7 Nov 2025 15:21:07 +0200 Subject: [PATCH 5/9] refactor(ui): cleanup unnecessary styles re #RI-7437 --- .../components/SlowLogTable/SlowLogTable.tsx | 11 +--- .../slow-log/components/styles.module.scss | 65 ------------------- 2 files changed, 2 insertions(+), 74 deletions(-) delete mode 100644 redisinsight/ui/src/pages/slow-log/components/styles.module.scss diff --git a/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.tsx b/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.tsx index ff5e3201f2..553bb09fcc 100644 --- a/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/SlowLogTable/SlowLogTable.tsx @@ -12,7 +12,6 @@ import { } from 'uiSrc/components/base/layout/table' import { FormatedDate, RiTooltip } from 'uiSrc/components' -import styles from '../styles.module.scss' import { SlowLog } from 'apiSrc/modules/slow-log/models' import { StyledTableWrapper } from './SlowLogTable.styles' @@ -65,14 +64,8 @@ const SlowLogTable = (props: Props) => { const command = getValue() as string return ( - - - {command} - + + {command} ) }, diff --git a/redisinsight/ui/src/pages/slow-log/components/styles.module.scss b/redisinsight/ui/src/pages/slow-log/components/styles.module.scss deleted file mode 100644 index d2861b0dbc..0000000000 --- a/redisinsight/ui/src/pages/slow-log/components/styles.module.scss +++ /dev/null @@ -1,65 +0,0 @@ -.tableWrapper { - flex-grow: 1; - height: calc(100% - 100px); - - @media screen and (max-width: 1024px) { - height: calc(100% - 120px); - } - - :global { - .ReactVirtualized__Table__row { - border: 1px solid var(--tableDarkestBorderColor) !important; - - &:first-of-type { - border-top: 0 !important; - } - - &:not(:last-of-type) { - border-bottom: 0 !important; - } - } - - .ReactVirtualized__Table__rowColumn { - &:not(:last-of-type) { - border-right: 1px solid var(--tableDarkestBorderColor) !important; - } - } - } - - .commandTooltip { - max-width: 100%; - } - - .commandText { - display: block; - text-overflow: ellipsis; - max-width: 100%; - white-space: nowrap; - overflow: hidden; - line-height: 1.4; - } -} - -.noSlowLogWrapper { - border: 1px solid var(--tableDarkestBorderColor); - min-height: 152px; - width: 100%; - display: flex; - align-items: center; - padding: 18px; - - .noFoundTitle { - font: normal normal 500 18px/24px Graphik, sans-serif; - margin-bottom: 12px; - } - - .noSlowLogText { - margin: 0 auto; - max-width: 882px; - width: 100%; - } -} - -.timestampCell { - overflow: hidden; -} From 835799db8bf6e5fede422baa3b6257b646c53534 Mon Sep 17 00:00:00 2001 From: Valentin Kirilov Date: Mon, 10 Nov 2025 12:49:58 +0200 Subject: [PATCH 6/9] feat(ui): replace "clear slo log" popover with a modal re #RI-7437 --- .../components/Actions/Actions.styles.ts | 4 - .../slow-log/components/Actions/Actions.tsx | 80 ++++------------- .../ClearSlowLogModal.spec.tsx | 86 +++++++++++++++++++ .../ClearSlowLogModal.styles.ts | 7 ++ .../ClearSlowLogModal/ClearSlowLogModal.tsx | 69 +++++++++++++++ 5 files changed, 179 insertions(+), 67 deletions(-) create mode 100644 redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.spec.tsx create mode 100644 redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.styles.ts create mode 100644 redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.tsx diff --git a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts index 777d747f15..ec6bf5081f 100644 --- a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts +++ b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts @@ -5,7 +5,3 @@ export const StyledInfoIcon = styled.span` align-self: center; cursor: pointer; ` - -export const StyledDatabaseName = styled.span` - color: ${({ theme }) => theme.semantic.color.text.primary500}; -` diff --git a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx index 3d2941467d..3977a5d5a6 100644 --- a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx @@ -8,24 +8,18 @@ import { AutoRefresh } from 'uiSrc/components' import { RiPopover, RiTooltip } from 'uiSrc/components/base' import { Nullable } from 'uiSrc/utils' import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry' -import { - Col, - FlexGroup, - FlexItem, - Row, -} from 'uiSrc/components/base/layout/flex' +import { FlexItem, Row } from 'uiSrc/components/base/layout/flex' import { Spacer } from 'uiSrc/components/base/layout/spacer' import { EraserIcon, SettingsIcon } from 'uiSrc/components/base/icons' import { - DestructiveButton, IconButton, SecondaryButton, } from 'uiSrc/components/base/forms/buttons' -import { Text, Title } from 'uiSrc/components/base/text' import { RiIcon } from 'uiSrc/components/base/icons/RiIcon' import SlowLogConfig from '../SlowLogConfig' -import { StyledDatabaseName, StyledInfoIcon } from './Actions.styles' +import { StyledInfoIcon } from './Actions.styles' +import { ClearSlowLogModal } from '../ClearSlowLogModal/ClearSlowLogModal' export interface Props { width: number @@ -67,11 +61,6 @@ const Actions = (props: Props) => { setIsPopoverConfigOpen(false) } - const handleClearClick = () => { - onClear() - closePopoverClear() - } - const handleEnableAutoRefresh = ( enableAutoRefresh: boolean, refreshRate: string, @@ -102,35 +91,6 @@ const Actions = (props: Props) => { } } - const ToolTipContent = ( - - - - Clear Slow Log? - - - - Slow Log will be cleared for  - {name} - - - NOTE: This is server configuration - - - - - handleClearClick()} - data-testid="reset-confirm-btn" - > - Clear - - - - ) - return ( @@ -174,27 +134,21 @@ const Actions = (props: Props) => { {!isEmptySlowLog && ( - - + showClearPopover()} + data-testid="clear-btn" + /> + + - showClearPopover()} - data-testid="clear-btn" - /> - - } - > - {ToolTipContent} - - + onClose={closePopoverClear} + onClear={onClear} + /> + )} diff --git a/redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.spec.tsx b/redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.spec.tsx new file mode 100644 index 0000000000..f01857e7f7 --- /dev/null +++ b/redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.spec.tsx @@ -0,0 +1,86 @@ +import React from 'react' + +import { ClearSlowLogModal, ClearSlowLogModalProps } from './ClearSlowLogModal' +import { render, screen, fireEvent } from 'uiSrc/utils/test-utils' + +const renderClearSlowLogModal = (props?: Partial) => { + const defaultProps: ClearSlowLogModalProps = { + name: 'test-database', + isOpen: true, + onClose: jest.fn(), + onClear: jest.fn(), + } + + return render() +} + +describe('ClearSlowLogModal', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should render the modal when isOpen is true', () => { + const props: Partial = { + name: 'test-database', + isOpen: true, + } + + renderClearSlowLogModal(props) + + const title = screen.getByText('Clear slow log') + expect(title).toBeInTheDocument() + + const message = screen.getByText( + /Slow Log will be cleared for\s+test-database/, + ) + expect(message).toBeInTheDocument() + + const note = screen.getByText('NOTE: This is server configuration') + expect(note).toBeInTheDocument() + + const cancelButton = screen.getByTestId('reset-cancel-btn') + expect(cancelButton).toBeInTheDocument() + expect(cancelButton).toHaveTextContent('Cancel') + + const clearButton = screen.getByTestId('reset-confirm-btn') + expect(clearButton).toBeInTheDocument() + expect(clearButton).toHaveTextContent('Clear') + }) + + it('should not render the modal when isOpen is false', () => { + renderClearSlowLogModal({ isOpen: false }) + + const modal = screen.queryByTestId('clear-slow-log-modal') + expect(modal).not.toBeInTheDocument() + }) + + it('should call onClose when Cancel button is clicked', () => { + const props: Partial = { + onClose: jest.fn(), + onClear: jest.fn(), + } + + renderClearSlowLogModal(props) + + const cancelButton = screen.getByTestId('reset-cancel-btn') + fireEvent.click(cancelButton) + + expect(props.onClose).toHaveBeenCalledTimes(1) + expect(props.onClear).not.toHaveBeenCalled() + }) + + it('should call onClear and onClose when Clear button is clicked', () => { + const props: Partial = { + onClose: jest.fn(), + onClear: jest.fn(), + } + + renderClearSlowLogModal(props) + + const clearButton = screen.getByTestId('reset-confirm-btn') + fireEvent.click(clearButton) + + expect(props.onClear).toHaveBeenCalledTimes(1) + expect(props.onClose).toHaveBeenCalledTimes(1) + }) +}) diff --git a/redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.styles.ts b/redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.styles.ts new file mode 100644 index 0000000000..fada3bc1f0 --- /dev/null +++ b/redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.styles.ts @@ -0,0 +1,7 @@ +import styled from 'styled-components' +import FormDialog from 'uiSrc/components/form-dialog/FormDialog' + +export const StyledFormDialog = styled(FormDialog)` + width: 402px; + height: auto; +` diff --git a/redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.tsx b/redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.tsx new file mode 100644 index 0000000000..5318bde055 --- /dev/null +++ b/redisinsight/ui/src/pages/slow-log/components/ClearSlowLogModal/ClearSlowLogModal.tsx @@ -0,0 +1,69 @@ +import React from 'react' + +import { Button, DestructiveButton } from 'uiSrc/components/base/forms/buttons' +import { Col, FlexGroup, Row } from 'uiSrc/components/base/layout/flex' +import { Title, Text } from 'uiSrc/components/base/text' +import { EraserIcon } from 'uiSrc/components/base/icons' +import { Spacer } from 'uiSrc/components/base/layout' + +import { StyledFormDialog } from './ClearSlowLogModal.styles' + +export interface ClearSlowLogModalProps { + name: string + isOpen: boolean + onClose: () => void + onClear: () => void +} + +export const ClearSlowLogModal = ({ + name, + isOpen, + onClose, + onClear, +}: ClearSlowLogModalProps) => { + const handleClearClick = () => { + onClear() + onClose() + } + + return ( + Clear slow log} + footer={ + + + handleClearClick()} + data-testid="reset-confirm-btn" + > + Clear + + + } + > + + + + + Slow Log will be cleared for {name} + + + NOTE: This is server configuration + + + + + ) +} From 9149c63700a58800c98e05485cfa03e4c194c3ae Mon Sep 17 00:00:00 2001 From: Valentin Kirilov Date: Mon, 10 Nov 2025 12:52:22 +0200 Subject: [PATCH 7/9] refactor(ui): update state names to reflect the modal change re #RI-7437 --- .../slow-log/components/Actions/Actions.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx index 3977a5d5a6..2c194c1e34 100644 --- a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx @@ -43,15 +43,15 @@ const Actions = (props: Props) => { const { name = '' } = useSelector(connectedInstanceSelector) const { loading, lastRefreshTime } = useSelector(slowLogSelector) - const [isPopoverClearOpen, setIsPopoverClearOpen] = useState(false) + const [isClearModalOpen, setIsClearModalOpen] = useState(false) const [isPopoverConfigOpen, setIsPopoverConfigOpen] = useState(false) - const showClearPopover = () => { - setIsPopoverClearOpen((isPopoverClearOpen) => !isPopoverClearOpen) + const showClearModal = () => { + setIsClearModalOpen((isClearModalOpen) => !isClearModalOpen) } - const closePopoverClear = () => { - setIsPopoverClearOpen(false) + const closeClearModal = () => { + setIsClearModalOpen(false) } const showConfigPopover = () => { setIsPopoverConfigOpen((isPopoverConfigOpen) => !isPopoverConfigOpen) @@ -138,14 +138,14 @@ const Actions = (props: Props) => { showClearPopover()} + onClick={() => showClearModal()} data-testid="clear-btn" /> From fe750a2b9479d96ab22de0ade82daff6a4d37678 Mon Sep 17 00:00:00 2001 From: Valentin Kirilov Date: Mon, 10 Nov 2025 13:24:49 +0200 Subject: [PATCH 8/9] refactor(ui): apply comments imporvements re #RI-7437 --- .../src/pages/slow-log/components/Actions/Actions.styles.ts | 2 +- .../ui/src/pages/slow-log/components/Actions/Actions.tsx | 6 +++--- .../components/SlowLogConfig/SlowLogConfig.styles.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts index ec6bf5081f..180b106bd4 100644 --- a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts +++ b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.styles.ts @@ -1,6 +1,6 @@ import styled from 'styled-components' -export const StyledInfoIcon = styled.span` +export const StyledInfoIconWrapper = styled.span` display: flex; align-self: center; cursor: pointer; diff --git a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx index 2c194c1e34..fa9eb7e63b 100644 --- a/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/Actions/Actions.tsx @@ -18,7 +18,7 @@ import { import { RiIcon } from 'uiSrc/components/base/icons/RiIcon' import SlowLogConfig from '../SlowLogConfig' -import { StyledInfoIcon } from './Actions.styles' +import { StyledInfoIconWrapper } from './Actions.styles' import { ClearSlowLogModal } from '../ClearSlowLogModal/ClearSlowLogModal' export interface Props { @@ -168,9 +168,9 @@ const Actions = (props: Props) => { } > - + - + diff --git a/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.styles.ts b/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.styles.ts index d407763fb1..6079ae5133 100644 --- a/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.styles.ts +++ b/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.styles.ts @@ -6,7 +6,7 @@ import { theme } from '@redis-ui/styles' export const StyledContainer = styled(Col)<{ $isCluster?: boolean }>` width: ${({ $isCluster }) => ($isCluster ? '394px' : '550px')}; padding: ${theme.core.space.space200}; - border-radius: 4px; + border-radius: ${theme.core.space.space050}; ` export const StyledInput = styled(TextInput)` From 315224d12bcb5dee42214179b9eaf4fd845c5ea6 Mon Sep 17 00:00:00 2001 From: Valentin Kirilov Date: Mon, 10 Nov 2025 13:29:16 +0200 Subject: [PATCH 9/9] refactor(ui): cleanup unnecessary markup --- .../SlowLogConfig/SlowLogConfig.tsx | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.tsx b/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.tsx index ab09c74192..c50ad848ae 100644 --- a/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.tsx +++ b/redisinsight/ui/src/pages/slow-log/components/SlowLogConfig/SlowLogConfig.tsx @@ -231,21 +231,17 @@ const SlowLogConfig = ({ closePopover, onRefresh }: Props) => { } > - <> -
- { - setMaxLen(validateNumber(value.trim())) - }} - autoComplete="off" - data-testid="max-len-input" - /> -
- + { + setMaxLen(validateNumber(value.trim())) + }} + autoComplete="off" + data-testid="max-len-input" + />