mirror of
https://github.com/logos-storage/logos-storage-marketplace-ui.git
synced 2026-01-08 00:13:06 +00:00
Make style responsive
This commit is contained in:
parent
3304a6411f
commit
3f64433d7d
12
package-lock.json
generated
12
package-lock.json
generated
@ -9,7 +9,7 @@
|
||||
"version": "0.0.13",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.46",
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.47",
|
||||
"@codex-storage/sdk-js": "^0.0.16",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@sentry/react": "^8.31.0",
|
||||
@ -27,7 +27,7 @@
|
||||
"@preact/preset-vite": "^2.9.1",
|
||||
"@svgr/plugin-svgo": "^8.1.0",
|
||||
"@tanstack/router-plugin": "^1.58.4",
|
||||
"@types/node": "^22.7.5",
|
||||
"@types/node": "^22.9.1",
|
||||
"@types/react": "^18.3.8",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.7.0",
|
||||
@ -42,7 +42,7 @@
|
||||
"prettier": "^3.3.3",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"typescript": "5.5.4",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.7",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"vitest": "^2.1.4"
|
||||
@ -395,9 +395,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codex-storage/marketplace-ui-components": {
|
||||
"version": "0.0.46",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.46.tgz",
|
||||
"integrity": "sha512-HEkuKOFjigOOkgY/If68WLXYN89+jJ1bCTwL8dNBt2N5QdFF/KmoKQ67X0SlOFAgxz6DdK4cskd07iLTiT5YUA==",
|
||||
"version": "0.0.47",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.47.tgz",
|
||||
"integrity": "sha512-dcnZsYEFB1IoEx9sSC3Z1gaReLf6SAFmFNvfnBs3U81mAFnOd/nGi7IUY/ipPyN9U+J8nrGlAOsWnzNiPlEOpA==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
|
||||
@ -15,7 +15,8 @@
|
||||
"preview": "vite preview --host 127.0.0.1 --port 5173",
|
||||
"format": "prettier --write ./src",
|
||||
"test": "npx playwright test",
|
||||
"test:unit": "vitest run"
|
||||
"test:unit": "vitest run",
|
||||
"knip": "knip"
|
||||
},
|
||||
"keywords": [
|
||||
"Codex",
|
||||
@ -25,7 +26,7 @@
|
||||
"React"
|
||||
],
|
||||
"dependencies": {
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.46",
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.47",
|
||||
"@codex-storage/sdk-js": "^0.0.16",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@sentry/react": "^8.31.0",
|
||||
@ -43,7 +44,7 @@
|
||||
"@preact/preset-vite": "^2.9.1",
|
||||
"@svgr/plugin-svgo": "^8.1.0",
|
||||
"@tanstack/router-plugin": "^1.58.4",
|
||||
"@types/node": "^22.7.5",
|
||||
"@types/node": "^22.9.1",
|
||||
"@types/react": "^18.3.8",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.7.0",
|
||||
@ -58,7 +59,7 @@
|
||||
"prettier": "^3.3.3",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"typescript": "5.5.4",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.7",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"vitest": "^2.1.4"
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
<svg
|
||||
data-testid="icon-success"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
|
Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 391 B |
@ -19,9 +19,12 @@ import HelpIcon from "../../assets/icons/help.svg?react";
|
||||
import DisclaimerIcon from "../../assets/icons/disclaimer.svg?react";
|
||||
import { WalletConnect } from "../WalletLogin/WalletLogin";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Logo from "../../assets/icons/logo.svg?react";
|
||||
import { useIsMobile } from "../../hooks/useMobile";
|
||||
|
||||
type Props = {
|
||||
onIconClick: () => void;
|
||||
onExpanded: (val: boolean) => void;
|
||||
};
|
||||
|
||||
const icons: Record<string, ReactElement> = {
|
||||
@ -50,12 +53,13 @@ const descriptions: Record<string, string> = {
|
||||
disclaimer: "Important information.",
|
||||
};
|
||||
|
||||
export function AppBar({ onIconClick }: Props) {
|
||||
export function AppBar({ onIconClick, onExpanded }: Props) {
|
||||
const online = useNetwork();
|
||||
const queryClient = useQueryClient();
|
||||
const codex = useCodexConnection();
|
||||
const persistence = usePersistence(codex.enabled);
|
||||
const navigate = useNavigate();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
useEffect(() => {
|
||||
queryClient.invalidateQueries({
|
||||
@ -80,6 +84,12 @@ export function AppBar({ onIconClick }: Props) {
|
||||
? "#3EE089"
|
||||
: "var(--codex-input-color-warning)";
|
||||
|
||||
const icon = isMobile ? (
|
||||
<Logo onClick={() => onExpanded(true)}></Logo>
|
||||
) : (
|
||||
icons[title]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@ -89,7 +99,7 @@ export function AppBar({ onIconClick }: Props) {
|
||||
["app-bar--no-persistence", !persistence.enabled]
|
||||
)}>
|
||||
<div className="row gap">
|
||||
<span onClick={onIconClick}>{icons[title]}</span>
|
||||
<span onClick={onIconClick}>{icon}</span>
|
||||
|
||||
<div>
|
||||
<h1>{title}</h1>
|
||||
|
||||
@ -86,4 +86,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
aside {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,12 @@
|
||||
|
||||
.row {
|
||||
margin-bottom: 16px;
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
& {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.group {
|
||||
|
||||
@ -185,40 +185,38 @@ export function AvailabilityForm({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="row gap">
|
||||
<div className="group">
|
||||
<Input
|
||||
id="minPrice"
|
||||
name="minPrice"
|
||||
type="number"
|
||||
label="Min price"
|
||||
min={0}
|
||||
onChange={onInputChange}
|
||||
value={availability.minPrice.toString()}
|
||||
/>
|
||||
<Tooltip message={"Minimum price to be paid (in amount of tokens)"}>
|
||||
<InfoIcon></InfoIcon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="row gap">
|
||||
<div className="group">
|
||||
<Input
|
||||
id="minPrice"
|
||||
name="minPrice"
|
||||
type="number"
|
||||
label="Min price"
|
||||
min={0}
|
||||
onChange={onInputChange}
|
||||
value={availability.minPrice.toString()}
|
||||
/>
|
||||
<Tooltip message={"Minimum price to be paid (in amount of tokens)"}>
|
||||
<InfoIcon></InfoIcon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div className="group">
|
||||
<Input
|
||||
id="maxCollateral"
|
||||
name="maxCollateral"
|
||||
type="number"
|
||||
label="Max collateral"
|
||||
min={0}
|
||||
onChange={onInputChange}
|
||||
value={availability.maxCollateral.toString()}
|
||||
/>
|
||||
<Tooltip
|
||||
message={
|
||||
"Maximum collateral user is willing to pay per filled Slot (in amount of tokens)"
|
||||
}>
|
||||
<InfoIcon></InfoIcon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="group">
|
||||
<Input
|
||||
id="maxCollateral"
|
||||
name="maxCollateral"
|
||||
type="number"
|
||||
label="Max collateral"
|
||||
min={0}
|
||||
onChange={onInputChange}
|
||||
value={availability.maxCollateral.toString()}
|
||||
/>
|
||||
<Tooltip
|
||||
message={
|
||||
"Maximum collateral user is willing to pay per filled Slot (in amount of tokens)"
|
||||
}>
|
||||
<InfoIcon></InfoIcon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ export function AvailabilitySuccess({ dispatch }: AvailabilityComponentProps) {
|
||||
|
||||
return (
|
||||
<Placeholder
|
||||
Icon={<SuccessCircleIcon />}
|
||||
Icon={<SuccessCircleIcon width={40} height={40} />}
|
||||
title="Success"
|
||||
message="The new sale will appear in your sale list. You can safely close this dialog."></Placeholder>
|
||||
);
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
.sunburst {
|
||||
height: 350px;
|
||||
width: 350px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ import { Strings } from "../../utils/strings";
|
||||
import { PrettyBytes } from "../../utils/bytes";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { CallbackDataParams, ECBasicOption } from "echarts/types/dist/shared";
|
||||
// Import the echarts core module, which provides the necessary interfaces for using echarts.
|
||||
import * as echarts from "echarts/core";
|
||||
|
||||
// Import bar charts, all suffixed with Chart
|
||||
@ -19,7 +18,6 @@ type Props = {
|
||||
};
|
||||
|
||||
import { TooltipComponent } from "echarts/components";
|
||||
|
||||
import { SVGRenderer } from "echarts/renderers";
|
||||
|
||||
echarts.use([SunburstChart, TooltipComponent, SVGRenderer]);
|
||||
@ -97,80 +95,80 @@ export function Sunburst({ availabilities, space }: Props) {
|
||||
};
|
||||
});
|
||||
|
||||
const option: ECBasicOption = {
|
||||
series: {
|
||||
type: "sunburst",
|
||||
data: [
|
||||
...data,
|
||||
{
|
||||
name: "Space remaining",
|
||||
value:
|
||||
space.quotaMaxBytes -
|
||||
space.quotaReservedBytes -
|
||||
space.quotaUsedBytes,
|
||||
children: [],
|
||||
itemStyle: {
|
||||
color: "#2F2F2F",
|
||||
borderColor: "transparent",
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: "#333",
|
||||
textStyle: {
|
||||
color: "#fff",
|
||||
},
|
||||
formatter: (params: CallbackDataParams) => {
|
||||
return (
|
||||
params.marker +
|
||||
" Space remaining " +
|
||||
PrettyBytes(
|
||||
space.quotaMaxBytes -
|
||||
space.quotaReservedBytes -
|
||||
space.quotaUsedBytes
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
radius: [60, "90%"],
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
levels: [
|
||||
{},
|
||||
{
|
||||
r0: "35%",
|
||||
r: "70%",
|
||||
label: {
|
||||
align: "right",
|
||||
},
|
||||
},
|
||||
{
|
||||
r0: "75%",
|
||||
r: "85%",
|
||||
itemStyle: {},
|
||||
label: {
|
||||
position: "outside",
|
||||
textShadowBlur: 5,
|
||||
textShadowColor: "#333",
|
||||
},
|
||||
downplay: {
|
||||
label: {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
tooltip: {
|
||||
// type: "item",
|
||||
},
|
||||
};
|
||||
|
||||
if (chart.current) {
|
||||
const option: ECBasicOption = {
|
||||
series: {
|
||||
type: "sunburst",
|
||||
data: [
|
||||
...data,
|
||||
{
|
||||
name: "Space remaining",
|
||||
value:
|
||||
space.quotaMaxBytes -
|
||||
space.quotaReservedBytes -
|
||||
space.quotaUsedBytes,
|
||||
children: [],
|
||||
itemStyle: {
|
||||
color: "#2F2F2F",
|
||||
borderColor: "transparent",
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: "#333",
|
||||
textStyle: {
|
||||
color: "#fff",
|
||||
},
|
||||
formatter: (params: CallbackDataParams) => {
|
||||
return (
|
||||
params.marker +
|
||||
" Space remaining " +
|
||||
PrettyBytes(
|
||||
space.quotaMaxBytes -
|
||||
space.quotaReservedBytes -
|
||||
space.quotaUsedBytes
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
radius: [60, "90%"],
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
levels: [
|
||||
{},
|
||||
{
|
||||
r0: "35%",
|
||||
r: "70%",
|
||||
label: {
|
||||
align: "right",
|
||||
},
|
||||
},
|
||||
{
|
||||
r0: "75%",
|
||||
r: "85%",
|
||||
itemStyle: {},
|
||||
label: {
|
||||
position: "outside",
|
||||
textShadowBlur: 5,
|
||||
textShadowColor: "#333",
|
||||
},
|
||||
downplay: {
|
||||
label: {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
tooltip: {
|
||||
// type: "item",
|
||||
},
|
||||
};
|
||||
|
||||
chart.current.setOption(option);
|
||||
// chart.current.off("click");
|
||||
// chart.current.on("click", function (params) {
|
||||
@ -190,5 +188,16 @@ export function Sunburst({ availabilities, space }: Props) {
|
||||
// });
|
||||
}
|
||||
|
||||
return <div id="chart" ref={div} className="sunburst"></div>;
|
||||
const size = window.innerWidth > 500 ? 350 : 300;
|
||||
|
||||
return (
|
||||
<div
|
||||
id="chart"
|
||||
ref={div}
|
||||
className="sunburst"
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
}}></div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 550px;
|
||||
/* min-width: 550px; */
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
|
||||
@ -13,7 +13,7 @@ export const CustomStateCellRender = ({ state, message }: Props) => {
|
||||
pending: PurchaseStateIcon,
|
||||
submitted: PurchaseStateIcon,
|
||||
started: PurchaseStateIcon,
|
||||
finished: SuccessCircleIcon,
|
||||
finished: () => <SuccessCircleIcon width={20} />,
|
||||
cancelled: ErrorCircleIcon,
|
||||
errored: ErrorCircleIcon,
|
||||
};
|
||||
|
||||
@ -16,4 +16,10 @@
|
||||
border: 1px solid #96969633;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.folder-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,4 +26,15 @@
|
||||
table thead tr th {
|
||||
background-color: #14141499;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
table th:nth-child(2),
|
||||
table td:nth-child(2),
|
||||
table th:nth-child(3),
|
||||
table td:nth-child(3),
|
||||
section,
|
||||
.filters {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ export function FolderButton({ folders, onFolderToggle }: Props) {
|
||||
{folders.map(([folder, isActive]) => (
|
||||
<div key={folder} onClick={() => onFolderToggle(folder)}>
|
||||
<span>{folder}</span>
|
||||
{isActive && <SuccessCircleIcon></SuccessCircleIcon>}
|
||||
{isActive && <SuccessCircleIcon width={20}></SuccessCircleIcon>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -116,7 +116,7 @@ export function HealthChecks({ online, onStepValid }: Props) {
|
||||
{isAddressInvalid ? (
|
||||
<ErrorCircleIcon width={16} />
|
||||
) : (
|
||||
<SuccessCircleIcon />
|
||||
<SuccessCircleIcon width={20} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -129,7 +129,7 @@ export function HealthChecks({ online, onStepValid }: Props) {
|
||||
value={port}
|
||||
isInvalid={isPortInvalid}
|
||||
placeholder="8080"></Input>
|
||||
<SuccessCircleIcon></SuccessCircleIcon>
|
||||
<SuccessCircleIcon width={20}></SuccessCircleIcon>
|
||||
</div>
|
||||
|
||||
<div className="refresh">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { attributes } from "../../utils/attributes";
|
||||
import "./menu.css";
|
||||
import { ComponentType, useState } from "react";
|
||||
import { ComponentType, useEffect } from "react";
|
||||
import { classnames } from "../../utils/classnames";
|
||||
import HomeIcon from "../../assets/icons/home.svg?react";
|
||||
import ExpandIcon from "../../assets/icons/expand.svg?react";
|
||||
@ -19,11 +19,17 @@ import SettingsIcon from "../../assets/icons/settings.svg?react";
|
||||
import HelpIcon from "../../assets/icons/help.svg?react";
|
||||
import DisclaimerIcon from "../../assets/icons/disclaimer.svg?react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { useIsMobile } from "../../hooks/useMobile";
|
||||
|
||||
export type MenuItemComponentProps = {
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
export type Props = {
|
||||
isExpanded: boolean;
|
||||
onExpanded: (val: boolean) => void;
|
||||
};
|
||||
|
||||
export type MenuItem =
|
||||
| {
|
||||
type: "separator";
|
||||
@ -36,16 +42,28 @@ export type MenuItem =
|
||||
Component: ComponentType<MenuItemComponentProps>;
|
||||
};
|
||||
|
||||
export function Menu() {
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
export function Menu({ isExpanded, onExpanded }: Props) {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const onLogoClick = () => {
|
||||
if (isExpanded === false) {
|
||||
setIsExpanded(true);
|
||||
onExpanded(true);
|
||||
}
|
||||
};
|
||||
|
||||
const onExpandMenu = () => setIsExpanded(!isExpanded);
|
||||
useEffect(() => {
|
||||
if (isMobile) {
|
||||
onExpanded(false);
|
||||
}
|
||||
}, [isMobile, onExpanded]);
|
||||
|
||||
const onExpandMenu = () => onExpanded(!isExpanded);
|
||||
|
||||
const onClose = () => {
|
||||
if (isMobile) {
|
||||
onExpanded(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -64,35 +82,38 @@ export function Menu() {
|
||||
</header>
|
||||
|
||||
<div className="items">
|
||||
<NavLink to="/dashboard" end>
|
||||
<NavLink onClick={onClose} to="/dashboard" end>
|
||||
<span>
|
||||
<HomeIcon />
|
||||
</span>
|
||||
<span>Dashboard</span>
|
||||
</NavLink>
|
||||
<NavLink to="/dashboard/wallet">
|
||||
<NavLink onClick={onClose} to="/dashboard/wallet">
|
||||
<span>
|
||||
<WalletIcon width={20} height={20} />
|
||||
</span>
|
||||
<span>Wallet</span>
|
||||
</NavLink>
|
||||
<NavLink to="/dashboard/files">
|
||||
<NavLink onClick={onClose} to="/dashboard/files">
|
||||
<span>
|
||||
<FilesIcon width={20} />
|
||||
</span>
|
||||
<span>Files</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/dashboard/nodes"
|
||||
onClick={onClose}
|
||||
to="#"
|
||||
aria-disabled={true}
|
||||
data-title="Coming soon">
|
||||
data-title="Coming soon"
|
||||
end>
|
||||
<span>
|
||||
<NodesIcon width={20} />
|
||||
</span>
|
||||
<span>Nodes</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/dashboard/analytics"
|
||||
onClick={onClose}
|
||||
to="#"
|
||||
aria-disabled={true}
|
||||
title="Coming soon"
|
||||
data-title="Coming soon">
|
||||
@ -102,7 +123,8 @@ export function Menu() {
|
||||
<span>Analytics</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/dashboard/device"
|
||||
onClick={onClose}
|
||||
to="#"
|
||||
aria-disabled={true}
|
||||
title="Coming soon"
|
||||
data-title="Coming soon">
|
||||
@ -112,45 +134,45 @@ export function Menu() {
|
||||
<span>Devices</span>
|
||||
</NavLink>
|
||||
<hr />
|
||||
<NavLink to="/dashboard/purchases">
|
||||
<NavLink onClick={onClose} to="/dashboard/purchases">
|
||||
<span>
|
||||
<PurchaseIcon />
|
||||
</span>
|
||||
<span>Purchases</span>
|
||||
</NavLink>
|
||||
<NavLink to="/dashboard/availabilities">
|
||||
<NavLink onClick={onClose} to="/dashboard/availabilities">
|
||||
<span>
|
||||
<HostIcon />
|
||||
</span>
|
||||
<span>Host</span>
|
||||
</NavLink>
|
||||
<hr />
|
||||
<NavLink to="/dashboard/peers">
|
||||
<NavLink onClick={onClose} to="/dashboard/peers">
|
||||
<span>
|
||||
<PeersIcon width={20} />
|
||||
</span>
|
||||
<span>Peers</span>
|
||||
</NavLink>
|
||||
<NavLink to="/dashboard/logs">
|
||||
<NavLink onClick={onClose} to="/dashboard/logs">
|
||||
<span>
|
||||
<LogsIcon width={24} />
|
||||
</span>
|
||||
<span>Log</span>
|
||||
</NavLink>
|
||||
<section></section>
|
||||
<NavLink to="/dashboard/settings">
|
||||
<NavLink onClick={onClose} to="/dashboard/settings">
|
||||
<span>
|
||||
<SettingsIcon width={24} />
|
||||
</span>
|
||||
<span>Settings</span>
|
||||
</NavLink>
|
||||
<NavLink to="/dashboard/help">
|
||||
<NavLink onClick={onClose} to="/dashboard/help">
|
||||
<span>
|
||||
<HelpIcon />
|
||||
</span>
|
||||
<span>Help</span>
|
||||
</NavLink>
|
||||
<NavLink to="/dashboard/disclaimer">
|
||||
<NavLink onClick={onClose} to="/dashboard/disclaimer">
|
||||
<span>
|
||||
<DisclaimerIcon />
|
||||
</span>
|
||||
|
||||
@ -6,33 +6,25 @@
|
||||
transition: left 0.25s;
|
||||
position: sticky;
|
||||
z-index: 10;
|
||||
view-transition-name: main-menu;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
transition:
|
||||
width 0.5s,
|
||||
font-size 0.5s,
|
||||
left 0.05s;
|
||||
min-width: 0;
|
||||
left 0.5s;
|
||||
width: 272px;
|
||||
min-width: 80px;
|
||||
|
||||
@media (max-width: 1199px) {
|
||||
@media (max-width: 800px) {
|
||||
& {
|
||||
width: 80px;
|
||||
.items {
|
||||
a {
|
||||
width: 26px;
|
||||
gap: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
left: -300px;
|
||||
position: fixed;
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
span + span {
|
||||
font-size: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
&[aria-expanded] {
|
||||
left: 0px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,9 +55,11 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
span + span {
|
||||
font-size: 0;
|
||||
display: none;
|
||||
@media (min-width: 801px) {
|
||||
span + span {
|
||||
font-size: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,17 +148,17 @@
|
||||
top: 115px;
|
||||
}
|
||||
|
||||
&:has(.active:nth-child(4))::before {
|
||||
/* &:has(.active:nth-child(4))::before {
|
||||
top: 158px;
|
||||
}
|
||||
|
||||
} */
|
||||
/*
|
||||
&:has(.active:nth-child(5))::before {
|
||||
top: 201px;
|
||||
}
|
||||
|
||||
&:has(.active:nth-child(6))::before {
|
||||
top: 244px;
|
||||
}
|
||||
} */
|
||||
|
||||
&:has(.active:nth-child(8))::before {
|
||||
top: 339px;
|
||||
@ -227,7 +221,7 @@
|
||||
margin-left: 6px;
|
||||
|
||||
&:hover:not([aria-disabled="true"]),
|
||||
&.active {
|
||||
&.active:not([aria-disabled="true"]) {
|
||||
background-color: var(--codex-highlight-color);
|
||||
color: #c7c7c7;
|
||||
}
|
||||
@ -244,10 +238,15 @@
|
||||
span + span {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&.active span:first-child {
|
||||
@media (min-width: 801px) {
|
||||
span + span {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.active:not([aria-disabled="true"]) span:first-child {
|
||||
color: var(--codex-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,14 +12,14 @@ export function PeersCard() {
|
||||
|
||||
const nodes = data?.table.nodes ?? [];
|
||||
const actives = PeerUtils.countActives(nodes);
|
||||
const degrees = PeerUtils.calculareDegrees(nodes);
|
||||
const percent = PeerUtils.calcularePercent(nodes);
|
||||
const good = PeerUtils.isGoodQuality(actives);
|
||||
|
||||
return (
|
||||
<div className="peers-card">
|
||||
<main className="row gap">
|
||||
<PeersMap nodes={data?.table.nodes || []}></PeersMap>
|
||||
<PeersChart actives={actives} degrees={degrees}></PeersChart>
|
||||
<PeersChart actives={actives} percent={percent}></PeersChart>
|
||||
</main>
|
||||
<footer>
|
||||
<PeersQuality good={good}></PeersQuality>
|
||||
|
||||
@ -1,23 +1,110 @@
|
||||
import "./PeersChart.css";
|
||||
import * as echarts from "echarts/core";
|
||||
import { TooltipComponent } from "echarts/components";
|
||||
import { SVGRenderer } from "echarts/renderers";
|
||||
import { GaugeChart } from "echarts/charts";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
type Props = {
|
||||
actives: number;
|
||||
degrees: number;
|
||||
percent: number;
|
||||
};
|
||||
|
||||
type CustomCSSProperties = React.CSSProperties & {
|
||||
"--codex-peers-degrees": number;
|
||||
};
|
||||
// type CustomCSSProperties = React.CSSProperties & {
|
||||
// "--codex-peers-degrees": number;
|
||||
// };
|
||||
|
||||
export function PeersChart({ actives, degrees }: Props) {
|
||||
const style: CustomCSSProperties = {
|
||||
"--codex-peers-degrees": degrees,
|
||||
};
|
||||
echarts.use([GaugeChart, TooltipComponent, SVGRenderer]);
|
||||
|
||||
export function PeersChart({ actives, percent }: Props) {
|
||||
const div = useRef<HTMLDivElement>(null);
|
||||
const chart = useRef<echarts.EChartsType | null>(null);
|
||||
const [, setRefresher] = useState(Date.now());
|
||||
|
||||
useEffect(() => {
|
||||
if (div.current && !chart.current) {
|
||||
chart.current = echarts.init(div.current, null, {
|
||||
renderer: "svg",
|
||||
});
|
||||
setRefresher(Date.now());
|
||||
}
|
||||
}, [chart, div]);
|
||||
|
||||
// const style: CustomCSSProperties = {
|
||||
// "--codex-peers-degrees": percent,
|
||||
// };
|
||||
|
||||
if (chart.current) {
|
||||
const options = {
|
||||
series: [
|
||||
{
|
||||
type: "gauge",
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
width: 30,
|
||||
color: [
|
||||
[0.2, "var(--codex-color-error-hexa)"],
|
||||
[0.5, "rgb(var(--codex-color-warning))"],
|
||||
[1, "var(--codex-color-primary)"],
|
||||
],
|
||||
},
|
||||
},
|
||||
pointer: {
|
||||
itemStyle: {
|
||||
color: "auto",
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
distance: -30,
|
||||
length: 8,
|
||||
lineStyle: {
|
||||
color: "#fff",
|
||||
width: 2,
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
distance: -30,
|
||||
length: 30,
|
||||
lineStyle: {
|
||||
color: "#fff",
|
||||
width: 2,
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
color: "inherit",
|
||||
distance: 40,
|
||||
fontSize: 0,
|
||||
},
|
||||
detail: {
|
||||
valueAnimation: true,
|
||||
formatter: actives + "",
|
||||
color: "inherit",
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: percent * 100,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
chart.current.setOption(options);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={style} className="peers-chart">
|
||||
<div></div>
|
||||
<span>{actives}</span>
|
||||
</div>
|
||||
<>
|
||||
{/* <div style={style} className="peers-chart">
|
||||
<div></div>
|
||||
<span>{actives}</span>
|
||||
</div> */}
|
||||
<div
|
||||
id="chart"
|
||||
ref={div}
|
||||
className="gauge"
|
||||
style={{
|
||||
width: 250,
|
||||
height: 250,
|
||||
}}></div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ export function PeersQuality({ good }: Props) {
|
||||
if (good) {
|
||||
return (
|
||||
<div className="peers-quality">
|
||||
<SuccessCircleIcon></SuccessCircleIcon>
|
||||
<SuccessCircleIcon width={20}></SuccessCircleIcon>
|
||||
<span>Peer connections in good standing. </span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -55,6 +55,14 @@ export const PeerUtils = {
|
||||
return (actives / total) * 180
|
||||
},
|
||||
|
||||
calcularePercent: (peers: PeerNode[]) => {
|
||||
const actives = PeerUtils.countActives(peers);
|
||||
const total = peers.length || 1;
|
||||
|
||||
return (actives / total) * 100
|
||||
},
|
||||
|
||||
|
||||
isGoodQuality(actives: number) {
|
||||
return actives > 0
|
||||
},
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
width: 500px;
|
||||
/* width: 500px; */
|
||||
flex: 1 1 auto;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
.storage-request {
|
||||
.modal dialog {
|
||||
width: 80%;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
@ -29,11 +29,3 @@
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.storageRequestFileChooser-hr {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.storageRequestFileChooser-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@ -13,6 +13,13 @@
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
@media screen and (max-width: 801px) {
|
||||
& {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
> div {
|
||||
height: 74px;
|
||||
position: relative;
|
||||
@ -58,6 +65,12 @@
|
||||
cursor: pointer;
|
||||
transition: 0.35s box-shadow;
|
||||
|
||||
@media screen and (max-width: 801px) {
|
||||
& {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 2px var(--codex-preset-border-color);
|
||||
}
|
||||
@ -128,6 +141,12 @@
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
@media screen and (max-width: 801px) {
|
||||
& {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ export function StorageRequestSuccess({
|
||||
|
||||
return (
|
||||
<Placeholder
|
||||
Icon={<SuccessCircleIcon />}
|
||||
Icon={<SuccessCircleIcon width={40} height={40} />}
|
||||
className="storage-success"
|
||||
title="Your request is being processed."
|
||||
message="Processing your request may take some time. Once completed, it will
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 50%;
|
||||
min-width: 420px;
|
||||
/* min-width: 420px; */
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
@ -69,4 +69,10 @@
|
||||
footer {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
img {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
src/hooks/useMobile.ts
Normal file
19
src/hooks/useMobile.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export const useIsMobile = () => {
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth <= 800);
|
||||
|
||||
const checkIsMobile = () => {
|
||||
setIsMobile(window.innerWidth <= 800);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', checkIsMobile);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', checkIsMobile);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return isMobile;
|
||||
};
|
||||
@ -4,15 +4,24 @@
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
|
||||
dialog {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
> .card {
|
||||
flex: 1 1 50%;
|
||||
}
|
||||
|
||||
.table {
|
||||
@media (max-width: 800px) {
|
||||
th:nth-child(3),
|
||||
td:nth-child(3),
|
||||
th:nth-child(4),
|
||||
td:nth-child(4),
|
||||
th:nth-child(5),
|
||||
td:nth-child(5),
|
||||
th:nth-child(6),
|
||||
td:nth-child(6) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
table thead tr th {
|
||||
background-color: #14141499;
|
||||
}
|
||||
@ -61,7 +70,6 @@
|
||||
|
||||
aside {
|
||||
display: flex;
|
||||
width: 400px;
|
||||
flex: 1 1 30%;
|
||||
|
||||
.card {
|
||||
|
||||
@ -43,11 +43,17 @@
|
||||
|
||||
h4 {
|
||||
font-family: Inter;
|
||||
font-size: 32px;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 38.73px;
|
||||
letter-spacing: 0.01em;
|
||||
color: white;
|
||||
|
||||
@media (min-width: 801px) {
|
||||
& {
|
||||
font-size: 32px;
|
||||
line-height: 38.73px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emoji {
|
||||
@ -89,4 +95,11 @@
|
||||
flex-basis: 66%;
|
||||
}
|
||||
}
|
||||
|
||||
.gauge {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,13 @@
|
||||
border-radius: 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
& {
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
> div:first-child {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
@ -9,13 +9,12 @@
|
||||
}
|
||||
|
||||
> div:first-child {
|
||||
width: calc(100% - 16px);
|
||||
width: 100%;
|
||||
border: 1px solid #96969633;
|
||||
padding: 16px;
|
||||
border-radius: 16px;
|
||||
position: relative;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
@media (min-width: 801px) {
|
||||
& {
|
||||
width: calc(100% - 128px - 16px);
|
||||
padding: 16px 16px 16px 128px;
|
||||
@ -26,7 +25,7 @@
|
||||
ul {
|
||||
display: none;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
@media (min-width: 801px) {
|
||||
& {
|
||||
list-style-type: none;
|
||||
width: 71px;
|
||||
@ -94,24 +93,18 @@
|
||||
border-radius: 16px;
|
||||
max-width: 280px;
|
||||
padding: 16px;
|
||||
transform: scale(0.7);
|
||||
/* transform: scale(0.7); */
|
||||
width: 280px;
|
||||
|
||||
@media (max-width: 999px) {
|
||||
@media (max-width: 800px) {
|
||||
& {
|
||||
position: relative;
|
||||
bottom: -32px;
|
||||
left: -32px;
|
||||
width: calc(100% - 32px);
|
||||
max-width: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
@media (min-width: 801px) {
|
||||
& {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
@ -170,15 +163,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.peers-chart {
|
||||
transform: scale(0.5);
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
transform: scale(0.73);
|
||||
@media (max-width: 800px) {
|
||||
th:nth-child(2),
|
||||
td:nth-child(2) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gauge {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ export const PeersRoute = () => {
|
||||
<Cell>
|
||||
{node.seen ? (
|
||||
<div className="status--active">
|
||||
<SuccessCircleIcon /> Active
|
||||
<SuccessCircleIcon width={20} /> Active
|
||||
</div>
|
||||
) : (
|
||||
<div className="status--inactive">
|
||||
@ -76,7 +76,7 @@ export const PeersRoute = () => {
|
||||
});
|
||||
|
||||
const actives = PeerUtils.countActives(sorted);
|
||||
const degrees = PeerUtils.calculareDegrees(sorted);
|
||||
const percent = PeerUtils.calcularePercent(sorted);
|
||||
const good = PeerUtils.isGoodQuality(actives);
|
||||
|
||||
return (
|
||||
@ -96,7 +96,7 @@ export const PeersRoute = () => {
|
||||
<span>Connections</span>
|
||||
</header>
|
||||
<main>
|
||||
<PeersChart actives={actives} degrees={degrees}></PeersChart>
|
||||
<PeersChart actives={actives} percent={percent}></PeersChart>
|
||||
</main>
|
||||
<footer>
|
||||
<PeersQuality good={good}></PeersQuality>
|
||||
|
||||
@ -10,5 +10,18 @@
|
||||
table thead tr th {
|
||||
background-color: #14141499;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
th:nth-child(2),
|
||||
td:nth-child(2),
|
||||
th:nth-child(3),
|
||||
td:nth-child(3),
|
||||
th:nth-child(5),
|
||||
td:nth-child(5),
|
||||
th:nth-child(6),
|
||||
td:nth-child(6) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
.wallet-page {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.card {
|
||||
filter: grayscale(30);
|
||||
@ -12,7 +13,6 @@
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
@ -20,6 +20,13 @@
|
||||
border-bottom: 1px solid #96969633;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
display: none;
|
||||
|
||||
@media (min-width: 801px) {
|
||||
& {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
|
||||
@ -4,31 +4,35 @@ import { useState } from "react";
|
||||
import { AppBar } from "../components/AppBar/AppBar";
|
||||
import { Backdrop } from "@codex-storage/marketplace-ui-components";
|
||||
import { Outlet, ScrollRestoration } from "react-router-dom";
|
||||
import { useIsMobile } from "../hooks/useMobile";
|
||||
|
||||
export const Root = () => {
|
||||
const [hasMobileMenu, setHasMobileMenu] = useState(false);
|
||||
const isMobile = useIsMobile();
|
||||
const [isExpanded, setIsExpanded] = useState(!isMobile);
|
||||
|
||||
const onExpanded = (val: boolean) => setIsExpanded(val);
|
||||
|
||||
const onIconClick = () => {
|
||||
if (window.innerWidth <= 999) {
|
||||
setHasMobileMenu(true);
|
||||
if (isMobile) {
|
||||
setIsExpanded(true);
|
||||
}
|
||||
};
|
||||
|
||||
const onClose = () => setHasMobileMenu(false);
|
||||
const onClose = () => setIsExpanded(false);
|
||||
|
||||
return (
|
||||
<div className="layout">
|
||||
<Menu></Menu>
|
||||
<Menu isExpanded={isExpanded} onExpanded={onExpanded}></Menu>
|
||||
|
||||
<main>
|
||||
<AppBar onIconClick={onIconClick} />
|
||||
<AppBar onIconClick={onIconClick} onExpanded={onExpanded} />
|
||||
<div>
|
||||
<ScrollRestoration></ScrollRestoration>
|
||||
<Outlet />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Backdrop onClose={onClose} open={hasMobileMenu}></Backdrop>
|
||||
<Backdrop onClose={onClose} open={isExpanded && isMobile}></Backdrop>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user