Update dashboard

This commit is contained in:
Arnaud 2024-10-25 19:36:49 +02:00
parent 7f60e54a63
commit f2e3ed6958
No known key found for this signature in database
GPG Key ID: 69D6CE281FCAE663
32 changed files with 346 additions and 89 deletions

10
package-lock.json generated
View File

@ -9,7 +9,7 @@
"version": "0.0.7",
"license": "MIT",
"dependencies": {
"@codex-storage/marketplace-ui-components": "^0.0.26",
"@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",
@ -374,9 +374,9 @@
"dev": true
},
"node_modules/@codex-storage/marketplace-ui-components": {
"version": "0.0.26",
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.26.tgz",
"integrity": "sha512-x8niBv1HRJU6VnKJ5+tv3+yYMxOj0tnMdNaPGzHVXMTHpj5WeHRcqUR1UhxxxnIlnsOjhmjX2tVUYPxpAz+OEw==",
"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"
}

View File

@ -24,7 +24,7 @@
"React"
],
"dependencies": {
"@codex-storage/marketplace-ui-components": "^0.0.26",
"@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",

BIN
public/img/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
public/img/avatar.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

BIN
public/img/avatar@1.5x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
public/img/avatar@1.5x.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/img/avatar@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
public/img/avatar@2x.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
public/img/avatar@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/img/avatar@3x.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
public/img/avatar@4x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
public/img/avatar@4x.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -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>
);

View File

@ -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>
);
}

View File

@ -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) {

View 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>
);
}

View 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;
}

View File

@ -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>
);
}

View File

@ -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>
);

View 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>
);
}

View File

@ -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"

View 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;
}

View File

@ -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>
</>
);
}

View File

@ -1,14 +1,16 @@
type Props = {
variant: "primary" | "error";
className?: string;
};
export function AlphaIcon({ variant }: Props) {
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"

View File

@ -5,24 +5,24 @@ export function OnBoardingImage() {
<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

View File

@ -19,11 +19,11 @@ export function OnBoardingStepOne({ onNextStep }: Props) {
<>
<div className="index-column-section">
<div>
<AlphaIcon variant="primary" />
<AlphaIcon variant="error" />
</div>
<div className="index-alphaText">
<p>
<AlphaText></AlphaText>
<AlphaText variant="default"></AlphaText>
</p>
<p>
<SimpleText className="index-version" variant="normal">

View File

@ -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>

View File

@ -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));

View File

@ -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}
/>
);

View 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;
}

View File

@ -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

View File

@ -23,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);