Update design
This commit is contained in:
parent
47915d7432
commit
ea593359e3
|
@ -9,7 +9,7 @@
|
|||
"version": "0.0.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.41",
|
||||
"@codex-storage/marketplace-ui-components": "0.0.42",
|
||||
"@codex-storage/sdk-js": "^0.0.15",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@sentry/react": "^8.31.0",
|
||||
|
@ -26,7 +26,6 @@
|
|||
"devDependencies": {
|
||||
"@playwright/test": "^1.48.0",
|
||||
"@svgr/plugin-svgo": "^8.1.0",
|
||||
"@tanstack/router-devtools": "^1.58.7",
|
||||
"@tanstack/router-plugin": "^1.58.4",
|
||||
"@types/node": "^22.7.5",
|
||||
"@types/react": "^18.3.8",
|
||||
|
@ -425,9 +424,9 @@
|
|||
"peer": true
|
||||
},
|
||||
"node_modules/@codex-storage/marketplace-ui-components": {
|
||||
"version": "0.0.41",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.41.tgz",
|
||||
"integrity": "sha512-zAbiN7yzpCDpGgGfqbJTutDjsxIKCj3QZ0wWuk3sk74nqhv16LGjvYTL6r2E35LG69Fp+yOGk3wC6t+zSmYcYw==",
|
||||
"version": "0.0.42",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.42.tgz",
|
||||
"integrity": "sha512-JRs7v5rsxNnH3T30UV+DHuJNr25kJ1a1EAqgthA/0okDrcr9IlOVEvl7XrsNBGRDDbJC5MDJkWo9VD2Jh3gAgQ==",
|
||||
"dependencies": {
|
||||
"lucide-react": "^0.453.0"
|
||||
},
|
||||
|
@ -1411,28 +1410,6 @@
|
|||
"react-dom": "^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/router-devtools": {
|
||||
"version": "1.58.7",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/router-devtools/-/router-devtools-1.58.7.tgz",
|
||||
"integrity": "sha512-bZL3VDmS63gOW+RKSXRQ7uagATP1k8sM+ucHrcLy98hcVxzYRVwIwVgqTZY2KtUSXgFwb4LXClAdZdiJM9i+gw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"clsx": "^2.1.1",
|
||||
"goober": "^2.1.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/react-router": "^1.58.7",
|
||||
"react": ">=18",
|
||||
"react-dom": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/router-generator": {
|
||||
"version": "1.74.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.74.2.tgz",
|
||||
|
@ -2728,14 +2705,6 @@
|
|||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
|
@ -3592,14 +3561,6 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/goober": {
|
||||
"version": "2.1.14",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"csstype": "^3.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/graphemer": {
|
||||
"version": "1.4.0",
|
||||
"dev": true,
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"React"
|
||||
],
|
||||
"dependencies": {
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.41",
|
||||
"@codex-storage/marketplace-ui-components": "0.0.42",
|
||||
"@codex-storage/sdk-js": "^0.0.15",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@sentry/react": "^8.31.0",
|
||||
|
@ -42,7 +42,6 @@
|
|||
"devDependencies": {
|
||||
"@playwright/test": "^1.48.0",
|
||||
"@svgr/plugin-svgo": "^8.1.0",
|
||||
"@tanstack/router-devtools": "^1.58.7",
|
||||
"@tanstack/router-plugin": "^1.58.4",
|
||||
"@types/node": "^22.7.5",
|
||||
"@types/react": "^18.3.8",
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M10.926 8.5H16.176L9.42603 18.25V11.5H4.17603L10.926 1.75V8.5Z"
|
||||
fill="#3EE089" />
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 214 B After Width: | Height: | Size: 219 B |
|
@ -13,7 +13,11 @@ import FilesIcon from "../../assets/icons/files.svg?react";
|
|||
import LogsIcon from "../../assets/icons/logs.svg?react";
|
||||
import HostIcon from "../../assets/icons/host.svg?react";
|
||||
import SettingsIcon from "../../assets/icons/settings.svg?react";
|
||||
import WalletIcon from "../../assets/icons/wallet.svg?react";
|
||||
import NetworkFlashIcon from "../../assets/icons/flash.svg?react";
|
||||
import PurchasesIcon from "../../assets/icons/purchase.svg?react";
|
||||
import HelpIcon from "../../assets/icons/help.svg?react";
|
||||
import DisclaimerIcon from "../../assets/icons/disclaimer.svg?react";
|
||||
import { WalletConnect } from "../WalletLogin/WalletLogin";
|
||||
|
||||
type Props = {
|
||||
|
@ -27,6 +31,10 @@ const icons: Record<string, ReactElement> = {
|
|||
files: <FilesIcon width={24} />,
|
||||
logs: <LogsIcon width={24} />,
|
||||
availabilities: <HostIcon width={24} />,
|
||||
wallet: <WalletIcon width={24} />,
|
||||
purchases: <PurchasesIcon width={24} />,
|
||||
help: <HelpIcon width={24} />,
|
||||
disclaimer: <DisclaimerIcon width={24} />,
|
||||
};
|
||||
|
||||
const descriptions: Record<string, string> = {
|
||||
|
@ -36,6 +44,10 @@ const descriptions: Record<string, string> = {
|
|||
files: "Manage your files in your local vault.",
|
||||
logs: "Manage your logs and debug console.",
|
||||
availabilities: "Manage your host data.",
|
||||
wallet: "Manage your Codex wallet.",
|
||||
purchases: "Manage your storage requests.",
|
||||
help: "Quick help resources.",
|
||||
disclaimer: "Important information.",
|
||||
};
|
||||
|
||||
export function AppBar({ onIconClick }: Props) {
|
||||
|
@ -59,6 +71,12 @@ export function AppBar({ onIconClick }: Props) {
|
|||
|
||||
const title =
|
||||
location.pathname.split("/")[2] || location.pathname.split("/")[1];
|
||||
const networkIconColor = online
|
||||
? "#3EE089"
|
||||
: "var(--codex-input-color-error)";
|
||||
const nodesIconColor = codex.enabled
|
||||
? "#3EE089"
|
||||
: "var(--codex-input-color-error)";
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -79,11 +97,11 @@ export function AppBar({ onIconClick }: Props) {
|
|||
<aside className="row gap">
|
||||
<WalletConnect></WalletConnect>
|
||||
<div className="row gap">
|
||||
<NetworkFlashIcon />
|
||||
<NetworkFlashIcon color={networkIconColor} />
|
||||
<span>Network</span>
|
||||
</div>
|
||||
<div className="row gap" onClick={onNodeClick}>
|
||||
<NodesIcon color="var(--codex-color-primary)" width={20} />
|
||||
<NodesIcon color={nodesIconColor} width={20} />
|
||||
<span>Node</span>
|
||||
</div>
|
||||
</aside>
|
||||
|
|
|
@ -5,33 +5,21 @@ import EditIcon from "../../assets/icons/edit.svg?react";
|
|||
|
||||
type Props = {
|
||||
availability: CodexAvailability;
|
||||
// onEdit: () => void;
|
||||
};
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
export function AvailabilityActionsCell(_: Props) {
|
||||
// const onEditClick = async () => {
|
||||
// const unit = availability.totalSize >= 1_000_000_000 ? "gb" : "mb";
|
||||
// const totalSize =
|
||||
// unit === "gb"
|
||||
// ? availability.totalSize / 1_000_000_000
|
||||
// : availability.totalSize / 1_000_000;
|
||||
|
||||
// await WebStorage.set("availability-step-1", {
|
||||
// ...availability,
|
||||
// totalSize,
|
||||
// totalSizeUnit: unit,
|
||||
// });
|
||||
|
||||
// onEdit();
|
||||
// };
|
||||
export function AvailabilityActionsCell({ availability }: Props) {
|
||||
const onEditClick = async () => {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("codexavailabilityedit", { detail: availability })
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Cell>
|
||||
<div className="availability-actions">
|
||||
<ButtonIcon
|
||||
variant="small"
|
||||
// onClick={() => onDetails(content.cid)}
|
||||
onClick={onEditClick}
|
||||
Icon={EditIcon}></ButtonIcon>
|
||||
</div>
|
||||
</Cell>
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
.availability-confirm {
|
||||
header {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.availabilitConfirm-bottom {
|
||||
margin-top: 1.5rem;
|
||||
display: flex;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import "./AvailabilityForm.css";
|
||||
import "./AvailabilityConfirm.css";
|
||||
import { AvailabilityComponentProps } from "./types";
|
||||
import "./AvailabilityConfirm.css";
|
||||
import { Info } from "lucide-react";
|
||||
import { AvailabilitySpaceAllocation } from "./AvailabilitySpaceAllocation";
|
||||
import { useEffect } from "react";
|
||||
import { SpaceAllocation } from "@codex-storage/marketplace-ui-components";
|
||||
import NodesIcon from "../../assets/icons/nodes.svg?react";
|
||||
|
||||
export function AvailabilityConfirm({
|
||||
dispatch,
|
||||
|
@ -18,9 +19,40 @@ export function AvailabilityConfirm({
|
|||
});
|
||||
}, [dispatch]);
|
||||
|
||||
const { quotaMaxBytes, quotaReservedBytes, quotaUsedBytes } = space;
|
||||
const isUpdating = !!availability.id;
|
||||
const allocated = isUpdating
|
||||
? quotaReservedBytes - availability.totalSize + quotaUsedBytes
|
||||
: quotaReservedBytes + quotaUsedBytes;
|
||||
const remaining =
|
||||
availability.totalSize > quotaMaxBytes - allocated
|
||||
? quotaMaxBytes - allocated
|
||||
: quotaMaxBytes - allocated - availability.totalSize;
|
||||
|
||||
return (
|
||||
<>
|
||||
<AvailabilitySpaceAllocation availability={availability} space={space} />
|
||||
<div className="availability-confirm">
|
||||
<header>
|
||||
<NodesIcon width={20}></NodesIcon>
|
||||
<h6>Disk</h6>
|
||||
</header>
|
||||
<SpaceAllocation
|
||||
data={[
|
||||
{
|
||||
title: "Allocated",
|
||||
size: space.quotaUsedBytes,
|
||||
color: "#FF6E61",
|
||||
},
|
||||
{
|
||||
title: "Available",
|
||||
size: space.quotaReservedBytes,
|
||||
color: "#34A0FF",
|
||||
},
|
||||
{
|
||||
title: "Free",
|
||||
size: remaining,
|
||||
color: "#6F6F6F",
|
||||
},
|
||||
]}></SpaceAllocation>
|
||||
|
||||
<div className="availabilitConfirm-bottom">
|
||||
<div className="availabilitConfirm-iconContainer">
|
||||
|
@ -36,6 +68,6 @@ export function AvailabilityConfirm({
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,48 @@
|
|||
.availability-edit {
|
||||
.button div {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
@media (min-width: 801px) {
|
||||
.availabilityCreate .stepper-body {
|
||||
width: 600px;
|
||||
> .button {
|
||||
top: 0;
|
||||
bottom: 0px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
border-radius: 100%;
|
||||
height: 88px;
|
||||
width: 88px;
|
||||
background-color: white;
|
||||
|
||||
div {
|
||||
position: relative;
|
||||
left: 4px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.011em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
svg {
|
||||
color: #969696;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.space-allocation {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,19 +4,20 @@ import {
|
|||
Button,
|
||||
Modal,
|
||||
} from "@codex-storage/marketplace-ui-components";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { AvailabilityForm } from "./AvailabilityForm";
|
||||
import { Pencil } from "lucide-react";
|
||||
import { CodexNodeSpace } from "@codex-storage/sdk-js";
|
||||
import { AvailabilityConfirm } from "./AvailabilityConfirmation";
|
||||
import { WebStorage } from "../../utils/web-storage";
|
||||
import { AvailabilityState } from "./types";
|
||||
import { STEPPER_DURATION } from "../../utils/constants";
|
||||
import { GB, STEPPER_DURATION } from "../../utils/constants";
|
||||
import { useAvailabilityMutation } from "./useAvailabilityMutation";
|
||||
import { AvailabilitySuccess } from "./AvailabilitySuccess";
|
||||
import { AvailabilityError } from "./AvailabilityError";
|
||||
import "./AvailabilityEdit.css";
|
||||
import PlusIcon from "../../assets/icons/plus.svg?react";
|
||||
import HostIcon from "../../assets/icons/host.svg?react";
|
||||
import { Times } from "../../utils/times";
|
||||
|
||||
type Props = {
|
||||
space: CodexNodeSpace;
|
||||
|
@ -27,8 +28,8 @@ type Props = {
|
|||
const CONFIRM_STATE = 2;
|
||||
|
||||
const defaultAvailabilityData: AvailabilityState = {
|
||||
totalSize: 1,
|
||||
duration: 1,
|
||||
totalSize: 0.5 * GB,
|
||||
duration: Times.unitValue("days"),
|
||||
minPrice: 0,
|
||||
maxCollateral: 0,
|
||||
totalSizeUnit: "gb",
|
||||
|
@ -46,7 +47,7 @@ export function AvailabilityEdit({
|
|||
);
|
||||
const { state, dispatch } = useStepperReducer();
|
||||
const { mutateAsync, error } = useAvailabilityMutation(dispatch, state);
|
||||
const [availabilityId, setAvailabilityId] = useState<string | null>(null);
|
||||
const editAvailabilityValue = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
|
@ -67,24 +68,24 @@ export function AvailabilityEdit({
|
|||
}, [dispatch]);
|
||||
|
||||
// We use a custom event to not re render the sunburst component
|
||||
useEffect(() => {
|
||||
const onAvailabilityIdChange = (e: Event) => {
|
||||
const custom = e as CustomEvent;
|
||||
setAvailabilityId(custom.detail);
|
||||
};
|
||||
// useEffect(() => {
|
||||
// const onAvailabilityIdChange = (e: Event) => {
|
||||
// const custom = e as CustomEvent;
|
||||
// setAvailabilityId(custom.detail);
|
||||
// };
|
||||
|
||||
document.addEventListener(
|
||||
"codexavailabilityid",
|
||||
onAvailabilityIdChange,
|
||||
false
|
||||
);
|
||||
// document.addEventListener(
|
||||
// "codexavailabilityid",
|
||||
// onAvailabilityIdChange,
|
||||
// false
|
||||
// );
|
||||
|
||||
return () =>
|
||||
document.removeEventListener(
|
||||
"codexavailabilityid",
|
||||
onAvailabilityIdChange
|
||||
);
|
||||
}, []);
|
||||
// return () =>
|
||||
// document.removeEventListener(
|
||||
// "codexavailabilityid",
|
||||
// onAvailabilityIdChange
|
||||
// );
|
||||
// }, []);
|
||||
|
||||
const components = [
|
||||
AvailabilityForm,
|
||||
|
@ -111,7 +112,8 @@ export function AvailabilityEdit({
|
|||
WebStorage.set("availability-step", step);
|
||||
|
||||
if (step == CONFIRM_STATE) {
|
||||
mutateAsync(availability);
|
||||
const { slots, name, ...rest } = availability as any;
|
||||
mutateAsync(rest);
|
||||
} else {
|
||||
dispatch({
|
||||
step,
|
||||
|
@ -123,18 +125,12 @@ export function AvailabilityEdit({
|
|||
const onAvailabilityChange = (data: Partial<AvailabilityState>) => {
|
||||
const val = { ...availability, ...data };
|
||||
|
||||
WebStorage.set("availability", val);
|
||||
|
||||
setAvailability(val);
|
||||
};
|
||||
|
||||
const onOpen = () => {
|
||||
if (availability.id) {
|
||||
WebStorage.set("availability-step", 0);
|
||||
WebStorage.set("availability", defaultAvailabilityData);
|
||||
|
||||
setAvailability(defaultAvailabilityData);
|
||||
}
|
||||
const onOpen = useCallback(() => {
|
||||
setAvailability(defaultAvailabilityData);
|
||||
editAvailabilityValue.current = 0;
|
||||
|
||||
dispatch({
|
||||
type: "open",
|
||||
|
@ -144,7 +140,7 @@ export function AvailabilityEdit({
|
|||
step: 0,
|
||||
type: "next",
|
||||
});
|
||||
};
|
||||
}, [editAvailabilityValue, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("codexavailabilitycreate", onOpen, false);
|
||||
|
@ -153,6 +149,40 @@ export function AvailabilityEdit({
|
|||
document.removeEventListener("codexavailabilitycreate", onOpen);
|
||||
}, [onOpen]);
|
||||
|
||||
const onEdit = useCallback(
|
||||
(event: Event) => {
|
||||
const e = event as CustomEvent<AvailabilityState>;
|
||||
const a = e.detail;
|
||||
|
||||
editAvailabilityValue.current = a.totalSize;
|
||||
WebStorage.set("availability-step", 0);
|
||||
WebStorage.set("availability", a);
|
||||
|
||||
const unit = Times.unit(a.duration);
|
||||
|
||||
setAvailability({
|
||||
...a,
|
||||
durationUnit: unit as "hours" | "days" | "months",
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: "open",
|
||||
});
|
||||
|
||||
dispatch({
|
||||
step: 0,
|
||||
type: "next",
|
||||
});
|
||||
},
|
||||
[editAvailabilityValue, dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("codexavailabilityedit", onEdit, false);
|
||||
|
||||
return () => document.removeEventListener("codexavailabilityedit", onEdit);
|
||||
}, [onEdit, dispatch]);
|
||||
|
||||
const onClose = () => dispatch({ type: "close" });
|
||||
|
||||
const Body = components[state.step] || (() => <span />);
|
||||
|
@ -164,17 +194,18 @@ export function AvailabilityEdit({
|
|||
<div className="availability-edit">
|
||||
<Button
|
||||
label={hasLabel ? "Sale" : ""}
|
||||
Icon={
|
||||
!availabilityId ? () => <PlusIcon width={40} fill="#000" /> : Pencil
|
||||
}
|
||||
Icon={() => <PlusIcon width={40} fill="#000" />}
|
||||
onClick={onOpen}
|
||||
variant="outline"
|
||||
className={className}
|
||||
/>
|
||||
|
||||
<Modal open={state.open} onClose={onClose} displayCloseButton={false}>
|
||||
<Modal
|
||||
open={state.open}
|
||||
onClose={onClose}
|
||||
title="Availability"
|
||||
Icon={HostIcon}>
|
||||
<Stepper
|
||||
className="availabilityCreate"
|
||||
titles={steps.current}
|
||||
state={state}
|
||||
dispatch={dispatch}
|
||||
|
@ -189,6 +220,7 @@ export function AvailabilityEdit({
|
|||
availability={availability}
|
||||
space={space}
|
||||
error={error}
|
||||
editAvailabilityValue={editAvailabilityValue.current}
|
||||
/>
|
||||
</Stepper>
|
||||
</Modal>
|
||||
|
|
|
@ -1,31 +1,65 @@
|
|||
.availabilityForm-itemInput {
|
||||
width: 100%;
|
||||
}
|
||||
.availability-form {
|
||||
input {
|
||||
width: 100%;
|
||||
font-family: Inter;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
line-height: 32px;
|
||||
letter-spacing: -0.015em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.availabilityForm-item {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
option {
|
||||
background-color: #232323;
|
||||
}
|
||||
|
||||
.availabilityForm-item--error .input,
|
||||
.availabilityForm-item--error .inputGroup-helper,
|
||||
.availabilityForm-item--error .inputGroup-select {
|
||||
color: rgb(var(--codex-color-error));
|
||||
border-color: rgb(var(--codex-color-error));
|
||||
}
|
||||
header {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.availabilityForm-row {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.availabilityForm-itemInput-maxSize {
|
||||
color: var(--codex-color-primary);
|
||||
padding-right: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
transition: 0.35s opacity;
|
||||
}
|
||||
.group {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
margin-top: 16px;
|
||||
|
||||
.availabilityForm-itemInput-maxSize:hover {
|
||||
opacity: 0.7;
|
||||
&[aria-invalid] {
|
||||
.input-group > div > div > div:nth-child(2) {
|
||||
border-color: var(--codex-input-color-error);
|
||||
}
|
||||
|
||||
label,
|
||||
svg,
|
||||
select {
|
||||
color: var(--codex-input-color-error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.input-group p {
|
||||
max-width: inherit;
|
||||
position: relative;
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
color: var(--codex-color-primary);
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
min-width: min-content;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +1,39 @@
|
|||
import { Input, InputGroup } from "@codex-storage/marketplace-ui-components";
|
||||
import { ChangeEvent, useEffect, useState } from "react";
|
||||
import {
|
||||
Input,
|
||||
InputGroup,
|
||||
SpaceAllocation,
|
||||
Tooltip,
|
||||
} from "@codex-storage/marketplace-ui-components";
|
||||
import { ChangeEvent, useEffect } from "react";
|
||||
import "./AvailabilityForm.css";
|
||||
import { AvailabilityComponentProps } from "./types";
|
||||
import { classnames } from "../../utils/classnames";
|
||||
import { AvailabilitySpaceAllocation } from "./AvailabilitySpaceAllocation";
|
||||
import {
|
||||
availabilityMax,
|
||||
availabilityUnit,
|
||||
isAvailabilityValid,
|
||||
} from "./availability.domain";
|
||||
import NodesIcon from "../../assets/icons/nodes.svg?react";
|
||||
import InfoIcon from "../../assets/icons/info.svg?react";
|
||||
import { attributes } from "../../utils/attributes";
|
||||
import { AvailabilityUtils } from "./availability.utils";
|
||||
import { Times } from "../../utils/times";
|
||||
|
||||
export function AvailabilityForm({
|
||||
dispatch,
|
||||
onAvailabilityChange,
|
||||
availability,
|
||||
space,
|
||||
editAvailabilityValue,
|
||||
}: AvailabilityComponentProps) {
|
||||
const [availabilityValue, setAvailabilityValue] = useState(
|
||||
(
|
||||
availability.totalSize / availabilityUnit(availability.totalSizeUnit)
|
||||
).toFixed(2)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const max = availabilityMax(space);
|
||||
const isValid = isAvailabilityValid(availability, max);
|
||||
let max = AvailabilityUtils.maxValue(space);
|
||||
if (availability.id && editAvailabilityValue) {
|
||||
max += editAvailabilityValue;
|
||||
}
|
||||
|
||||
const isValid = AvailabilityUtils.isValid(availability, max);
|
||||
|
||||
dispatch({
|
||||
type: "toggle-buttons",
|
||||
isNextEnable: isValid,
|
||||
isBackEnable: true,
|
||||
});
|
||||
}, [dispatch, space, availability]);
|
||||
}, [dispatch, space, availability, editAvailabilityValue]);
|
||||
|
||||
const onTotalSizeUnitChange = async (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
const element = e.currentTarget;
|
||||
|
@ -42,24 +44,33 @@ export function AvailabilityForm({
|
|||
});
|
||||
};
|
||||
|
||||
const onDurationUnitChange = async (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
const onDurationChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const element = e.currentTarget;
|
||||
const unitValue = Times.unitValue(availability.durationUnit);
|
||||
|
||||
onAvailabilityChange({
|
||||
duration: 1,
|
||||
durationUnit: element.value as "hours" | "days" | "months",
|
||||
duration: parseInt(element.value) * unitValue,
|
||||
});
|
||||
};
|
||||
|
||||
const onDurationUnitChange = async (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
const element = e.currentTarget;
|
||||
const unit = element.value as "hours" | "days" | "months";
|
||||
const unitValue = Times.unitValue(unit);
|
||||
|
||||
onAvailabilityChange({
|
||||
duration: unitValue,
|
||||
durationUnit: unit,
|
||||
});
|
||||
};
|
||||
|
||||
const onAvailablityChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const element = e.currentTarget;
|
||||
const v = element.value;
|
||||
const unit = availabilityUnit(availability.totalSizeUnit);
|
||||
|
||||
setAvailabilityValue(v);
|
||||
const unit = AvailabilityUtils.unitValue(availability.totalSizeUnit);
|
||||
|
||||
onAvailabilityChange({
|
||||
[element.name]: parseFloat(v) * unit,
|
||||
totalSize: parseFloat(v) * unit,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -72,128 +83,162 @@ export function AvailabilityForm({
|
|||
});
|
||||
};
|
||||
|
||||
// const domain = new AvailabilityDomain(space, availability);
|
||||
|
||||
const onMaxSize = () => {
|
||||
const available =
|
||||
space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes;
|
||||
|
||||
const unit = availabilityUnit(availability.totalSizeUnit);
|
||||
|
||||
setAvailabilityValue((available / unit).toFixed(2));
|
||||
const available = AvailabilityUtils.maxValue(space);
|
||||
|
||||
onAvailabilityChange({
|
||||
totalSize: available,
|
||||
});
|
||||
};
|
||||
|
||||
const available =
|
||||
space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes;
|
||||
const isValid = available >= availability.totalSize;
|
||||
const unit = availabilityUnit(availability.totalSizeUnit);
|
||||
const max = available / unit;
|
||||
let available = AvailabilityUtils.maxValue(space);
|
||||
if (availability.id && editAvailabilityValue) {
|
||||
available += editAvailabilityValue;
|
||||
}
|
||||
|
||||
const isValid =
|
||||
availability.totalSize > 0 && available >= availability.totalSize;
|
||||
|
||||
const helper = isValid
|
||||
? "Total size of sale's storage in bytes"
|
||||
: "The total size cannot exceed the space available.";
|
||||
|
||||
const value = AvailabilityUtils.toUnit(
|
||||
availability.totalSize,
|
||||
availability.totalSizeUnit
|
||||
).toFixed(2);
|
||||
|
||||
const unitValue = Times.unitValue(availability.durationUnit);
|
||||
const duration = availability.duration / unitValue;
|
||||
|
||||
return (
|
||||
<>
|
||||
<AvailabilitySpaceAllocation availability={availability} space={space} />
|
||||
<div className="availability-form">
|
||||
<header>
|
||||
<NodesIcon width={20}></NodesIcon>
|
||||
<h6>Disk</h6>
|
||||
</header>
|
||||
<SpaceAllocation
|
||||
data={[
|
||||
{
|
||||
title: "Allocated",
|
||||
size: space.quotaUsedBytes,
|
||||
color: "#FF6E61",
|
||||
},
|
||||
{
|
||||
title: "Available",
|
||||
size: space.quotaReservedBytes,
|
||||
color: "#34A0FF",
|
||||
},
|
||||
{
|
||||
title: "Free",
|
||||
size: isValid ? available - availability.totalSize : available,
|
||||
color: "#6F6F6F",
|
||||
},
|
||||
]}></SpaceAllocation>
|
||||
|
||||
<InputGroup
|
||||
id="totalSize"
|
||||
name="totalSize"
|
||||
type="number"
|
||||
label="Total size"
|
||||
helper={helper}
|
||||
className={classnames(
|
||||
["availabilityForm-item"],
|
||||
["availabilityForm-item--error", !isValid]
|
||||
)}
|
||||
inputClassName="availabilityForm-itemInput"
|
||||
min={0.01}
|
||||
max={max.toFixed(2)}
|
||||
onChange={onAvailablityChange}
|
||||
onGroupChange={onTotalSizeUnitChange}
|
||||
value={availabilityValue}
|
||||
step={"0.01"}
|
||||
group={[
|
||||
["gb", "GB"],
|
||||
["tb", "TB"],
|
||||
]}
|
||||
groupValue={availability.totalSizeUnit}
|
||||
extra={
|
||||
<a onClick={onMaxSize} className="availabilityForm-itemInput-maxSize">
|
||||
Use max size
|
||||
</a>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="availabilityForm-item">
|
||||
<InputGroup
|
||||
id="duration"
|
||||
name="duration"
|
||||
type="number"
|
||||
label="Duration"
|
||||
helper="The duration of the request in seconds"
|
||||
inputClassName="availabilityForm-itemInput"
|
||||
min={1}
|
||||
onChange={onInputChange}
|
||||
onGroupChange={onDurationUnitChange}
|
||||
group={[
|
||||
["hours", "Hours"],
|
||||
["days", "Days"],
|
||||
["months", "Months"],
|
||||
]}
|
||||
value={availability.duration.toString()}
|
||||
groupValue={availability.durationUnit}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="availabilityForm-row">
|
||||
<div className="availabilityForm-item">
|
||||
<Input
|
||||
id="minPrice"
|
||||
name="minPrice"
|
||||
<div className="row gap">
|
||||
<div className="group" {...attributes({ "aria-invalid": !isValid })}>
|
||||
<InputGroup
|
||||
id="totalSize"
|
||||
name="totalSize"
|
||||
type="number"
|
||||
label="Min price"
|
||||
min={0}
|
||||
helper="Minimum price to be paid (in amount of tokens)"
|
||||
inputClassName="availabilityForm-itemInput"
|
||||
onChange={onInputChange}
|
||||
value={availability.minPrice.toString()}
|
||||
label="Total size"
|
||||
min={0.01}
|
||||
isInvalid={!isValid}
|
||||
max={available.toFixed(2)}
|
||||
onChange={onAvailablityChange}
|
||||
onGroupChange={onTotalSizeUnitChange}
|
||||
step={"0.01"}
|
||||
value={value}
|
||||
group={[
|
||||
["gb", "GB"],
|
||||
// ["tb", "TB"],
|
||||
]}
|
||||
groupValue={availability.totalSizeUnit}
|
||||
extra={<a onClick={onMaxSize}>Use max size</a>}
|
||||
/>
|
||||
<Tooltip message={helper}>
|
||||
<InfoIcon></InfoIcon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div className="availabilityForm-item">
|
||||
<Input
|
||||
id="maxCollateral"
|
||||
name="maxCollateral"
|
||||
<div className="group">
|
||||
<InputGroup
|
||||
id="duration"
|
||||
name="duration"
|
||||
type="number"
|
||||
label="Max collateral"
|
||||
min={0}
|
||||
helper="Maximum collateral user is willing to pay per filled Slot (in amount of tokens)"
|
||||
inputClassName="availabilityForm-itemInput"
|
||||
onChange={onInputChange}
|
||||
value={availability.maxCollateral.toString()}
|
||||
label="Duration"
|
||||
min={1}
|
||||
onChange={onDurationChange}
|
||||
onGroupChange={onDurationUnitChange}
|
||||
group={[
|
||||
["hours", "Hours"],
|
||||
["days", "Days"],
|
||||
["months", "Months"],
|
||||
]}
|
||||
value={duration.toString()}
|
||||
groupValue={availability.durationUnit}
|
||||
/>
|
||||
<Tooltip message={"The duration of the request in seconds"}>
|
||||
<InfoIcon></InfoIcon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="availabilityForm-item">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div className="group">
|
||||
<Input
|
||||
id="name"
|
||||
name="name"
|
||||
type="string"
|
||||
label="Nickname"
|
||||
max={9}
|
||||
helper="You can add a custom name to easily retrieve your sale."
|
||||
inputClassName="availabilityForm-itemInput"
|
||||
onChange={onInputChange}
|
||||
value={availability.name?.toString()}
|
||||
value={availability.name?.toString() || ""}
|
||||
maxLength={9}
|
||||
autoComplete="falsep"
|
||||
/>
|
||||
<Tooltip
|
||||
message={"You can add a custom name to easily retrieve your sale."}>
|
||||
<InfoIcon></InfoIcon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -142,7 +142,6 @@ export function AvailabilitySheetCreate({
|
|||
|
||||
<Modal open={state.open} onClose={onClose}>
|
||||
<Stepper
|
||||
className="availabilityCreate"
|
||||
titles={steps.current}
|
||||
state={state}
|
||||
dispatch={dispatch}
|
||||
|
|
|
@ -162,22 +162,22 @@ export function Sunburst({ availabilities, space }: Props) {
|
|||
|
||||
if (chart.current) {
|
||||
chart.current.setOption(option);
|
||||
chart.current.off("click");
|
||||
chart.current.on("click", function (params) {
|
||||
// console.info(params.componentIndex);
|
||||
// console.info(params.dataIndex);
|
||||
// chart.current.off("click");
|
||||
// chart.current.on("click", function (params) {
|
||||
// // console.info(params.componentIndex);
|
||||
// // console.info(params.dataIndex);
|
||||
|
||||
const index = params.dataIndex;
|
||||
// const index = params.dataIndex;
|
||||
|
||||
const detail =
|
||||
params.dataIndex === 0 ? null : availabilities[index - 1].id;
|
||||
// const detail =
|
||||
// params.dataIndex === 0 ? null : availabilities[index - 1].id;
|
||||
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("codexavailabilityid", {
|
||||
detail,
|
||||
})
|
||||
);
|
||||
});
|
||||
// document.dispatchEvent(
|
||||
// new CustomEvent("codexavailabilityid", {
|
||||
// detail,
|
||||
// })
|
||||
// );
|
||||
// });
|
||||
}
|
||||
|
||||
return <div id="chart" ref={div} className="sunburst"></div>;
|
||||
|
|
|
@ -38,12 +38,6 @@ export class AvailabilityDomain {
|
|||
}
|
||||
}
|
||||
|
||||
export const availabilityUnit = (unit: "gb" | "tb") =>
|
||||
unit === "gb" ? GB : TB;
|
||||
|
||||
export const availabilityMax = (space: CodexNodeSpace) =>
|
||||
space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes;
|
||||
|
||||
export const isAvailabilityValid = (
|
||||
availability: AvailabilityState,
|
||||
max: number
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { TabSortState } from "@codex-storage/marketplace-ui-components"
|
||||
import { AvailabilityWithSlots } from "./types"
|
||||
import { AvailabilityState, AvailabilityWithSlots } from "./types"
|
||||
import { GB, TB } from "../../utils/constants";
|
||||
import { CodexNodeSpace } from "@codex-storage/sdk-js";
|
||||
|
||||
export const AvailabilityUtils = {
|
||||
sortById: (state: TabSortState) =>
|
||||
|
@ -33,4 +35,18 @@ export const AvailabilityUtils = {
|
|||
? b.maxCollateral - a.maxCollateral
|
||||
: a.maxCollateral - b.maxCollateral
|
||||
,
|
||||
toUnit(bytes: number, unit: "gb" | "tb") {
|
||||
return bytes / this.unitValue(unit || "gb")
|
||||
},
|
||||
maxValue(space: CodexNodeSpace) {
|
||||
return space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes
|
||||
},
|
||||
unitValue(unit: "gb" | "tb") {
|
||||
return unit === "tb" ? TB : GB
|
||||
},
|
||||
isValid: (
|
||||
availability: AvailabilityState,
|
||||
max: number
|
||||
) => availability.totalSize > 0 && availability.totalSize <= max
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ export type AvailabilityComponentProps = {
|
|||
onAvailabilityChange: (data: Partial<AvailabilityState>) => void;
|
||||
availability: AvailabilityState;
|
||||
error: Error | null;
|
||||
editAvailabilityValue?: number;
|
||||
};
|
||||
|
||||
export type AvailabilityWithSlots = CodexAvailability & {
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
StepperAction,
|
||||
StepperState,
|
||||
} from "@codex-storage/marketplace-ui-components";
|
||||
import { Times } from "../../utils/times";
|
||||
import { CodexSdk } from "../../sdk/codex";
|
||||
import { CodexAvailabilityCreateResponse } from "@codex-storage/sdk-js";
|
||||
|
||||
|
@ -25,12 +24,10 @@ export function useAvailabilityMutation(
|
|||
totalSize,
|
||||
totalSizeUnit,
|
||||
duration,
|
||||
durationUnit = "days",
|
||||
durationUnit,
|
||||
name,
|
||||
...input
|
||||
}: AvailabilityState) => {
|
||||
const time = Times.toSeconds(duration, durationUnit);
|
||||
|
||||
const fn: (
|
||||
input: Omit<AvailabilityState, "totalSizeUnit" | "durationUnit">
|
||||
) => Promise<"" | CodexAvailabilityCreateResponse> = input.id
|
||||
|
@ -45,7 +42,7 @@ export function useAvailabilityMutation(
|
|||
|
||||
return fn({
|
||||
...input,
|
||||
duration: time,
|
||||
duration,
|
||||
totalSize: Math.trunc(totalSize),
|
||||
});
|
||||
},
|
||||
|
|
|
@ -35,6 +35,7 @@ export function Card({
|
|||
label={buttonLabel}
|
||||
variant="outline"
|
||||
Icon={buttonIcon}
|
||||
size="small"
|
||||
onClick={buttonAction}></Button>
|
||||
)}
|
||||
</header>
|
||||
|
|
|
@ -21,16 +21,6 @@
|
|||
letter-spacing: -0.015em;
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
|
||||
&::-webkit-outer-spin-button,
|
||||
&::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
|
@ -67,6 +57,6 @@
|
|||
color: var(--codex-card-number-unit-color);
|
||||
position: absolute;
|
||||
top: 54px;
|
||||
right: 16px;
|
||||
right: 48px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ export function Download() {
|
|||
placeholder="CID"
|
||||
inputClassName="download-input"
|
||||
size={"medium" as any}
|
||||
autoComplete="off"
|
||||
onChange={onCidChange}></Input>
|
||||
<Button label="Download" onClick={onDownload} variant="outline"></Button>
|
||||
</main>
|
||||
|
|
|
@ -55,7 +55,7 @@ export function FileCell({ requestId, purchaseCid, data, onMetadata }: Props) {
|
|||
}
|
||||
}
|
||||
});
|
||||
}, [requestId, data]);
|
||||
}, [requestId, data, onMetadata]);
|
||||
|
||||
let filename = metadata.filename || "-";
|
||||
|
||||
|
|
|
@ -196,6 +196,7 @@ export function Files({ limit }: Props) {
|
|||
value={folder}
|
||||
required={true}
|
||||
pattern="[A-Za-z0-9_\-]*"
|
||||
autoComplete="off"
|
||||
maxLength={9}
|
||||
size={"medium" as any}
|
||||
placeholder="Folder name"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
gap: 16px;
|
||||
margin-top: 14px;
|
||||
|
||||
> div {
|
||||
> div:first-child {
|
||||
position: relative;
|
||||
|
||||
svg {
|
||||
|
|
|
@ -39,7 +39,7 @@ export function LogLevel() {
|
|||
const [toast, setToast] = useState({
|
||||
time: 0,
|
||||
message: "",
|
||||
variant: "success" as "success" | "error" | "default",
|
||||
variant: "success" as "success" | "error",
|
||||
});
|
||||
|
||||
function onChange(e: React.FormEvent<HTMLSelectElement>) {
|
||||
|
@ -86,6 +86,7 @@ export function LogLevel() {
|
|||
message={toast.message}
|
||||
time={toast.time}
|
||||
variant={toast.variant}
|
||||
duration={400000000}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -49,6 +49,7 @@ export function ManifestFetch() {
|
|||
value={cid}
|
||||
placeholder="CID"
|
||||
size={"medium" as any}
|
||||
autoComplete="off"
|
||||
onChange={onCidChange}></Input>
|
||||
<Button label="Fetch" onClick={onDownload} variant="outline"></Button>
|
||||
</div>
|
||||
|
|
|
@ -36,9 +36,7 @@ export type MenuItem =
|
|||
Component: ComponentType<MenuItemComponentProps>;
|
||||
};
|
||||
|
||||
type Props = {};
|
||||
|
||||
export function Menu({}: Props) {
|
||||
export function Menu() {
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
|
||||
const onLogoClick = () => {
|
||||
|
@ -72,7 +70,11 @@ export function Menu({}: Props) {
|
|||
</span>
|
||||
<span>Dashboard</span>
|
||||
</Link>
|
||||
<Link to="/dashboard/wallet">
|
||||
<Link
|
||||
to="/dashboard/wallet"
|
||||
disabled={true}
|
||||
aria-disabled={true}
|
||||
data-title="Coming soon">
|
||||
<span>
|
||||
<WalletIcon width={20} height={20} />
|
||||
</span>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
&:not([aria-expanded="false"]) a[data-title]:hover::after {
|
||||
&[aria-expanded] a[data-title]:hover::after {
|
||||
content: attr(data-title);
|
||||
background-color: #2f2f2f;
|
||||
color: #fff;
|
||||
|
|
|
@ -66,14 +66,13 @@
|
|||
gap: 8px;
|
||||
|
||||
li {
|
||||
& {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: white;
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
transition: opacity 0.35s;
|
||||
}
|
||||
cursor: pointer;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: white;
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
transition: opacity 0.35s;
|
||||
|
||||
&:hover {
|
||||
animation-name: pulse;
|
||||
|
|
|
@ -4,6 +4,7 @@ import Logotype from "../../assets/icons/logotype.svg?react";
|
|||
import { attributes } from "../../utils/attributes";
|
||||
import "./OnBoardingLayout.css";
|
||||
import { BackgroundImage } from "../BackgroundImage/BackgroundImage";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
type Props = {
|
||||
children: ReactElement<{ onStepValid: (isValid: boolean) => void }>;
|
||||
|
@ -12,6 +13,8 @@ type Props = {
|
|||
};
|
||||
|
||||
export function OnBoardingLayout({ children, step }: Props) {
|
||||
const navigate = useNavigate({ from: window.location.pathname });
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
|
@ -29,9 +32,15 @@ export function OnBoardingLayout({ children, step }: Props) {
|
|||
|
||||
<footer>
|
||||
<ul>
|
||||
<li {...attributes({ "aria-selected": step === 0 })}></li>
|
||||
<li {...attributes({ "aria-selected": step === 1 })}></li>
|
||||
<li {...attributes({ "aria-selected": step === 2 })}></li>
|
||||
<li
|
||||
{...attributes({ "aria-selected": step === 0 })}
|
||||
onClick={() => navigate({ to: "/" })}></li>
|
||||
<li
|
||||
{...attributes({ "aria-selected": step === 1 })}
|
||||
onClick={() => navigate({ to: "/onboarding-name" })}></li>
|
||||
<li
|
||||
{...attributes({ "aria-selected": step === 2 })}
|
||||
onClick={() => navigate({ to: "/onboarding-checks" })}></li>
|
||||
</ul>
|
||||
</footer>
|
||||
</section>
|
||||
|
|
|
@ -24,16 +24,26 @@ export function PeersMap({ nodes, onPinAdd }: Props) {
|
|||
setPins((val) => PeerUtils.incPin(val, { ...node, ...geo }));
|
||||
onPinAdd?.(node, geo);
|
||||
},
|
||||
[setPins]
|
||||
[setPins, onPinAdd]
|
||||
);
|
||||
|
||||
pins.map(([pin, quantity]) =>
|
||||
pins.map(([pin, quantity]) => {
|
||||
let radius = 0.65;
|
||||
|
||||
if (quantity > 3) {
|
||||
radius = 0.85;
|
||||
}
|
||||
|
||||
if (quantity > 5) {
|
||||
radius = 0.95;
|
||||
}
|
||||
|
||||
map.addPin({
|
||||
lat: pin.latitude,
|
||||
lng: pin.longitude,
|
||||
svgOptions: { color: "#d6ff79", radius: 0.1 * quantity },
|
||||
})
|
||||
);
|
||||
svgOptions: { color: "#d6ff79", radius },
|
||||
});
|
||||
});
|
||||
|
||||
const svgMap = map
|
||||
.getSVG({
|
||||
|
@ -60,7 +70,7 @@ export function PeersMap({ nodes, onPinAdd }: Props) {
|
|||
opacity: 1; /* Fully opaque */
|
||||
}
|
||||
50% {
|
||||
r: 2; /* Increased radius */
|
||||
r: 1.5; /* Increased radius */
|
||||
opacity: 1; /* Slightly transparent */
|
||||
}
|
||||
100% {
|
||||
|
|
|
@ -17,12 +17,9 @@ import { TruncateCell } from "../TruncateCell/TruncateCell";
|
|||
import { CustomStateCellRender } from "../CustomStateCellRender/CustomStateCellRender";
|
||||
import { PurchaseUtils } from "./purchase.utils";
|
||||
|
||||
type Props = {};
|
||||
|
||||
type SortFn = (a: CodexPurchase, b: CodexPurchase) => number;
|
||||
|
||||
export function PurchasesTable({}: Props) {
|
||||
const [metadata, setMetadata] = useState<{ [key: string]: number }>({});
|
||||
export function PurchasesTable() {
|
||||
const content = useData();
|
||||
const { data, isPending } = useQuery({
|
||||
queryFn: () =>
|
||||
|
@ -49,21 +46,21 @@ export function PurchasesTable({}: Props) {
|
|||
throwOnError: true,
|
||||
});
|
||||
|
||||
const onMetadata = (
|
||||
requestId: string,
|
||||
{ uploadedAt }: { uploadedAt: number }
|
||||
) => {
|
||||
setMetadata((m) => ({ ...m, [requestId]: uploadedAt }));
|
||||
setSortFn(() =>
|
||||
PurchaseUtils.sortByUploadedAt("desc", {
|
||||
...metadata,
|
||||
[requestId]: uploadedAt,
|
||||
})
|
||||
);
|
||||
};
|
||||
// const onMetadata = (
|
||||
// requestId: string,
|
||||
// { uploadedAt }: { uploadedAt: number }
|
||||
// ) => {
|
||||
// setMetadata((m) => ({ ...m, [requestId]: uploadedAt }));
|
||||
// setSortFn(() =>
|
||||
// PurchaseUtils.sortByUploadedAt("desc", {
|
||||
// ...metadata,
|
||||
// [requestId]: uploadedAt,
|
||||
// })
|
||||
// );
|
||||
// };
|
||||
|
||||
const [sortFn, setSortFn] = useState<SortFn>(() =>
|
||||
PurchaseUtils.sortByUploadedAt("desc", metadata)
|
||||
PurchaseUtils.sortByDuration("desc")
|
||||
);
|
||||
|
||||
const onSortByDuration = (state: TabSortState) =>
|
||||
|
@ -75,11 +72,11 @@ export function PurchasesTable({}: Props) {
|
|||
const onSortByState = (state: TabSortState) =>
|
||||
setSortFn(() => PurchaseUtils.sortByState(state));
|
||||
|
||||
const onSortByUploadedAt = (state: TabSortState) =>
|
||||
setSortFn(() => PurchaseUtils.sortByUploadedAt(state, metadata));
|
||||
// const onSortByUploadedAt = (state: TabSortState) =>
|
||||
// setSortFn(() => PurchaseUtils.sortByUploadedAt(state, metadata));
|
||||
|
||||
const headers = [
|
||||
["file", onSortByUploadedAt],
|
||||
["file"],
|
||||
["request id"],
|
||||
["duration", onSortByDuration],
|
||||
["slots"],
|
||||
|
@ -104,7 +101,6 @@ export function PurchasesTable({}: Props) {
|
|||
purchaseCid={r.content.cid}
|
||||
index={index}
|
||||
data={content}
|
||||
onMetadata={onMetadata}
|
||||
/>,
|
||||
<TruncateCell value={r.id} />,
|
||||
<Cell>{Times.pretty(duration)}</Cell>,
|
||||
|
@ -116,8 +112,6 @@ export function PurchasesTable({}: Props) {
|
|||
);
|
||||
});
|
||||
|
||||
console.info(metadata);
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<div className="purchases-loader">
|
||||
|
@ -128,7 +122,7 @@ export function PurchasesTable({}: Props) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Table headers={headers} rows={rows} defaultSortIndex={0} />
|
||||
<Table headers={headers} rows={rows} defaultSortIndex={2} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ export const PurchaseUtils = {
|
|||
,
|
||||
sortByUploadedAt: (state: TabSortState, table: Record<string, number>) =>
|
||||
(a: CodexPurchase, b: CodexPurchase) => {
|
||||
console.info(table)
|
||||
return state === "desc"
|
||||
? (table[b.requestId] || 0) - (table[a.requestId] || 0)
|
||||
: (table[a.requestId] || 0) - (table[b.requestId] || 0)
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { classnames } from "../../utils/classnames";
|
||||
import "./AssistanceImage.css";
|
||||
|
||||
type Props = {};
|
||||
|
||||
export function AssistanceImage({}: Props) {
|
||||
export function AssistanceImage() {
|
||||
return (
|
||||
<picture>
|
||||
<source
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
max-width: 508px;
|
||||
|
||||
h5 {
|
||||
font-family: Azeret Mono;
|
||||
|
|
|
@ -22,14 +22,14 @@ const CONFIRM_STATE = 2;
|
|||
|
||||
const defaultStorageRequest: StorageRequest = {
|
||||
cid: "",
|
||||
availabilityUnit: "days",
|
||||
availabilityUnit: "months",
|
||||
availability: 1,
|
||||
tolerance: 1,
|
||||
proofProbability: 1,
|
||||
nodes: 3,
|
||||
reward: 10,
|
||||
collateral: 10,
|
||||
expiration: 300,
|
||||
expiration: 5,
|
||||
};
|
||||
|
||||
export function StorageRequestCreate() {
|
||||
|
@ -88,7 +88,7 @@ export function StorageRequestCreate() {
|
|||
mutateAsync({
|
||||
...rest,
|
||||
duration: Times.toSeconds(availability, availabilityUnit),
|
||||
expiry: expiration,
|
||||
expiry: expiration * 60,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
|
|
|
@ -114,22 +114,6 @@
|
|||
& {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.storageRequestReview-presets {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.storageRequestReview-presets-blocs {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.storageRequestReview-alert {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.storageRequestReview-expiration {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 801px) {
|
||||
|
@ -149,114 +133,3 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.storageRequestReview-hr {
|
||||
margin-bottom: 1.5rem;
|
||||
margin-top: 0rem;
|
||||
}
|
||||
|
||||
.storageRequestReview-numbers {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.storageRequestReview-range {
|
||||
margin: 0.5rem 0 1rem 0;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.storageRequestReview-alert .alert-message {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.storageRequestReview-range--disabled .range {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.storageRequestReview-presets {
|
||||
display: flex;
|
||||
padding: 0 0.5rem 2rem 0.5rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.storageRequestReview-presets-blocs {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.storageRequestReview-presets-bloc {
|
||||
flex: 1;
|
||||
border-radius: var(--codex-border-radius);
|
||||
background-color: rgb(56 56 56);
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
transition: opacity 0.35s;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.storageRequest-price {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.storageRequestReview-presets-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.storageRequestReview-presets-bloc:not(
|
||||
.storageRequestReview-presets--selected
|
||||
):hover {
|
||||
border: 1px solid var(--codex-border-color);
|
||||
}
|
||||
|
||||
.storageRequestReview-presets--selected {
|
||||
border: 1px solid var(--codex-color-primary);
|
||||
}
|
||||
|
||||
.storageRequestReview-alert {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.storageRequestReview-expiration {
|
||||
min-width: 33%;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.storageRequestReview-numbers {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.storageRequestReview-presets {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.storageRequestReview-presets-blocs {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.storageRequestReview-alert {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.storageRequestReview-expiration {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 801px) {
|
||||
.storageRequestReview-numbers {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -314,14 +314,14 @@ export function StorageRequestReview({
|
|||
|
||||
<footer>
|
||||
<CardNumbers
|
||||
helper="Represents expiry threshold in seconds from when the Request is submitted. When the threshold is reached and the Request does not find requested amount of nodes to host the data, the Request is voided. "
|
||||
helper="Represents expiry threshold in minutes from when the Request is submitted. When the threshold is reached and the Request does not find requested amount of nodes to host the data, the Request is voided. "
|
||||
id="expiration"
|
||||
unit={"Expiration"}
|
||||
value={storageRequest.expiration.toString()}
|
||||
onChange={onExpirationChange}
|
||||
className="storageRequestReview-expiration"
|
||||
onValidation={isInvalidNumber}
|
||||
title="Request expiration in seconds"></CardNumbers>
|
||||
title="Request expiration in minutes"></CardNumbers>
|
||||
<Alert
|
||||
Icon={<FileWarning />}
|
||||
title="Warning"
|
||||
|
|
|
@ -76,6 +76,7 @@ export function UserInfo({ onNameChange }: Props) {
|
|||
onChange={onDisplayNameChange}
|
||||
label="Preferred name"
|
||||
id="displayName"
|
||||
autoComplete="off"
|
||||
value={displayName}></Input>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,15 @@
|
|||
gap: 16px;
|
||||
background-color: #252525;
|
||||
|
||||
& {
|
||||
filter: grayscale(30);
|
||||
transition: filter 0.5s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
div {
|
||||
> p {
|
||||
font-family: Inter;
|
||||
|
|
92
src/proxy.ts
92
src/proxy.ts
|
@ -2,76 +2,16 @@ import {
|
|||
CodexCreateStorageRequestInput,
|
||||
CodexData,
|
||||
CodexMarketplace,
|
||||
CodexReservation,
|
||||
SafeValue,
|
||||
} from "@codex-storage/sdk-js";
|
||||
import { CodexSdk as Sdk } from "./sdk/codex";
|
||||
import { PortForwardingUtil as PUtil } from "./hooks/port-forwarding.util";
|
||||
import { WebStorage } from "./utils/web-storage";
|
||||
import { GB } from "./utils/constants";
|
||||
|
||||
class CodexDataMock extends CodexData {
|
||||
// override upload(
|
||||
// file: File,
|
||||
// onProgress?: (loaded: number, total: number) => void
|
||||
// ): UploadResponse {
|
||||
// // const url = CodexSdk.url() + "/api/codex/v1/data";
|
||||
|
||||
// // const xhr = new XMLHttpRequest();
|
||||
|
||||
// // const promise = new Promise<SafeValue<string>>((resolve) => {
|
||||
// // xhr.upload.onprogress = (evt) => {
|
||||
// // if (evt.lengthComputable) {
|
||||
// // onProgress?.(evt.loaded, evt.total);
|
||||
// // }
|
||||
// // };
|
||||
|
||||
// // xhr.open("POST", url, true);
|
||||
// // xhr.setRequestHeader("Content-Disposition", "attachment; filename=\"" + file.name + "\"")
|
||||
// // xhr.send(file);
|
||||
|
||||
// // xhr.onload = function () {
|
||||
// // if (xhr.status != 200) {
|
||||
// // resolve({
|
||||
// // error: true,
|
||||
// // data: new CodexError(xhr.responseText, {
|
||||
// // code: xhr.status,
|
||||
// // }),
|
||||
// // });
|
||||
// // } else {
|
||||
// // resolve({ error: false, data: xhr.response });
|
||||
// // }
|
||||
// // };
|
||||
|
||||
// // xhr.onerror = function () {
|
||||
// // resolve({
|
||||
// // error: true,
|
||||
// // data: new CodexError("Something went wrong during the file upload."),
|
||||
// // });
|
||||
// // };
|
||||
// // });
|
||||
|
||||
// // return {
|
||||
// // result: promise,
|
||||
// // abort: () => {
|
||||
// // xhr.abort();
|
||||
// // },
|
||||
// // };
|
||||
// const { result, abort } = super.upload(file, onProgress);
|
||||
|
||||
// return {
|
||||
// abort,
|
||||
// result: result.then((safe) => {
|
||||
// if (!safe.error) {
|
||||
// return WebStorage.files.set(safe.data, {
|
||||
// mimetype: file.type,
|
||||
// name: file.name,
|
||||
// uploadedAt: new Date().toJSON(),
|
||||
// }).then(() => safe);
|
||||
// }
|
||||
|
||||
// return safe;
|
||||
// }),
|
||||
// };
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
@ -167,6 +107,34 @@ class CodexMarketplaceMock extends CodexMarketplace {
|
|||
// ],
|
||||
// });
|
||||
// }
|
||||
|
||||
// override reservations(): Promise<SafeValue<CodexReservation[]>> {
|
||||
// return Promise.resolve({
|
||||
// error: false,
|
||||
// data: [
|
||||
// {
|
||||
// id: "0x123456789",
|
||||
// availabilityId: "0x12345678910",
|
||||
// requestId: "0x1234567891011",
|
||||
// size: GB * 0.5 + "",
|
||||
// slotIndex: "2",
|
||||
// },
|
||||
// {
|
||||
// id: "0x987654321",
|
||||
// availabilityId: "0x9876543210",
|
||||
// requestId: "0x98765432100",
|
||||
// /**
|
||||
// * Size in bytes
|
||||
// */
|
||||
// size: GB * 0.25 + "",
|
||||
// /**
|
||||
// * Slot Index as hexadecimal string
|
||||
// */
|
||||
// slotIndex: "1",
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
export const CodexSdk = {
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
import { createRootRoute, Outlet } from "@tanstack/react-router";
|
||||
import React from "react";
|
||||
|
||||
const TanStackRouterDevtools = import.meta.env.PROD
|
||||
? () => null // Render nothing in production
|
||||
: React.lazy(() =>
|
||||
// Lazy load in development
|
||||
import("@tanstack/router-devtools").then((res) => ({
|
||||
default: res.TanStackRouterDevtools,
|
||||
// For Embedded Mode
|
||||
// default: res.TanStackRouterDevtoolsPanel
|
||||
}))
|
||||
);
|
||||
import {
|
||||
createRootRoute,
|
||||
Outlet,
|
||||
ScrollRestoration,
|
||||
} from "@tanstack/react-router";
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: () => {
|
||||
return (
|
||||
<>
|
||||
<ScrollRestoration></ScrollRestoration>
|
||||
<Outlet />
|
||||
<TanStackRouterDevtools />
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
||||
import {
|
||||
createFileRoute,
|
||||
Outlet,
|
||||
ScrollRestoration,
|
||||
} from "@tanstack/react-router";
|
||||
import "./layout.css";
|
||||
import { Menu } from "../components/Menu/Menu";
|
||||
import { useState } from "react";
|
||||
|
@ -23,6 +27,7 @@ const Layout = () => {
|
|||
<main>
|
||||
<AppBar onIconClick={onIconClick} />
|
||||
<div>
|
||||
<ScrollRestoration></ScrollRestoration>
|
||||
<Outlet />
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
|
||||
dialog {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
> .card {
|
||||
flex: 1 1 50%;
|
||||
}
|
||||
|
@ -67,24 +71,6 @@
|
|||
main {
|
||||
> div {
|
||||
position: relative;
|
||||
|
||||
.button {
|
||||
top: 0;
|
||||
bottom: 0px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
border-radius: 100%;
|
||||
height: 88px;
|
||||
width: 88px;
|
||||
background-color: white;
|
||||
|
||||
div {
|
||||
position: relative;
|
||||
left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .button {
|
||||
|
@ -103,7 +89,7 @@
|
|||
}
|
||||
|
||||
footer {
|
||||
padding: 16px 0;
|
||||
padding-top: 16px;
|
||||
|
||||
b {
|
||||
display: block;
|
||||
|
|
|
@ -22,6 +22,7 @@ import { WebStorage } from "../../utils/web-storage";
|
|||
import { NodeSpace } from "../../components/NodeSpace/NodeSpace";
|
||||
import PlusIcon from "../../assets/icons/plus-circle.svg?react";
|
||||
import UploadIcon from "../../assets/icons/upload.svg?react";
|
||||
import { AvailabilityUtils } from "../../components/Availability/availability.utils";
|
||||
|
||||
const defaultSpace = {
|
||||
quotaMaxBytes: 0,
|
||||
|
@ -121,9 +122,7 @@ export function Availabilities() {
|
|||
|
||||
allocation.push({
|
||||
title: "Space remaining",
|
||||
// TODO move this to domain
|
||||
size:
|
||||
space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes,
|
||||
size: AvailabilityUtils.maxValue(space),
|
||||
color: "transparent",
|
||||
});
|
||||
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
|
||||
.card {
|
||||
> .card {
|
||||
margin-bottom: 16px;
|
||||
flex: 1 1 67%;
|
||||
|
||||
&:first-child {
|
||||
flex: 1 1 70%;
|
||||
}
|
||||
}
|
||||
|
||||
aside {
|
||||
|
@ -13,5 +16,11 @@
|
|||
flex-direction: column;
|
||||
gap: 16px;
|
||||
flex: 1 1 auto;
|
||||
|
||||
.card:first-child {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* flex: 1 1 67%; */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,26 @@
|
|||
.card--main {
|
||||
flex: 1 1 60%;
|
||||
|
||||
&:first-child {
|
||||
filter: grayscale(30);
|
||||
transition: filter 0.5s;
|
||||
|
||||
&:hover {
|
||||
filter: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 2000px) {
|
||||
& {
|
||||
&:nth-child(n + 1) {
|
||||
flex: 1 1 34%;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
flex: 1 1 20%;
|
||||
}
|
||||
|
||||
&.card--main--files {
|
||||
flex: 1 1 50%;
|
||||
flex: 1 1 62%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +79,10 @@
|
|||
}
|
||||
|
||||
@media (min-width: 2000px) {
|
||||
.column:nth-child(2) {
|
||||
flex: 1 1 15%;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex: 1 1 25%;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ function Dashboard() {
|
|||
className="card--main"
|
||||
title="Connected Account"
|
||||
buttonLabel="Add Wallet"
|
||||
buttonIcon={PlusIcon}>
|
||||
buttonIcon={() => <PlusIcon width={20} />}>
|
||||
<ConnectedAccount></ConnectedAccount>
|
||||
</Card>
|
||||
|
||||
|
|
|
@ -7,38 +7,39 @@ import LogsIcon from "../../assets/icons/logs.svg?react";
|
|||
|
||||
const throwOnError = false;
|
||||
|
||||
export const Route = createFileRoute("/dashboard/logs")({
|
||||
component: () => {
|
||||
const { data } = useDebug(throwOnError);
|
||||
const Logs = () => {
|
||||
const { data } = useDebug(throwOnError);
|
||||
|
||||
const { table, ...rest } = data ?? {};
|
||||
const { table, ...rest } = data ?? {};
|
||||
|
||||
return (
|
||||
<div className="logs">
|
||||
<div className="logs-card">
|
||||
<div>
|
||||
<h5>Log level</h5>
|
||||
<small>
|
||||
Manage the type of logs being displayed on your CLI for Codex
|
||||
Node.
|
||||
</small>
|
||||
<LogLevel></LogLevel>
|
||||
</div>
|
||||
<RequireAssitance></RequireAssitance>
|
||||
</div>
|
||||
|
||||
<div className="card node">
|
||||
<header>
|
||||
<div>
|
||||
<LogsIcon width={24}></LogsIcon>
|
||||
<h5>Node</h5>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<pre>{JSON.stringify(rest, null, 2)}</pre>
|
||||
</main>
|
||||
return (
|
||||
<div className="logs">
|
||||
<div className="logs-card">
|
||||
<div>
|
||||
<h5>Log level</h5>
|
||||
<small>
|
||||
Manage the type of logs being displayed on your CLI for Codex Node.
|
||||
</small>
|
||||
<LogLevel></LogLevel>
|
||||
</div>
|
||||
<RequireAssitance></RequireAssitance>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
<div className="card node">
|
||||
<header>
|
||||
<div>
|
||||
<LogsIcon width={24}></LogsIcon>
|
||||
<h5>Node</h5>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<pre>{JSON.stringify(rest, null, 2)}</pre>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute("/dashboard/logs")({
|
||||
component: Logs,
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ const OnBoardingChecks = () => {
|
|||
|
||||
const onNextStep = () => {
|
||||
if (isStepValid) {
|
||||
navigate({ to: "/onboarding-checks" });
|
||||
navigate({ to: "/dashboard" });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -36,7 +36,7 @@ const OnBoardingChecks = () => {
|
|||
</section>
|
||||
<section className="main">
|
||||
<h1>
|
||||
Nice to meet {displayName},<br />
|
||||
Nice to meet you {displayName},<br />
|
||||
Let’s establish our connection.
|
||||
</h1>
|
||||
|
||||
|
|
|
@ -61,4 +61,33 @@ export const Times = {
|
|||
|
||||
return plural(value, "seconds");
|
||||
},
|
||||
|
||||
unit(value: number) {
|
||||
let seconds = 30 * 24 * 60 * 60;
|
||||
|
||||
if (value >= seconds) {
|
||||
return "months";
|
||||
}
|
||||
|
||||
seconds /= 30;
|
||||
if (value >= seconds) {
|
||||
return "days"
|
||||
}
|
||||
|
||||
return "hours"
|
||||
},
|
||||
|
||||
unitValue(unit: "hours" | "days" | "months") {
|
||||
switch (unit) {
|
||||
case "months": {
|
||||
return 30 * 24 * 60 * 60
|
||||
}
|
||||
case "days": {
|
||||
return 24 * 60 * 60
|
||||
}
|
||||
default: {
|
||||
return 60 * 60
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue