From bc1da83d038cbbca4cc9ead1da6463730508a5c4 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 30 Oct 2024 19:29:14 +0100 Subject: [PATCH] Add settings and peers integration and css nesting --- package-lock.json | 193 ++++++- package.json | 8 +- postcss.config.json | 6 + public/icons/circle-error.svg | 12 + src/components/AppBar/AppBar.tsx | 64 ++- src/components/AppBar/appBar.css | 118 +++-- .../Availability/AvailabilityDiskRow.tsx | 10 +- .../Availability/AvailabilityIdCell.tsx | 15 +- .../Availability/AvailabilitySlotRow.tsx | 10 +- .../BackgroundImage/BackgroundImage.css | 15 + .../BackgroundImage/BackgroundImage.tsx | 35 ++ src/components/CardNumbers/CardNumbers.tsx | 12 +- .../ErrorCircleIcon/ErrorCircleIcon.tsx | 30 +- src/components/ErrorIcon/ErrorIcon.tsx | 5 +- .../HealthCheckIcon.tsx | 0 src/components/HealthChecks/HealthChecks.css | 85 +++ src/components/HealthChecks/HealthChecks.tsx | 199 +++++++ .../HttpNetworkIndicator.css | 24 - .../HttpNetworkIndicator.tsx | 16 - src/components/Logo/Logo.tsx | 12 +- src/components/Logotype/Logotype.tsx | 53 +- src/components/Menu/Menu.tsx | 163 ++++-- src/components/Menu/menu.css | 492 ++++++++---------- .../NetworkFlashIcon/NetworkFlashIcon.tsx | 14 +- .../NodeIndicator/NodeIndicator.css | 24 - .../NodeIndicator/NodeIndicator.tsx | 28 - src/components/OnBoarding/HealthCheckItem.css | 5 - src/components/OnBoarding/HealthCheckItem.tsx | 21 - src/components/OnBoarding/OnBoardingImage.css | 7 - src/components/OnBoarding/OnBoardingImage.tsx | 35 -- .../OnBoarding/OnBoardingLayout.css | 217 ++++++++ .../OnBoarding/OnBoardingLayout.tsx | 41 ++ .../OnBoarding/OnBoardingStatusIcon.tsx | 33 -- .../OnBoarding/OnBoardingStepOne.tsx | 100 ---- .../OnBoarding/OnBoardingStepThree.css | 118 ----- .../OnBoarding/OnBoardingStepThree.tsx | 219 -------- .../OnBoarding/OnBoardingStepTwo.css | 44 -- .../OnBoarding/OnBoardingStepTwo.tsx | 46 -- src/components/Page/Page.tsx | 35 -- src/components/Page/page.css | 8 - src/components/Peers/PeerCountryCell.css | 8 +- src/components/Peers/PeerCountryCell.tsx | 2 +- .../SuccessCheckIcon/SuccessCheckIcon.tsx | 27 +- src/components/SuccessIcon/SuccessIcon.tsx | 5 +- src/components/UserInfo/UserInfo.css | 22 + src/components/UserInfo/UserInfo.tsx | 80 +++ src/components/Versions/Versions.css | 34 ++ src/components/Versions/Versions.tsx | 28 + src/components/WarningIcon/WarningIcon.tsx | 15 + src/components/Welcome/Welcome.tsx | 5 +- src/index.css | 25 +- src/routeTree.gen.ts | 54 +- src/routes/dashboard.tsx | 211 +------- src/routes/dashboard/index.css | 55 +- src/routes/dashboard/index.tsx | 70 +-- src/routes/dashboard/peers.css | 257 +++++++-- src/routes/dashboard/peers.tsx | 64 ++- src/routes/dashboard/settings.css | 52 +- src/routes/dashboard/settings.tsx | 46 +- src/routes/index.css | 202 ------- src/routes/index.tsx | 156 +++--- src/routes/{dashboard.css => layout.css} | 20 +- src/routes/onboarding-checks.tsx | 61 +++ src/routes/onboarding-name.tsx | 55 ++ src/utils/onboarding.ts | 11 +- vite.config.ts | 6 + 66 files changed, 2219 insertions(+), 1924 deletions(-) create mode 100644 postcss.config.json create mode 100644 public/icons/circle-error.svg create mode 100644 src/components/BackgroundImage/BackgroundImage.css create mode 100644 src/components/BackgroundImage/BackgroundImage.tsx rename src/components/{OnBoarding => HealthChecks}/HealthCheckIcon.tsx (100%) create mode 100644 src/components/HealthChecks/HealthChecks.css create mode 100644 src/components/HealthChecks/HealthChecks.tsx delete mode 100644 src/components/HttpNetworkIndicator/HttpNetworkIndicator.css delete mode 100644 src/components/HttpNetworkIndicator/HttpNetworkIndicator.tsx delete mode 100644 src/components/NodeIndicator/NodeIndicator.css delete mode 100644 src/components/NodeIndicator/NodeIndicator.tsx delete mode 100644 src/components/OnBoarding/HealthCheckItem.css delete mode 100644 src/components/OnBoarding/HealthCheckItem.tsx delete mode 100644 src/components/OnBoarding/OnBoardingImage.css delete mode 100644 src/components/OnBoarding/OnBoardingImage.tsx create mode 100644 src/components/OnBoarding/OnBoardingLayout.css create mode 100644 src/components/OnBoarding/OnBoardingLayout.tsx delete mode 100644 src/components/OnBoarding/OnBoardingStatusIcon.tsx delete mode 100644 src/components/OnBoarding/OnBoardingStepOne.tsx delete mode 100644 src/components/OnBoarding/OnBoardingStepThree.css delete mode 100644 src/components/OnBoarding/OnBoardingStepThree.tsx delete mode 100644 src/components/OnBoarding/OnBoardingStepTwo.css delete mode 100644 src/components/OnBoarding/OnBoardingStepTwo.tsx delete mode 100644 src/components/Page/Page.tsx delete mode 100644 src/components/Page/page.css create mode 100644 src/components/UserInfo/UserInfo.css create mode 100644 src/components/UserInfo/UserInfo.tsx create mode 100644 src/components/Versions/Versions.css create mode 100644 src/components/Versions/Versions.tsx create mode 100644 src/components/WarningIcon/WarningIcon.tsx delete mode 100644 src/routes/index.css rename src/routes/{dashboard.css => layout.css} (78%) create mode 100644 src/routes/onboarding-checks.tsx create mode 100644 src/routes/onboarding-name.tsx diff --git a/package-lock.json b/package-lock.json index f07ccd0..1fb38e7 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.27", + "@codex-storage/marketplace-ui-components": "^0.0.28", "@codex-storage/sdk-js": "^0.0.12", "@sentry/browser": "^8.32.0", "@sentry/react": "^8.31.0", @@ -17,6 +17,7 @@ "@tanstack/react-router": "^1.58.7", "dotted-map": "^2.2.3", "echarts": "^5.5.1", + "emoji-picker-react": "^4.12.0", "idb-keyval": "^6.2.1", "lucide-react": "^0.445.0", "react": "^18.3.1", @@ -36,8 +37,9 @@ "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.12", + "postcss": "^8.4.47", + "postcss-nesting": "^13.0.1", "prettier": "^3.3.3", - "sass-embedded": "^1.79.4", "typescript": "5.5.4", "vite": "^5.4.7" }, @@ -371,12 +373,14 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.1.0.tgz", "integrity": "sha512-+2Mx67Y3skJ4NCD/qNSdBJNWtu6x6Qr53jeNg+QcwiL6mt0wK+3jwHH2x1p7xaYH6Ve2JKOVn0OxU35WsmqI9A==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/@codex-storage/marketplace-ui-components": { - "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==", + "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==", "dependencies": { "lucide-react": "^0.453.0" }, @@ -385,6 +389,7 @@ }, "peerDependencies": { "@codex-storage/sdk-js": ">=0.0.12", + "postcss-nesting": "^13.0.1", "react": "^18.3.1", "react-dom": "^18.3.1" } @@ -408,6 +413,48 @@ "node": ">=20" } }, + "node_modules/@csstools/selector-resolve-nested": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz", + "integrity": "sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "dev": true, @@ -2101,7 +2148,9 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz", "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/callsites": { "version": "3.1.0", @@ -2209,7 +2258,9 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/concat-map": { "version": "0.0.1", @@ -2234,6 +2285,17 @@ "node": ">= 8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.3", "dev": true, @@ -2312,6 +2374,20 @@ "integrity": "sha512-VbeVexmZ1IFh+5EfrYz1I0HTzHVIlJa112UEWhciPyeOcKJGeTv6N8WnG4wsQB81DGCaVEGhpSb6o6a8WYFXXg==", "dev": true }, + "node_modules/emoji-picker-react": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/emoji-picker-react/-/emoji-picker-react-4.12.0.tgz", + "integrity": "sha512-q2c8UcZH0eRIMj41bj0k1akTjk69tsu+E7EzkW7giN66iltF6H9LQvQvw6ugscsxdC+1lmt3WZpQkkY65J95tg==", + "dependencies": { + "flairup": "1.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -2680,6 +2756,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flairup": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/flairup/-/flairup-1.0.0.tgz", + "integrity": "sha512-IKlE+pNvL2R+kVL1kEhUYqRxVqeFnjiIvHWDMLFXNaqyUdFXQM2wte44EfMYJNHkW16X991t2Zg8apKkhv7OBA==" + }, "node_modules/flat-cache": { "version": "3.2.0", "dev": true, @@ -2828,7 +2909,9 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/import-fresh": { "version": "3.3.0", @@ -3086,7 +3169,6 @@ }, "node_modules/nanoid": { "version": "3.3.7", - "dev": true, "funding": [ { "type": "github", @@ -3209,7 +3291,6 @@ }, "node_modules/picocolors": { "version": "1.1.0", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -3254,8 +3335,9 @@ } }, "node_modules/postcss": { - "version": "8.4.45", - "dev": true, + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -3270,16 +3352,53 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-nesting": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz", + "integrity": "sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/selector-resolve-nested": "^3.0.0", + "@csstools/selector-specificity": "^5.0.0", + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "dev": true, @@ -3497,6 +3616,8 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -3506,6 +3627,8 @@ "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.80.3.tgz", "integrity": "sha512-aTxTl4ToSAWg7ILFgAe+kMenj+zNlwHmHK/ZNPrOM8+HTef1Q6zuxolptYLijmHdZHKSMOkWYHgo5MMN6+GIyg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@bufbuild/protobuf": "^2.0.0", "buffer-builder": "^0.2.0", @@ -3556,6 +3679,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3572,6 +3696,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3588,6 +3713,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3604,6 +3730,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3620,6 +3747,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3636,6 +3764,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3652,6 +3781,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3668,6 +3798,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3684,6 +3815,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3700,6 +3832,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3716,6 +3849,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3732,6 +3866,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3748,6 +3883,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3764,6 +3900,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3780,6 +3917,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3796,6 +3934,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3812,6 +3951,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3828,6 +3968,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3844,6 +3985,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3860,6 +4002,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -3869,6 +4012,8 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -3878,6 +4023,8 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3925,7 +4072,6 @@ }, "node_modules/source-map-js": { "version": "1.2.1", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -4013,7 +4159,9 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/type-check": { "version": "0.4.0", @@ -4122,6 +4270,11 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/valibot": { "version": "0.32.0", "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.32.0.tgz", @@ -4131,7 +4284,9 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/vite": { "version": "5.4.9", diff --git a/package.json b/package.json index 27fc147..cddc05e 100644 --- a/package.json +++ b/package.json @@ -24,14 +24,15 @@ "React" ], "dependencies": { - "@codex-storage/marketplace-ui-components": "^0.0.27", + "@codex-storage/marketplace-ui-components": "^0.0.28", "@codex-storage/sdk-js": "^0.0.12", "@sentry/browser": "^8.32.0", "@sentry/react": "^8.31.0", "@tanstack/react-query": "^5.51.15", "@tanstack/react-router": "^1.58.7", - "echarts": "^5.5.1", "dotted-map": "^2.2.3", + "echarts": "^5.5.1", + "emoji-picker-react": "^4.12.0", "idb-keyval": "^6.2.1", "lucide-react": "^0.445.0", "react": "^18.3.1", @@ -51,8 +52,9 @@ "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.12", + "postcss": "^8.4.47", + "postcss-nesting": "^13.0.1", "prettier": "^3.3.3", - "sass-embedded": "^1.79.4", "typescript": "5.5.4", "vite": "^5.4.7" }, diff --git a/postcss.config.json b/postcss.config.json new file mode 100644 index 0000000..65ccabb --- /dev/null +++ b/postcss.config.json @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + 'postcss-nesting': { /* plugin options */ }, + }, + } + \ No newline at end of file diff --git a/public/icons/circle-error.svg b/public/icons/circle-error.svg new file mode 100644 index 0000000..90de158 --- /dev/null +++ b/public/icons/circle-error.svg @@ -0,0 +1,12 @@ + + + + + diff --git a/src/components/AppBar/AppBar.tsx b/src/components/AppBar/AppBar.tsx index 2abbac9..f9a2679 100644 --- a/src/components/AppBar/AppBar.tsx +++ b/src/components/AppBar/AppBar.tsx @@ -1,7 +1,13 @@ import "./appBar.css"; import { DashboardIcon } from "../DashboardIcon/DashboardIcon"; -import { NodeIndicator } from "../NodeIndicator/NodeIndicator"; -import { HttpNetworkIndicator } from "../HttpNetworkIndicator/HttpNetworkIndicator"; +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 { useCodexConnection } from "../../hooks/useCodexConnection"; +import { NodesIcon } from "../Menu/NodesIcon"; +import { usePersistence } from "../../hooks/usePersistence"; type Props = { /** @@ -11,29 +17,53 @@ type Props = { onExpand: () => void; }; -export function AppBar(props: Props) { - console.info(props); +export function AppBar(_: Props) { + console.debug(_); + const online = useNetwork(); + const queryClient = useQueryClient(); + const codex = useCodexConnection(); + const persistence = usePersistence(codex.enabled); + + useEffect(() => { + queryClient.invalidateQueries({ + type: "active", + refetchType: "all", + }); + }, [queryClient, codex.enabled]); + + const offline = !online || !codex.enabled; + return ( -
-
+
+
{/* */} -
+ -
-
-
Dashboard
-
- Get Overview of your Codex Vault -
+ + +
+

Dashboard

+

Get Overview of your Codex Vault

-
- - -
+
); } diff --git a/src/components/AppBar/appBar.css b/src/components/AppBar/appBar.css index 90ad824..e544e8a 100644 --- a/src/components/AppBar/appBar.css +++ b/src/components/AppBar/appBar.css @@ -1,60 +1,80 @@ -.appBar { +.app-bar { height: 80px; justify-content: space-between; border-bottom: 1px solid var(--codex-border-color); - view-transition-name: main-header; display: flex; - padding: 20px 40px 20px 40px; - border-bottom: 1px solid #2b303b; + padding-right: 48px; + padding-left: 48px; + padding-top: 20px; + padding-bottom: 20px; + border-bottom: 1px solid #96969633; box-sizing: border-box; background-color: #1c1c1c; + border-right: 12px solid transparent; + position: sticky; + top: 0; + z-index: 1; + + &:not(.app-bar--offline):not(.app-bar--no-persistence) { + border-right-color: #6ccc93; + } + + &.app-bar--offline { + border-right-color: var(--codex-input-color-error); + } + + &.app-bar--no-persistence { + border-right-color: rgb(var(--codex-color-warning)); + } + + h1 { + font: + 500 18px/24px "Inter", + sans-serif; + letter-spacing: -0.015em; + color: white; + } + + h2 { + font: + 400 14px/20px "Inter", + sans-serif; + letter-spacing: -0.006em; + color: #969696cc; + } + + > div { + span { + background: #141414; + height: 48px; + width: 48px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #353639; + border-radius: 50%; + } + } + + aside { + svg { + padding: 10px; + background-color: #141414; + border-radius: var(--codex-border-radius); + } + + span { + font: + 500 14px/20px "Inter", + sans-serif; + letter-spacing: -0.006em; + color: #8d8d8d; + } + } } -.appBar-burger { - cursor: pointer; - color: var(--codex-color); - display: flex; -} - -.appBar, -.appBar-left, -.appBar-right { - display: flex; - align-items: center; - gap: 16px; -} - -.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) { +/* @media (min-width: 1000px) { .appBar-burger { display: none; } -} +} */ diff --git a/src/components/Availability/AvailabilityDiskRow.tsx b/src/components/Availability/AvailabilityDiskRow.tsx index 01e7732..247bef6 100644 --- a/src/components/Availability/AvailabilityDiskRow.tsx +++ b/src/components/Availability/AvailabilityDiskRow.tsx @@ -1,8 +1,4 @@ -import { - Cell, - Row, - SimpleText, -} from "@codex-storage/marketplace-ui-components"; +import { Cell, Row } from "@codex-storage/marketplace-ui-components"; import { PrettyBytes } from "../../utils/bytes"; import "./AvailabilityDiskRow.css"; import { classnames } from "../../utils/classnames"; @@ -26,9 +22,9 @@ export function AvailabilityDiskRow({ bytes }: Props) {
Node
- + {PrettyBytes(bytes)} allocated for the node - +
, diff --git a/src/components/Availability/AvailabilityIdCell.tsx b/src/components/Availability/AvailabilityIdCell.tsx index 5d46917..41768e0 100644 --- a/src/components/Availability/AvailabilityIdCell.tsx +++ b/src/components/Availability/AvailabilityIdCell.tsx @@ -1,6 +1,6 @@ import "./AvailabilityIdCell.css"; import { Strings } from "../../utils/strings"; -import { Cell, SimpleText } from "@codex-storage/marketplace-ui-components"; +import { Cell } from "@codex-storage/marketplace-ui-components"; import { PrettyBytes } from "../../utils/bytes"; import { availabilityColors } from "./availability.colors"; import { AvailabilityWithSlots } from "./types"; @@ -19,18 +19,13 @@ export function AvailabilityIdCell({ value, index }: Props) {
{value.name || Strings.shortId(value.id)}
- + {PrettyBytes(value.totalSize)} allocated for the availability - +
- {/*
- - {a.id} - -
*/} - + Max collateral {value.maxCollateral} | Min price {value.minPrice} - +
diff --git a/src/components/Availability/AvailabilitySlotRow.tsx b/src/components/Availability/AvailabilitySlotRow.tsx index 137a655..ee99d96 100644 --- a/src/components/Availability/AvailabilitySlotRow.tsx +++ b/src/components/Availability/AvailabilitySlotRow.tsx @@ -1,8 +1,4 @@ -import { - Cell, - Row, - SimpleText, -} from "@codex-storage/marketplace-ui-components"; +import { Cell, Row } from "@codex-storage/marketplace-ui-components"; import { PrettyBytes } from "../../utils/bytes"; import "./AvailabilitySlotRow.css"; import { classnames } from "../../utils/classnames"; @@ -52,9 +48,9 @@ export function AvailabilitySlotRow({ bytes, active, id }: Props) {
Slot {id}
- + {PrettyBytes(bytes)} allocated for the slot - + , diff --git a/src/components/BackgroundImage/BackgroundImage.css b/src/components/BackgroundImage/BackgroundImage.css new file mode 100644 index 0000000..0bda81c --- /dev/null +++ b/src/components/BackgroundImage/BackgroundImage.css @@ -0,0 +1,15 @@ +.background-img { + position: absolute; + right: -40px; + max-height: 90%; + width: auto; + /* z-index: -1; */ + transition: opacity 0.35s; + opacity: 0.3; + + @media (min-width: 1200px) { + & { + opacity: 1; + } + } +} diff --git a/src/components/BackgroundImage/BackgroundImage.tsx b/src/components/BackgroundImage/BackgroundImage.tsx new file mode 100644 index 0000000..c84c2e5 --- /dev/null +++ b/src/components/BackgroundImage/BackgroundImage.tsx @@ -0,0 +1,35 @@ +import "./BackgroundImage.css"; + +export function BackgroundImage() { + return ( + + + + Background Image + + ); +} diff --git a/src/components/CardNumbers/CardNumbers.tsx b/src/components/CardNumbers/CardNumbers.tsx index a9e7750..5e11bcd 100644 --- a/src/components/CardNumbers/CardNumbers.tsx +++ b/src/components/CardNumbers/CardNumbers.tsx @@ -1,7 +1,4 @@ -import { - ButtonIcon, - SimpleText, -} from "@codex-storage/marketplace-ui-components"; +import { ButtonIcon } from "@codex-storage/marketplace-ui-components"; import "./CardNumbers.css"; import { Check, CircleX, Pencil } from "lucide-react"; import { ChangeEvent, useCallback, useEffect, useRef, useState } from "react"; @@ -165,12 +162,7 @@ export function CardNumbers({ {error ? ( {error} ) : ( - - {helper} - + {helper} )} ); diff --git a/src/components/ErrorCircleIcon/ErrorCircleIcon.tsx b/src/components/ErrorCircleIcon/ErrorCircleIcon.tsx index 79d0658..c696124 100644 --- a/src/components/ErrorCircleIcon/ErrorCircleIcon.tsx +++ b/src/components/ErrorCircleIcon/ErrorCircleIcon.tsx @@ -1,21 +1,15 @@ -type Props = { - className?: string; -}; - -export function ErrorCircleIcon({ className = "" }: Props) { +export function ErrorCircleIcon() { return ( - - - - - + + + ); } diff --git a/src/components/ErrorIcon/ErrorIcon.tsx b/src/components/ErrorIcon/ErrorIcon.tsx index 8982be3..1f41214 100644 --- a/src/components/ErrorIcon/ErrorIcon.tsx +++ b/src/components/ErrorIcon/ErrorIcon.tsx @@ -1,10 +1,9 @@ import { CircleX } from "lucide-react"; -import { SimpleText } from "@codex-storage/marketplace-ui-components"; export function ErrorIcon() { return ( - + - + ); } diff --git a/src/components/OnBoarding/HealthCheckIcon.tsx b/src/components/HealthChecks/HealthCheckIcon.tsx similarity index 100% rename from src/components/OnBoarding/HealthCheckIcon.tsx rename to src/components/HealthChecks/HealthCheckIcon.tsx diff --git a/src/components/HealthChecks/HealthChecks.css b/src/components/HealthChecks/HealthChecks.css new file mode 100644 index 0000000..c921956 --- /dev/null +++ b/src/components/HealthChecks/HealthChecks.css @@ -0,0 +1,85 @@ +.address { + display: flex; + align-items: center; + position: relative; + gap: 16px; + + > div { + position: relative; + } + + svg { + position: absolute; + top: 68px; + bottom: 0; + right: 18px; + } + + .refresh { + position: relative; + top: 24px; + cursor: pointer; + + 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; + } +} + +.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 { + 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 new file mode 100644 index 0000000..22c9c33 --- /dev/null +++ b/src/components/HealthChecks/HealthChecks.tsx @@ -0,0 +1,199 @@ +import { useQueryClient } from "@tanstack/react-query"; +import { useEffect, useState, ClipboardEvent } from "react"; +import { useDebug } from "../../hooks/useDebug"; +import { usePersistence } from "../../hooks/usePersistence"; +import { usePortForwarding } from "../../hooks/usePortForwarding"; +import { CodexSdk } from "../../proxy"; +import { ErrorCircleIcon } from "../ErrorCircleIcon/ErrorCircleIcon"; +import { SuccessCheckIcon } from "../SuccessCheckIcon/SuccessCheckIcon"; +import { WarningIcon } from "../WarningIcon/WarningIcon"; +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"; + +type Props = { + online: boolean; + onStepValid: (valid: boolean) => void; +}; + +const throwOnError = false; +const defaultPort = 8070; + +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 queryClient = useQueryClient(); + + useEffect(() => { + if (codex.isSuccess) { + persistence.refetch(); + portForwarding.refetch().then(({ isError }) => onStepValid(isError)); + } else { + onStepValid(false); + } + }, [ + persistence.refetch, + onStepValid, + portForwarding.refetch, + codex.isSuccess, + ]); + + const onAddressChange = (e: React.FormEvent) => { + const [, port] = Strings.splitURLAndPort(url); + const element = e.currentTarget; + const value = e.currentTarget.value; + + if ( + value.startsWith("http://") === false && + value.startsWith("https://") === false + ) { + setIsInvalid(true); + return; + } + + 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 onPortChange = (e: React.FormEvent) => { + 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()); + }; + + let forwardingPortValue = defaultPort; + + if (codex.isSuccess && codex.data) { + const port = DebugUtils.getTcpPort(codex.data); + if (!port.error) { + forwardingPortValue = port.data; + } + } + const [address, port] = Strings.splitURLAndPort(url); + + return ( + <> +
+
+ + {isInvalid ? ( + + ) : ( + + )} +
+ +
+ + +
+ +
+ +
+
+ +
    +
  • Port forwarding should be default {forwardingPortValue}.
  • +
+ +
    +
  • + + + + Health Check +
  • +
  • + + {online ? ( + + ) : ( + + )} + + Internet connection +
  • +
  • + + {portForwarding.enabled ? ( + + ) : ( + + )} + + Port forwarding +
  • +
  • + + {codex.isSuccess ? ( + + ) : ( + + )} + + Codex connection +
  • +
  • + + {persistence.enabled ? ( + + ) : ( + + )} + + Marketplace +
  • +
+ + ); +} diff --git a/src/components/HttpNetworkIndicator/HttpNetworkIndicator.css b/src/components/HttpNetworkIndicator/HttpNetworkIndicator.css deleted file mode 100644 index 1125719..0000000 --- a/src/components/HttpNetworkIndicator/HttpNetworkIndicator.css +++ /dev/null @@ -1,24 +0,0 @@ -.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; -} diff --git a/src/components/HttpNetworkIndicator/HttpNetworkIndicator.tsx b/src/components/HttpNetworkIndicator/HttpNetworkIndicator.tsx deleted file mode 100644 index d0c98df..0000000 --- a/src/components/HttpNetworkIndicator/HttpNetworkIndicator.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { useNetwork } from "../../network/useNetwork"; -import { NetworkFlashIcon } from "../NetworkFlashIcon/NetworkFlashIcon"; -import "./HttpNetworkIndicator.css"; - -export function HttpNetworkIndicator() { - const online = useNetwork(); - - return ( -
-
- -
- Network -
- ); -} diff --git a/src/components/Logo/Logo.tsx b/src/components/Logo/Logo.tsx index 648edfb..d96015a 100644 --- a/src/components/Logo/Logo.tsx +++ b/src/components/Logo/Logo.tsx @@ -1,14 +1,20 @@ +import { attributes } from "../../utils/attributes"; + type Props = { onClick?: () => void; + width?: number; + height?: number; }; -export function Logo({ onClick }: Props) { +export function Logo({ onClick, width, height }: Props) { return ( diff --git a/src/components/Logotype/Logotype.tsx b/src/components/Logotype/Logotype.tsx index 34b00b8..cefe0d0 100644 --- a/src/components/Logotype/Logotype.tsx +++ b/src/components/Logotype/Logotype.tsx @@ -10,21 +10,62 @@ export function Logotype({ height, width, className }: Props) { return ( + })} + viewBox="0 0 122 47" + fill="none" + xmlns="http://www.w3.org/2000/svg"> + + + + + + + + + + ); } diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 0624c9b..9850900 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -6,10 +6,23 @@ import { Logo } from "../Logo/Logo"; import { Logotype } from "../Logotype/Logotype"; import { ExpandIcon } from "./ExpandIcon"; import { classnames } from "../../utils/classnames"; +import { Link } from "@tanstack/react-router"; +import { HomeIcon } from "./HomeIcon"; +import { WalletIcon } from "./WalletIcon"; +import { FilesIcon } from "./FilesIcon"; +import { NodesIcon } from "./NodesIcon"; +import { AnalyticsIcon } from "./AnalyticsIcon"; +import { DeviceIcon } from "./DeviceIcon"; +import { PurchaseIcon } from "./PurchaseIcon"; +import { HostIcon } from "./HostIcon"; +import { PeersIcon } from "./PeersIcon"; +import { LogsIcon } from "./LogsIcon"; +import { SettingsIcon } from "./SettingsIcon"; +import { HelpIcon } from "./HelpIcon"; +import { DisclaimerIcon } from "./DisclaimerIcon"; export type MenuItemComponentProps = { onClick: () => void; - className: string; }; export type MenuItem = @@ -33,27 +46,9 @@ type Props = { onClose: () => void; onOpen?: () => void; - - /** - * The menu items to be displayed - */ - items: MenuItem[]; - - className?: string; - - /** - * The application version - */ - version?: string; }; -export function Menu({ - expanded, - onClose, - onOpen, - items, - className = "", -}: Props) { +export function Menu({ expanded, onClose, onOpen }: Props) { const [isExpanded, setIsExpanded] = useState(null); useEffect(() => { @@ -70,44 +65,122 @@ export function Menu({ const onExpandMenu = () => setIsExpanded(isExpanded === false ? true : false); - const renderItem = (i: MenuItem, index: number) => { - switch (i.type) { - case "separator": - return
; - case "space": - return
; - case "item": - return ( - - ); - } - }; - return ( <> diff --git a/src/components/Menu/menu.css b/src/components/Menu/menu.css index bb42c95..a958e2e 100644 --- a/src/components/Menu/menu.css +++ b/src/components/Menu/menu.css @@ -14,283 +14,223 @@ transition: width 0.5s; min-width: 0; width: 272px; -} + transform: translatex(0px); -.logo { - cursor: pointer; - min-width: 30px; -} - -.menu--expanded .logo { - cursor: default; -} - -.menu--expanded { - width: 272px; -} - -.menu--unexpanded { - width: 80px; -} - -/* @keyframes menu-in { - 0% { - width: 0; - display: none; + &[aria-expanded] { + transform: translatex(0); + min-width: 200px; } - 100% { - width: auto; - display: inline-block; + + &:not(.menu--unexpanded) a[data-title]:hover::after { + content: attr(data-title); + background-color: #2f2f2f; + color: #fff; + padding: 8px; + border-radius: 4px; + font-size: 12px; + line-height: 14px; + display: block; + white-space: nowrap; + position: absolute; + right: 1rem; + overflow: visible; + } + + &.menu--expanded { + width: 272px; + } + + &.menu--unexpanded { + width: 80px; + + .items { + a { + width: 26px; + gap: 0; + display: flex; + justify-content: center; + + span + span { + font-size: 0; + } + } + } + } + + > div { + display: flex; + flex-direction: column; + padding: 12px; + position: sticky; + top: 0; + height: calc(100vh - 24px); + overflow: auto; + } + + header { + padding: 13px; + display: flex; + align-items: center; + gap: 1.5rem; + background-color: #060606; + border-radius: 8px; + + > svg:first-child { + cursor: pointer; + min-width: 30px; + } + + div { + flex: 1; + text-align: right; + transition: opacity 0.35s; + display: inline-block; + overflow: hidden; + min-width: 0; + + svg { + cursor: pointer; + } + + &:hover { + animation-name: example; + animation-duration: 2.5s; + animation-iteration-count: infinite; + } + } + } + + .items { + padding-top: 1.5rem; + display: flex; + flex-direction: column; + position: relative; + height: 100%; + margin-bottom: 2.5rem; + gap: 0.5rem; + border-top: 1px solid #96969633; + + &::before { + height: 20px; + width: 8px; + background-color: var(--codex-color-primary); + position: absolute; + content: " "; + transition: + top 1s, + bottom 1s; + border-radius: 4px; + left: -16px; + } + + &:has(.active:nth-child(1))::before { + top: 30px; + } + + &:has(.active:nth-child(2))::before { + top: 72px; + } + + &:has(.active:nth-child(3))::before { + top: 115px; + } + + &:has(.active:nth-child(4))::before { + top: 158px; + } + + &:has(.active:nth-child(5))::before { + top: 201px; + } + + &:has(.active:nth-child(6))::before { + top: 244px; + } + + &:has(.active:nth-child(8))::before { + top: 339px; + } + + &:has(.active:nth-child(9))::before { + top: 382px; + } + + &:has(.active:nth-child(11))::before { + top: 475px; + } + + &:has(.active:nth-child(12))::before { + top: 513px; + } + + &:has(.active:nth-child(14))::before { + top: calc(100% - 113px); + } + + &:has(.active:nth-child(15))::before { + top: calc(100% - 70px); + } + + &:has(.active:nth-child(16))::before { + top: calc(100% - 27px); + } + + &:not(:first-child) { + margin-top: 0.5rem; + } + + hr { + margin-top: 1.5rem; + margin-bottom: 1.5rem; + border: 0.1px solid #96969633; + width: 100%; + } + + section { + flex: 1; + } + + a { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 8px 10px; + margin-bottom: 0; + text-decoration: none; + font-size: 14px; + font-weight: 500; + line-height: 20px; + letter-spacing: -0.006em; + color: #969696; + border-radius: 8px; + transition: background-color 0.35s; + position: relative; + margin-left: 6px; + + &:hover:not([aria-disabled="true"]), + &.active { + background-color: var(--codex-highlight-color); + color: #c7c7c7; + } + + span:first-child { + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + transition: color 1s; + } + + span + span { + display: inline-block; + overflow: hidden; + min-width: 0; + } + + &.active span:first-child { + color: var(--codex-color-primary); + } + } } } -*/ -.menu-logotype, -.menu-header-right, -.menu-text { - display: inline-block; - overflow: hidden; - min-width: 0; -} - -/* .menu--expanded .menu-header-right svg, -.menu--expanded .menu-logotype, -.menu--expanded .menu-header-right { - animation-name: header-right-menu-in; - animation-duration: 1s; - animation-fill-mode: forwards; -} - -.menu--expanded .menu-header-right svg, -.menu--expanded .menu-logotype { */ -/* display: none; */ -/* animation-name: logotype-menu-in; - animation-duration: 1s; - animation-fill-mode: forwards; -} */ - -/* .menu--unexpanded .menu-logotype, -.menu--unexpanded .menu-header-right { - display: none; -} */ - -.menu-container { - display: flex; - flex-direction: column; - padding: 12px; - position: sticky; - top: 0; - height: calc(100vh - 24px); - overflow: auto; -} - -.menu-header-right { - flex: 1; - text-align: right; - transition: opacity 0.35s; - cursor: pointer; -} - -.menu-header-right:hover { - animation-name: example; - animation-duration: 2.5s; - animation-iteration-count: infinite; -} - -.menu-backdrop { - display: none; -} - -.menu-items { - border-top: 2px solid var(--codex-border-color); - padding-top: 1.5rem; - display: flex; - flex-direction: column; - position: relative; - height: 100%; - margin-bottom: 2.5rem; -} - -.menu[aria-expanded] { - transform: translatex(0); - min-width: 200px; -} - -.menu-header { - padding: 12px; - display: flex; - align-items: center; - gap: 1.5rem; - margin-bottom: 1rem; - background-color: #060606; - border-radius: 8px; -} - -.menu-item { - display: flex; - align-items: center; - gap: 0.75rem; -} - -.menu-items::before { - height: 20px; - width: 8px; - background-color: var(--codex-color-primary); - position: absolute; - content: " "; - transition: - top 1s, - bottom 1s; - border-radius: 4px; - left: -16px; -} - -.menu-items:has(.active:nth-child(1))::before { - top: 30px; -} - -.menu-items:has(.active:nth-child(2))::before { - top: 72px; -} - -.menu-items:has(.active:nth-child(3))::before { - top: 115px; -} - -.menu-items:has(.active:nth-child(4))::before { - top: 158px; -} - -.menu-items:has(.active:nth-child(5))::before { - top: 201px; -} - -.menu-items:has(.active:nth-child(6))::before { - top: 244px; -} - -.menu-items:has(.active:nth-child(8))::before { - top: 332px; -} - -.menu-items:has(.active:nth-child(9))::before { - top: 375px; -} - -.menu-items:has(.active:nth-child(11))::before { - top: 461px; -} - -.menu-items:has(.active:nth-child(12))::before { - top: 504px; -} - -.menu-items:has(.active:nth-child(14))::before { - top: calc(100% - 113px); -} - -.menu-items:has(.active:nth-child(15))::before { - top: calc(100% - 70px); -} - -.menu-items:has(.active:nth-child(16))::before { - top: calc(100% - 27px); -} - -.menu-item:not(:first-child) { - margin-top: 0.5rem; -} - -.menu-item { - padding: 8px 18px; - margin-bottom: 0; - text-decoration: none; - font-size: 14px; - font-weight: 500; - line-height: 20px; - letter-spacing: -0.006em; - color: #969696; - border-radius: 8px; - transition: background-color 0.35s; -} - -.menu-item:hover, -.menu-item.active { - background-color: var(--codex-highlight-color); - color: #c7c7c7; -} - -.menu-item.active .menu-icon { - color: var(--codex-color-primary); -} - -.menu-title { - text-transform: uppercase; - padding-top: 0.75rem; - padding-bottom: 0.75rem; - padding-left: 0.75rem; - display: inline-block; - font-weight: 500; -} - -.menu-item-separator { - margin-top: 1.5rem; - margin-bottom: 1.5rem; - border: 0.1px solid var(--codex-border-color); - width: 100%; -} - -.menu-state { - font-size: 0.6rem; - background-color: var(--codex-background-light); - padding: 0.25rem; - border-radius: var(--codex-border-radius); - position: relative; - top: 1px; -} - -.menu-footer { - text-align: center; -} - -.menu-version { - padding: 0; -} - -.menu-space { - flex: 1; -} - -.menu-icon { - display: flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - transition: color 1s; -} - -@media (min-width: 1000px) { - .menu { - transform: translatex(0px); - /* position: inherit; */ - } -} - -.menu a { - position: relative; -} - -.menu:not(.menu--unexpanded) a[data-title]:hover::after { - content: attr(data-title); - background-color: #2f2f2f; - color: #fff; - padding: 8px; - border-radius: 4px; - font-size: 12px; - line-height: 14px; - display: block; - white-space: nowrap; - position: absolute; - right: 1rem; - overflow: visible; -} diff --git a/src/components/NetworkFlashIcon/NetworkFlashIcon.tsx b/src/components/NetworkFlashIcon/NetworkFlashIcon.tsx index 2aa98ed..7c6e329 100644 --- a/src/components/NetworkFlashIcon/NetworkFlashIcon.tsx +++ b/src/components/NetworkFlashIcon/NetworkFlashIcon.tsx @@ -1,18 +1,20 @@ type Props = { - online: boolean; + className?: string; }; -export function NetworkFlashIcon({ online }: Props) { - const color = online ? "#3EE089" : "var(--codex-color-error-hexa)"; - +export function NetworkFlashIcon({ className }: Props) { return ( - + ); } diff --git a/src/components/NodeIndicator/NodeIndicator.css b/src/components/NodeIndicator/NodeIndicator.css deleted file mode 100644 index 1125719..0000000 --- a/src/components/NodeIndicator/NodeIndicator.css +++ /dev/null @@ -1,24 +0,0 @@ -.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; -} diff --git a/src/components/NodeIndicator/NodeIndicator.tsx b/src/components/NodeIndicator/NodeIndicator.tsx deleted file mode 100644 index 7270d0b..0000000 --- a/src/components/NodeIndicator/NodeIndicator.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useQueryClient } from "@tanstack/react-query"; -import { useEffect } from "react"; -import { useCodexConnection } from "../../hooks/useCodexConnection"; -import "./NodeIndicator.css"; -import { NodesIcon } from "../Menu/NodesIcon"; - -export function NodeIndicator() { - const queryClient = useQueryClient(); - const codex = useCodexConnection(); - - useEffect(() => { - queryClient.invalidateQueries({ - type: "active", - refetchType: "all", - }); - }, [queryClient, codex.enabled]); - - return ( - <> -
-
- -
- Node -
- - ); -} diff --git a/src/components/OnBoarding/HealthCheckItem.css b/src/components/OnBoarding/HealthCheckItem.css deleted file mode 100644 index bb3c1c4..0000000 --- a/src/components/OnBoarding/HealthCheckItem.css +++ /dev/null @@ -1,5 +0,0 @@ -.onboarding-healthCheckItem { - display: flex; - align-items: center; - gap: 16px; -} diff --git a/src/components/OnBoarding/HealthCheckItem.tsx b/src/components/OnBoarding/HealthCheckItem.tsx deleted file mode 100644 index a039a7d..0000000 --- a/src/components/OnBoarding/HealthCheckItem.tsx +++ /dev/null @@ -1,21 +0,0 @@ -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 ( -
- -
-

{text}

-
-
- ); -} diff --git a/src/components/OnBoarding/OnBoardingImage.css b/src/components/OnBoarding/OnBoardingImage.css deleted file mode 100644 index 5cacdf4..0000000 --- a/src/components/OnBoarding/OnBoardingImage.css +++ /dev/null @@ -1,7 +0,0 @@ -.onboarding-image { - position: absolute; - right: -40px; - max-height: 90%; - width: auto; - z-index: -1; -} diff --git a/src/components/OnBoarding/OnBoardingImage.tsx b/src/components/OnBoarding/OnBoardingImage.tsx deleted file mode 100644 index d798ef6..0000000 --- a/src/components/OnBoarding/OnBoardingImage.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import "./OnBoardingImage.css"; - -export function OnBoardingImage() { - return ( - - - - Onboarding Image - - ); -} diff --git a/src/components/OnBoarding/OnBoardingLayout.css b/src/components/OnBoarding/OnBoardingLayout.css new file mode 100644 index 0000000..e326a2d --- /dev/null +++ b/src/components/OnBoarding/OnBoardingLayout.css @@ -0,0 +1,217 @@ +.onboarding { + width: 100%; + padding: 3rem 6rem; + display: flex; + + > section { + display: flex; + flex-direction: column; + justify-content: space-between; + } + + > section:first-child { + max-width: 500px; + } + + section { + flex: 1; + } + + &.onboarding--second .alpha { + flex: 0.3; + } + + &.onboarding--third .alpha { + flex: 0.5; + } + + section > *:first-child { + flex: 0.5; + } + + h1 { + font-family: Inter; + font-size: 36px; + font-weight: 300; + line-height: 43.57px; + letter-spacing: 0.01em; + + b { + font-weight: 400; + } + + b + b { + font-weight: 900; + text-transform: uppercase; + } + } + + footer { + display: flex; + align-items: center; + justify-content: space-between; + flex: 0; + + ul { + display: flex; + gap: 8px; + + li { + & { + width: 12px; + height: 12px; + background-color: white; + display: inline-block; + border-radius: 50%; + transition: opacity 0.35s; + } + + &:hover { + animation-name: pulse; + animation-duration: 2.5s; + animation-iteration-count: infinite; + } + + &:not([aria-selected]) { + opacity: 0.4; + } + + &[aria-selected] { + box-shadow: 0px 0px 12px 0px #fff; + opacity: 1; + } + } + } + } + + .alpha { + > div { + margin-top: 4px; + display: block; + } + + b { + font-weight: 500; + opacity: 0.6; + display: block; + } + + a { + text-decoration: underline; + font-family: Azeret Mono; + font-size: 12px; + font-weight: 400; + line-height: 14px; + margin-top: 16px; + cursor: pointer; + display: inline-flex; + align-items: center; + color: var(--codex-color-error-hexa); + + &:hover { + animation-name: example; + animation-duration: 2.5s; + animation-iteration-count: infinite; + } + } + + p { + margin-top: 1rem; + font-family: Azeret Mono; + font-size: 14px; + font-weight: 400; + line-height: 16.34px; + display: inline-block; + color: var(--codex-color-primary); + } + } + + .main { + p { + font-family: Azeret Mono; + font-size: 14px; + font-weight: 400; + line-height: 16.34px; + max-width: 532px; + margin-top: 20px; + color: var(--codex-input-label-color); + } + + label { + margin-top: 1rem; + } + } + + .get-started { + a { + font-size: 24px; + font-weight: 600; + line-height: 29.05px; + letter-spacing: 0.01em; + margin-top: 32px; + font-family: Inter; + color: #7bfbaf; + gap: 4px; + text-decoration: none; + border-bottom: 2px solid #7bfbaf; + cursor: pointer; + display: inline-flex; + align-items: center; + } + } + + .modal { + max-width: 600px; + margin: auto; + + h1 { + margin-top: 0; + margin-bottom: 3rem; + } + + p { + line-height: 1.5rem; + } + } + + .navigation { + cursor: pointer; + position: absolute; + right: 6rem; + bottom: 3rem; + border-bottom: none; + text-decoration: none; + z-index: 1; + + &:hover { + animation-name: example; + animation-duration: 2.5s; + animation-iteration-count: infinite; + } + + &[aria-disabled="true"] { + cursor: not-allowed; + } + } +} + +@keyframes rotate { + from { + transform: rotate(0deg); /* Start at 0 degrees */ + } + to { + transform: rotate(360deg); /* End at 360 degrees */ + } +} + +@keyframes pulse { + 0% { + opacity: 0.4; + } + 30% { + opacity: 0.8; + } + 100% { + opacity: 0.4; + } +} diff --git a/src/components/OnBoarding/OnBoardingLayout.tsx b/src/components/OnBoarding/OnBoardingLayout.tsx new file mode 100644 index 0000000..1be6bc6 --- /dev/null +++ b/src/components/OnBoarding/OnBoardingLayout.tsx @@ -0,0 +1,41 @@ +import { ReactElement } from "react"; +import { classnames } from "../../utils/classnames"; +import { Logotype } from "../Logotype/Logotype"; +import { attributes } from "../../utils/attributes"; +import "./OnBoardingLayout.css"; +import { BackgroundImage } from "../BackgroundImage/BackgroundImage"; + +type Props = { + children: ReactElement<{ onStepValid: (isValid: boolean) => void }>; + step: number; + defaultIsStepValid: boolean; +}; + +export function OnBoardingLayout({ children, step }: Props) { + return ( +
+
+
+ +
+ + {children} + +
+
    +
  • +
  • +
  • +
+
+
+ +
+ ); +} diff --git a/src/components/OnBoarding/OnBoardingStatusIcon.tsx b/src/components/OnBoarding/OnBoardingStatusIcon.tsx deleted file mode 100644 index a6f5887..0000000 --- a/src/components/OnBoarding/OnBoardingStatusIcon.tsx +++ /dev/null @@ -1,33 +0,0 @@ -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 ; - - case "failure": - return ; - - case "warning": - return ( - - - - - - ); - } -} diff --git a/src/components/OnBoarding/OnBoardingStepOne.tsx b/src/components/OnBoarding/OnBoardingStepOne.tsx deleted file mode 100644 index f66f260..0000000 --- a/src/components/OnBoarding/OnBoardingStepOne.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { AlphaIcon } from "./AlphaIcon"; -import { AlphaText } from "../AlphaText/AlphaText"; -import { Modal, SimpleText } from "@codex-storage/marketplace-ui-components"; -import { useState } from "react"; -import { ArrowRight } from "lucide-react"; - -type Props = { - onNextStep: () => void; -}; - -export function OnBoardingStepOne({ onNextStep }: Props) { - const [modal, setModal] = useState(false); - - const onLegalDisclaimerOpen = () => setModal(true); - - const onLegalDisclaimerClose = () => setModal(false); - - return ( - <> -
-
- -
-
-

- -

-

- - {import.meta.env.PACKAGE_VERSION} - -

-

- - Legal Disclaimer - -

-
-
-
-

- Hello, -
Welcome to Codex{" "} - Vault -

-

- - Codex is a durable, decentralised data storage protocol, created so - the world community can preserve its most important knowledge - without risk of censorship. - -

-
-
- - - Let’s get started - - - - -

- Disclaimer -

- -

- The website and the content herein is not intended for public use - and is for informational and demonstration purposes only. -

- -
- -

- The website and any associated functionalities are provided on an - “as is” basis without any guarantees, warranties, or representations - of any kind, either express or implied. The website and any - associated functionalities may not reflect the final version of the - project and is subject to changes, updates, or removal at any time - and without notice. -

- -
- -

- By accessing and using this website, you agree that we, Logos - Collective Association and its affiliates, will not be liable for - any direct, indirect, incidental, or consequential damages arising - from the use of, or inability to use, this website. Any data, - content, or interactions on this site are non-binding and should not - be considered final or actionable. Your use of this website is at - your sole risk. -

-
-
- - ); -} diff --git a/src/components/OnBoarding/OnBoardingStepThree.css b/src/components/OnBoarding/OnBoardingStepThree.css deleted file mode 100644 index 0c56661..0000000 --- a/src/components/OnBoarding/OnBoardingStepThree.css +++ /dev/null @@ -1,118 +0,0 @@ -.onboarding-check { - display: flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 1rem; -} - -.onboarding-check-icon--valid { - color: var(--codex-color-primary); -} - -.onboarding-check-icon--invalid { - color: rgb(var(--codex-color-error)); -} - -.onboarding-check-icon--warning { - color: rgb(var(--codex-color-warning)); -} - -.onboarding-check-line { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.onboarding-check-refresh { - cursor: pointer; -} - -.onboarding-group { - display: flex; - align-items: center; - gap: 1rem; - margin-bottom: 0.5rem; -} - -.onboarding-codex { - margin-top: 1rem; - 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 */ - } - to { - transform: rotate(360deg); /* End at 360 degrees */ - } -} diff --git a/src/components/OnBoarding/OnBoardingStepThree.tsx b/src/components/OnBoarding/OnBoardingStepThree.tsx deleted file mode 100644 index 2de83bc..0000000 --- a/src/components/OnBoarding/OnBoardingStepThree.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import { classnames } from "../../utils/classnames"; -import "./OnBoardingStepThree.css"; -import { usePortForwarding } from "../../hooks/usePortForwarding"; -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; - onStepValid: (valid: boolean) => void; -}; - -const throwOnError = false; -const defaultPort = 8070; - -export function OnBoardingStepThree({ online, onStepValid }: Props) { - const codex = useDebug(throwOnError); - const portForwarding = usePortForwarding(codex.data); - 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); - }, [portForwarding.enabled, codex.isSuccess, onStepValid, online]); - - useEffect(() => { - if (codex.isSuccess) { - persistence.refetch(); - portForwarding.refetch(); - } - }, [persistence, portForwarding, codex.isSuccess]); - - const onAddressChange = (e: React.FormEvent) => { - const [, port] = Strings.splitURLAndPort(url); - const element = e.currentTarget; - const value = e.currentTarget.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 onPortChange = (e: React.FormEvent) => { - 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()); - }; - - let forwardingPortValue = defaultPort; - - if (codex.isSuccess && codex.data) { - const port = DebugUtils.getTcpPort(codex.data); - if (!port.error) { - forwardingPortValue = port.data; - } - } - - const displayName = OnBoardingUtils.getDisplayName(); - const [address, port] = Strings.splitURLAndPort(url); - - return ( - <> -
-
- -
- - - Connection /
- Device Health Check -
-
-
-
-

- Nice to meet {displayName},
- Let’s establish our connection. -

-
-
-
- - {isInvalid ? ( - - ) : ( - - )} -
-
- - -
- - -
-
- -
    -
  • Port forwarding should be default {forwardingPortValue}.
  • -
- -
-
- - - - - - Health Check - -
- -
- -
- -
- -
- -
- -
- -
- -
-
-
- - ); -} diff --git a/src/components/OnBoarding/OnBoardingStepTwo.css b/src/components/OnBoarding/OnBoardingStepTwo.css deleted file mode 100644 index c45f7ed..0000000 --- a/src/components/OnBoarding/OnBoardingStepTwo.css +++ /dev/null @@ -1,44 +0,0 @@ -.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; -} diff --git a/src/components/OnBoarding/OnBoardingStepTwo.tsx b/src/components/OnBoarding/OnBoardingStepTwo.tsx deleted file mode 100644 index 93c88f8..0000000 --- a/src/components/OnBoarding/OnBoardingStepTwo.tsx +++ /dev/null @@ -1,46 +0,0 @@ -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; -}; - -export function OnBoardingStepTwo({ onStepValid }: Props) { - const [displayName, setDisplayName] = useState(""); - - const onDisplayNameChange = (e: ChangeEvent) => { - const value = e.currentTarget.value; - OnBoardingUtils.setDisplayName(value); - setDisplayName(value); - onStepValid(!!value); - }; - - return ( - <> -
-
- -
- - Personalization - -
-
-

- Let’s get you setup.
- What do you want to be called? -

-
- -
-
- - ); -} diff --git a/src/components/Page/Page.tsx b/src/components/Page/Page.tsx deleted file mode 100644 index 94a3fd4..0000000 --- a/src/components/Page/Page.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { ReactNode, useState } from "react"; -import { Menu, MenuItem } from "../Menu/Menu"; -import { AppBar } from "../AppBar/AppBar"; -import "./page.css"; - -type Props = { - children: ReactNode; - - items: MenuItem[]; - - version?: string; -}; - -export function Page({ children, items, version = "" }: Props) { - const [open, setOpen] = useState(false); - - const onClose = () => setOpen(false); - - const onExpand = () => setOpen(true); - - return ( -
- - -
- - {children} -
-
- ); -} diff --git a/src/components/Page/page.css b/src/components/Page/page.css deleted file mode 100644 index afcf2a4..0000000 --- a/src/components/Page/page.css +++ /dev/null @@ -1,8 +0,0 @@ -.page { - display: flex; - flex: 1; -} - -.page-main { - flex: 1; -} diff --git a/src/components/Peers/PeerCountryCell.css b/src/components/Peers/PeerCountryCell.css index ac159a5..b2489b3 100644 --- a/src/components/Peers/PeerCountryCell.css +++ b/src/components/Peers/PeerCountryCell.css @@ -1,5 +1,11 @@ -.peerCountry { +.peer-country { display: flex; align-items: center; gap: 1rem; + + span:first-child { + background-color: #141414; + border-radius: 50%; + padding: 12px; + } } diff --git a/src/components/Peers/PeerCountryCell.tsx b/src/components/Peers/PeerCountryCell.tsx index 5ffef3d..c34a421 100644 --- a/src/components/Peers/PeerCountryCell.tsx +++ b/src/components/Peers/PeerCountryCell.tsx @@ -56,7 +56,7 @@ export function PeerCountryCell({ address, onPinAdd }: Props) { return ( -
+
{data ? ( <> {!!data && getFlagEmoji(data.country_iso)} diff --git a/src/components/SuccessCheckIcon/SuccessCheckIcon.tsx b/src/components/SuccessCheckIcon/SuccessCheckIcon.tsx index 4a19441..4577050 100644 --- a/src/components/SuccessCheckIcon/SuccessCheckIcon.tsx +++ b/src/components/SuccessCheckIcon/SuccessCheckIcon.tsx @@ -1,24 +1,21 @@ type Props = { variant: "primary" | "default"; - className?: string; }; -export function SuccessCheckIcon({ variant, className = "" }: Props) { +export function SuccessCheckIcon({ variant }: Props) { const color = variant === "primary" ? "#1FC16B" : "#444444"; return ( - - - - - + + + ); } diff --git a/src/components/SuccessIcon/SuccessIcon.tsx b/src/components/SuccessIcon/SuccessIcon.tsx index e53eab6..ea06811 100644 --- a/src/components/SuccessIcon/SuccessIcon.tsx +++ b/src/components/SuccessIcon/SuccessIcon.tsx @@ -1,10 +1,9 @@ -import { SimpleText } from "@codex-storage/marketplace-ui-components"; import { CircleCheck } from "lucide-react"; export function SuccessIcon() { return ( - + - + ); } diff --git a/src/components/UserInfo/UserInfo.css b/src/components/UserInfo/UserInfo.css new file mode 100644 index 0000000..2adbff1 --- /dev/null +++ b/src/components/UserInfo/UserInfo.css @@ -0,0 +1,22 @@ +.user-info { + display: flex; + align-items: center; + gap: 32px; + + .emoji { + position: relative; + + aside { + position: absolute; + top: -140px; + left: 116px; + z-index: 1; + } + + .input input { + width: 64px; + text-align: center; + cursor: pointer; + } + } +} diff --git a/src/components/UserInfo/UserInfo.tsx b/src/components/UserInfo/UserInfo.tsx new file mode 100644 index 0000000..38e5172 --- /dev/null +++ b/src/components/UserInfo/UserInfo.tsx @@ -0,0 +1,80 @@ +import { ChangeEvent, useState } from "react"; +import "./UserInfo.css"; +import { OnBoardingUtils } from "../../utils/onboarding"; +import { Input } from "@codex-storage/marketplace-ui-components"; +import EmojiPicker, { + EmojiClickData, + EmojiStyle, + Theme, +} from "emoji-picker-react"; + +type Props = { + onNameChange?: (value: string) => void; +}; + +export function UserInfo({ onNameChange }: Props) { + const [displayName, setDisplayName] = useState( + OnBoardingUtils.getDisplayName() + ); + const [emoji, setEmoji] = useState(OnBoardingUtils.getEmoji()); + const [areEmojiVisible, setAreEmojiVisible] = useState(false); + + const onDisplayNameChange = (e: ChangeEvent) => { + const value = e.currentTarget.value; + OnBoardingUtils.setDisplayName(value); + setDisplayName(value); + onNameChange?.(value); + }; + + const onDisplayEmoji = () => setAreEmojiVisible(!areEmojiVisible); + + const onEmojiClick = (emojiData: EmojiClickData) => { + setEmoji(emojiData.emoji); + OnBoardingUtils.setEmoji(emojiData.emoji); + setAreEmojiVisible(false); + }; + + return ( +
+
+ {areEmojiVisible && ( + + )} +
+ +
+
+ +
+ +
+
+ ); +} diff --git a/src/components/Versions/Versions.css b/src/components/Versions/Versions.css new file mode 100644 index 0000000..4169b52 --- /dev/null +++ b/src/components/Versions/Versions.css @@ -0,0 +1,34 @@ +.versions { + display: flex; + gap: 32px; + + > svg { + position: relative; + top: 6px; + } + + > div { + width: 50px; + text-align: right; + + p { + font-family: Inter; + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: -0.011em; + color: #99a0ae; + } + + small { + 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; + } + } +} diff --git a/src/components/Versions/Versions.tsx b/src/components/Versions/Versions.tsx new file mode 100644 index 0000000..234b615 --- /dev/null +++ b/src/components/Versions/Versions.tsx @@ -0,0 +1,28 @@ +import { useDebug } from "../../hooks/useDebug"; +import { AlphaText } from "../AlphaText/AlphaText"; +import { AlphaIcon } from "../OnBoarding/AlphaIcon"; +import "./Versions.css"; + +const throwOnError = true; + +export function Versions() { + const debug = useDebug(throwOnError); + + const parts = debug.data?.codex.version.split("\n") || [""]; + const version = parts[parts.length - 1]; + + return ( +
+ +
+

Client

+ VER. {version} +
+
+

Vault

+ VER. {import.meta.env.PACKAGE_VERSION} + +
+
+ ); +} diff --git a/src/components/WarningIcon/WarningIcon.tsx b/src/components/WarningIcon/WarningIcon.tsx new file mode 100644 index 0000000..6a3df4f --- /dev/null +++ b/src/components/WarningIcon/WarningIcon.tsx @@ -0,0 +1,15 @@ +export function WarningIcon() { + return ( + + + + ); +} diff --git a/src/components/Welcome/Welcome.tsx b/src/components/Welcome/Welcome.tsx index 0c31579..65a6090 100644 --- a/src/components/Welcome/Welcome.tsx +++ b/src/components/Welcome/Welcome.tsx @@ -1,4 +1,3 @@ -import { SimpleText } from "@codex-storage/marketplace-ui-components"; import "./Welcome.css"; import { Link } from "@tanstack/react-router"; import { ChevronRight } from "lucide-react"; @@ -8,12 +7,12 @@ export function Welcome() {

Welcome to Codex Marketplace

- + Begin your journey with Codex by uploading new files for testing. Experience the power of our decentralized data storage platform and explore its features. Your feedback is invaluable as we continue to improve! - +
diff --git a/src/index.css b/src/index.css index f47b4af..0271dd1 100644 --- a/src/index.css +++ b/src/index.css @@ -46,6 +46,7 @@ --codex-input-border-color: #494949; --codex-input-background: #232323; --codex-input-color-error: #fb3748; + --codex-row-gap: 16px; -webkit-tap-highlight-color: transparent; -webkit-text-size-adjust: 100%; @@ -136,11 +137,6 @@ main { max-width: 100%; } -hr { - border: 0.1px solid var(--codex-border-color); - width: 100%; -} - ul { margin: 0; padding: 0; @@ -158,12 +154,6 @@ pre { word-break: break-word; } -a { - text-decoration: inherit; - color: var(--codex-color); - transition: opacity 0.35s; -} - a[aria-disabled] { opacity: 0.6; cursor: not-allowed; @@ -175,10 +165,6 @@ a[aria-disabled] { max-width: 100%; } -.page { - max-width: 100%; -} - input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, @@ -186,3 +172,12 @@ input:-webkit-autofill:active { -webkit-transition-delay: 9999s; transition-delay: 9999s; } + +.row { + display: flex; + align-items: center; +} + +.gap { + gap: var(--codex-row-gap); +} diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index ff30d60..d5991c6 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -11,6 +11,8 @@ // Import Routes import { Route as rootRoute } from './routes/__root' +import { Route as OnboardingNameImport } from './routes/onboarding-name' +import { Route as OnboardingChecksImport } from './routes/onboarding-checks' import { Route as DashboardImport } from './routes/dashboard' import { Route as IndexImport } from './routes/index' import { Route as DashboardIndexImport } from './routes/dashboard/index' @@ -32,6 +34,18 @@ import { Route as DashboardAboutImport } from './routes/dashboard/about' // Create/Update Routes +const OnboardingNameRoute = OnboardingNameImport.update({ + id: '/onboarding-name', + path: '/onboarding-name', + getParentRoute: () => rootRoute, +} as any) + +const OnboardingChecksRoute = OnboardingChecksImport.update({ + id: '/onboarding-checks', + path: '/onboarding-checks', + getParentRoute: () => rootRoute, +} as any) + const DashboardRoute = DashboardImport.update({ id: '/dashboard', path: '/dashboard', @@ -158,6 +172,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardImport parentRoute: typeof rootRoute } + '/onboarding-checks': { + id: '/onboarding-checks' + path: '/onboarding-checks' + fullPath: '/onboarding-checks' + preLoaderRoute: typeof OnboardingChecksImport + parentRoute: typeof rootRoute + } + '/onboarding-name': { + id: '/onboarding-name' + path: '/onboarding-name' + fullPath: '/onboarding-name' + preLoaderRoute: typeof OnboardingNameImport + parentRoute: typeof rootRoute + } '/dashboard/about': { id: '/dashboard/about' path: '/about' @@ -320,6 +348,8 @@ const DashboardRouteWithChildren = DashboardRoute._addFileChildren( export interface FileRoutesByFullPath { '/': typeof IndexRoute '/dashboard': typeof DashboardRouteWithChildren + '/onboarding-checks': typeof OnboardingChecksRoute + '/onboarding-name': typeof OnboardingNameRoute '/dashboard/about': typeof DashboardAboutRoute '/dashboard/analytics': typeof DashboardAnalyticsRoute '/dashboard/availabilities': typeof DashboardAvailabilitiesRoute @@ -340,6 +370,8 @@ export interface FileRoutesByFullPath { export interface FileRoutesByTo { '/': typeof IndexRoute + '/onboarding-checks': typeof OnboardingChecksRoute + '/onboarding-name': typeof OnboardingNameRoute '/dashboard/about': typeof DashboardAboutRoute '/dashboard/analytics': typeof DashboardAnalyticsRoute '/dashboard/availabilities': typeof DashboardAvailabilitiesRoute @@ -362,6 +394,8 @@ export interface FileRoutesById { __root__: typeof rootRoute '/': typeof IndexRoute '/dashboard': typeof DashboardRouteWithChildren + '/onboarding-checks': typeof OnboardingChecksRoute + '/onboarding-name': typeof OnboardingNameRoute '/dashboard/about': typeof DashboardAboutRoute '/dashboard/analytics': typeof DashboardAnalyticsRoute '/dashboard/availabilities': typeof DashboardAvailabilitiesRoute @@ -385,6 +419,8 @@ export interface FileRouteTypes { fullPaths: | '/' | '/dashboard' + | '/onboarding-checks' + | '/onboarding-name' | '/dashboard/about' | '/dashboard/analytics' | '/dashboard/availabilities' @@ -404,6 +440,8 @@ export interface FileRouteTypes { fileRoutesByTo: FileRoutesByTo to: | '/' + | '/onboarding-checks' + | '/onboarding-name' | '/dashboard/about' | '/dashboard/analytics' | '/dashboard/availabilities' @@ -424,6 +462,8 @@ export interface FileRouteTypes { | '__root__' | '/' | '/dashboard' + | '/onboarding-checks' + | '/onboarding-name' | '/dashboard/about' | '/dashboard/analytics' | '/dashboard/availabilities' @@ -446,11 +486,15 @@ export interface FileRouteTypes { export interface RootRouteChildren { IndexRoute: typeof IndexRoute DashboardRoute: typeof DashboardRouteWithChildren + OnboardingChecksRoute: typeof OnboardingChecksRoute + OnboardingNameRoute: typeof OnboardingNameRoute } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, DashboardRoute: DashboardRouteWithChildren, + OnboardingChecksRoute: OnboardingChecksRoute, + OnboardingNameRoute: OnboardingNameRoute, } export const routeTree = rootRoute @@ -466,7 +510,9 @@ export const routeTree = rootRoute "filePath": "__root.tsx", "children": [ "/", - "/dashboard" + "/dashboard", + "/onboarding-checks", + "/onboarding-name" ] }, "/": { @@ -493,6 +539,12 @@ export const routeTree = rootRoute "/dashboard/" ] }, + "/onboarding-checks": { + "filePath": "onboarding-checks.tsx" + }, + "/onboarding-name": { + "filePath": "onboarding-name.tsx" + }, "/dashboard/about": { "filePath": "dashboard/about.tsx", "parent": "/dashboard" diff --git a/src/routes/dashboard.tsx b/src/routes/dashboard.tsx index aa0e0ed..145f603 100644 --- a/src/routes/dashboard.tsx +++ b/src/routes/dashboard.tsx @@ -1,200 +1,25 @@ -import { createFileRoute, Link, Outlet } from "@tanstack/react-router"; -import "./dashboard.css"; -import { Page } from "../components/Page/Page"; -import { HomeIcon } from "../components/Menu/HomeIcon"; -import { WalletIcon } from "../components/Menu/WalletIcon"; -import { NodesIcon } from "../components/Menu/NodesIcon"; -import { FilesIcon } from "../components/Menu/FilesIcon"; -import { AnalyticsIcon } from "../components/Menu/AnalyticsIcon"; -import { PurchaseIcon } from "../components/Menu/PurchaseIcon"; -import { PeersIcon } from "../components/Menu/PeersIcon"; -import { LogsIcon } from "../components/Menu/LogsIcon"; -import { MenuItem, MenuItemComponentProps } from "../components/Menu/Menu"; -import { HelpIcon } from "../components/Menu/HelpIcon"; -import { DisclaimerIcon } from "../components/Menu/DisclaimerIcon"; -import { SettingsIcon } from "../components/Menu/SettingsIcon"; -import { HostIcon } from "../components/Menu/HostIcon"; -import { DeviceIcon } from "../components/Menu/DeviceIcon"; +import { createFileRoute, Outlet } from "@tanstack/react-router"; +import "./layout.css"; +import { Menu } from "../components/Menu/Menu"; +import { useState } from "react"; +import { AppBar } from "../components/AppBar/AppBar"; const Layout = () => { - const items = [ - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Dashboard - - ), - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Wallet - - ), - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Files - - ), - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Nodes - - ), - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Analytics - - ), - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Devices - - ), - }, - { - type: "separator", - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Purchases - - ), - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Host - - ), - }, - { - type: "separator", - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Peers - - ), - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Log - - ), - }, - { - type: "space", - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Settings - - ), - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Help - - ), - }, - { - type: "item", - Component: (p: MenuItemComponentProps) => ( - - - - - Disclaimer - - ), - }, - ] satisfies MenuItem[]; + const [open, setOpen] = useState(false); + + const onClose = () => setOpen(false); + + const onExpand = () => setOpen(true); return ( - } - items={items} - version={import.meta.env.PACKAGE_VERSION} - /> +
+ + +
+ + +
+
); }; diff --git a/src/routes/dashboard/index.css b/src/routes/dashboard/index.css index ab107e6..416974b 100644 --- a/src/routes/dashboard/index.css +++ b/src/routes/dashboard/index.css @@ -1,34 +1,35 @@ -.dashboard-welcomeContainer { +.dashboard { padding: 24px 48px; display: flex; justify-content: space-between; -} -.dashboard-welcome-avatarContainer { - display: flex; - gap: 16px; -} + h3 { + font-family: Inter; + font-size: 12px; + font-weight: 700; + line-height: 14.52px; + letter-spacing: 0.01em; + color: #969696cc; + text-transform: uppercase; + } -.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; -} + h4 { + font-family: Inter; + font-size: 32px; + font-weight: 400; + line-height: 38.73px; + letter-spacing: 0.01em; + color: white; + } -.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; + .emoji { + border-radius: 50%; + width: 52px; + height: 52px; + background-color: #4a9a73; + display: flex; + align-items: center; + justify-content: center; + font-size: 26px; + } } diff --git a/src/routes/dashboard/index.tsx b/src/routes/dashboard/index.tsx index 52c5d74..f2acc09 100644 --- a/src/routes/dashboard/index.tsx +++ b/src/routes/dashboard/index.tsx @@ -10,19 +10,14 @@ 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"; +import { Versions } from "../../components/Versions/Versions.tsx"; export const Route = createFileRoute("/dashboard/")({ component: Dashboard, }); -const throwOnError = false; - function Dashboard() { const queryClient = useQueryClient(); - const debug = useDebug(throwOnError); const onSuccess = () => { queryClient.invalidateQueries({ queryKey: ["cids"] }); @@ -30,62 +25,21 @@ function Dashboard() { const username = OnBoardingUtils.getDisplayName(); - const parts = debug.data?.codex.version.split("\n") || [""]; - const version = parts[parts.length - 1]; + const emoji = OnBoardingUtils.getEmoji(); return ( <> -
-
- - - - Avatar - -
-
Welcome back,
-
{username}
-
-
-
- -
-

Client

-

VER. {version}

-
-
-

Vault

-

- VER. {import.meta.env.PACKAGE_VERSION} -

- -
-
-
+
+
{emoji}
+
+

Welcome back,

+

{username}

+
+
+ +
+
( diff --git a/src/routes/dashboard/peers.css b/src/routes/dashboard/peers.css index 7f308ee..a966739 100644 --- a/src/routes/dashboard/peers.css +++ b/src/routes/dashboard/peers.css @@ -1,41 +1,228 @@ -.peers-map { - max-width: 1000px; - width: 100%; -} - -.peers-table { - margin-top: 1rem; - width: calc(100% - 4rem); - max-width: calc(1000px - 4rem); -} - .peers { - display: flex; - flex-direction: column; - align-items: center; - padding-bottom: 4rem; - padding-left: 2rem; - padding-right: 2rem; + max-width: 1320px; + margin-left: auto; + margin-right: auto; + width: calc(100% - 2 * 24px); + padding: 24px; + + > div { + max-width: 1320px; + } + + > div:first-child { + width: calc(100% - 128px - 16px); + border: 1px solid #96969633; + padding: 16px 16px 16px 128px; + border-radius: 16px; + position: relative; + height: 600px; + + circle[fill="#d6ff79"] { + stroke: var(--codex-color-primary); + stroke-width: 0.6px; + fill: #141414; + animation: circle-pulse 1.5s infinite; + } + + ul { + list-style-type: none; + width: 71px; + position: absolute; + left: 16px; + top: 16px; + + li { + border-bottom: 1px solid #969696cc; + padding: 16px 0; + text-align: right; + } + + li:first-child { + font-size: 20px; + font-weight: 500; + line-height: 20px; + letter-spacing: -0.006em; + text-align: left; + color: #7b7b7b; + } + + li:not(:first-child) { + font-family: Inter; + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: -0.011em; + position: relative; + } + + li:not(:first-child)::before { + content: " "; + border: 4px solid var(--codex-color-primary); + border-radius: 50%; + height: 8px; + width: 8px; + display: inline-block; + position: absolute; + left: 0; + top: 20px; + } + + li:nth-child(3)::before { + border-width: 5px; + height: 11px; + width: 11px; + top: 18px; + } + + li:nth-child(4)::before { + border-width: 6px; + height: 12px; + width: 12px; + top: 16px; + } + } + + .connections { + background-color: #232323; + border: 1px solid #96969633; + border-radius: 16px; + position: absolute; + bottom: 16px; + left: 16px; + width: 280px; + padding: 16px; + + header { + display: flex; + align-items: center; + gap: 8px; + color: #969696; + padding-bottom: 16px; + border-bottom: 1px solid #96969633; + + span { + font-family: Inter; + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: -0.011em; + color: white; + } + } + + main { + > div { + position: relative; + width: 350px; + height: 175px; + overflow: hidden; + transform: scale(0.73); + margin: auto; + left: -32px; + + *, + &::before { + box-sizing: border-box; + } + + &::before, + &::after { + position: absolute; + } + + &::before { + content: ""; + width: inherit; + height: inherit; + border: 45px solid #323232; + border-bottom: none; + border-top-left-radius: 175px; + border-top-right-radius: 175px; + } + + div { + position: absolute; + top: 100%; + width: inherit; + height: inherit; + border: 45px solid #323232; + border-top: none; + border-bottom-left-radius: 175px; + border-bottom-right-radius: 175px; + transform-origin: 50% 0; + } + + div:nth-child(1) { + border-color: var(--codex-color-primary); + transform: rotate(calc(var(--codex-peers-percent) * 1deg)); + background-color: transparent; + } + } + + span { + font-family: Inter; + font-size: 38.67px; + font-weight: 500; + line-height: 48.34px; + letter-spacing: -0.005em; + text-align: center; + position: absolute; + bottom: 0; + left: calc(50% - 24px); + } + } + + footer { + border-top: 1px solid #96969633; + padding-top: 16px; + display: flex; + align-items: center; + gap: 8px; + font-family: Inter; + font-size: 14px; + font-weight: 400; + line-height: 20px; + letter-spacing: -0.006em; + } + } + } + + > div:nth-child(2) { + width: calc(100% - 64px); + } + + table { + td:last-child { + div { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px; + border-radius: 8px; + + &.status--active { + color: #1daf61; + background-color: #6fcb9433; + } + + &.status--inactive { + color: #fb3748; + background-color: #fb374833; + } + } + } + } } -.peers circle[fill="#d6ff79"] { - /* fill: yellow; */ - animation: dash 3s linear infinite; - stroke: white; - stroke-width: 0.6px; - stroke-dasharray: 0.3; -} - -@keyframes dash { - from { - stroke-dashoffset: 2; +@keyframes circle-pulse { + 0% { + opacity: 1; /* Fully opaque */ } - to { - stroke-dashoffset: 0; - } -} -@keyframes circleAn { - to { - /* stroke-dashoffset: 100px; */ + 50% { + r: 2; /* Increased radius */ + opacity: 1; /* Slightly transparent */ + } + 100% { + opacity: 1; /* Fully opaque */ } } diff --git a/src/routes/dashboard/peers.tsx b/src/routes/dashboard/peers.tsx index 491e867..b423fb5 100644 --- a/src/routes/dashboard/peers.tsx +++ b/src/routes/dashboard/peers.tsx @@ -11,10 +11,17 @@ import "./peers.css"; import { CodexSdk } from "../../sdk/codex"; import { ErrorBoundary } from "@sentry/react"; import { ErrorPlaceholder } from "../../components/ErrorPlaceholder/ErrorPlaceholder"; +import { PeersIcon } from "../../components/Menu/PeersIcon"; +import { SuccessCheckIcon } from "../../components/SuccessCheckIcon/SuccessCheckIcon"; +import { ErrorCircleIcon } from "../../components/ErrorCircleIcon/ErrorCircleIcon"; // This function accepts the same arguments as DottedMap in the example above. const mapJsonString = getMapJSON({ height: 60, grid: "diagonal" }); +type CustomCSSProperties = React.CSSProperties & { + "--codex-peers-percent": number; +}; + const Peers = () => { const [pins, setPins] = useState<[PeerPin, number][]>([]); const { data } = useQuery({ @@ -61,10 +68,10 @@ const Peers = () => { ); const svgMap = map.getSVG({ - radius: 0.42, - color: "#423B38", + radius: 0.32, + color: "#969696", shape: "circle", - backgroundColor: "#020300", + backgroundColor: "#141414", }); const headers = ["Country", "PeerId", "Active"]; @@ -79,26 +86,55 @@ const Peers = () => { {node.peerId}, {node.seen ? ( -
+
+ Active +
) : ( -
+
+ Inactive +
)}
, ]}> )) || []; + const actives = + data?.table.nodes.reduce((acc, cur) => acc + (cur.seen ? 1 : 0), 0) || 0; + const total = data?.table.nodes.length || 1; + + const styles: CustomCSSProperties = { + "--codex-peers-percent": (actives / total) * 180, + }; + return (
- {/* */} +
+
    +
  • Legend
  • +
  • 1-3
  • +
  • 3-5
  • +
  • 5 +
  • +
+
+
+ + Connections +
+
+
+
+ {actives} +
+
+
+ {" "} + Peer connections in Good standing. +
+
+
+
-
- - +
); }; diff --git a/src/routes/dashboard/settings.css b/src/routes/dashboard/settings.css index fd82edf..12a4317 100644 --- a/src/routes/dashboard/settings.css +++ b/src/routes/dashboard/settings.css @@ -1,11 +1,49 @@ .settings { - border-radius: var(--codex-border-radius); - border: 1px solid var(--codex-border-color); - background-color: var(--codex-background-secondary); - padding: 1rem 1.5rem; - margin: 1rem 1.5rem; -} + padding: 24px 48px; + header { + display: flex; + align-items: center; + justify-content: space-between; + } + + main { + margin-top: 16px; + padding: 16px; + max-width: 800px; + z-index: 1; + position: relative; + + h3 { + font-family: Inter; + font-size: 18px; + font-weight: 500; + line-height: 24px; + letter-spacing: -0.015em; + margin-bottom: 16px; + } + + .user-info { + padding-bottom: 16px; + margin-bottom: 16px; + border-bottom: 1px solid #96969633; + } + } + + .background-img { + top: 200px; + right: -400px; + } + + .refresh { + top: 17px; + } + + .address svg { + top: 55px; + } +} +/* .settings-title { font-weight: bold; font-size: 1.125rem; @@ -19,4 +57,4 @@ .settings-debug-loader { margin: auto; -} +} */ diff --git a/src/routes/dashboard/settings.tsx b/src/routes/dashboard/settings.tsx index e46deee..460c656 100644 --- a/src/routes/dashboard/settings.tsx +++ b/src/routes/dashboard/settings.tsx @@ -1,16 +1,26 @@ import { createFileRoute } from "@tanstack/react-router"; import "./settings.css"; -import { LogLevel } from "../../components/LogLevel/LogLevel"; -import { Debug } from "../../components/Debug/Debug"; -import { CodexUrlSettings } from "../../components/CodexUrllSettings/CodexUrlSettings"; import { ErrorBoundary } from "@sentry/react"; import { ErrorPlaceholder } from "../../components/ErrorPlaceholder/ErrorPlaceholder"; -import { useEffect } from "react"; +import { UserInfo } from "../../components/UserInfo/UserInfo"; +import { HealthChecks } from "../../components/HealthChecks/HealthChecks"; +import { Logotype } from "../../components/Logotype/Logotype"; +import { Versions } from "../../components/Versions/Versions"; +import { Logo } from "../../components/Logo/Logo"; +import { BackgroundImage } from "../../components/BackgroundImage/BackgroundImage"; export const Route = createFileRoute("/dashboard/settings")({ component: () => ( - <> -
+
+
+
+ + +
+ +
+
+

Personalization

( )}> - + -
-
+

Connection

+ + ( + + )}> + {}} /> + + + + + + {/*
( -
+
*/} {/*
*/} - +
), }); diff --git a/src/routes/index.css b/src/routes/index.css deleted file mode 100644 index dcf4e53..0000000 --- a/src/routes/index.css +++ /dev/null @@ -1,202 +0,0 @@ -.index { - width: 100%; - padding: 3rem 6rem; -} - -.index-logo { - position: relative; -} - -.index-network { - display: flex; - position: absolute; - top: -19px; - left: -161px; - gap: 4px; - width: 200px; - justify-content: flex-end; -} - -.index-network-text { - font-family: Inter; - font-size: 11px; - font-weight: 500; - line-height: 12px; - letter-spacing: 0.02em; - text-transform: uppercase; - color: var(--text-soft-400, #99a0ae); - text-wrap: nowrap; -} - -.index-container { - display: flex; - justify-content: space-between; - height: 100%; -} - -.index-alphaText { - margin-top: 4px; -} - -.index-title { - font-weight: 600; - font-size: 36px; - line-height: 43.2px; -} - -.index-version { - font-weight: 500; - opacity: 0.6; -} - -.index-link { - text-decoration: underline; - font-family: Azeret Mono; - font-size: 12px; - font-weight: 400; - line-height: 14px; - color: inherit; - margin-top: 16px; - cursor: pointer; - display: inline-flex; - align-items: center; -} - -.index-link2:hover, -.index-link:hover { - animation-name: example; - animation-duration: 2.5s; - animation-iteration-count: infinite; -} - -@keyframes example { - 0% { - filter: brightness(1); - -webkit-filter: brightness(1); - } - 30% { - filter: brightness(0.7); - -webkit-filter: brightness(0.7); - } - 100% { - filter: brightness(1); - -webkit-filter: brightness(1); - } -} - -.index-mainTitle { - font-family: Inter; - font-size: 36px; - font-weight: 300; - line-height: 43.57px; - letter-spacing: 0.01em; -} - -.index-codex { - font-weight: 400; -} - -.index-vault { - font-weight: 900; - text-transform: uppercase; -} - -.index-description { - font-family: Azeret Mono; - font-size: 14px; - font-weight: 400; - line-height: 16.34px; - max-width: 532px; - margin-top: 20px; -} - -.index-subtitle { - font-weight: 900; - font-size: 16px; - text-transform: uppercase; - line-height: 19.36px; - color: var(--codex-color-primary); -} - -.index-getStarted { - font-size: 24px; - font-weight: 600; - line-height: 29.05px; - letter-spacing: 0.01em; - margin-top: 32px; - font-family: Inter; - color: #7bfbaf; - gap: 4px; - text-decoration: none; - border-bottom: 2px solid #7bfbaf; -} - -.index-columnRight, -.index-column { - display: flex; - flex-direction: column; - justify-content: space-between; -} - -.index-column-section { - max-width: 500px; -} - -.index-column-section { - flex: 1; -} - -.index-column-section:first-child { - flex: 0.5; -} - -.index-column-section:last-child { - display: flex; - flex-direction: column; - justify-content: space-between; -} - -.index-dots { - display: flex; - gap: 8px; -} - -.index-dot { - width: 12px; - height: 12px; - background-color: white; - display: inline-block; - border-radius: 50%; - transition: opacity 0.35s; -} - -.index-dot:not(.index-dot--active) { - opacity: 0.4; -} - -.index-dot:hover { - animation-name: pulse; - animation-duration: 2.5s; - animation-iteration-count: infinite; -} - -.index-dot--active { - box-shadow: 0px 0px 12px 0px #fff; - opacity: 1; -} - -.index-displayName { - margin-top: 1rem; -} - -@keyframes pulse { - 0% { - opacity: 0.4; - } - 30% { - opacity: 0.8; - } - 100% { - opacity: 0.4; - } -} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 8c6054f..a34d638 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,16 +1,11 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router"; -import "./index.css"; -import { ArrowRightCircle } from "../components/ArrowRightCircle/ArrowRightCircle"; -import { useNetwork } from "../network/useNetwork"; -import { Logotype } from "../components/Logotype/Logotype"; import { useState } from "react"; -import { OnBoardingStepOne } from "../components/OnBoarding/OnBoardingStepOne"; -import { OnBoardingStepTwo } from "../components/OnBoarding/OnBoardingStepTwo"; -import { classnames } from "../utils/classnames"; -import { OnBoardingStepThree } from "../components/OnBoarding/OnBoardingStepThree"; -import { attributes } from "../utils/attributes"; -import { OnBoardingImage } from "../components/OnBoarding/OnBoardingImage"; -import { OnBoardingUtils } from "../utils/onboarding"; +import { Modal } from "@codex-storage/marketplace-ui-components"; +import { ArrowRight } from "lucide-react"; +import { AlphaText } from "../components/AlphaText/AlphaText"; +import { AlphaIcon } from "../components/OnBoarding/AlphaIcon"; +import { OnBoardingLayout } from "../components/OnBoarding/OnBoardingLayout"; +import { ArrowRightCircle } from "../components/ArrowRightCircle/ArrowRightCircle"; export const Route = createFileRoute("/")({ component: Index, @@ -22,87 +17,80 @@ export const Route = createFileRoute("/")({ }); function Index() { - const [isStepValid, setIsStepValid] = useState(true); - // const [step, setStep] = useState(OnBoardingUtils.getStep()); - const [step, setStep] = useState(0); - const online = useNetwork(); + const [modal, setModal] = useState(false); const navigate = useNavigate({ from: "/" }); - const onStepValid = (valid: boolean) => setIsStepValid(valid); - const onNextStep = () => { - if (!isStepValid) { - return; - } + const onLegalDisclaimerOpen = () => setModal(true); - if (step === 2) { - navigate({ to: "/dashboard" }); - return; - } + const onLegalDisclaimerClose = () => setModal(false); - OnBoardingUtils.setStep(step + 1); - - setStep(step + 1); - setIsStepValid(false); - }; - - const components = [ - , - , - , - ]; - - // const text = online ? "Network connected" : "Network disconnected"; + const onNextStep = () => navigate({ to: "/onboarding-name" }); return ( -
-
-
-
- -
- - {components[step]} - -
-
- - - + <> + + <> +
+ +
+ + {import.meta.env.PACKAGE_VERSION} + Legal Disclaimer
-
-
-
- -
-
-
- {/*
-
-

{text}

- -
- -
*/} - + +
+

+ Hello, +
Welcome to Codex Vault +

+

+ Codex is a durable, decentralised data storage protocol, created + so the world community can preserve its most important knowledge + without risk of censorship. +

+
+
+ + Let’s get started + + + +

Disclaimer

+ +

+ The website and the content herein is not intended for public + use and is for informational and demonstration purposes only. +

+ +
+ +

+ The website and any associated functionalities are provided on + an “as is” basis without any guarantees, warranties, or + representations of any kind, either express or implied. The + website and any associated functionalities may not reflect the + final version of the project and is subject to changes, updates, + or removal at any time and without notice. +

+ +
+ +

+ By accessing and using this website, you agree that we, Logos + Collective Association and its affiliates, will not be liable + for any direct, indirect, incidental, or consequential damages + arising from the use of, or inability to use, this website. Any + data, content, or interactions on this site are non-binding and + should not be considered final or actionable. Your use of this + website is at your sole risk. +

+
+
+ -
-
-
+ + + ); } diff --git a/src/routes/dashboard.css b/src/routes/layout.css similarity index 78% rename from src/routes/dashboard.css rename to src/routes/layout.css index cd5ea62..07b3aa0 100644 --- a/src/routes/dashboard.css +++ b/src/routes/layout.css @@ -1,8 +1,12 @@ -.dashboard { - padding: 1.5rem; - display: grid; - grid-template-columns: 1fr; - gap: 0.75rem; +.layout { + display: flex; + flex: 1; + max-width: 100%; + + > main { + flex: 1; + background-color: #141414; + } } .dashboard-download { @@ -51,9 +55,3 @@ text-transform: uppercase; white-space: nowrap; } - -@media (min-width: 1000px) { - .dashboard { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} diff --git a/src/routes/onboarding-checks.tsx b/src/routes/onboarding-checks.tsx new file mode 100644 index 0000000..43d36f6 --- /dev/null +++ b/src/routes/onboarding-checks.tsx @@ -0,0 +1,61 @@ +import { createFileRoute, useNavigate } from "@tanstack/react-router"; +import { useState } from "react"; +import { AlphaIcon } from "../components/OnBoarding/AlphaIcon"; +import { OnBoardingUtils } from "../utils/onboarding"; +import { attributes } from "../utils/attributes"; +import { ArrowRightCircle } from "../components/ArrowRightCircle/ArrowRightCircle"; +import { OnBoardingLayout } from "../components/OnBoarding/OnBoardingLayout"; +import { HealthChecks } from "../components/HealthChecks/HealthChecks"; +import { useNetwork } from "../network/useNetwork"; + +const OnBoardingChecks = () => { + const online = useNetwork(); + const displayName = OnBoardingUtils.getDisplayName(); + const [isStepValid, setIsStepValid] = useState(false); + const navigate = useNavigate({ from: "/onboarding-checks" }); + + const onNextStep = () => { + if (isStepValid) { + navigate({ to: "/onboarding-checks" }); + } + }; + + const onStepValid = (valid: boolean) => setIsStepValid(valid); + + return ( + + <> +
+
+ +
+

+ Connection /
+ Device Health Check +

+
+
+

+ Nice to meet {displayName},
+ Let’s establish our connection. +

+ + +
+ + + + + +
+ ); +}; + +export const Route = createFileRoute("/onboarding-checks")({ + component: OnBoardingChecks, +}); diff --git a/src/routes/onboarding-name.tsx b/src/routes/onboarding-name.tsx new file mode 100644 index 0000000..5ba1597 --- /dev/null +++ b/src/routes/onboarding-name.tsx @@ -0,0 +1,55 @@ +import { createFileRoute, useNavigate } from "@tanstack/react-router"; +import { useState } from "react"; +import { AlphaIcon } from "../components/OnBoarding/AlphaIcon"; +import { OnBoardingLayout } from "../components/OnBoarding/OnBoardingLayout"; +import { OnBoardingUtils } from "../utils/onboarding"; +import { attributes } from "../utils/attributes"; +import { ArrowRightCircle } from "../components/ArrowRightCircle/ArrowRightCircle"; +import { UserInfo } from "../components/UserInfo/UserInfo"; + +const OnBoardingName = () => { + const [isStepValid, setIsStepValid] = useState( + !!OnBoardingUtils.getDisplayName() + ); + const navigate = useNavigate({ from: "/onboarding-name" }); + + const onNameChange = (value: string) => setIsStepValid(!!value); + + const onNextStep = () => { + if (isStepValid) { + navigate({ to: "/onboarding-checks" }); + } + }; + + return ( + + <> +
+
+ +
+

Personalization

+
+
+

+ Let’s get you setup.
+ What do you want to be called? +

+ + +
+ + + + + +
+ ); +}; + +export const Route = createFileRoute("/onboarding-name")({ + component: OnBoardingName, +}); diff --git a/src/utils/onboarding.ts b/src/utils/onboarding.ts index afd4553..9a8d602 100644 --- a/src/utils/onboarding.ts +++ b/src/utils/onboarding.ts @@ -1,3 +1,4 @@ + export const OnBoardingUtils = { getStep() { return parseInt(localStorage.getItem("onboarding-step") || "0", 10) @@ -13,5 +14,13 @@ export const OnBoardingUtils = { getDisplayName() { return localStorage.getItem("display-name") || "" - } + }, + + setEmoji(emoji: string) { + localStorage.setItem("emoji", emoji) + }, + + getEmoji() { + return localStorage.getItem("emoji") || "🤖" + }, } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 105be43..991bd02 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -19,7 +19,13 @@ export default defineConfig({ defaultHandler(warning); }, + output: { + manualChunks: { + "emoji-picker-react": ["emoji-picker-react"] + } + } }, + }, resolve: { alias: {