Merge branch 'feat/ui/integration' into mock
18
package-lock.json
generated
@ -9,8 +9,8 @@
|
||||
"version": "0.0.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.25",
|
||||
"@codex-storage/sdk-js": "^0.0.9",
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.27",
|
||||
"@codex-storage/sdk-js": "^0.0.12",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@sentry/react": "^8.31.0",
|
||||
"@tanstack/react-query": "^5.51.15",
|
||||
@ -374,9 +374,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@codex-storage/marketplace-ui-components": {
|
||||
"version": "0.0.25",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.25.tgz",
|
||||
"integrity": "sha512-oWR/E0yNWK/Lj2LI3lAqKDzuMw6sTUyYfkhZBbV4yK/4VnGtIIoW0OU+jggishJYAwh3y4s71OfMjWaGGafWlQ==",
|
||||
"version": "0.0.27",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.27.tgz",
|
||||
"integrity": "sha512-jCNUFeHNUptKSupSCQriwTXjK0zC8Yi7kcVWI20p9GtfNKPMRykhrpqGgQ6AdMMB9ZAliY5m+PH9ie3J/PWicw==",
|
||||
"dependencies": {
|
||||
"lucide-react": "^0.453.0"
|
||||
},
|
||||
@ -384,7 +384,7 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@codex-storage/sdk-js": ">=0.0.7",
|
||||
"@codex-storage/sdk-js": ">=0.0.12",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
}
|
||||
@ -398,9 +398,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codex-storage/sdk-js": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/sdk-js/-/sdk-js-0.0.9.tgz",
|
||||
"integrity": "sha512-HZvfMa0ss155+Q8oxLDHBeCGvIZfWA9/m0it01z3SS7WQvwtIrP2XiWj82LJxl3u98P5yXhfHyj4K44eTI/ibw==",
|
||||
"version": "0.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/sdk-js/-/sdk-js-0.0.12.tgz",
|
||||
"integrity": "sha512-WjMZ9fkeVFBdDlFViWRDIirk2s4nX0U5r4WixAeieAkDBlRq5nqWcm9rqz5/8u8EQudgk/y3p0vPZc8cpyK4bA==",
|
||||
"dependencies": {
|
||||
"valibot": "^0.32.0"
|
||||
},
|
||||
|
||||
@ -24,8 +24,8 @@
|
||||
"React"
|
||||
],
|
||||
"dependencies": {
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.25",
|
||||
"@codex-storage/sdk-js": "^0.0.9",
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.27",
|
||||
"@codex-storage/sdk-js": "^0.0.12",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@sentry/react": "^8.31.0",
|
||||
"@tanstack/react-query": "^5.51.15",
|
||||
|
||||
BIN
public/img/avatar.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
public/img/avatar.webp
Normal file
|
After Width: | Height: | Size: 774 B |
BIN
public/img/avatar@1.5x.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/img/avatar@1.5x.webp
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/img/avatar@2x.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
public/img/avatar@2x.webp
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
public/img/avatar@3x.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/img/avatar@3x.webp
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/img/avatar@4x.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
public/img/avatar@4x.webp
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
@ -1,35 +1,40 @@
|
||||
export function AlphaText() {
|
||||
type Props = {
|
||||
variant: "default" | "failure";
|
||||
className?: string;
|
||||
width?: number;
|
||||
};
|
||||
|
||||
export function AlphaText({ variant, className = "", width = 72 }: Props) {
|
||||
const attr =
|
||||
variant === "default"
|
||||
? { opacity: "0.6", fill: "white" }
|
||||
: { fill: "#CC6C6C" };
|
||||
return (
|
||||
<svg
|
||||
width="72"
|
||||
height="11"
|
||||
className={className}
|
||||
width={width}
|
||||
viewBox="0 0 72 11"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
opacity="0.6"
|
||||
d="M67.7256 8.55733H61.5796L60.7396 10.1953H58.2336L63.2876 0.395325H65.9196L71.1276 10.1953H68.5936L67.7256 8.55733ZM66.7036 6.61132L64.6316 2.74733L62.5876 6.61132H66.7036Z"
|
||||
fill="white"
|
||||
{...attr}
|
||||
/>
|
||||
<path
|
||||
opacity="0.6"
|
||||
d="M56.9429 0.395325V10.1953H54.5629V6.17733H38.3229V10.1953H35.9429V0.395325H38.3229V4.13333H54.5629V0.395325H56.9429Z"
|
||||
fill="white"
|
||||
{...attr}
|
||||
/>
|
||||
<path
|
||||
opacity="0.6"
|
||||
d="M24.3967 10.1953V0.395325H30.2767C32.6567 0.395325 34.1267 1.80932 34.1267 3.99333C34.1267 6.26133 32.6567 7.70333 30.2767 7.70333H26.7767L26.8047 10.1953H24.3967ZM26.7767 5.67333H30.2767C31.1027 5.67333 31.6207 5.11333 31.6207 4.02133C31.6207 2.97132 31.1167 2.42533 30.2767 2.42533H26.7767V5.67333Z"
|
||||
fill="white"
|
||||
{...attr}
|
||||
/>
|
||||
<path
|
||||
opacity="0.6"
|
||||
d="M14.3544 10.1953V0.395325H16.7344V8.16533H22.8524V10.1953H14.3544Z"
|
||||
fill="white"
|
||||
{...attr}
|
||||
/>
|
||||
<path
|
||||
opacity="0.6"
|
||||
d="M9.66003 8.55733H3.51403L2.67403 10.1953H0.16803L5.22203 0.395325H7.85403L13.062 10.1953H10.528L9.66003 8.55733ZM8.63803 6.61132L6.56603 2.74733L4.52203 6.61132H8.63803Z"
|
||||
fill="white"
|
||||
{...attr}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Menu } from "lucide-react";
|
||||
import "./appBar.css";
|
||||
import { ReactNode } from "react";
|
||||
import { DashboardIcon } from "../DashboardIcon/DashboardIcon";
|
||||
import { NodeIndicator } from "../NodeIndicator/NodeIndicator";
|
||||
import { HttpNetworkIndicator } from "../HttpNetworkIndicator/HttpNetworkIndicator";
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
@ -8,23 +9,31 @@ type Props = {
|
||||
* menu button.
|
||||
*/
|
||||
onExpand: () => void;
|
||||
|
||||
/**
|
||||
* React node to add to the right part of the application bar
|
||||
*/
|
||||
Right: ReactNode;
|
||||
};
|
||||
|
||||
export function AppBar({ onExpand, Right }: Props) {
|
||||
export function AppBar(props: Props) {
|
||||
console.info(props);
|
||||
return (
|
||||
<div className="appBar">
|
||||
<div className="appBar-left">
|
||||
<a className="appBar-burger" onClick={onExpand}>
|
||||
{/* <a className="appBar-burger" onClick={onExpand}>
|
||||
<Menu size={"1.25rem"} />
|
||||
</a>
|
||||
<span>Home</span>
|
||||
</a> */}
|
||||
|
||||
<div className="appBar-icon">
|
||||
<DashboardIcon />
|
||||
</div>
|
||||
<div className="appBar-textContainer">
|
||||
<div className="appBar-title">Dashboard</div>
|
||||
<div className="appBar-subtitle">
|
||||
Get Overview of your Codex Vault
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="appBar-right">
|
||||
<HttpNetworkIndicator />
|
||||
<NodeIndicator />
|
||||
</div>
|
||||
<div className="appBar-right">{Right}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
.appBar {
|
||||
height: 40px;
|
||||
height: 80px;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--codex-border-color);
|
||||
view-transition-name: main-header;
|
||||
display: flex;
|
||||
padding: 0.75rem 1.5rem;
|
||||
padding: 20px 40px 20px 40px;
|
||||
border-bottom: 1px solid #2b303b;
|
||||
box-sizing: border-box;
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
|
||||
.appBar-burger {
|
||||
@ -18,11 +21,36 @@
|
||||
.appBar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.appBar-left,
|
||||
.appBar-right {
|
||||
gap: 0.75rem;
|
||||
.appBar-icon {
|
||||
background: #141414;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #353639;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.appBar-title {
|
||||
font-family: Inter;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.015em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.appBar-subtitle {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.006em;
|
||||
color: #969696cc;
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
|
||||
15
src/components/DashboardIcon/DashboardIcon.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
export function DashboardIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M9.89999 17.1V8.09999H17.1V17.1H9.89999ZM0.899994 9.89999V0.899994H8.09999V9.89999H0.899994ZM6.29999 8.09999V2.69999H2.69999V8.09999H6.29999ZM0.899994 17.1V11.7H8.09999V17.1H0.899994ZM2.69999 15.3H6.29999V13.5H2.69999V15.3ZM11.7 15.3H15.3V9.89999H11.7V15.3ZM9.89999 0.899994H17.1V6.29999H9.89999V0.899994ZM11.7 2.69999V4.49999H15.3V2.69999H11.7Z"
|
||||
fill="#969696"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
21
src/components/ErrorCircleIcon/ErrorCircleIcon.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function ErrorCircleIcon({ className = "" }: Props) {
|
||||
return (
|
||||
<span className={"onboarding-healthCheck-icon " + className}>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM8 6.9395L5.879 4.81775L4.81775 5.879L6.9395 8L4.81775 10.121L5.879 11.1823L8 9.0605L10.121 11.1823L11.1823 10.121L9.0605 8L11.1823 5.879L10.121 4.81775L8 6.9395Z"
|
||||
fill="#FB3748"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
24
src/components/HttpNetworkIndicator/HttpNetworkIndicator.css
Normal file
@ -0,0 +1,24 @@
|
||||
.network-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.network-indicator-icon {
|
||||
background-color: #141414;
|
||||
border-radius: var(--codex-border-radius);
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.network-indicator-text {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.006em;
|
||||
color: #8d8d8d;
|
||||
}
|
||||
@ -1,10 +1,16 @@
|
||||
import { NetworkIndicator } from "@codex-storage/marketplace-ui-components";
|
||||
import { useNetwork } from "../../network/useNetwork";
|
||||
import { NetworkFlashIcon } from "../NetworkFlashIcon/NetworkFlashIcon";
|
||||
import "./HttpNetworkIndicator.css";
|
||||
|
||||
export function HttpNetworkIndicator() {
|
||||
const online = useNetwork();
|
||||
|
||||
const text = online ? "Online" : "Offline";
|
||||
|
||||
return <NetworkIndicator online={online} text={text} />;
|
||||
return (
|
||||
<div className="network-indicator">
|
||||
<div className="network-indicator-icon">
|
||||
<NetworkFlashIcon online={online} />
|
||||
</div>
|
||||
<span className="network-indicator-text">Network</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,23 +2,26 @@ import { Button, Input } from "@codex-storage/marketplace-ui-components";
|
||||
import "./ManifestFetch.css";
|
||||
import { ChangeEvent, useState } from "react";
|
||||
import { CodexSdk } from "../../sdk/codex";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { Promises } from "../../utils/promises";
|
||||
|
||||
export function ManifestFetch() {
|
||||
const [cid, setCid] = useState("");
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { refetch } = useQuery({
|
||||
queryFn: () =>
|
||||
CodexSdk.data()
|
||||
queryFn: () => {
|
||||
return CodexSdk.data()
|
||||
.fetchManifest(cid)
|
||||
.then((s) => {
|
||||
if (s.error === false) {
|
||||
setCid("");
|
||||
queryClient.invalidateQueries({ queryKey: ["cids"] });
|
||||
}
|
||||
return Promises.rejectOnError(s);
|
||||
}),
|
||||
queryKey: ["cids"],
|
||||
});
|
||||
},
|
||||
queryKey: ["manifest"],
|
||||
|
||||
// Disable the fetch to make it available on refetch only
|
||||
enabled: false,
|
||||
@ -44,6 +47,7 @@ export function ManifestFetch() {
|
||||
<div className="fetch-inputContainer">
|
||||
<Input
|
||||
id="cid"
|
||||
value={cid}
|
||||
placeholder="CID"
|
||||
inputClassName="fetch-input"
|
||||
onChange={onCidChange}></Input>
|
||||
|
||||
@ -1,4 +1,18 @@
|
||||
export function NodesIcon() {
|
||||
type Props = {
|
||||
variant: "default" | "success" | "failure";
|
||||
};
|
||||
|
||||
export function NodesIcon({ variant }: Props) {
|
||||
let color = "currentColor";
|
||||
|
||||
if (variant === "success") {
|
||||
color = "#3EE089";
|
||||
}
|
||||
|
||||
if (variant === "failure") {
|
||||
color = "var(--codex-color-error-hexa)";
|
||||
}
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
@ -8,7 +22,7 @@ export function NodesIcon() {
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M5.5 0.5C5.914 0.5 6.25 0.836 6.25 1.25V4.25C6.25 4.664 5.914 5 5.5 5H4V6.5H7.75V5.75C7.75 5.336 8.086 5 8.5 5H13C13.414 5 13.75 5.336 13.75 5.75V8.75C13.75 9.164 13.414 9.5 13 9.5H8.5C8.086 9.5 7.75 9.164 7.75 8.75V8H4V12.5H7.75V11.75C7.75 11.336 8.086 11 8.5 11H13C13.414 11 13.75 11.336 13.75 11.75V14.75C13.75 15.164 13.414 15.5 13 15.5H8.5C8.086 15.5 7.75 15.164 7.75 14.75V14H3.25C2.836 14 2.5 13.664 2.5 13.25V5H1C0.586 5 0.25 4.664 0.25 4.25V1.25C0.25 0.836 0.586 0.5 1 0.5H5.5ZM12.25 12.5H9.25V14H12.25V12.5ZM12.25 6.5H9.25V8H12.25V6.5ZM4.75 2H1.75V3.5H4.75V2Z"
|
||||
fill="currentColor"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
18
src/components/NetworkFlashIcon/NetworkFlashIcon.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
type Props = {
|
||||
online: boolean;
|
||||
};
|
||||
|
||||
export function NetworkFlashIcon({ online }: Props) {
|
||||
const color = online ? "#3EE089" : "var(--codex-color-error-hexa)";
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.75 8.5H16L9.25 18.25V11.5H4L10.75 1.75V8.5Z" fill={color} />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@ -3,9 +3,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export function NetworkIcon({ active }: Props) {
|
||||
const stroke = active
|
||||
? "var(--codex-color-primary)"
|
||||
: "rgb(var(--codex-color-error))";
|
||||
const stroke = active ? "#3EE089" : "rgb(var(--codex-color-error))";
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
|
||||
24
src/components/NodeIndicator/NodeIndicator.css
Normal file
@ -0,0 +1,24 @@
|
||||
.network-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.network-indicator-icon {
|
||||
background-color: #141414;
|
||||
border-radius: var(--codex-border-radius);
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.network-indicator-text {
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.006em;
|
||||
color: #8d8d8d;
|
||||
}
|
||||
@ -1,17 +1,11 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
NetworkIndicator,
|
||||
Toast,
|
||||
} from "@codex-storage/marketplace-ui-components";
|
||||
import { useEffect } from "react";
|
||||
import { useCodexConnection } from "../../hooks/useCodexConnection";
|
||||
import "./NodeIndicator.css";
|
||||
import { NodesIcon } from "../Menu/NodesIcon";
|
||||
|
||||
export function NodeIndicator() {
|
||||
const queryClient = useQueryClient();
|
||||
const [toast] = useState({
|
||||
time: 0,
|
||||
message: "",
|
||||
});
|
||||
const codex = useCodexConnection();
|
||||
|
||||
useEffect(() => {
|
||||
@ -23,8 +17,12 @@ export function NodeIndicator() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast message={toast.message} time={toast.time} variant="success" />
|
||||
<NetworkIndicator online={codex.enabled} text="Codex node" />
|
||||
<div className="network-indicator">
|
||||
<div className="network-indicator-icon">
|
||||
<NodesIcon variant={codex.enabled ? "success" : "failure"} />
|
||||
</div>
|
||||
<span className="network-indicator-text">Node</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,16 @@
|
||||
export function AlphaIcon() {
|
||||
type Props = {
|
||||
variant: "primary" | "error";
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function AlphaIcon({ variant, className = "" }: Props) {
|
||||
const color =
|
||||
variant === "primary"
|
||||
? "var(--codex-color-primary)"
|
||||
: "var(--codex-color-error-hexa)";
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
width="27"
|
||||
height="27"
|
||||
viewBox="0 0 27 27"
|
||||
@ -8,15 +18,10 @@ export function AlphaIcon() {
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12.957 1.38171C13.0441 1.33141 13.1515 1.33141 13.2386 1.38171L23.1736 7.11769C23.2607 7.16799 23.3144 7.26095 23.3144 7.36156V18.8335C23.3144 18.9341 23.2607 19.0271 23.1736 19.0774L13.2386 24.8134C13.1515 24.8637 13.0441 24.8637 12.957 24.8134L3.02198 19.0774L2.47465 20.0254L3.02198 19.0774C2.93485 19.0271 2.88118 18.9341 2.88118 18.8335V7.36156C2.88118 7.26095 2.93485 7.16799 3.02198 7.11769L2.45878 6.14219L3.02198 7.11769L12.957 1.38171Z"
|
||||
stroke="var(--codex-color-error-hexa)"
|
||||
stroke={color}
|
||||
strokeWidth="2.25282"
|
||||
/>
|
||||
<circle
|
||||
cx="13.0978"
|
||||
cy="13.0975"
|
||||
r="3.92933"
|
||||
fill="var(--codex-color-error-hexa)"
|
||||
/>
|
||||
<circle cx="13.0978" cy="13.0975" r="3.92933" fill={color} />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
15
src/components/OnBoarding/HealthCheckIcon.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
export function HealthCheckIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M16.1999 0C16.6967 0 17.0999 0.4032 17.0999 0.9V17.1C17.0999 17.5968 16.6967 18 16.1999 18H3.5999C3.1031 18 2.6999 17.5968 2.6999 17.1V15.3H0.899902V13.5H2.6999V11.7H0.899902V9.9H2.6999V8.1H0.899902V6.3H2.6999V4.5H0.899902V2.7H2.6999V0.9C2.6999 0.4032 3.1031 0 3.5999 0H16.1999ZM15.2999 1.8H4.4999V16.2H15.2999V1.8ZM10.7999 5.4V8.1H13.4999V9.9H10.799L10.7999 12.6H8.9999L8.999 9.9H6.2999V8.1H8.9999V5.4H10.7999Z"
|
||||
fill="#6FCB94"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
5
src/components/OnBoarding/HealthCheckItem.css
Normal file
@ -0,0 +1,5 @@
|
||||
.onboarding-healthCheckItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
21
src/components/OnBoarding/HealthCheckItem.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { classnames } from "../../utils/classnames";
|
||||
import { OnBoardingStatusIcon } from "./OnBoardingStatusIcon";
|
||||
import "./HealthCheckItem.css";
|
||||
|
||||
type Props = {
|
||||
value: "success" | "failure" | "warning";
|
||||
text: string;
|
||||
};
|
||||
|
||||
export function HealthCheckItem({ value, text }: Props) {
|
||||
return (
|
||||
<div
|
||||
data-testid="network"
|
||||
className={classnames(["onboarding-healthCheckItem"])}>
|
||||
<OnBoardingStatusIcon value={value} />
|
||||
<div>
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,28 +1,28 @@
|
||||
import "./OmBoardingImage.css";
|
||||
import "./OnBoardingImage.css";
|
||||
|
||||
export function OnBoardingImage() {
|
||||
return (
|
||||
<picture>
|
||||
<source
|
||||
srcSet="/img/onboarding@4x.webp 4x,
|
||||
/img/onboarding@3x.webp 3x,
|
||||
/img/onboarding@2x.webp 2x,
|
||||
/img/onboarding@1.5x.webp 1.5x,
|
||||
/img/onboarding.webp 1x"
|
||||
/img/onboarding@3x.webp 3x,
|
||||
/img/onboarding@2x.webp 2x,
|
||||
/img/onboarding@1.5x.webp 1.5x,
|
||||
/img/onboarding.webp 1x"
|
||||
sizes="(max-width: 600px) 100vw,
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
type="image/webp"
|
||||
/>
|
||||
<source
|
||||
srcSet="/img/onboarding@4x.png 4x,
|
||||
/img/onboarding@3x.png 3x,
|
||||
/img/onboarding@2x.png 2x,
|
||||
/img/onboarding@1.5x.png 1.5x,
|
||||
/img/onboarding.png 1x"
|
||||
/img/onboarding@3x.png 3x,
|
||||
/img/onboarding@2x.png 2x,
|
||||
/img/onboarding@1.5x.png 1.5x,
|
||||
/img/onboarding.png 1x"
|
||||
sizes="(max-width: 600px) 100vw,
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
type="image/png"
|
||||
/>
|
||||
<img
|
||||
|
||||
33
src/components/OnBoarding/OnBoardingStatusIcon.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { ErrorCircleIcon } from "../ErrorCircleIcon/ErrorCircleIcon";
|
||||
import { SuccessCheckIcon } from "../SuccessCheckIcon/SuccessCheckIcon";
|
||||
|
||||
type Props = {
|
||||
value: "success" | "failure" | "warning";
|
||||
};
|
||||
|
||||
export function OnBoardingStatusIcon({ value }: Props) {
|
||||
switch (value) {
|
||||
case "success":
|
||||
return <SuccessCheckIcon variant="primary"></SuccessCheckIcon>;
|
||||
|
||||
case "failure":
|
||||
return <ErrorCircleIcon />;
|
||||
|
||||
case "warning":
|
||||
return (
|
||||
<span className="onboarding-healthCheck-icon">
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM4.25 7.25V8.75H11.75V7.25H4.25Z"
|
||||
fill="#FF8447"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -19,11 +19,11 @@ export function OnBoardingStepOne({ onNextStep }: Props) {
|
||||
<>
|
||||
<div className="index-column-section">
|
||||
<div>
|
||||
<AlphaIcon />
|
||||
<AlphaIcon variant="error" />
|
||||
</div>
|
||||
<div className="index-alphaText">
|
||||
<p>
|
||||
<AlphaText></AlphaText>
|
||||
<AlphaText variant="default"></AlphaText>
|
||||
</p>
|
||||
<p>
|
||||
<SimpleText className="index-version" variant="normal">
|
||||
|
||||
@ -27,10 +27,6 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.onboarding-check-refresh--fetching {
|
||||
animation: rotateAnimation 2s linear infinite;
|
||||
}
|
||||
|
||||
.onboarding-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -43,6 +39,75 @@
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.onboarding-deviceCheck {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
font-family: Azeret Mono;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 16.34px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.onboarding--deviceCheck-block {
|
||||
flex: 0.3;
|
||||
}
|
||||
|
||||
.onboarding-displayName {
|
||||
margin-bottom: 1.75rem;
|
||||
}
|
||||
|
||||
.onboarding-healthCheck-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 0;
|
||||
gap: 16px;
|
||||
border-top: 1px solid #3c3d3e;
|
||||
border-bottom: 1px solid #3c3d3e;
|
||||
}
|
||||
|
||||
.onboarding-healthCheck-itemText {
|
||||
font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.011em;
|
||||
}
|
||||
|
||||
.onboarding-healthChecks {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.onboarding-healthCheck-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.onboarding-refresh {
|
||||
position: relative;
|
||||
top: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.onboarding-refresh--fetching {
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
|
||||
.onboarding-portContainer,
|
||||
.onboarding-addressContainer {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.onboarding--addressSuccessIcon {
|
||||
position: absolute;
|
||||
top: 53px;
|
||||
bottom: 0;
|
||||
right: 18px;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg); /* Start at 0 degrees */
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
import { CheckIcon, RefreshCcw, Save, ShieldAlert, X } from "lucide-react";
|
||||
import { classnames } from "../../utils/classnames";
|
||||
import "./OnBoardingStepThree.css";
|
||||
import { usePortForwarding } from "../../hooks/usePortForwarding";
|
||||
import {
|
||||
Alert,
|
||||
ButtonIcon,
|
||||
Input,
|
||||
SimpleText,
|
||||
} from "@codex-storage/marketplace-ui-components";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Input, SimpleText } from "@codex-storage/marketplace-ui-components";
|
||||
import { ClipboardEvent, useEffect, useState } from "react";
|
||||
import { CodexSdk } from "../../sdk/codex";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { usePersistence } from "../../hooks/usePersistence";
|
||||
import { useDebug } from "../../hooks/useDebug";
|
||||
import { DebugUtils } from "../../utils/debug";
|
||||
import { AlphaIcon } from "./AlphaIcon";
|
||||
import { OnBoardingUtils } from "../../utils/onboarding";
|
||||
import { RefreshIcon } from "../RefreshIcon/RefreshIcon";
|
||||
import { HealthCheckIcon } from "./HealthCheckIcon";
|
||||
import { HealthCheckItem } from "./HealthCheckItem";
|
||||
import { Strings } from "../../utils/strings";
|
||||
import { SuccessCheckIcon } from "../SuccessCheckIcon/SuccessCheckIcon";
|
||||
import { ErrorCircleIcon } from "../ErrorCircleIcon/ErrorCircleIcon";
|
||||
|
||||
type Props = {
|
||||
online: boolean;
|
||||
@ -29,6 +31,7 @@ export function OnBoardingStepThree({ online, onStepValid }: Props) {
|
||||
const persistence = usePersistence(codex.isSuccess);
|
||||
const [url, setUrl] = useState(CodexSdk.url);
|
||||
const queryClient = useQueryClient();
|
||||
const [isInvalid, setIsInvalid] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
onStepValid(online && portForwarding.enabled && codex.isSuccess);
|
||||
@ -37,179 +40,180 @@ export function OnBoardingStepThree({ online, onStepValid }: Props) {
|
||||
useEffect(() => {
|
||||
if (codex.isSuccess) {
|
||||
persistence.refetch();
|
||||
portForwarding.refetch();
|
||||
}
|
||||
}, [persistence, codex.isSuccess]);
|
||||
}, [persistence, portForwarding, codex.isSuccess]);
|
||||
|
||||
const onChange = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const onAddressChange = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const [, port] = Strings.splitURLAndPort(url);
|
||||
const element = e.currentTarget;
|
||||
const value = e.currentTarget.value;
|
||||
if (value) {
|
||||
setUrl(value);
|
||||
|
||||
if (
|
||||
value.startsWith("http://") === false &&
|
||||
value.startsWith("https://") === false
|
||||
) {
|
||||
setIsInvalid(true);
|
||||
return;
|
||||
}
|
||||
|
||||
console.info("isInvalid", isInvalid);
|
||||
|
||||
setIsInvalid(!element.checkValidity());
|
||||
setUrl(value + ":" + port);
|
||||
};
|
||||
|
||||
const onPaste = (e: ClipboardEvent) => {
|
||||
const text = e.clipboardData.getData("text");
|
||||
|
||||
try {
|
||||
new URL(text);
|
||||
setUrl(text);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (_) {
|
||||
// Nothing to do here
|
||||
}
|
||||
};
|
||||
|
||||
const onSave = () =>
|
||||
const onPortChange = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const [address] = Strings.splitURLAndPort(url);
|
||||
const element = e.currentTarget;
|
||||
const value = element.value;
|
||||
|
||||
setUrl(address + ":" + value);
|
||||
};
|
||||
|
||||
const onSave = () => {
|
||||
if (isInvalid === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
CodexSdk.updateURL(url)
|
||||
.then(() => queryClient.invalidateQueries())
|
||||
.then(() => codex.refetch());
|
||||
};
|
||||
|
||||
const InternetIcon = online ? CheckIcon : X;
|
||||
const PortForWarningIcon = portForwarding.enabled ? CheckIcon : X;
|
||||
const CodexIcon = codex.isSuccess ? CheckIcon : X;
|
||||
const PersistenceIcon = persistence.enabled ? CheckIcon : ShieldAlert;
|
||||
|
||||
let hasPortForwarningWarning = false;
|
||||
let portValue = 0;
|
||||
let forwardingPortValue = defaultPort;
|
||||
|
||||
if (codex.isSuccess && codex.data) {
|
||||
const port = DebugUtils.getTcpPort(codex.data);
|
||||
if (port.error === false && port.data !== defaultPort) {
|
||||
hasPortForwarningWarning = true;
|
||||
}
|
||||
if (!port.error) {
|
||||
portValue = port.data;
|
||||
forwardingPortValue = port.data;
|
||||
}
|
||||
}
|
||||
|
||||
const displayName = OnBoardingUtils.getDisplayName();
|
||||
const [address, port] = Strings.splitURLAndPort(url);
|
||||
|
||||
return (
|
||||
<div className="index-column-section">
|
||||
<div className="onboarding-group">
|
||||
<>
|
||||
<div className="index-column-section onboarding--deviceCheck-block">
|
||||
<div>
|
||||
<Input
|
||||
id="url"
|
||||
label="Codex client node URL"
|
||||
onChange={onChange}
|
||||
value={url}
|
||||
inputClassName="settings-input"></Input>
|
||||
<AlphaIcon variant="primary" />
|
||||
</div>
|
||||
|
||||
<ButtonIcon Icon={Save} onClick={onSave}></ButtonIcon>
|
||||
</div>
|
||||
<div
|
||||
data-testid="network"
|
||||
className={classnames(
|
||||
["onboarding-check"],
|
||||
["onboarding-check--valid", online]
|
||||
)}>
|
||||
<InternetIcon
|
||||
className={classnames(
|
||||
["onboarding-check-icon--valid", online],
|
||||
["onboarding-check-icon--invalid", !online]
|
||||
)}
|
||||
/>
|
||||
<div>
|
||||
<p>Internet connection</p>
|
||||
<SimpleText variant="light">
|
||||
Status indicator for the Internet.
|
||||
</SimpleText>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classnames(
|
||||
["onboarding-check"],
|
||||
["onboarding-check--valid", portForwarding.enabled]
|
||||
)}>
|
||||
<PortForWarningIcon
|
||||
className={classnames(
|
||||
["onboarding-check-icon--valid", portForwarding.enabled],
|
||||
["onboarding-check-icon--invalid", !portForwarding.enabled]
|
||||
)}
|
||||
/>
|
||||
<div className="onboarding-check-line">
|
||||
<div>
|
||||
<p>Port forwarding</p>
|
||||
<SimpleText variant="light">
|
||||
Status indicator for port forwarding activation.
|
||||
</SimpleText>
|
||||
{portValue && (
|
||||
<>
|
||||
<br />
|
||||
<SimpleText variant="light">
|
||||
TCP Port detected: {portValue}.
|
||||
</SimpleText>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{!portForwarding.enabled && (
|
||||
<a
|
||||
className="onboarding-check-refresh"
|
||||
onClick={() => portForwarding.refetch()}>
|
||||
<RefreshCcw
|
||||
size={"1.25rem"}
|
||||
className={classnames([
|
||||
"onboarding-check-refresh--fetching",
|
||||
portForwarding.isFetching,
|
||||
])}
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<p>Codex</p>
|
||||
<div className="onboarding-codex">
|
||||
<div
|
||||
className={classnames(
|
||||
["onboarding-check"],
|
||||
["onboarding-check--valid", codex.isSuccess]
|
||||
)}>
|
||||
<CodexIcon
|
||||
className={classnames(
|
||||
["onboarding-check-icon--valid", codex.isSuccess],
|
||||
["onboarding-check-icon--invalid", !codex.isSuccess]
|
||||
)}
|
||||
/>
|
||||
<div className="onboarding-check-line">
|
||||
<div>
|
||||
<p>Codex connection</p>
|
||||
<SimpleText variant="light">
|
||||
Status indicator for the Codex network.
|
||||
</SimpleText>
|
||||
</div>
|
||||
{!persistence.enabled && (
|
||||
<a
|
||||
className="onboarding-check-refresh"
|
||||
onClick={() => persistence.refetch()}>
|
||||
<RefreshCcw
|
||||
size={"1.25rem"}
|
||||
className={classnames([
|
||||
"onboarding-check-refresh--fetching",
|
||||
persistence.isFetching,
|
||||
])}
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classnames(
|
||||
["onboarding-check"],
|
||||
["onboarding-check--valid", persistence.enabled]
|
||||
)}>
|
||||
<PersistenceIcon
|
||||
className={classnames(
|
||||
["onboarding-check-icon--valid", persistence.enabled],
|
||||
["onboarding-check-icon--warning", !persistence.enabled]
|
||||
)}
|
||||
/>
|
||||
<div className="onboarding-check-line">
|
||||
<div>
|
||||
<p>Marketplace</p>
|
||||
<SimpleText variant="light">
|
||||
Status indicator for the marketplace on the Codex node.
|
||||
</SimpleText>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{hasPortForwarningWarning && (
|
||||
<Alert variant="warning" title="Warning">
|
||||
<SimpleText variant="primary" className="onboarding-deviceCheck">
|
||||
<span>
|
||||
It seems like you are using a different port than the default one (
|
||||
{defaultPort}). Be sure the port forwarning is enabled for the port
|
||||
you are running.
|
||||
Connection /<br />
|
||||
Device Health Check
|
||||
</span>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</SimpleText>
|
||||
</div>
|
||||
<div className="index-column-section">
|
||||
<h3 className="index-mainTitle onboarding-displayName">
|
||||
Nice to meet {displayName},<br />
|
||||
Let’s establish our connection.
|
||||
</h3>
|
||||
<div className="onboarding-group">
|
||||
<div className="onboarding-addressAndPort">
|
||||
<div className="onboarding-addressContainer">
|
||||
<Input
|
||||
onPaste={onPaste}
|
||||
id="url"
|
||||
type="url"
|
||||
label="Address"
|
||||
isInvalid={isInvalid}
|
||||
onChange={onAddressChange}
|
||||
value={address}
|
||||
placeholder="127.0.0.1"></Input>
|
||||
{isInvalid ? (
|
||||
<ErrorCircleIcon className="onboarding--addressSuccessIcon" />
|
||||
) : (
|
||||
<SuccessCheckIcon
|
||||
className="onboarding--addressSuccessIcon"
|
||||
variant="default"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="onboarding-portContainer">
|
||||
<Input
|
||||
inputClassName="onboarding-port"
|
||||
id="port"
|
||||
label="Port"
|
||||
type="number"
|
||||
onChange={onPortChange}
|
||||
value={port}
|
||||
placeholder="8080"></Input>
|
||||
<SuccessCheckIcon
|
||||
className="onboarding--addressSuccessIcon"
|
||||
variant="default"></SuccessCheckIcon>
|
||||
</div>
|
||||
|
||||
<RefreshIcon
|
||||
onClick={onSave}
|
||||
className={classnames(
|
||||
["onboarding-refresh"],
|
||||
[
|
||||
"onboarding-refresh--fetching",
|
||||
portForwarding.isFetching || codex.isPending,
|
||||
]
|
||||
)}></RefreshIcon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="onboarding-portForwardingHelp">
|
||||
<li>Port forwarding should be default {forwardingPortValue}.</li>
|
||||
</ul>
|
||||
|
||||
<div className="onboarding-healthChecks">
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<span className="onboarding-healthCheck-icon">
|
||||
<HealthCheckIcon />
|
||||
</span>
|
||||
|
||||
<span className="onboarding-healthCheck-itemText">
|
||||
Health Check
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={online ? "success" : "failure"}
|
||||
text="Internet connection"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={portForwarding.enabled ? "success" : "failure"}
|
||||
text="Port forwarding"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={codex.isSuccess ? "success" : "failure"}
|
||||
text="Codex connection"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={codex.isSuccess ? "success" : "warning"}
|
||||
text="Marketplace"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
44
src/components/OnBoarding/OnBoardingStepTwo.css
Normal file
@ -0,0 +1,44 @@
|
||||
.onboarding-personalization {
|
||||
margin-top: 1rem;
|
||||
font-family: Azeret Mono;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 16.34px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.onboarding--personalization-block {
|
||||
flex: 0.3;
|
||||
}
|
||||
|
||||
.onboarding-addressAndPort {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.onboarding-port {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.onboarding-port::-webkit-outer-spin-button,
|
||||
.onboarding-port::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
.onboarding-port[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
.onboarding-portForwardingHelp {
|
||||
font-family: Azeret Mono;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 14px;
|
||||
color: #828282;
|
||||
padding-left: 1.25rem;
|
||||
margin-top: 1.75rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
import { Input } from "@codex-storage/marketplace-ui-components";
|
||||
import { Input, SimpleText } from "@codex-storage/marketplace-ui-components";
|
||||
import { ChangeEvent, useState } from "react";
|
||||
import { AlphaIcon } from "./AlphaIcon";
|
||||
import "./OnBoardingStepTwo.css";
|
||||
import { OnBoardingUtils } from "../../utils/onboarding";
|
||||
|
||||
type Props = {
|
||||
onStepValid: (valid: boolean) => void;
|
||||
@ -9,18 +12,34 @@ export function OnBoardingStepTwo({ onStepValid }: Props) {
|
||||
const [displayName, setDisplayName] = useState("");
|
||||
|
||||
const onDisplayNameChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setDisplayName(e.currentTarget.value);
|
||||
onStepValid(!!e.currentTarget.value);
|
||||
const value = e.currentTarget.value;
|
||||
OnBoardingUtils.setDisplayName(value);
|
||||
setDisplayName(value);
|
||||
onStepValid(!!value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="index-column-section onboarding--personalization-block">
|
||||
<div>
|
||||
<AlphaIcon variant="primary" />
|
||||
</div>
|
||||
<SimpleText variant="primary" className="onboarding-personalization">
|
||||
<span>Personalization</span>
|
||||
</SimpleText>
|
||||
</div>
|
||||
<div className="index-column-section">
|
||||
<Input
|
||||
onChange={onDisplayNameChange}
|
||||
label="Display name"
|
||||
id="displayName"
|
||||
value={displayName}></Input>
|
||||
<h3 className="index-mainTitle">
|
||||
Let’s get you setup. <br />
|
||||
What do you want to be called?
|
||||
</h3>
|
||||
<div className="index-displayName">
|
||||
<Input
|
||||
onChange={onDisplayNameChange}
|
||||
label="Display name"
|
||||
id="displayName"
|
||||
value={displayName}></Input>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -6,14 +6,12 @@ import "./page.css";
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
|
||||
Right: ReactNode;
|
||||
|
||||
items: MenuItem[];
|
||||
|
||||
version?: string;
|
||||
};
|
||||
|
||||
export function Page({ children, Right, items, version = "" }: Props) {
|
||||
export function Page({ children, items, version = "" }: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const onClose = () => setOpen(false);
|
||||
@ -29,7 +27,7 @@ export function Page({ children, Right, items, version = "" }: Props) {
|
||||
version={version}></Menu>
|
||||
|
||||
<main className="page-main">
|
||||
<AppBar onExpand={onExpand} Right={Right} />
|
||||
<AppBar onExpand={onExpand} />
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -52,7 +52,7 @@ export function PeerCountryCell({ address, onPinAdd }: Props) {
|
||||
lng: data.longitude,
|
||||
});
|
||||
}
|
||||
}, [data]);
|
||||
}, [data, onPinAdd]);
|
||||
|
||||
return (
|
||||
<Cell>
|
||||
|
||||
22
src/components/RefreshIcon/RefreshIcon.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
type Props = {
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function RefreshIcon({ className, onClick }: Props) {
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
onClick={onClick}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 24C5.3724 24 0 18.6276 0 12C0 5.3724 5.3724 0 12 0C18.6276 0 24 5.3724 24 12C24 18.6276 18.6276 24 12 24ZM17.784 18.0912C19.2325 16.7182 20.1449 14.8744 20.3576 12.8899C20.5703 10.9055 20.0695 8.91012 18.9449 7.26133C17.8203 5.61253 16.1454 4.41803 14.2202 3.89182C12.295 3.36561 10.2453 3.54208 8.4384 4.3896L9.6084 6.4956C10.5215 6.09874 11.519 5.93535 12.511 6.02015C13.503 6.10495 14.4584 6.43528 15.2909 6.98135C16.1234 7.52743 16.8069 8.27209 17.2798 9.14821C17.7528 10.0243 18.0003 11.0044 18 12H14.4L17.784 18.0912ZM15.5616 19.6104L14.3916 17.5044C13.4785 17.9013 12.481 18.0646 11.489 17.9798C10.497 17.8951 9.54165 17.5647 8.70914 17.0186C7.87664 16.4726 7.1931 15.7279 6.72016 14.8518C6.24722 13.9757 5.99973 12.9956 6 12H9.6L6.216 5.9088C4.76747 7.28176 3.85511 9.12563 3.64239 11.1101C3.42966 13.0945 3.93047 15.0899 5.05508 16.7387C6.17969 18.3875 7.85462 19.582 9.77982 20.1082C11.705 20.6344 13.7547 20.4579 15.5616 19.6104V19.6104Z"
|
||||
fill="var(--codex-color-primary)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
24
src/components/SuccessCheckIcon/SuccessCheckIcon.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
type Props = {
|
||||
variant: "primary" | "default";
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function SuccessCheckIcon({ variant, className = "" }: Props) {
|
||||
const color = variant === "primary" ? "#1FC16B" : "#444444";
|
||||
|
||||
return (
|
||||
<span className={"onboarding-healthCheck-icon " + className}>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM7.25225 11L12.5548 5.69675L11.4943 4.63625L7.25225 8.879L5.1305 6.75725L4.07 7.81775L7.25225 11Z"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -31,6 +31,7 @@
|
||||
--codex-color-disabled: #717171;
|
||||
--codex-color-light: rgb(150 150 150);
|
||||
--codex-border-color: #2b303b;
|
||||
--codex-input-border-color: #494949;
|
||||
--codex-background-secondary: rgb(38 38 38);
|
||||
--codex-highlight-color: #2f2f2f;
|
||||
--codex-background-light: rgb(64 64 64);
|
||||
@ -41,6 +42,10 @@
|
||||
BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans,
|
||||
sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol,
|
||||
Noto Color Emoji;
|
||||
--codex-input-label-color: #7b7b7b;
|
||||
--codex-input-border-color: #494949;
|
||||
--codex-input-background: #232323;
|
||||
--codex-input-color-error: #fb3748;
|
||||
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
@ -173,3 +178,11 @@ a[aria-disabled] {
|
||||
.page {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:focus,
|
||||
input:-webkit-autofill:active {
|
||||
-webkit-transition-delay: 9999s;
|
||||
transition-delay: 9999s;
|
||||
}
|
||||
|
||||
@ -22,6 +22,36 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.dashboard-welcome-versions {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.dashboard-welcome-versionContainer {
|
||||
width: 50px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dashboard-welcome-versionTitle {
|
||||
font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.011em;
|
||||
color: #99a0ae;
|
||||
}
|
||||
|
||||
.dashboard-welcome-versionValue {
|
||||
font-family: Inter;
|
||||
font-size: 10px;
|
||||
font-weight: 400;
|
||||
line-height: 12.1px;
|
||||
letter-spacing: 0.01em;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.dashboard {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { createFileRoute, Link, Outlet } from "@tanstack/react-router";
|
||||
import "./dashboard.css";
|
||||
import { NodeIndicator } from "../components/NodeIndicator/NodeIndicator";
|
||||
import { HttpNetworkIndicator } from "../components/HttpNetworkIndicator/HttpNetworkIndicator";
|
||||
import { Page } from "../components/Page/Page";
|
||||
import { HomeIcon } from "../components/Menu/HomeIcon";
|
||||
import { WalletIcon } from "../components/Menu/WalletIcon";
|
||||
@ -19,13 +17,6 @@ import { HostIcon } from "../components/Menu/HostIcon";
|
||||
import { DeviceIcon } from "../components/Menu/DeviceIcon";
|
||||
|
||||
const Layout = () => {
|
||||
const Right = (
|
||||
<>
|
||||
<NodeIndicator />
|
||||
<HttpNetworkIndicator />
|
||||
</>
|
||||
);
|
||||
|
||||
const items = [
|
||||
{
|
||||
type: "item",
|
||||
@ -70,7 +61,7 @@ const Layout = () => {
|
||||
aria-disabled={true}
|
||||
data-title="Coming soon">
|
||||
<span className="menu-icon">
|
||||
<NodesIcon />
|
||||
<NodesIcon variant="default" />
|
||||
</span>
|
||||
<span className="menu-text">Nodes</span>
|
||||
</Link>
|
||||
@ -202,7 +193,6 @@ const Layout = () => {
|
||||
<Page
|
||||
children={<Outlet />}
|
||||
items={items}
|
||||
Right={Right}
|
||||
version={import.meta.env.PACKAGE_VERSION}
|
||||
/>
|
||||
);
|
||||
|
||||
34
src/routes/dashboard/index.css
Normal file
@ -0,0 +1,34 @@
|
||||
.dashboard-welcomeContainer {
|
||||
padding: 24px 48px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.dashboard-welcome-avatarContainer {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.dashboard-welcome-avatarTitle {
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 14.52px;
|
||||
letter-spacing: 0.01em;
|
||||
color: #969696cc;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.dashboard-welcome-avatarSubtitle {
|
||||
font-family: Inter;
|
||||
font-size: 32px;
|
||||
font-weight: 400;
|
||||
line-height: 38.73px;
|
||||
letter-spacing: 0.01em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dashboard-welcome-alpha {
|
||||
position: relative;
|
||||
top: 6px;
|
||||
}
|
||||
@ -8,20 +8,83 @@ import { ErrorBoundary } from "@sentry/react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Download } from "../../components/Download/Download.tsx";
|
||||
import { ManifestFetch } from "../../components/ManifestFetch/ManifestFetch.tsx";
|
||||
import { OnBoardingUtils } from "../../utils/onboarding.ts";
|
||||
import "./index.css";
|
||||
import { AlphaIcon } from "../../components/OnBoarding/AlphaIcon.tsx";
|
||||
import { AlphaText } from "../../components/AlphaText/AlphaText.tsx";
|
||||
import { useDebug } from "../../hooks/useDebug.ts";
|
||||
|
||||
export const Route = createFileRoute("/dashboard/")({
|
||||
component: About,
|
||||
component: Dashboard,
|
||||
});
|
||||
|
||||
function About() {
|
||||
const throwOnError = false;
|
||||
|
||||
function Dashboard() {
|
||||
const queryClient = useQueryClient();
|
||||
const debug = useDebug(throwOnError);
|
||||
|
||||
const onSuccess = () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["cids"] });
|
||||
};
|
||||
|
||||
const username = OnBoardingUtils.getDisplayName();
|
||||
|
||||
const parts = debug.data?.codex.version.split("\n") || [""];
|
||||
const version = parts[parts.length - 1];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="dashboard-welcomeContainer">
|
||||
<div className="dashboard-welcome-avatarContainer">
|
||||
<picture>
|
||||
<source
|
||||
srcSet="/img/avatar@4x.webp 4x,
|
||||
/img/avatar@3x.webp 3x,
|
||||
/img/avatar@2x.webp 2x,
|
||||
/img/avatar@1.5x.webp 1.5x,
|
||||
/img/avatar.webp 1x"
|
||||
sizes="(max-width: 600px) 100vw,
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
type="image/webp"
|
||||
/>
|
||||
<source
|
||||
srcSet="/img/avatar@4x.png 4x,
|
||||
/img/avatar@3x.png 3x,
|
||||
/img/avatar@2x.png 2x,
|
||||
/img/avatar@1.5x.png 1.5x,
|
||||
/img/avatar.png 1x"
|
||||
sizes="(max-width: 600px) 100vw,
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
type="image/png"
|
||||
/>
|
||||
<img src="/img/avatar.png" height={52} width={52} alt="Avatar" />
|
||||
</picture>
|
||||
<div>
|
||||
<div className="dashboard-welcome-avatarTitle">Welcome back,</div>
|
||||
<div className="dashboard-welcome-avatarSubtitle">{username}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dashboard-welcome-versions">
|
||||
<AlphaIcon variant="error" className="dashboard-welcome-alpha" />
|
||||
<div className="dashboard-welcome-versionContainer">
|
||||
<p className="dashboard-welcome-versionTitle">Client</p>
|
||||
<p className="dashboard-welcome-versionValue">VER. {version}</p>
|
||||
</div>
|
||||
<div className="dashboard-welcome-versionContainer">
|
||||
<p className="dashboard-welcome-versionTitle">Vault</p>
|
||||
<p className="dashboard-welcome-versionValue">
|
||||
VER. {import.meta.env.PACKAGE_VERSION}
|
||||
</p>
|
||||
<AlphaText
|
||||
variant="failure"
|
||||
className="dashboard-welcome-alphaText"
|
||||
width={37}></AlphaText>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dashboard">
|
||||
<div>
|
||||
<ErrorBoundary
|
||||
|
||||
@ -139,15 +139,15 @@
|
||||
}
|
||||
|
||||
.index-column-section {
|
||||
max-width: 450px;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.index-column-section:not(:first-child) {
|
||||
.index-column-section {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.index-column-section:first-child {
|
||||
flex: 0.7;
|
||||
flex: 0.5;
|
||||
}
|
||||
|
||||
.index-column-section:last-child {
|
||||
@ -185,6 +185,10 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.index-displayName {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 0.4;
|
||||
|
||||
@ -2,7 +2,6 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||
import "./index.css";
|
||||
import { ArrowRightCircle } from "../components/ArrowRightCircle/ArrowRightCircle";
|
||||
import { useNetwork } from "../network/useNetwork";
|
||||
import { NetworkIcon } from "../components/NetworkIcon/NetworkIcon";
|
||||
import { Logotype } from "../components/Logotype/Logotype";
|
||||
import { useState } from "react";
|
||||
import { OnBoardingStepOne } from "../components/OnBoarding/OnBoardingStepOne";
|
||||
@ -10,7 +9,6 @@ import { OnBoardingStepTwo } from "../components/OnBoarding/OnBoardingStepTwo";
|
||||
import { classnames } from "../utils/classnames";
|
||||
import { OnBoardingStepThree } from "../components/OnBoarding/OnBoardingStepThree";
|
||||
import { attributes } from "../utils/attributes";
|
||||
import { CodexLogo } from "../components/CodexLogo/CodexLogo";
|
||||
import { OnBoardingImage } from "../components/OnBoarding/OnBoardingImage";
|
||||
import { OnBoardingUtils } from "../utils/onboarding";
|
||||
|
||||
@ -25,7 +23,8 @@ export const Route = createFileRoute("/")({
|
||||
|
||||
function Index() {
|
||||
const [isStepValid, setIsStepValid] = useState(true);
|
||||
const [step, setStep] = useState(OnBoardingUtils.getStep());
|
||||
// const [step, setStep] = useState(OnBoardingUtils.getStep());
|
||||
const [step, setStep] = useState(0);
|
||||
const online = useNetwork();
|
||||
const navigate = useNavigate({ from: "/" });
|
||||
const onStepValid = (valid: boolean) => setIsStepValid(valid);
|
||||
@ -52,7 +51,7 @@ function Index() {
|
||||
<OnBoardingStepThree online={online} onStepValid={onStepValid} />,
|
||||
];
|
||||
|
||||
const text = online ? "Network connected" : "Network disconnected";
|
||||
// const text = online ? "Network connected" : "Network disconnected";
|
||||
|
||||
return (
|
||||
<div className="index">
|
||||
@ -88,13 +87,14 @@ function Index() {
|
||||
<OnBoardingImage />
|
||||
</div>
|
||||
<div className="index-columnRight">
|
||||
<div className="index-logo">
|
||||
<div></div>
|
||||
{/* <div className="index-logo">
|
||||
<div className="index-network">
|
||||
<p className="index-network-text">{text}</p>
|
||||
<NetworkIcon active={online}></NetworkIcon>
|
||||
</div>
|
||||
<CodexLogo></CodexLogo>
|
||||
</div>
|
||||
</div> */}
|
||||
<a
|
||||
className="index-link2"
|
||||
{...attributes({ "aria-disabled": !isStepValid })}
|
||||
|
||||
@ -5,5 +5,13 @@ export const OnBoardingUtils = {
|
||||
|
||||
setStep(step: number) {
|
||||
localStorage.setItem("onboarding-step", step.toString())
|
||||
},
|
||||
|
||||
setDisplayName(displayName: string) {
|
||||
localStorage.setItem("display-name", displayName)
|
||||
},
|
||||
|
||||
getDisplayName() {
|
||||
return localStorage.getItem("display-name") || ""
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,11 @@
|
||||
export const Strings = {
|
||||
shortId: (id: string) => id.slice(0, 5) + "..." + id.slice(-5),
|
||||
|
||||
splitURLAndPort(url: string) {
|
||||
const [protocol, hostname = "", port = ""] = url.split(":")
|
||||
|
||||
return [protocol + ":" + hostname, port]
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||