Add self-bond and operating costs to NodeTable

This commit is contained in:
Yana
2025-05-14 19:40:31 +03:00
parent 128cf7c070
commit 42f9edd408
3 changed files with 111 additions and 13 deletions
@@ -212,7 +212,7 @@ const NodeTable = ({ nodes }: { nodes: MappedNymNodes }) => {
{
id: "name",
header: "",
size: 190,
size: 210,
Header: <ColumnHeading>Node</ColumnHeading>,
accessorKey: "name",
@@ -329,6 +329,40 @@ const NodeTable = ({ nodes }: { nodes: MappedNymNodes }) => {
);
},
},
{
id: "selfBond",
header: "Self-bond",
accessorKey: "selfBond",
Header: <ColumnHeading>Self-bond</ColumnHeading>,
Cell: ({ row }) => {
const value = row.original.selfBond;
let color = "#000000";
if (value === 0) {
color = "#EF4444";
}
return (
<Typography
variant="body4"
sx={{ color, fontWeight: value === 0 ? 400 : 300 }}
>
{row.original.selfBond} NYM
</Typography>
);
},
},
{
id: "operatingCosts",
header: "Operating costs",
accessorKey: "operatingCosts",
Header: <ColumnHeading>Operating costs</ColumnHeading>,
Cell: ({ row }) => (
<Typography variant="body4">
{row.original.operatingCosts} NYM
</Typography>
),
},
{
id: "profitMarginPercentage",
header: "Profit margin",
@@ -9,6 +9,7 @@ import { countryName } from "../../utils/countryName";
import NodeTable from "./NodeTable";
import { useState, useEffect } from "react";
import AdvancedFilters from "./AdvancedFilters";
import { formatBigNum } from "@/utils/formatBigNumbers";
// Utility function to calculate node saturation point
function getNodeSaturationPoint(
@@ -44,12 +45,24 @@ const mappedNSApiNodes = (
"&"
);
const selfBondFormatted = node.rewarding_details?.operator
? formatBigNum(Number(node.rewarding_details.operator) / 1_000_000)
: 0;
const operatingCostsFormatted = node.rewarding_details
? Number(
node.rewarding_details.cost_params.interval_operating_cost.amount
) / 1_000_000
: 0;
return {
name: cleanMoniker,
nodeId: node.node_id,
identity_key: node.identity_key,
countryCode: node.geoip?.country || null,
countryName: countryName(node.geoip?.country || null) || null,
selfBond: selfBondFormatted,
operatingCosts: operatingCostsFormatted,
profitMarginPercentage: node.rewarding_details
? +node.rewarding_details.cost_params.profit_margin_percent * 100
: 0,
@@ -80,11 +93,60 @@ const NodeTableWithAction = () => {
// All hooks at the top!
const [activeFilter, setActiveFilter] = useState<
"all" | "mixnodes" | "gateways"
>("all");
const [uptime, setUptime] = useState<[number, number]>([0, 100]);
>(() => {
const stored = sessionStorage.getItem("nodeTableActiveFilter");
return (stored as "all" | "mixnodes" | "gateways") || "all";
});
const [uptime, setUptime] = useState<[number, number]>(() => {
const stored = sessionStorage.getItem("nodeTableUptime");
return stored ? JSON.parse(stored) : [0, 100];
});
const [saturation, setSaturation] = useState<[number, number]>([0, 100]);
const [profitMargin, setProfitMargin] = useState<[number, number]>([0, 100]);
const [advancedOpen, setAdvancedOpen] = useState(false);
const [profitMargin, setProfitMargin] = useState<[number, number]>(() => {
const stored = sessionStorage.getItem("nodeTableProfitMargin");
return stored ? JSON.parse(stored) : [0, 100];
});
const [advancedOpen, setAdvancedOpen] = useState(() => {
const stored = sessionStorage.getItem("nodeTableAdvancedOpen");
return stored ? JSON.parse(stored) : false;
});
// Wrapper functions to handle filter changes and sessionStorage
const handleActiveFilterChange = (
newFilter: "all" | "mixnodes" | "gateways"
) => {
setActiveFilter(newFilter);
sessionStorage.setItem("nodeTableActiveFilter", newFilter);
};
const handleUptimeChange = (newUptime: [number, number]) => {
setUptime(newUptime);
sessionStorage.setItem("nodeTableUptime", JSON.stringify(newUptime));
};
const handleSaturationChange = (newSaturation: [number, number]) => {
setSaturation(newSaturation);
sessionStorage.setItem(
"nodeTableSaturation",
JSON.stringify(newSaturation)
);
};
const handleProfitMarginChange = (newProfitMargin: [number, number]) => {
setProfitMargin(newProfitMargin);
sessionStorage.setItem(
"nodeTableProfitMargin",
JSON.stringify(newProfitMargin)
);
};
const handleAdvancedOpenChange = (newAdvancedOpen: boolean) => {
setAdvancedOpen(newAdvancedOpen);
sessionStorage.setItem(
"nodeTableAdvancedOpen",
JSON.stringify(newAdvancedOpen)
);
};
// Use React Query to fetch epoch rewards
const {
@@ -125,9 +187,12 @@ const NodeTableWithAction = () => {
...nsApiNodesData.map((n) => n.stakeSaturation || 0)
);
// Ensure saturation filter always covers the full range when maxSaturation changes, but only after data is loaded
// Initialize saturation from sessionStorage or set to maxSaturation when data is loaded
useEffect(() => {
if (nsApiNodesData.length > 0) {
const stored = sessionStorage.getItem("nodeTableSaturation");
if (stored) {
setSaturation(JSON.parse(stored));
} else if (nsApiNodesData.length > 0) {
setSaturation([0, maxSaturation]);
}
}, [maxSaturation, nsApiNodesData.length]);
@@ -209,16 +274,16 @@ const NodeTableWithAction = () => {
<Stack spacing={3}>
<AdvancedFilters
open={advancedOpen}
setOpen={setAdvancedOpen}
setOpen={handleAdvancedOpenChange}
uptime={uptime}
setUptime={setUptime}
setUptime={handleUptimeChange}
saturation={saturation}
setSaturation={setSaturation}
setSaturation={handleSaturationChange}
profitMargin={profitMargin}
setProfitMargin={setProfitMargin}
setProfitMargin={handleProfitMarginChange}
maxSaturation={maxSaturation}
activeFilter={activeFilter}
setActiveFilter={setActiveFilter}
setActiveFilter={handleActiveFilterChange}
nodeCounts={nodeCounts}
/>
<NodeTable nodes={filteredNodes} />
@@ -226,7 +226,6 @@ export const NodeRoleCard = ({ paramId }: Props) => {
}
if (!nodeInfo) return null;
console.log("nodeInfo", nodeInfo);
const NodeRoles = nodeRoles.map((role) => (
<Stack key={role} direction="row" gap={1}>