Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@mui/material": "^7.3.6",
"@mui/x-data-grid": "^8.14.1",
"@oceanprotocol/contracts": "2.6.0",
"@oceanprotocol/lib": "8.0.3",
"@oceanprotocol/lib": "8.0.6",
"@ramp-network/ramp-instant-sdk": "^6.2.0",
"@tanstack/react-query": "^5.28.4",
"@wagmi/core": "^2.15.0",
Expand Down
27 changes: 25 additions & 2 deletions src/components/Navigation/profile-button.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import Avatar from '@/components/avatar/avatar';
import Menu from '@/components/menu/menu';
import EditAccessListModal from '@/components/node-storage/edit-access-list-modal';
import { useProfileContext } from '@/context/profile-context';
import { useOceanAccount } from '@/lib/use-ocean-account';
import { GrantStatus } from '@/types/grant';
import { formatWalletAddress } from '@/utils/formatters';
import { useAuthModal, useLogout } from '@account-kit/react';
import ListAltIcon from '@mui/icons-material/ListAlt';
import LogoutIcon from '@mui/icons-material/Logout';
import PersonIcon from '@mui/icons-material/Person';
import RedeemIcon from '@mui/icons-material/Redeem';
Expand All @@ -16,18 +18,20 @@ import { useEffect, useMemo, useRef, useState } from 'react';
import Button from '../button/button';
import styles from './navigation.module.css';

const ProfileButton = () => {
const ProfileButton: React.FC = () => {
const router = useRouter();

const { closeAuthModal, isOpen: isAuthModalOpen, openAuthModal } = useAuthModal();
const { isLoggingOut, logout } = useLogout();

const { account } = useOceanAccount();
const { account, provider } = useOceanAccount();

const { ensName, ensProfile, grantStatus } = useProfileContext();

const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [isClient, setIsClient] = useState(false);
const [isAccessListModalOpen, setIsAccessListModalOpen] = useState(false);

const buttonRef = useRef<HTMLButtonElement>(null);

// This is a workaround for the modal not closing after connecting
Expand Down Expand Up @@ -136,6 +140,20 @@ const ProfileButton = () => {
</ListItemIcon>
Convert to COMPY
</MenuItem>
{provider && (
<MenuItem
disableRipple
onClick={() => {
setIsAccessListModalOpen(true);
handleCloseMenu();
}}
>
<ListItemIcon>
<ListAltIcon />
</ListItemIcon>
Edit access list
</MenuItem>
)}
<MenuItem
sx={{
color: 'var(--error-darker)',
Expand All @@ -152,6 +170,11 @@ const ProfileButton = () => {
Log out
</MenuItem>
</Menu>
<EditAccessListModal
currentAccount={account.address}
isOpen={isAccessListModalOpen}
onClose={() => setIsAccessListModalOpen(false)}
/>
</>
) : (
<Button className={styles.loginButton} color="accent1" loading={isLoggingOut} onClick={openAuthModal}>
Expand Down
10 changes: 7 additions & 3 deletions src/components/button/copy-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ import { useState } from 'react';

type CopyButtonProps = Pick<ButtonProps, 'className' | 'color' | 'size' | 'variant'> & {
contentToCopy: string;
label?: string;
labelCopied?: string;
};

const CopyButton = ({
const CopyButton: React.FC<CopyButtonProps> = ({
className,
color = 'accent2',
contentToCopy,
label = 'Copy',
labelCopied = 'Copied!',
size = 'sm',
variant = 'filled',
}: CopyButtonProps) => {
}) => {
const [copied, setCopied] = useState(false);

const handleClick = () => {
Expand All @@ -33,7 +37,7 @@ const CopyButton = ({
size={size}
variant={variant}
>
{copied ? 'Copied!' : 'Copy'}
{copied ? labelCopied : label}
</Button>
);
};
Expand Down
36 changes: 36 additions & 0 deletions src/components/modal/confirm-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Button from '@/components/button/button';
import Modal from '@/components/modal/modal';

type ConfirmModalProps = {
confirmLabel?: string;
isOpen: boolean;
message: string;
onCancel: () => void;
onConfirm: () => void;
title?: string;
};

const ConfirmModal: React.FC<ConfirmModalProps> = ({
confirmLabel = 'Confirm',
isOpen,
message,
onCancel,
onConfirm,
title = 'Confirm',
}) => {
return (
<Modal isOpen={isOpen} onClose={onCancel} title={title} width="xs" fullWidth>
<p style={{ margin: 0 }}>{message}</p>
<div className="actionsGroupMdEnd">
<Button color="accent1" onClick={onCancel} size="md" variant="outlined" type="button">
Cancel
</Button>
<Button color="accent1" onClick={onConfirm} size="md" variant="filled" type="button">
{confirmLabel}
</Button>
</div>
</Modal>
);
};

export default ConfirmModal;
24 changes: 24 additions & 0 deletions src/components/modal/modal.module.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
/* Header */
.header {
align-items: start;
border-bottom: 1px solid var(--border-glass);
box-shadow: var(--drop-shadow-black);
display: flex;
gap: 24px;
justify-content: space-between;
padding: 16px;
position: sticky;
top: 0;
z-index: 1;

@media (min-width: 576px) {
padding: 16px 24px;
}
}

/* Body */
.body {
display: flex;
flex-direction: column;
gap: 16px;
overflow-y: auto;
padding: 16px;

@media (min-width: 576px) {
gap: 24px;
padding: 24px;
}
}

/* Title */
Expand Down
8 changes: 3 additions & 5 deletions src/components/modal/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ const StyledDialog = styled(Dialog)(({ theme }) => ({
color: 'var(--text-primary)',
display: 'flex',
flexDirection: 'column',
gap: 24,
padding: 24,
overflow: 'hidden',
padding: 0,

[theme.breakpoints.down('sm')]: {
borderRadius: 16,
gap: 16,
margin: 16,
padding: 16,
width: 'calc(100% - 32px)',
},
},
Expand All @@ -49,7 +47,7 @@ const Modal = ({ children, fullWidth, hideCloseButton, isOpen, onClose, title, w
</button>
)}
</div>
{children}
<div className={styles.body}>{children}</div>
</StyledDialog>
);

Expand Down
71 changes: 71 additions & 0 deletions src/components/node-details/node-details-page-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Container from '@/components/container/container';
import SectionTitle from '@/components/section-title/section-title';
import TabBar from '@/components/tab-bar/tab-bar';
import { CircularProgress } from '@mui/material';
import React from 'react';

type TabKey = 'info' | 'storage';

type NodePageLayoutProps = {
activeTab: TabKey;
children?: React.ReactNode;
isWalletConnected?: boolean;
loading?: boolean;
nodeId?: string;
notFound?: boolean;
subtitle: string;
};

const NodeDetailsPageLayout: React.FC<NodePageLayoutProps> = ({
activeTab,
children,
isWalletConnected,
loading,
nodeId,
notFound,
subtitle,
}) => {
if (loading) {
return (
<Container className="pageRoot">
<SectionTitle
moreReadable
title="Node details"
subTitle={
<div className="flexRow alignItemsCenter gapMd">
<CircularProgress size={24} />
<span>Retrieving node details...</span>
</div>
}
/>
</Container>
);
}

if (notFound) {
return (
<Container className="pageRoot">
<SectionTitle moreReadable title="Node details" subTitle="Node not found" />
</Container>
);
}

const tabs: { key: TabKey; label: string; href: string }[] = [
{ key: 'info', label: 'Node info', href: `/nodes/${nodeId}` },
{ key: 'storage' as TabKey, label: 'Remote storage', href: `/nodes/${nodeId}/storage` },
];

return (
<Container className="pageRoot">
<SectionTitle
contentBetween={isWalletConnected ? <TabBar activeKey={activeTab} tabs={tabs} /> : null}
moreReadable
subTitle={subtitle}
title="Node details"
/>
<div className="pageContentWrapper">{children}</div>
</Container>
);
};

export default NodeDetailsPageLayout;
78 changes: 29 additions & 49 deletions src/components/node-details/node-details-page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import Container from '@/components/container/container';
import BenchmarkJobs from '@/components/node-details/benchmark-jobs';
import Environments from '@/components/node-details/environments';
import JobsRevenueStats from '@/components/node-details/jobs-revenue-stats';
import NodeDetailsPageLayout from '@/components/node-details/node-details-page-layout';
import NodeInfo from '@/components/node-details/node-info';
import UnbanRequests from '@/components/node-details/unban-requests';
import SectionTitle from '@/components/section-title/section-title';
import { useNodesContext } from '@/context/nodes-context';
import { useUnbanRequestsContext } from '@/context/unban-requests-context';
import { useP2P } from '@/contexts/P2PContext';
import { directNodeCommand } from '@/lib/direct-node-command';
import { useOceanAccount } from '@/lib/use-ocean-account';
import { ComputeEnvironment } from '@/types/environments';
import { CircularProgress } from '@mui/material';
import { useParams } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';

const NodeDetailsPage = () => {
const NodeDetailsPage: React.FC = () => {
const params = useParams<{ nodeId: string }>();

const { account } = useOceanAccount();

const { getEnvs: getEnvsP2P, isReady: isP2PReady, sendCommand } = useP2P();

const { selectedNode, fetchNode, loadingFetchNode } = useNodesContext();
Expand Down Expand Up @@ -79,52 +80,31 @@ const NodeDetailsPage = () => {
}
}, [fetchUnbanRequests, node]);

if (loadingFetchNode) {
return (
<Container className="pageRoot">
<SectionTitle
moreReadable
title="Node details"
subTitle={
<div className="flexRow alignItemsCenter gapMd">
<CircularProgress size={24} />
<span>Retrieving node details...</span>
</div>
}
/>
</Container>
);
}

if (!node) {
return (
<Container className="pageRoot">
<SectionTitle moreReadable title="Node details" subTitle="Node not found" />
</Container>
);
}

return (
<Container className="pageRoot">
<SectionTitle
moreReadable
title="Node details"
subTitle="Check node status, performance, and available resources before running a job"
/>
<div className="pageContentWrapper">
<NodeInfo envs={nodeEnvs} node={node} nodeOnline={connectedP2P || connectedDirectNodeCommand} />
<JobsRevenueStats envs={nodeEnvs} />
<BenchmarkJobs />
<Environments
envs={nodeEnvs}
nodeInfo={{
friendlyName: node.friendlyName,
id: node.id ?? node.nodeId,
}}
/>
{node.banned === false && unbanRequests?.length === 0 ? null : <UnbanRequests node={node} />}
</div>
</Container>
<NodeDetailsPageLayout
activeTab="info"
isWalletConnected={account.isConnected}
loading={loadingFetchNode}
nodeId={node?.id ?? node?.nodeId}
notFound={!node}
subtitle="Check node status, performance, and available resources before running a job"
>
{node ? (
<>
<NodeInfo envs={nodeEnvs} node={node} nodeOnline={connectedP2P || connectedDirectNodeCommand} />
<JobsRevenueStats envs={nodeEnvs} />
<BenchmarkJobs />
<Environments
envs={nodeEnvs}
nodeInfo={{
friendlyName: node.friendlyName,
id: node.id ?? node.nodeId,
}}
/>
{node.banned === false && unbanRequests?.length === 0 ? null : <UnbanRequests node={node} />}
</>
) : null}
</NodeDetailsPageLayout>
);
};

Expand Down
Loading
Loading