From 635b115a4df421ea5fa9d0851d4af000c0e44c69 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Thu, 31 Oct 2024 19:18:21 +0100 Subject: [PATCH] Improve design and merge folders --- package-lock.json | 10 +- package.json | 2 +- src/components/AppBar/AppBar.tsx | 84 ++-- src/components/AppBar/appBar.css | 29 +- .../BackgroundImage/BackgroundImage.css | 4 +- src/components/Files/FolderButton.tsx | 10 +- src/components/HealthChecks/HealthChecks.css | 173 +++++--- src/components/HealthChecks/HealthChecks.tsx | 76 ++-- src/components/Logotype/Logotype.tsx | 2 +- src/components/Menu/Menu.tsx | 39 +- src/components/Menu/menu.css | 31 +- .../OnBoarding/OnBoardingLayout.css | 26 +- src/components/Peers/PeerCountryCell.tsx | 4 +- src/components/UserInfo/UserInfo.css | 31 +- src/components/UserInfo/UserInfo.tsx | 1 + src/main.tsx | 1 + src/routeTree.gen.ts | 396 +++++++----------- src/routes/dashboard.tsx | 37 +- src/routes/dashboard/index.css | 1 - src/routes/dashboard/peers.css | 75 +++- src/routes/dashboard/peers.lazy.tsx | 191 ++++++--- src/routes/dashboard/settings.css | 2 - src/routes/layout.css | 8 + src/utils/constants.ts | 2 + src/utils/network.ts | 6 + vite.config.ts | 6 +- 26 files changed, 718 insertions(+), 529 deletions(-) create mode 100644 src/utils/network.ts diff --git a/package-lock.json b/package-lock.json index ee93a5a..8918c89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.7", "license": "MIT", "dependencies": { - "@codex-storage/marketplace-ui-components": "^0.0.28", + "@codex-storage/marketplace-ui-components": "^0.0.29", "@codex-storage/sdk-js": "^0.0.12", "@sentry/browser": "^8.32.0", "@sentry/react": "^8.31.0", @@ -378,9 +378,9 @@ "peer": true }, "node_modules/@codex-storage/marketplace-ui-components": { - "version": "0.0.28", - "resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.28.tgz", - "integrity": "sha512-C2Wj6Rb4RzdkXRtXsj+dnzP5NwnJbmlHfQ1R2jxqgaJ4DXBDnAnEGiiljHzHE2oP0P4Exx7CQBjrP7vWChMpNg==", + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.29.tgz", + "integrity": "sha512-ctbjE/m2bOu3SXvlHcB8hulZyzFFymizjKPonNjHHriyCwlGMgryLeKLty2XpzAWw/JR/WZPSZTwlWJ7qOy0hQ==", "dependencies": { "lucide-react": "^0.453.0" }, @@ -4841,4 +4841,4 @@ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index cddc05e..c5a89c8 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "React" ], "dependencies": { - "@codex-storage/marketplace-ui-components": "^0.0.28", + "@codex-storage/marketplace-ui-components": "^0.0.29", "@codex-storage/sdk-js": "^0.0.12", "@sentry/browser": "^8.32.0", "@sentry/react": "^8.31.0", diff --git a/src/components/AppBar/AppBar.tsx b/src/components/AppBar/AppBar.tsx index f9a2679..809d844 100644 --- a/src/components/AppBar/AppBar.tsx +++ b/src/components/AppBar/AppBar.tsx @@ -4,25 +4,37 @@ import { classnames } from "../../utils/classnames"; import { useNetwork } from "../../network/useNetwork"; import { NetworkFlashIcon } from "../NetworkFlashIcon/NetworkFlashIcon"; import { useQueryClient } from "@tanstack/react-query"; -import { useEffect } from "react"; +import { ReactElement, useEffect } from "react"; import { useCodexConnection } from "../../hooks/useCodexConnection"; import { NodesIcon } from "../Menu/NodesIcon"; import { usePersistence } from "../../hooks/usePersistence"; +import { useNavigate, useRouterState } from "@tanstack/react-router"; +import { PeersIcon } from "../Menu/PeersIcon"; +import { SettingsIcon } from "../Menu/SettingsIcon"; type Props = { - /** - * Event triggered when the menu is expanding, after a click on the - * menu button. - */ - onExpand: () => void; + onIconClick: () => void; }; -export function AppBar(_: Props) { - console.debug(_); +const icons: Record = { + dashboard: , + peers: , + settings: , +}; + +const descriptions: Record = { + dashboard: "Get Overview of your Codex Vault", + peers: "Monitor your Codex peer connections", + settings: "Manage your Codex Vault", +}; + +export function AppBar({ onIconClick }: Props) { const online = useNetwork(); const queryClient = useQueryClient(); const codex = useCodexConnection(); const persistence = usePersistence(codex.enabled); + const router = useRouterState(); + const navigate = useNavigate({ from: router.location.pathname }); useEffect(() => { queryClient.invalidateQueries({ @@ -31,39 +43,45 @@ export function AppBar(_: Props) { }); }, [queryClient, codex.enabled]); + const onNodeClick = () => navigate({ to: "/dashboard/settings" }); + const offline = !online || !codex.enabled; + const title = + router.location.pathname.split("/")[2] || + router.location.pathname.split("/")[1]; + return ( -
-
- {/* + <> +
+
+ {/* */} - - - + {icons[title]} -
-

Dashboard

-

Get Overview of your Codex Vault

+
+

{title}

+

{descriptions[title]}

+
+
- -
+ ); } diff --git a/src/components/AppBar/appBar.css b/src/components/AppBar/appBar.css index e544e8a..37e5777 100644 --- a/src/components/AppBar/appBar.css +++ b/src/components/AppBar/appBar.css @@ -3,10 +3,7 @@ justify-content: space-between; border-bottom: 1px solid var(--codex-border-color); display: flex; - padding-right: 48px; - padding-left: 48px; - padding-top: 20px; - padding-bottom: 20px; + padding: 16px; border-bottom: 1px solid #96969633; box-sizing: border-box; background-color: #1c1c1c; @@ -15,6 +12,12 @@ top: 0; z-index: 1; + @media (min-width: 1000px) { + & { + padding: 20px 48px; + } + } + &:not(.app-bar--offline):not(.app-bar--no-persistence) { border-right-color: #6ccc93; } @@ -33,6 +36,7 @@ sans-serif; letter-spacing: -0.015em; color: white; + text-transform: capitalize; } h2 { @@ -53,6 +57,13 @@ justify-content: center; border: 1px solid #353639; border-radius: 50%; + color: #969696; + } + + @media (max-width: 999px) { + & { + cursor: pointer; + } } } @@ -69,6 +80,16 @@ sans-serif; letter-spacing: -0.006em; color: #8d8d8d; + + @media (max-width: 999px) { + & { + display: none; + } + } + } + + div:last-child { + cursor: pointer; } } } diff --git a/src/components/BackgroundImage/BackgroundImage.css b/src/components/BackgroundImage/BackgroundImage.css index 0bda81c..83ea6eb 100644 --- a/src/components/BackgroundImage/BackgroundImage.css +++ b/src/components/BackgroundImage/BackgroundImage.css @@ -1,5 +1,5 @@ .background-img { - position: absolute; + position: fixed; right: -40px; max-height: 90%; width: auto; @@ -7,7 +7,7 @@ transition: opacity 0.35s; opacity: 0.3; - @media (min-width: 1200px) { + @media (min-width: 1580px) { & { opacity: 1; } diff --git a/src/components/Files/FolderButton.tsx b/src/components/Files/FolderButton.tsx index 99a9dcf..45015a2 100644 --- a/src/components/Files/FolderButton.tsx +++ b/src/components/Files/FolderButton.tsx @@ -1,8 +1,4 @@ -import { - Backdrop, - ButtonIcon, - SimpleText, -} from "@codex-storage/marketplace-ui-components"; +import { Backdrop, ButtonIcon } from "@codex-storage/marketplace-ui-components"; import { CheckCircle, Folder } from "lucide-react"; import "./FolderButton.css"; import { useState } from "react"; @@ -49,9 +45,9 @@ export function FolderButton({ folders, onFolderToggle }: Props) {
{folder}
{isActive && ( - + - + )}
diff --git a/src/components/HealthChecks/HealthChecks.css b/src/components/HealthChecks/HealthChecks.css index c921956..6ff3ceb 100644 --- a/src/components/HealthChecks/HealthChecks.css +++ b/src/components/HealthChecks/HealthChecks.css @@ -1,85 +1,124 @@ -.address { - display: flex; - align-items: center; - position: relative; - gap: 16px; - - > div { +.health-checks { + .address { + display: flex; position: relative; - } + gap: 16px; + flex-direction: column; + align-items: flex-start; - svg { - position: absolute; - top: 68px; - bottom: 0; - right: 18px; - } + @media (min-width: 1000px) { + & { + flex-direction: row; + align-items: center; + } + } - .refresh { - position: relative; - top: 24px; - cursor: pointer; + > div { + position: relative; + + @media (max-width: 999px) { + &:not(.refresh) { + width: 100%; + } + } + } svg { - position: initial; + position: absolute; + top: 68px; + bottom: 0; + right: 18px; } - &.address--fetching .refresh svg { - animation: rotate 2s linear infinite; + .refresh { + position: relative; + cursor: pointer; + cursor: pointer; + + @media (max-width: 999px) { + & { + top: 0px; + left: 0; + transform: scale(1.5); + right: 0; + margin: auto; + } + } + + @media (min-width: 1000px) { + & { + top: 18px; + } + } + + svg { + position: initial; + } + + &.address--fetching .refresh svg { + animation: rotate 2s linear infinite; + } + } + + input[type="number"] { + width: 150px; + } + + input[type="number"]::-webkit-outer-spin-button, + input[type="number"]::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + + input[type="number"] { + -moz-appearance: textfield; + } + + @media (max-width: 999px) { + input[type="number"], + input { + width: 100%; + } } } - input[type="number"] { - width: 150px; + p { + 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; } - input[type="number"]::-webkit-outer-spin-button, - input[type="number"]::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; - } + ul { + margin-bottom: 32px; - input[type="number"] { - -moz-appearance: textfield; - } -} - -.helper { - 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; -} - -.health-checks { - margin-bottom: 32px; - - li { - display: flex; - align-items: center; - padding: 16px 0; - gap: 16px; - border-top: 1px solid #96969633; - border-bottom: 1px solid #96969633; - - &:first-child { - font-family: Inter; - font-size: 16px; - font-weight: 500; - line-height: 24px; - letter-spacing: -0.011em; - } - - span { + li { display: flex; align-items: center; - width: 20px; - height: 20px; - justify-content: center; + padding: 16px 0; + gap: 16px; + border-top: 1px solid #96969633; + border-bottom: 1px solid #96969633; + + &:first-child { + font-family: Inter; + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: -0.011em; + } + + span { + display: flex; + align-items: center; + width: 20px; + height: 20px; + justify-content: center; + } } } } diff --git a/src/components/HealthChecks/HealthChecks.tsx b/src/components/HealthChecks/HealthChecks.tsx index 22c9c33..3cc819f 100644 --- a/src/components/HealthChecks/HealthChecks.tsx +++ b/src/components/HealthChecks/HealthChecks.tsx @@ -1,5 +1,5 @@ import { useQueryClient } from "@tanstack/react-query"; -import { useEffect, useState, ClipboardEvent } from "react"; +import { useEffect, useState } from "react"; import { useDebug } from "../../hooks/useDebug"; import { usePersistence } from "../../hooks/usePersistence"; import { usePortForwarding } from "../../hooks/usePortForwarding"; @@ -11,7 +11,6 @@ import { HealthCheckIcon } from "./HealthCheckIcon"; import { Input } from "@codex-storage/marketplace-ui-components"; import { classnames } from "../../utils/classnames"; import { DebugUtils } from "../../utils/debug"; -import { Strings } from "../../utils/strings"; import { RefreshIcon } from "../RefreshIcon/RefreshIcon"; import "./HealthChecks.css"; @@ -27,8 +26,14 @@ export function HealthChecks({ online, onStepValid }: Props) { const codex = useDebug(throwOnError); const portForwarding = usePortForwarding(codex.data); const persistence = usePersistence(codex.isSuccess); - const [isInvalid, setIsInvalid] = useState(false); - const [url, setUrl] = useState(CodexSdk.url); + const [isAddressInvalid, setIsAddressInvalid] = useState(false); + const [isPortInvalid, setIsPortInvalid] = useState(false); + const [address, setAddress] = useState( + CodexSdk.url().split(":")[0] + ":" + CodexSdk.url().split(":")[1] + ); + const [port, setPort] = useState( + parseInt(CodexSdk.url().split(":")[2] || "80", 10) + ); const queryClient = useQueryClient(); useEffect(() => { @@ -46,48 +51,38 @@ export function HealthChecks({ online, onStepValid }: Props) { ]); const onAddressChange = (e: React.FormEvent) => { - const [, port] = Strings.splitURLAndPort(url); const element = e.currentTarget; - const value = e.currentTarget.value; + const parts = e.currentTarget.value.split(":"); - if ( - value.startsWith("http://") === false && - value.startsWith("https://") === false - ) { - setIsInvalid(true); - return; - } + setIsAddressInvalid(!element.checkValidity()); - setIsInvalid(!element.checkValidity()); - setUrl(value + ":" + port); - }; + if (parts.length > 2) { + const [protocol, addr, port] = parts; + setAddress(protocol + ":" + addr); - 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 p = parseInt(port, 10); + if (!isNaN(p)) { + setPort(p); + } + } else { + setAddress(parts.join(":")); } }; const onPortChange = (e: React.FormEvent) => { - const [address] = Strings.splitURLAndPort(url); const element = e.currentTarget; const value = element.value; - setUrl(address + ":" + value); + setIsPortInvalid(!element.checkValidity()); + setPort(parseInt(value, 10)); }; const onSave = () => { - if (isInvalid === true) { + if (isAddressInvalid || isPortInvalid) { return; } - CodexSdk.updateURL(url) + CodexSdk.updateURL(address + ":" + port) .then(() => queryClient.invalidateQueries()) .then(() => codex.refetch()); }; @@ -100,10 +95,9 @@ export function HealthChecks({ online, onStepValid }: Props) { forwardingPortValue = port.data; } } - const [address, port] = Strings.splitURLAndPort(url); return ( - <> +
- {isInvalid ? ( + {isAddressInvalid ? ( ) : ( @@ -133,6 +127,7 @@ export function HealthChecks({ online, onStepValid }: Props) { type="number" onChange={onPortChange} value={port} + isInvalid={isPortInvalid} placeholder="8080">
@@ -142,11 +137,14 @@ export function HealthChecks({ online, onStepValid }: Props) {
-
    -
  • Port forwarding should be default {forwardingPortValue}.
  • -
+

+

  • + Port forwarding should be {forwardingPortValue} for TCP and 8090 by + default for UDP. +
  • +

    -
      +
      • @@ -194,6 +192,6 @@ export function HealthChecks({ online, onStepValid }: Props) { Marketplace
      - +
    ); } diff --git a/src/components/Logotype/Logotype.tsx b/src/components/Logotype/Logotype.tsx index cefe0d0..2f48231 100644 --- a/src/components/Logotype/Logotype.tsx +++ b/src/components/Logotype/Logotype.tsx @@ -44,7 +44,7 @@ export function Logotype({ height, width, className }: Props) { void; - - onOpen?: () => void; + isMobileMenuDisplayed: boolean; }; -export function Menu({ expanded, onClose, onOpen }: Props) { +export function Menu({ isMobileMenuDisplayed }: Props) { const [isExpanded, setIsExpanded] = useState(null); - useEffect(() => { - if (expanded && onOpen) { - onOpen(); - } - }, [expanded, onOpen]); - const onLogoClick = () => { if (isExpanded === false) { setIsExpanded(true); @@ -67,15 +53,18 @@ export function Menu({ expanded, onClose, onOpen }: Props) { return ( <> - -