Skip to content

Commit dcded29

Browse files
authored
fix: 2dc format from backend (#2733)
1 parent a1ff7a2 commit dcded29

File tree

4 files changed

+95
-58
lines changed

4 files changed

+95
-58
lines changed

src/containers/Cluster/ClusterOverview/components/BridgeInfoTable.tsx

Lines changed: 26 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React from 'react';
22

3-
import {CircleCheckFill, CircleXmarkFill} from '@gravity-ui/icons';
4-
import {DefinitionList, Flex, Icon, Label, Text} from '@gravity-ui/uikit';
3+
import {DefinitionList, Flex, Label, Text} from '@gravity-ui/uikit';
4+
import type {LabelProps} from '@gravity-ui/uikit';
55

66
import type {TBridgePile} from '../../../../types/api/cluster';
7+
import {BridgePileState} from '../../../../types/api/cluster';
78
import {cn} from '../../../../utils/cn';
89
import {EMPTY_DATA_PLACEHOLDER} from '../../../../utils/constants';
910
import {formatNumber} from '../../../../utils/dataFormatters/dataFormatters';
@@ -13,6 +14,27 @@ import './BridgeInfoTable.scss';
1314

1415
const b = cn('bridge-info-table');
1516

17+
function getBridgePileStateTheme(state?: string): NonNullable<LabelProps['theme']> {
18+
if (!state) {
19+
return 'unknown';
20+
}
21+
22+
switch (state.toUpperCase()) {
23+
case BridgePileState.PRIMARY:
24+
case BridgePileState.PROMOTE:
25+
case BridgePileState.SYNCHRONIZED:
26+
return 'success'; // Green - healthy states
27+
case BridgePileState.NOT_SYNCHRONIZED:
28+
return 'warning'; // Yellow - needs attention
29+
case BridgePileState.SUSPENDED:
30+
case BridgePileState.DISCONNECTED:
31+
return 'danger'; // Red - critical states
32+
case BridgePileState.UNSPECIFIED:
33+
default:
34+
return 'unknown'; // Purple - unknown state
35+
}
36+
}
37+
1638
interface BridgeInfoTableProps {
1739
piles: TBridgePile[];
1840
}
@@ -22,57 +44,17 @@ interface BridgePileCardProps {
2244
}
2345

2446
const BridgePileCard = React.memo(function BridgePileCard({pile}: BridgePileCardProps) {
25-
const renderPrimaryStatus = React.useCallback(() => {
26-
const isPrimary = pile.IsPrimary;
27-
const icon = isPrimary ? CircleCheckFill : CircleXmarkFill;
28-
const text = isPrimary ? i18n('value_yes') : i18n('value_no');
29-
30-
return (
31-
<Flex gap={1} alignItems="center">
32-
<Icon data={icon} size={16} className={b('status-icon', {primary: isPrimary})} />
33-
<Text color="secondary">{text}</Text>
34-
</Flex>
35-
);
36-
}, [pile.IsPrimary]);
37-
3847
const renderStateStatus = React.useCallback(() => {
3948
if (!pile.State) {
4049
return EMPTY_DATA_PLACEHOLDER;
4150
}
4251

43-
const isSynchronized = pile.State.toUpperCase() === 'SYNCHRONIZED';
44-
const theme = isSynchronized ? 'success' : 'info';
45-
52+
const theme = getBridgePileStateTheme(pile.State);
4653
return <Label theme={theme}>{pile.State}</Label>;
4754
}, [pile.State]);
4855

49-
const renderBeingPromotedStatus = React.useCallback(() => {
50-
const isBeingPromoted = pile.IsBeingPromoted;
51-
const icon = isBeingPromoted ? CircleCheckFill : CircleXmarkFill;
52-
const text = isBeingPromoted ? i18n('value_yes') : i18n('value_no');
53-
54-
return (
55-
<Flex gap={1} alignItems="center">
56-
<Icon
57-
data={icon}
58-
size={16}
59-
className={b('status-icon', {primary: isBeingPromoted})}
60-
/>
61-
<Text color="secondary">{text}</Text>
62-
</Flex>
63-
);
64-
}, [pile.IsBeingPromoted]);
65-
6656
const info = React.useMemo(
6757
() => [
68-
{
69-
name: i18n('field_primary'),
70-
content: renderPrimaryStatus(),
71-
},
72-
{
73-
name: i18n('field_being-promoted'),
74-
content: renderBeingPromotedStatus(),
75-
},
7658
{
7759
name: i18n('field_state'),
7860
content: renderStateStatus(),
@@ -83,7 +65,7 @@ const BridgePileCard = React.memo(function BridgePileCard({pile}: BridgePileCard
8365
pile.Nodes === undefined ? EMPTY_DATA_PLACEHOLDER : formatNumber(pile.Nodes),
8466
},
8567
],
86-
[renderPrimaryStatus, renderBeingPromotedStatus, renderStateStatus, pile.Nodes],
68+
[renderStateStatus, pile.Nodes],
8769
);
8870

8971
return (

src/types/api/cluster.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,23 @@ function isClusterParticularVersionOrHigher(info: TClusterInfo | undefined, vers
9797
);
9898
}
9999

100+
export enum BridgePileState {
101+
UNSPECIFIED = 'UNSPECIFIED',
102+
PRIMARY = 'PRIMARY',
103+
PROMOTE = 'PROMOTE',
104+
SYNCHRONIZED = 'SYNCHRONIZED',
105+
NOT_SYNCHRONIZED = 'NOT_SYNCHRONIZED',
106+
SUSPENDED = 'SUSPENDED',
107+
DISCONNECTED = 'DISCONNECTED',
108+
}
109+
100110
export interface TBridgePile {
101111
/** unique pile identifier */
102112
PileId?: number;
103113
/** pile name, e.g., r1 */
104114
Name?: string;
105-
/** pile state (string from backend, e.g., SYNCHRONIZED) */
115+
/** pile state from backend */
106116
State?: string;
107-
/** whether this pile is primary */
108-
IsPrimary?: boolean;
109-
/** whether this pile is being promoted to primary */
110-
IsBeingPromoted?: boolean;
111117
/** number of nodes in the pile */
112118
Nodes?: number;
113119
}

tests/suites/bridge/bridge.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,7 @@ test.describe('Bridge mode - Cluster Overview', () => {
119119
// Check first pile content
120120
const firstPileContent = await clusterPage.getFirstPileContent();
121121
expect(firstPileContent).toContain('r1');
122-
expect(firstPileContent).toContain('Yes'); // Primary status
123-
expect(firstPileContent).toContain('No'); // Being Promoted status (false for first pile)
124-
expect(firstPileContent).toContain('SYNCHRONIZED');
122+
expect(firstPileContent).toContain('PRIMARY'); // State
125123
expect(firstPileContent).toContain('16'); // Nodes count
126124
});
127125
});

tests/suites/bridge/mocks.ts

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type {Page, Route} from '@playwright/test';
22

3+
import {BridgePileState} from '../../../src/types/api/cluster';
4+
35
export const mockCapabilities = (page: Page, enabled: boolean) => {
46
return page.route(`**/viewer/capabilities`, async (route: Route) => {
57
await route.fulfill({
@@ -56,6 +58,59 @@ export const mockStorageGroupsWithPile = (page: Page) => {
5658
});
5759
};
5860

61+
export const mockClusterWithAllBridgePileStates = (page: Page) => {
62+
return page.route(`**/viewer/json/cluster?*`, async (route: Route) => {
63+
await route.fulfill({
64+
status: 200,
65+
contentType: 'application/json',
66+
body: JSON.stringify({
67+
Version: 6,
68+
Domain: '/dev02',
69+
BridgeInfo: {
70+
Piles: [
71+
{
72+
PileId: 1,
73+
Name: 'primary-pile',
74+
State: BridgePileState.PRIMARY,
75+
Nodes: 16,
76+
},
77+
{
78+
PileId: 2,
79+
Name: 'promoting-pile',
80+
State: BridgePileState.PROMOTE,
81+
Nodes: 12,
82+
},
83+
{
84+
PileId: 3,
85+
Name: 'sync-pile',
86+
State: BridgePileState.SYNCHRONIZED,
87+
Nodes: 8,
88+
},
89+
{
90+
PileId: 4,
91+
Name: 'not-sync-pile',
92+
State: BridgePileState.NOT_SYNCHRONIZED,
93+
Nodes: 4,
94+
},
95+
{
96+
PileId: 5,
97+
Name: 'suspended-pile',
98+
State: BridgePileState.SUSPENDED,
99+
Nodes: 6,
100+
},
101+
{
102+
PileId: 6,
103+
Name: 'disconnected-pile',
104+
State: BridgePileState.DISCONNECTED,
105+
Nodes: 0,
106+
},
107+
],
108+
},
109+
}),
110+
});
111+
});
112+
};
113+
59114
export const mockClusterWithBridgePiles = (page: Page) => {
60115
return page.route(`**/viewer/json/cluster?*`, async (route: Route) => {
61116
await route.fulfill({
@@ -134,17 +189,13 @@ export const mockClusterWithBridgePiles = (page: Page) => {
134189
{
135190
PileId: 1,
136191
Name: 'r1',
137-
State: 'SYNCHRONIZED',
138-
IsPrimary: true,
139-
IsBeingPromoted: false,
192+
State: BridgePileState.PRIMARY,
140193
Nodes: 16,
141194
},
142195
{
143196
PileId: 2,
144197
Name: 'r2',
145-
State: 'READY',
146-
IsPrimary: false,
147-
IsBeingPromoted: true,
198+
State: BridgePileState.SYNCHRONIZED,
148199
Nodes: 12,
149200
},
150201
],

0 commit comments

Comments
 (0)