Skip to content

Commit

Permalink
feat: add developer ui links to disks popups (#1512)
Browse files Browse the repository at this point in the history
  • Loading branch information
artemmufazalov authored Oct 22, 2024
1 parent b537f54 commit 57d2092
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 31 deletions.
15 changes: 9 additions & 6 deletions src/components/HoverPopup/HoverPopup.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';

import type {PopupProps} from '@gravity-ui/uikit';
import {Popup} from '@gravity-ui/uikit';
import debounce from 'lodash/debounce';

Expand All @@ -11,15 +12,15 @@ const b = cn('hover-popup');

const DEBOUNCE_TIMEOUT = 100;

interface HoverPopupProps {
type HoverPopupProps = {
children: React.ReactNode;
popupContent: React.ReactNode;
showPopup?: boolean;
offset?: [number, number];
anchorRef?: React.RefObject<HTMLElement>;
onShowPopup?: VoidFunction;
onHidePopup?: VoidFunction;
}
} & Pick<PopupProps, 'placement' | 'contentClassName'>;

export const HoverPopup = ({
children,
Expand All @@ -29,6 +30,8 @@ export const HoverPopup = ({
anchorRef,
onShowPopup,
onHidePopup,
placement = ['top', 'bottom'],
contentClassName,
}: HoverPopupProps) => {
const [isPopupVisible, setIsPopupVisible] = React.useState(false);
const anchor = React.useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -88,18 +91,18 @@ export const HoverPopup = ({

return (
<React.Fragment>
<div ref={anchor} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
<span ref={anchor} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
{children}
</div>
</span>
<Popup
contentClassName={b()}
contentClassName={b(null, contentClassName)}
anchorRef={anchorRef || anchor}
open={open}
onMouseEnter={onPopupMouseEnter}
onMouseLeave={onPopupMouseLeave}
onEscapeKeyDown={onPopupEscapeKeyDown}
onBlur={onPopupBlur}
placement={['top', 'bottom']}
placement={placement}
hasArrow
// bigger offset for easier switching to neighbour nodes
// matches the default offset for popup with arrow out of a sense of beauty
Expand Down
27 changes: 25 additions & 2 deletions src/components/PDiskPopup/PDiskPopup.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import React from 'react';

import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {selectNodeHostsMap} from '../../store/reducers/nodesList';
import {EFlag} from '../../types/api/enums';
import {valueIsDefined} from '../../utils';
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
import {createPDiskDeveloperUILink} from '../../utils/developerUI/developerUI';
import {getPDiskId} from '../../utils/disks/helpers';
import type {PreparedPDisk} from '../../utils/disks/types';
import {useTypedSelector} from '../../utils/hooks';
import {bytesToGB} from '../../utils/utils';
import {InfoViewer} from '../InfoViewer';
import type {InfoViewerItem} from '../InfoViewer';
import {LinkWithIcon} from '../LinkWithIcon/LinkWithIcon';

const errorColors = [EFlag.Orange, EFlag.Red, EFlag.Yellow];

export const preparePDiskData = (data: PreparedPDisk, nodeHost?: string) => {
export const preparePDiskData = (
data: PreparedPDisk,
nodeHost?: string,
withDeveloperUILink?: boolean,
) => {
const {AvailableSize, TotalSize, State, PDiskId, NodeId, Path, Realtime, Type, Device} = data;

const pdiskData: InfoViewerItem[] = [
Expand Down Expand Up @@ -50,6 +57,18 @@ export const preparePDiskData = (data: PreparedPDisk, nodeHost?: string) => {
pdiskData.push({label: 'Device', value: Device});
}

if (withDeveloperUILink && valueIsDefined(NodeId) && valueIsDefined(PDiskId)) {
const pDiskInternalViewerPath = createPDiskDeveloperUILink({
nodeId: NodeId,
pDiskId: PDiskId,
});

pdiskData.push({
label: 'Links',
value: <LinkWithIcon title={'Developer UI'} url={pDiskInternalViewerPath} />,
});
}

return pdiskData;
};

Expand All @@ -58,9 +77,13 @@ interface PDiskPopupProps {
}

export const PDiskPopup = ({data}: PDiskPopupProps) => {
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);
const nodeHostsMap = useTypedSelector(selectNodeHostsMap);
const nodeHost = valueIsDefined(data.NodeId) ? nodeHostsMap?.get(data.NodeId) : undefined;
const info = React.useMemo(() => preparePDiskData(data, nodeHost), [data, nodeHost]);
const info = React.useMemo(
() => preparePDiskData(data, nodeHost, isUserAllowedToMakeChanges),
[data, nodeHost, isUserAllowedToMakeChanges],
);

return <InfoViewer title="PDisk" info={info} size="s" />;
};
63 changes: 57 additions & 6 deletions src/components/VDiskPopup/VDiskPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,30 @@ import React from 'react';

import {Label} from '@gravity-ui/uikit';

import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {selectNodeHostsMap} from '../../store/reducers/nodesList';
import {EFlag} from '../../types/api/enums';
import {valueIsDefined} from '../../utils';
import {cn} from '../../utils/cn';
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
import {stringifyVdiskId} from '../../utils/dataFormatters/dataFormatters';
import {createVDiskDeveloperUILink} from '../../utils/developerUI/developerUI';
import {isFullVDiskData} from '../../utils/disks/helpers';
import type {PreparedVDisk, UnavailableDonor} from '../../utils/disks/types';
import {useTypedSelector} from '../../utils/hooks';
import {bytesToGB, bytesToSpeed} from '../../utils/utils';
import type {InfoViewerItem} from '../InfoViewer';
import {InfoViewer} from '../InfoViewer';
import {InternalLink} from '../InternalLink';
import {LinkWithIcon} from '../LinkWithIcon/LinkWithIcon';
import {preparePDiskData} from '../PDiskPopup/PDiskPopup';
import {getVDiskLink} from '../VDisk/utils';

import './VDiskPopup.scss';

const b = cn('vdisk-storage-popup');

const prepareUnavailableVDiskData = (data: UnavailableDonor) => {
const prepareUnavailableVDiskData = (data: UnavailableDonor, withDeveloperUILink?: boolean) => {
const {NodeId, PDiskId, VSlotId, StoragePoolName} = data;

const vdiskData: InfoViewerItem[] = [{label: 'State', value: 'not available'}];
Expand All @@ -37,11 +40,33 @@ const prepareUnavailableVDiskData = (data: UnavailableDonor) => {
{label: 'VSlotId', value: VSlotId ?? EMPTY_DATA_PLACEHOLDER},
);

if (
withDeveloperUILink &&
valueIsDefined(NodeId) &&
valueIsDefined(PDiskId) &&
valueIsDefined(VSlotId)
) {
const vDiskInternalViewerPath = createVDiskDeveloperUILink({
nodeId: NodeId,
pDiskId: PDiskId,
vDiskSlotId: VSlotId,
});

vdiskData.push({
label: 'Links',
value: <LinkWithIcon title={'Developer UI'} url={vDiskInternalViewerPath} />,
});
}

return vdiskData;
};

const prepareVDiskData = (data: PreparedVDisk) => {
// eslint-disable-next-line complexity
const prepareVDiskData = (data: PreparedVDisk, withDeveloperUILink?: boolean) => {
const {
NodeId,
PDiskId,
VDiskSlotId,
StringifiedId,
VDiskState,
SatisfactionRank,
Expand Down Expand Up @@ -126,6 +151,24 @@ const prepareVDiskData = (data: PreparedVDisk) => {
});
}

if (
withDeveloperUILink &&
valueIsDefined(NodeId) &&
valueIsDefined(PDiskId) &&
valueIsDefined(VDiskSlotId)
) {
const vDiskInternalViewerPath = createVDiskDeveloperUILink({
nodeId: NodeId,
pDiskId: PDiskId,
vDiskSlotId: VDiskSlotId,
});

vdiskData.push({
label: 'Links',
value: <LinkWithIcon title={'Developer UI'} url={vDiskInternalViewerPath} />,
});
}

return vdiskData;
};

Expand All @@ -136,16 +179,24 @@ interface VDiskPopupProps {
export const VDiskPopup = ({data}: VDiskPopupProps) => {
const isFullData = isFullVDiskData(data);

const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);

const vdiskInfo = React.useMemo(
() => (isFullData ? prepareVDiskData(data) : prepareUnavailableVDiskData(data)),
[data, isFullData],
() =>
isFullData
? prepareVDiskData(data, isUserAllowedToMakeChanges)
: prepareUnavailableVDiskData(data, isUserAllowedToMakeChanges),
[data, isFullData, isUserAllowedToMakeChanges],
);

const nodeHostsMap = useTypedSelector(selectNodeHostsMap);
const nodeHost = valueIsDefined(data.NodeId) ? nodeHostsMap?.get(data.NodeId) : undefined;
const pdiskInfo = React.useMemo(
() => isFullData && data.PDisk && preparePDiskData(data.PDisk, nodeHost),
[data, nodeHost, isFullData],
() =>
isFullData &&
data.PDisk &&
preparePDiskData(data.PDisk, nodeHost, isUserAllowedToMakeChanges),
[data, nodeHost, isFullData, isUserAllowedToMakeChanges],
);

const donorsInfo: InfoViewerItem[] = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ContentWithPopup} from '../../../components/ContentWithPopup/ContentWithPopup';
import {DiskStateProgressBar} from '../../../components/DiskStateProgressBar/DiskStateProgressBar';
import {HoverPopup} from '../../../components/HoverPopup/HoverPopup';
import type {InfoViewerItem} from '../../../components/InfoViewer';
import {InfoViewer} from '../../../components/InfoViewer';
import {InternalLink} from '../../../components/InternalLink';
Expand Down Expand Up @@ -85,8 +85,8 @@ function Slot<T extends SlotItemType>({item, pDiskId, nodeId}: SlotProps<T>) {
: undefined;

return (
<ContentWithPopup
content={<VDiskInfo data={item.SlotData} withTitle />}
<HoverPopup
popupContent={<VDiskInfo data={item.SlotData} withTitle />}
contentClassName={b('vdisk-popup')}
placement={['right', 'top']}
>
Expand All @@ -105,13 +105,13 @@ function Slot<T extends SlotItemType>({item, pDiskId, nodeId}: SlotProps<T>) {
}
/>
</InternalLink>
</ContentWithPopup>
</HoverPopup>
);
}
if (isLogSlot(item)) {
return (
<ContentWithPopup
content={<LogInfo data={item.SlotData} />}
<HoverPopup
popupContent={<LogInfo data={item.SlotData} />}
contentClassName={b('vdisk-popup')}
placement={['right', 'top']}
>
Expand All @@ -127,14 +127,14 @@ function Slot<T extends SlotItemType>({item, pDiskId, nodeId}: SlotProps<T>) {
/>
}
/>
</ContentWithPopup>
</HoverPopup>
);
}

if (isEmptySlot(item)) {
return (
<ContentWithPopup
content={<EmptySlotInfo data={item.SlotData} />}
<HoverPopup
popupContent={<EmptySlotInfo data={item.SlotData} />}
contentClassName={b('vdisk-popup')}
placement={['right', 'top']}
>
Expand All @@ -150,7 +150,7 @@ function Slot<T extends SlotItemType>({item, pDiskId, nodeId}: SlotProps<T>) {
/>
}
/>
</ContentWithPopup>
</HoverPopup>
);
}

Expand Down
15 changes: 8 additions & 7 deletions src/store/reducers/pdisk/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [
TPDiskInfoResponse,
TEvSystemStateResponse,
]): PDiskData {
const rawNode = nodeResponse.SystemStateInfo?.[0];
const preparedNode = prepareNodeSystemState(rawNode);

const {BSC = {}, Whiteboard = {}} = pdiskResponse || {};

const {PDisk: WhiteboardPDiskData = {}, VDisks: WhiteboardVDisksData = []} = Whiteboard;
const {PDisk: BSCPDiskData = {}} = BSC;

const preparedPDisk = preparePDiskData(WhiteboardPDiskData, BSCPDiskData);

const NodeId = preparedPDisk.NodeId ?? preparedNode.NodeId;

const {
LogUsedSize,
LogTotalSize,
Expand All @@ -43,9 +48,8 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [
};
}

const preparedVDisks = WhiteboardVDisksData.map(prepareVDiskData).sort(
(disk1, disk2) => Number(disk2.VDiskSlotId) - Number(disk1.VDiskSlotId),
);
const preparedVDisks = WhiteboardVDisksData.map((disk) => prepareVDiskData({...disk, NodeId}));
preparedVDisks.sort((disk1, disk2) => Number(disk2.VDiskSlotId) - Number(disk1.VDiskSlotId));

const vdisksSlots: SlotItem<'vDisk'>[] = preparedVDisks.map((preparedVDisk) => {
return {
Expand Down Expand Up @@ -94,12 +98,9 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [
diskSlots.unshift(logSlot);
}

const rawNode = nodeResponse.SystemStateInfo?.[0];
const preparedNode = prepareNodeSystemState(rawNode);

return {
...preparedPDisk,
NodeId: preparedPDisk.NodeId ?? preparedNode.NodeId,
NodeId,
NodeHost: preparedNode.Host,
NodeType: preparedNode.Roles?.[0],
NodeDC: preparedNode.DC,
Expand Down

0 comments on commit 57d2092

Please sign in to comment.