in webui/src/app/namespaces/[namespace]/clusters/[cluster]/shards/[shard]/nodes/[node]/page.tsx [37:336]
export default function Node({
params,
}: {
params: { namespace: string; cluster: string; shard: string; node: string };
}) {
const { namespace, cluster, shard, node } = params;
const router = useRouter();
const [nodeData, setNodeData] = useState<any[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [copied, setCopied] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
const fetchedNodes = await listNodes(namespace, cluster, shard);
if (!fetchedNodes) {
console.error(`Shard ${shard} not found`);
router.push("/404");
return;
}
setNodeData(fetchedNodes);
} catch (error) {
console.error("Error fetching shard data:", error);
} finally {
setLoading(false);
}
};
fetchData();
}, [namespace, cluster, shard, router]);
if (loading) {
return <LoadingSpinner />;
}
const currentNode = nodeData[parseInt(node)];
if (!currentNode) {
return (
<div className="flex h-full">
<NodeSidebar namespace={namespace} cluster={cluster} shard={shard} />
<Box className="container-inner flex flex-1 items-center justify-center">
<Alert severity="error" variant="filled" className="shadow-lg">
Node not found
</Alert>
</Box>
</div>
);
}
// Get role color and text style
const getRoleStyles = (role: string) => {
if (role === "master") {
return {
color: "success",
textClass: "text-success font-medium",
icon: <CheckCircleIcon fontSize="small" className="mr-1" />,
};
}
return {
color: "info",
textClass: "text-info font-medium",
icon: <DeviceHubIcon fontSize="small" className="mr-1" />,
};
};
const copyToClipboard = (text: string, type: string) => {
navigator.clipboard.writeText(text);
setCopied(type);
setTimeout(() => setCopied(null), 2000);
};
const formattedDate = new Date(currentNode.created_at * 1000).toLocaleString();
return (
<div className="flex h-full">
<NodeSidebar namespace={namespace} cluster={cluster} shard={shard} />
<div className="flex-1 overflow-auto">
<Box className="container-inner">
<Box className="mb-6 flex items-center justify-between">
<div>
<Typography
variant="h5"
className="flex items-center font-medium text-gray-800 dark:text-gray-100"
>
<DeviceHubIcon className="mr-2 text-primary dark:text-primary-light" />
Node {parseInt(node) + 1}
<Chip
label={currentNode.role}
size="small"
color={getRoleStyles(currentNode.role).color as any}
className="ml-3"
icon={getRoleStyles(currentNode.role).icon}
/>
</Typography>
<Typography
variant="body2"
className="mt-1 text-gray-500 dark:text-gray-400"
>
Shard {parseInt(shard) + 1}, {cluster} cluster, {namespace}{" "}
namespace
</Typography>
</div>
</Box>
<Paper className="mb-6 rounded-lg border border-light-border bg-white p-6 shadow-card dark:border-dark-border dark:bg-dark-paper">
<Typography variant="h6" className="mb-4 flex items-center font-medium">
<StorageIcon fontSize="small" className="mr-2" />
Node Details
</Typography>
<Divider className="mb-4" />
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
<div className="space-y-4">
<div>
<Typography
variant="subtitle2"
className="mb-1 text-gray-500 dark:text-gray-400"
>
ID
</Typography>
<div className="flex items-center">
<Typography
variant="body1"
className="flex-1 overflow-hidden text-ellipsis rounded bg-gray-50 px-3 py-2 font-mono dark:bg-dark-border"
>
{currentNode.id}
</Typography>
<IconButton
onClick={() =>
copyToClipboard(currentNode.id, "id")
}
className="ml-2 text-gray-500 hover:text-primary"
title="Copy ID"
>
{copied === "id" ? (
<CheckCircleIcon
fontSize="small"
className="text-success"
/>
) : (
<ContentCopyIcon fontSize="small" />
)}
</IconButton>
</div>
</div>
<div>
<Typography
variant="subtitle2"
className="mb-1 text-gray-500 dark:text-gray-400"
>
Address
</Typography>
<div className="flex items-center">
<Typography
variant="body1"
className="flex-1 rounded bg-gray-50 px-3 py-2 dark:bg-dark-border"
>
{currentNode.addr}
</Typography>
<IconButton
onClick={() =>
copyToClipboard(currentNode.addr, "addr")
}
className="ml-2 text-gray-500 hover:text-primary"
title="Copy Address"
>
{copied === "addr" ? (
<CheckCircleIcon
fontSize="small"
className="text-success"
/>
) : (
<ContentCopyIcon fontSize="small" />
)}
</IconButton>
</div>
</div>
</div>
</Grid>
<Grid item xs={12} md={6}>
<div className="space-y-4">
<div>
<Typography
variant="subtitle2"
className="mb-1 text-gray-500 dark:text-gray-400"
>
Role
</Typography>
<Typography
variant="body1"
className={`${getRoleStyles(currentNode.role).textClass} flex items-center`}
>
{getRoleStyles(currentNode.role).icon}{" "}
{currentNode.role}
</Typography>
</div>
<div>
<Typography
variant="subtitle2"
className="mb-1 text-gray-500 dark:text-gray-400"
>
Created At
</Typography>
<Typography variant="body1" className="flex items-center">
<AccessTimeIcon
fontSize="small"
className="mr-1 text-gray-500"
/>
{formattedDate}
</Typography>
</div>
{currentNode.password && (
<div>
<Typography
variant="subtitle2"
className="mb-1 text-gray-500 dark:text-gray-400"
>
Authentication
</Typography>
<div className="flex items-center">
<Typography
variant="body2"
className="flex-1 rounded bg-gray-50 px-3 py-2 font-mono dark:bg-dark-border"
>
{currentNode.password
? "••••••••"
: "No password set"}
</Typography>
<IconButton
onClick={() =>
copyToClipboard(currentNode.password, "pwd")
}
className="ml-2 text-gray-500 hover:text-primary"
title="Copy Password"
disabled={!currentNode.password}
>
{copied === "pwd" ? (
<CheckCircleIcon
fontSize="small"
className="text-success"
/>
) : (
<LockIcon fontSize="small" />
)}
</IconButton>
</div>
</div>
)}
</div>
</Grid>
</Grid>
</Paper>
<Paper className="rounded-lg border border-light-border bg-white p-6 shadow-card dark:border-dark-border dark:bg-dark-paper">
<Typography variant="h6" className="mb-4 flex items-center font-medium">
<DnsIcon fontSize="small" className="mr-2" />
Shard Information
</Typography>
<Divider className="mb-4" />
<Grid container spacing={3}>
<Grid item xs={12} md={4}>
<Typography
variant="subtitle2"
className="mb-1 text-gray-500 dark:text-gray-400"
>
Shard
</Typography>
<Typography variant="body1">Shard {parseInt(shard) + 1}</Typography>
</Grid>
<Grid item xs={12} md={4}>
<Typography
variant="subtitle2"
className="mb-1 text-gray-500 dark:text-gray-400"
>
Cluster
</Typography>
<Typography variant="body1">{cluster}</Typography>
</Grid>
<Grid item xs={12} md={4}>
<Typography
variant="subtitle2"
className="mb-1 text-gray-500 dark:text-gray-400"
>
Namespace
</Typography>
<Typography variant="body1">{namespace}</Typography>
</Grid>
</Grid>
</Paper>
</Box>
</div>
</div>
);
}