Skip to content

Commit 6461507

Browse files
authored
Merge pull request #134 from santteegt/feat/minipool-cad-ui
Improve MinipoolCard status management + display minipool address
2 parents c44a594 + 8234ce9 commit 6461507

File tree

3 files changed

+162
-16
lines changed

3 files changed

+162
-16
lines changed

build/ui/src/components/Setup/MinipoolCard.tsx

Lines changed: 151 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,135 @@
11
import React from "react";
2-
import { Card, CardContent, Typography, Chip, Button } from "@mui/material";
3-
import { Minipool } from "../../types/MinipoolStatus";
4-
import { toEtherString } from "../../utils/Utils";
52
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
6-
import "./minipool.css";
3+
import { Button, Card, CardContent, Chip, Tooltip, Typography } from "@mui/material";
4+
import CopyToClipboardButton from "../Buttons/CopyToClipboardButton";
5+
import { Minipool } from "../../types/MinipoolStatus";
6+
import { ethBalanceGreaterThanMinStake, shortenAddress, toEtherString } from "../../utils/Utils";
77
import ImportToSignerDialog from "./ImportToSignerDialog";
8+
import "./minipool.css";
9+
10+
enum MinipoolStatus {
11+
STAKING = "Staking",
12+
DISSOLVED = "Dissolved",
13+
PRELAUNCH = "Prelaunch",
14+
FINALISED = "Finalised"
15+
}
16+
17+
enum ValidatorStatus {
18+
EXITED = "Exited",
19+
ENQUEUED = "Enqueued",
20+
ACTIVE = "Active",
21+
READY_TO_CLOSE = "Ready to Close",
22+
INACTIVE = "Inactive"
23+
}
24+
25+
// Color theme constants
26+
const COLOR_THEME = {
27+
SUCCESS: "#81C784", // Green
28+
WARNING: "#FFC107", // Amber
29+
ERROR: "#E57373", // Red
30+
INFO: "#FFB74D" // Orange
31+
} as const;
32+
33+
// Status information interface
34+
interface StatusInfo {
35+
status: string;
36+
color: string;
37+
}
38+
39+
// Validator Status Service
40+
class ValidatorStatusService {
41+
/**
42+
* Determines the minipool status and returns status info
43+
*/
44+
getMinipoolStatusInfo(minipoolData: Minipool): StatusInfo {
45+
const status = this.determineMinipoolStatus(minipoolData);
46+
const color = this.getMinipoolStatusColor(status);
47+
return { status, color };
48+
}
49+
50+
/**
51+
* Determines the validator status and returns status info
52+
*/
53+
getValidatorStatusInfo(minipoolData: Minipool): StatusInfo {
54+
const status = this.determineValidatorStatus(minipoolData);
55+
const color = this.getValidatorStatusColor(status, minipoolData);
56+
return { status, color };
57+
}
58+
59+
/**
60+
* Determines the minipool status based on finalised state
61+
*/
62+
private determineMinipoolStatus(minipoolData: Minipool): string {
63+
return minipoolData.finalised ? MinipoolStatus.FINALISED : minipoolData.status.status;
64+
}
65+
66+
/**
67+
* Determines the validator status with priority-based logic
68+
*/
69+
private determineValidatorStatus(minipoolData: Minipool): string {
70+
// Priority 1: Check if finalised
71+
if (minipoolData.finalised) {
72+
return ValidatorStatus.EXITED;
73+
}
74+
75+
// Priority 2: Check if enqueued (staking but no validator index)
76+
if (minipoolData.status.status === MinipoolStatus.STAKING && !minipoolData.validator.index) {
77+
return ValidatorStatus.ENQUEUED;
78+
}
79+
80+
// Priority 3: Check if validator is active
81+
if (minipoolData.validator.active) {
82+
return ValidatorStatus.ACTIVE;
83+
}
84+
85+
// Priority 4: Check if ready to close (staking with sufficient balance)
86+
if (minipoolData.status.status === MinipoolStatus.STAKING &&
87+
ethBalanceGreaterThanMinStake(minipoolData.balances.eth)) {
88+
return ValidatorStatus.READY_TO_CLOSE;
89+
}
90+
91+
// Default: Inactive
92+
return ValidatorStatus.INACTIVE;
93+
}
94+
95+
/**
96+
* Gets the color for minipool status
97+
*/
98+
private getMinipoolStatusColor(status: string): string {
99+
switch (status) {
100+
case MinipoolStatus.STAKING:
101+
return COLOR_THEME.SUCCESS;
102+
case MinipoolStatus.DISSOLVED:
103+
return COLOR_THEME.ERROR;
104+
case MinipoolStatus.FINALISED:
105+
return COLOR_THEME.WARNING;
106+
default:
107+
return COLOR_THEME.INFO;
108+
}
109+
}
110+
111+
/**
112+
* Gets the color for validator status
113+
*/
114+
private getValidatorStatusColor(validatorStatus: string, minipoolData: Minipool): string {
115+
// Success color for finalised or active validators
116+
if (minipoolData.finalised || minipoolData.validator.active) {
117+
return COLOR_THEME.SUCCESS;
118+
}
119+
120+
// Warning color for prelaunch, enqueued, or ready to close
121+
if (minipoolData.status.status === MinipoolStatus.PRELAUNCH ||
122+
[ValidatorStatus.ENQUEUED, ValidatorStatus.READY_TO_CLOSE].includes(validatorStatus as ValidatorStatus)) {
123+
return COLOR_THEME.WARNING;
124+
}
125+
126+
// Error color for inactive
127+
return COLOR_THEME.ERROR;
128+
}
129+
};
130+
131+
// Initialize status service
132+
const statusService = new ValidatorStatusService();
8133

9134
function MinipoolCard({
10135
data,
@@ -17,13 +142,11 @@ function MinipoolCard({
17142
}): JSX.Element {
18143
const [importToSignerDialogOpen, setImportToSignerDialogOpen] =
19144
React.useState<boolean>(false);
20-
21-
const backgroundColor =
22-
data.status.status === "Staking"
23-
? "#81C784"
24-
: data.status.status === "Dissolved"
25-
? "#E57373"
26-
: "#FFB74D";
145+
const [showAddress, setShowAddress] = React.useState<boolean>(false);
146+
147+
// Get status information using the service
148+
const minipoolStatusInfo = statusService.getMinipoolStatusInfo(data);
149+
const validatorStatusInfo = statusService.getValidatorStatusInfo(data);
27150

28151
return (
29152
<Card
@@ -36,16 +159,28 @@ function MinipoolCard({
36159
>
37160
<CardContent>
38161
<div className="chip-container">
39-
<Chip label={`#${data.validator.index}`} />
162+
<div className="chip-with-clipboard-container">
163+
<Tooltip title={showAddress ? "Minipool Address" : "Validator Index"}>
164+
<Chip
165+
label={showAddress ? shortenAddress(data.address) : `#${data.validator.index}`}
166+
onClick={() => setShowAddress(!showAddress)}
167+
sx={{ cursor: 'pointer' }}
168+
/>
169+
</Tooltip>
170+
<CopyToClipboardButton
171+
value={showAddress ? data.address : data.validator.index.toString()}
172+
fontSize="small"
173+
/>
174+
</div>
40175
<Chip
41-
label={data.finalised ? "Finalised" : data.status.status}
42-
sx={{ backgroundColor: data.finalised ? "#FFC107" : backgroundColor }}
176+
label={minipoolStatusInfo.status}
177+
sx={{ backgroundColor: minipoolStatusInfo.color }}
43178
/>
44179
</div>
45180
<div className="validator-status">
46181
<Chip
47-
label={data.finalised ? "Exited" : (data.validator.active ? "Active" : "Inactive")}
48-
sx={{ backgroundColor: data.finalised || data.validator.active ? "#81C784" : "#E57373" }}
182+
label={validatorStatusInfo.status}
183+
sx={{ backgroundColor: validatorStatusInfo.color }}
49184
/>
50185
</div>
51186

build/ui/src/components/Setup/minipool.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,17 @@
3939
margin-bottom: 0.5rem;
4040
}
4141

42+
.chip-with-clipboard-container {
43+
display: flex;
44+
align-items: center;
45+
}
46+
4247
.explorer-button-container {
4348
margin-top: 1rem;
4449
}
4550

4651
.minipool-ether {
52+
margin-top: 0.5rem !important;
4753
margin-bottom: 0.5rem !important;
4854
font-weight: bold !important;
4955
}

build/ui/src/utils/Utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ export function enoughEthBalance(
4141
return is8EthPool ? ethBalance >= minimum8Eth : ethBalance >= minimum16Eth;
4242
}
4343

44+
export function ethBalanceGreaterThanMinStake(ethBalance: number): boolean {
45+
const minStake = BigInt(32 * 10 ** 18);
46+
return BigInt(ethBalance) > minStake;
47+
}
48+
4449
export const escapeNewLine = (text: string) => text.replace(/(\\n)/g, "\n");

0 commit comments

Comments
 (0)