Skip to content
Draft
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
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
38 changes: 38 additions & 0 deletions src/components/node-config/configure-general.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.adminsSection {
display: flex;
flex-direction: column;
gap: 12px;

.adminsLabel {
font-size: 14px;
font-weight: 600;
padding: 0 16px;
}

.adminsList {
align-self: flex-start;
display: grid;
grid-template-columns: auto auto;
gap: 4px 8px;
padding: 0;
}

.addAdminRow {
align-items: center;
display: flex;
gap: 8px;
}
}

.addAdminInput {
flex: 1;
min-width: 0;
}

.toggleRow {
align-items: center;
align-self: flex-start;
display: flex;
flex-wrap: wrap;
gap: 16px;
}
101 changes: 101 additions & 0 deletions src/components/node-config/configure-general.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import Button from '@/components/button/button';
import Input from '@/components/input/input';
import Switch from '@/components/switch/switch';
import { NodeConfig } from '@/types/node-config';
import CloseIcon from '@mui/icons-material/Close';
import { ethers } from 'ethers';
import React, { useState } from 'react';
import { toast } from 'react-toastify';
import styles from './configure-general.module.css';
import commonStyles from './node-config.module.css';

type ConfigureGeneralProps = {
config: NodeConfig;
setConfig: (config: NodeConfig) => void;
};

const ConfigureGeneral: React.FC<ConfigureGeneralProps> = ({ config, setConfig }) => {
const [adminToAdd, setAdminToAdd] = useState('');

const admins = config.allowedAdmins ?? [];

const handleAddAdmin = () => {
const trimmed = adminToAdd.trim();
if (!trimmed || admins.includes(trimmed)) {
return;
}
if (!ethers.isAddress(trimmed)) {
toast.error('Invalid wallet address');
return;
}
setConfig({ ...config, allowedAdmins: [...admins, trimmed] });
setAdminToAdd('');
};

const handleRemoveAdmin = (address: string) => {
setConfig({ ...config, allowedAdmins: admins.filter((a) => a !== address) });
};

const handleHasHttpChange = (_e: unknown, checked: boolean) => {
setConfig({ ...config, hasHttp: checked });
};

const handleHttpPortChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const port = e.target.value === '' ? undefined : Number(e.target.value);
setConfig({ ...config, httpPort: port });
};

return (
<div className={commonStyles.sectionContent}>
<div className={styles.adminsSection}>
<h4 className={commonStyles.subsectionTitle}>Allowed admins</h4>
<div className={styles.adminsList}>
{admins.map((address) => (
<React.Fragment key={address}>
<span className="wordBreakAll">{address}</span>
<Button onClick={() => handleRemoveAdmin(address)} size="link" type="button" variant="transparent">
<CloseIcon className="textAccent1" fontSize="small" />
</Button>
</React.Fragment>
))}
</div>
<div className={styles.addAdminRow}>
<Input
className={styles.addAdminInput}
name="adminToAdd"
onChange={(e) => setAdminToAdd(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleAddAdmin();
}
}}
placeholder="0x..."
size="sm"
type="text"
value={adminToAdd}
/>
<Button color="accent1" onClick={handleAddAdmin} size="md" variant="filled">
Add
</Button>
</div>
</div>

<h4 className={commonStyles.subsectionTitle}>HTTP</h4>
<div className={styles.toggleRow}>
<Switch checked={!!config.hasHttp} label="Enable HTTP" name="hasHttp" onChange={handleHasHttpChange} />
<Input
disabled={!config.hasHttp}
name="httpPort"
onChange={handleHttpPortChange}
placeholder="Port"
size="sm"
startAdornment="Port"
type="number"
value={config.httpPort ?? ''}
/>
</div>
</div>
);
};

export default ConfigureGeneral;
38 changes: 38 additions & 0 deletions src/components/node-config/configure-indexer.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.indexerHeader {
align-items: center;
display: flex;
flex-wrap: wrap;
gap: 16px;
justify-content: space-between;
}

.networksGrid {
display: grid;
gap: 8px;
grid-template-columns: 1fr;

@media (min-width: 992px) {
grid-template-columns: 1fr 1fr;
}

.networkHeader {
align-items: center;
display: flex;
gap: 8px;

& > :first-child {
flex: 1;
min-width: 0;
}
}

.networkFieldsRow {
display: grid;
gap: 8px;
grid-template-columns: 1fr;

@media (min-width: 576px) {
grid-template-columns: 1fr 1fr 1fr;
}
}
}
Loading
Loading