mirror of
https://github.com/logos-storage/logos-storage-marketplace-ui.git
synced 2026-01-02 13:33:06 +00:00
Add settings and peers integration and css nesting
This commit is contained in:
parent
f2e3ed6958
commit
bc1da83d03
193
package-lock.json
generated
193
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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"
|
||||
},
|
||||
|
||||
6
postcss.config.json
Normal file
6
postcss.config.json
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'postcss-nesting': { /* plugin options */ },
|
||||
},
|
||||
}
|
||||
|
||||
12
public/icons/circle-error.svg
Normal file
12
public/icons/circle-error.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<span>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM8 6.9395L5.879 4.81775L4.81775 5.879L6.9395 8L4.81775 10.121L5.879 11.1823L8 9.0605L10.121 11.1823L11.1823 10.121L9.0605 8L11.1823 5.879L10.121 4.81775L8 6.9395Z"
|
||||
fill="#FB3748" />
|
||||
</svg>
|
||||
</span>
|
||||
@ -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 (
|
||||
<div className="appBar">
|
||||
<div className="appBar-left">
|
||||
<div
|
||||
className={classnames(
|
||||
["app-bar"],
|
||||
["app-bar--offline", offline],
|
||||
["app-bar--no-persistence", !persistence.enabled]
|
||||
)}>
|
||||
<div className="row gap">
|
||||
{/* <a className="appBar-burger" onClick={onExpand}>
|
||||
<Menu size={"1.25rem"} />
|
||||
</a> */}
|
||||
|
||||
<div className="appBar-icon">
|
||||
<span>
|
||||
<DashboardIcon />
|
||||
</div>
|
||||
<div className="appBar-textContainer">
|
||||
<div className="appBar-title">Dashboard</div>
|
||||
<div className="appBar-subtitle">
|
||||
Get Overview of your Codex Vault
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<h2>Get Overview of your Codex Vault</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="appBar-right">
|
||||
<HttpNetworkIndicator />
|
||||
<NodeIndicator />
|
||||
</div>
|
||||
<aside className="row gap">
|
||||
<div className="row gap">
|
||||
<NetworkFlashIcon />
|
||||
<span>Network</span>
|
||||
</div>
|
||||
<div className="row gap">
|
||||
<NodesIcon variant={codex.enabled ? "success" : "failure"} />
|
||||
<span>Node</span>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
@ -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) {
|
||||
<div>
|
||||
<b>Node</b>
|
||||
</div>
|
||||
<SimpleText size="small" variant="light">
|
||||
<small className="text--light">
|
||||
{PrettyBytes(bytes)} allocated for the node
|
||||
</SimpleText>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</Cell>,
|
||||
|
||||
@ -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) {
|
||||
<div>
|
||||
<b>{value.name || Strings.shortId(value.id)}</b>
|
||||
</div>
|
||||
<SimpleText size="small" variant="light">
|
||||
<small className="text--light">
|
||||
{PrettyBytes(value.totalSize)} allocated for the availability
|
||||
</SimpleText>
|
||||
</small>
|
||||
<div>
|
||||
{/* <div>
|
||||
<SimpleText size="small" variant="light">
|
||||
{a.id}
|
||||
</SimpleText>
|
||||
</div> */}
|
||||
<SimpleText size="small" variant="light">
|
||||
<small className="text--light">
|
||||
Max collateral {value.maxCollateral} | Min price {value.minPrice}
|
||||
</SimpleText>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -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) {
|
||||
<div>
|
||||
<b>Slot {id}</b>
|
||||
</div>
|
||||
<SimpleText size="small" variant="light">
|
||||
<small className="text--light">
|
||||
{PrettyBytes(bytes)} allocated for the slot
|
||||
</SimpleText>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</Cell>,
|
||||
|
||||
15
src/components/BackgroundImage/BackgroundImage.css
Normal file
15
src/components/BackgroundImage/BackgroundImage.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/components/BackgroundImage/BackgroundImage.tsx
Normal file
35
src/components/BackgroundImage/BackgroundImage.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import "./BackgroundImage.css";
|
||||
|
||||
export function BackgroundImage() {
|
||||
return (
|
||||
<picture>
|
||||
<source
|
||||
srcSet="/img/onboarding@4x.webp 4x,
|
||||
/img/onboarding@3x.webp 3x,
|
||||
/img/onboarding@2x.webp 2x,
|
||||
/img/onboarding@1.5x.webp 1.5x,
|
||||
/img/onboarding.webp 1x"
|
||||
sizes="(max-width: 600px) 100vw,
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
type="image/webp"
|
||||
/>
|
||||
<source
|
||||
srcSet="/img/onboarding@4x.png 4x,
|
||||
/img/onboarding@3x.png 3x,
|
||||
/img/onboarding@2x.png 2x,
|
||||
/img/onboarding@1.5x.png 1.5x,
|
||||
/img/onboarding.png 1x"
|
||||
sizes="(max-width: 600px) 100vw,
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
type="image/png"
|
||||
/>
|
||||
<img
|
||||
src="/img/onboarding.png"
|
||||
alt="Background Image"
|
||||
className="background-img"
|
||||
/>
|
||||
</picture>
|
||||
);
|
||||
}
|
||||
@ -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 ? (
|
||||
<small className="cardNumber-errorText">{error}</small>
|
||||
) : (
|
||||
<SimpleText
|
||||
size="small"
|
||||
variant="light"
|
||||
className="cardNumber-helperText">
|
||||
{helper}
|
||||
</SimpleText>
|
||||
<small className="cardNumber-helperText">{helper}</small>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,21 +1,15 @@
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function ErrorCircleIcon({ className = "" }: Props) {
|
||||
export function ErrorCircleIcon() {
|
||||
return (
|
||||
<span className={"onboarding-healthCheck-icon " + className}>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM8 6.9395L5.879 4.81775L4.81775 5.879L6.9395 8L4.81775 10.121L5.879 11.1823L8 9.0605L10.121 11.1823L11.1823 10.121L9.0605 8L11.1823 5.879L10.121 4.81775L8 6.9395Z"
|
||||
fill="#FB3748"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM8 6.9395L5.879 4.81775L4.81775 5.879L6.9395 8L4.81775 10.121L5.879 11.1823L8 9.0605L10.121 11.1823L11.1823 10.121L9.0605 8L11.1823 5.879L10.121 4.81775L8 6.9395Z"
|
||||
fill="#FB3748"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { CircleX } from "lucide-react";
|
||||
import { SimpleText } from "@codex-storage/marketplace-ui-components";
|
||||
|
||||
export function ErrorIcon() {
|
||||
return (
|
||||
<SimpleText variant="error">
|
||||
<span className="text--error">
|
||||
<CircleX size="4rem" />
|
||||
</SimpleText>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
85
src/components/HealthChecks/HealthChecks.css
Normal file
85
src/components/HealthChecks/HealthChecks.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
199
src/components/HealthChecks/HealthChecks.tsx
Normal file
199
src/components/HealthChecks/HealthChecks.tsx
Normal file
@ -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<HTMLInputElement>) => {
|
||||
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<HTMLInputElement>) => {
|
||||
const [address] = Strings.splitURLAndPort(url);
|
||||
const element = e.currentTarget;
|
||||
const value = element.value;
|
||||
|
||||
setUrl(address + ":" + value);
|
||||
};
|
||||
|
||||
const onSave = () => {
|
||||
if (isInvalid === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
CodexSdk.updateURL(url)
|
||||
.then(() => queryClient.invalidateQueries())
|
||||
.then(() => codex.refetch());
|
||||
};
|
||||
|
||||
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 (
|
||||
<>
|
||||
<div
|
||||
className={classnames(
|
||||
["address"],
|
||||
["address--fetching", portForwarding.isFetching || codex.isPending]
|
||||
)}>
|
||||
<div>
|
||||
<Input
|
||||
onPaste={onPaste}
|
||||
id="url"
|
||||
type="url"
|
||||
label="Address"
|
||||
isInvalid={isInvalid}
|
||||
onChange={onAddressChange}
|
||||
value={address}
|
||||
placeholder="127.0.0.1"></Input>
|
||||
{isInvalid ? (
|
||||
<ErrorCircleIcon />
|
||||
) : (
|
||||
<SuccessCheckIcon variant="default" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
id="port"
|
||||
label="Port"
|
||||
type="number"
|
||||
onChange={onPortChange}
|
||||
value={port}
|
||||
placeholder="8080"></Input>
|
||||
<SuccessCheckIcon variant="default"></SuccessCheckIcon>
|
||||
</div>
|
||||
|
||||
<div className="refresh">
|
||||
<RefreshIcon onClick={onSave}></RefreshIcon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="helper">
|
||||
<li>Port forwarding should be default {forwardingPortValue}.</li>
|
||||
</ul>
|
||||
|
||||
<ul className="health-checks">
|
||||
<li>
|
||||
<span>
|
||||
<HealthCheckIcon />
|
||||
</span>
|
||||
Health Check
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
{online ? (
|
||||
<SuccessCheckIcon variant="primary"></SuccessCheckIcon>
|
||||
) : (
|
||||
<ErrorCircleIcon />
|
||||
)}
|
||||
</span>
|
||||
Internet connection
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
{portForwarding.enabled ? (
|
||||
<SuccessCheckIcon variant="primary"></SuccessCheckIcon>
|
||||
) : (
|
||||
<ErrorCircleIcon />
|
||||
)}
|
||||
</span>
|
||||
Port forwarding
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
{codex.isSuccess ? (
|
||||
<SuccessCheckIcon variant="primary"></SuccessCheckIcon>
|
||||
) : (
|
||||
<ErrorCircleIcon />
|
||||
)}
|
||||
</span>
|
||||
Codex connection
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
{persistence.enabled ? (
|
||||
<SuccessCheckIcon variant="primary"></SuccessCheckIcon>
|
||||
) : (
|
||||
<WarningIcon />
|
||||
)}
|
||||
</span>
|
||||
Marketplace
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
import { useNetwork } from "../../network/useNetwork";
|
||||
import { NetworkFlashIcon } from "../NetworkFlashIcon/NetworkFlashIcon";
|
||||
import "./HttpNetworkIndicator.css";
|
||||
|
||||
export function HttpNetworkIndicator() {
|
||||
const online = useNetwork();
|
||||
|
||||
return (
|
||||
<div className="network-indicator">
|
||||
<div className="network-indicator-icon">
|
||||
<NetworkFlashIcon online={online} />
|
||||
</div>
|
||||
<span className="network-indicator-text">Network</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -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 (
|
||||
<svg
|
||||
onClick={onClick}
|
||||
className="logo"
|
||||
width="30"
|
||||
height="34"
|
||||
{...attributes({
|
||||
width: width?.toString() || false,
|
||||
height: height?.toString() || false,
|
||||
})}
|
||||
viewBox="0 0 30 34"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -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<boolean | null>(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 <hr className="menu-item-separator" key={index}></hr>;
|
||||
case "space":
|
||||
return <div className="menu-space" key={index}></div>;
|
||||
case "item":
|
||||
return (
|
||||
<i.Component onClick={onClose} className="menu-item" key={index} />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Backdrop onClose={onClose} open={expanded} />
|
||||
|
||||
<aside
|
||||
className={classnames(
|
||||
[`menu ${className}`],
|
||||
[`menu`],
|
||||
["menu--expanded", isExpanded === true],
|
||||
["menu--unexpanded", isExpanded === false]
|
||||
)}
|
||||
{...attributes({ "aria-expanded": expanded })}>
|
||||
<div className="menu-container">
|
||||
<div className="menu-header">
|
||||
<Logo onClick={onLogoClick} />
|
||||
<Logotype height={34} className={"menu-logotype"} />
|
||||
<div className="menu-header-right">
|
||||
<div>
|
||||
<header>
|
||||
<Logo onClick={onLogoClick} width={30} />
|
||||
<Logotype height={34} />
|
||||
<div>
|
||||
<ExpandIcon onClick={onExpandMenu}></ExpandIcon>
|
||||
</div>
|
||||
{/* <span className="menu-separator">|</span>
|
||||
<span className="menu-name">Codex</span>
|
||||
<span className="menu-state">ALPHA {version}</span> */}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="menu-items">
|
||||
{items.map((item, index) => renderItem(item, index))}
|
||||
<div className="items">
|
||||
<Link to="/dashboard" activeOptions={{ exact: true }}>
|
||||
<span>
|
||||
<HomeIcon />
|
||||
</span>
|
||||
<span>Dashboard</span>
|
||||
</Link>
|
||||
<Link to="/dashboard/wallet">
|
||||
<span>
|
||||
<WalletIcon />
|
||||
</span>
|
||||
<span>Wallet</span>
|
||||
</Link>
|
||||
<Link to="/dashboard/files">
|
||||
<span>
|
||||
<FilesIcon />
|
||||
</span>
|
||||
<span>Files</span>
|
||||
</Link>
|
||||
<Link
|
||||
to="/dashboard/nodes"
|
||||
disabled={true}
|
||||
aria-disabled={true}
|
||||
data-title="Coming soon">
|
||||
<span>
|
||||
<NodesIcon variant="default" />
|
||||
</span>
|
||||
<span>Nodes</span>
|
||||
</Link>
|
||||
<Link
|
||||
to="/dashboard/analytics"
|
||||
disabled={true}
|
||||
aria-disabled={true}
|
||||
title="Coming soon"
|
||||
data-title="Coming soon">
|
||||
<span>
|
||||
<AnalyticsIcon />
|
||||
</span>
|
||||
<span>Analytics</span>
|
||||
</Link>
|
||||
<Link
|
||||
to="/dashboard/device"
|
||||
disabled={true}
|
||||
aria-disabled={true}
|
||||
title="Coming soon"
|
||||
data-title="Coming soon">
|
||||
<span>
|
||||
<DeviceIcon />
|
||||
</span>
|
||||
<span>Devices</span>
|
||||
</Link>
|
||||
<hr />
|
||||
<Link to="/dashboard/purchases">
|
||||
<span>
|
||||
<PurchaseIcon />
|
||||
</span>
|
||||
<span>Purchases</span>
|
||||
</Link>
|
||||
<Link to="/dashboard/availabilities">
|
||||
<span>
|
||||
<HostIcon />
|
||||
</span>
|
||||
<span>Host</span>
|
||||
</Link>
|
||||
<hr />
|
||||
<Link to="/dashboard/peers">
|
||||
<span>
|
||||
<PeersIcon />
|
||||
</span>
|
||||
<span>Peers</span>
|
||||
</Link>
|
||||
<Link to="/dashboard/logs">
|
||||
<span>
|
||||
<LogsIcon />
|
||||
</span>
|
||||
<span>Log</span>
|
||||
</Link>
|
||||
<section></section>
|
||||
<Link to="/dashboard/settings">
|
||||
<span>
|
||||
<SettingsIcon />
|
||||
</span>
|
||||
<span>Settings</span>
|
||||
</Link>
|
||||
<Link to="/dashboard/help">
|
||||
<span>
|
||||
<HelpIcon />
|
||||
</span>
|
||||
<span>Help</span>
|
||||
</Link>
|
||||
<Link to="/dashboard/disclaimer">
|
||||
<span>
|
||||
<DisclaimerIcon />
|
||||
</span>
|
||||
<span>Disclaimer</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 (
|
||||
<svg
|
||||
className={className}
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
fill="#none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.75 8.5H16L9.25 18.25V11.5H4L10.75 1.75V8.5Z" fill={color} />
|
||||
<path
|
||||
d="M10.75 8.5H16L9.25 18.25V11.5H4L10.75 1.75V8.5Z"
|
||||
fill={"#3EE089"}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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 (
|
||||
<>
|
||||
<div className="network-indicator">
|
||||
<div className="network-indicator-icon">
|
||||
<NodesIcon variant={codex.enabled ? "success" : "failure"} />
|
||||
</div>
|
||||
<span className="network-indicator-text">Node</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
.onboarding-healthCheckItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
@ -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 (
|
||||
<div
|
||||
data-testid="network"
|
||||
className={classnames(["onboarding-healthCheckItem"])}>
|
||||
<OnBoardingStatusIcon value={value} />
|
||||
<div>
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
.onboarding-image {
|
||||
position: absolute;
|
||||
right: -40px;
|
||||
max-height: 90%;
|
||||
width: auto;
|
||||
z-index: -1;
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
import "./OnBoardingImage.css";
|
||||
|
||||
export function OnBoardingImage() {
|
||||
return (
|
||||
<picture>
|
||||
<source
|
||||
srcSet="/img/onboarding@4x.webp 4x,
|
||||
/img/onboarding@3x.webp 3x,
|
||||
/img/onboarding@2x.webp 2x,
|
||||
/img/onboarding@1.5x.webp 1.5x,
|
||||
/img/onboarding.webp 1x"
|
||||
sizes="(max-width: 600px) 100vw,
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
type="image/webp"
|
||||
/>
|
||||
<source
|
||||
srcSet="/img/onboarding@4x.png 4x,
|
||||
/img/onboarding@3x.png 3x,
|
||||
/img/onboarding@2x.png 2x,
|
||||
/img/onboarding@1.5x.png 1.5x,
|
||||
/img/onboarding.png 1x"
|
||||
sizes="(max-width: 600px) 100vw,
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
type="image/png"
|
||||
/>
|
||||
<img
|
||||
src="/img/onboarding.png"
|
||||
alt="Onboarding Image"
|
||||
className="onboarding-image"
|
||||
/>
|
||||
</picture>
|
||||
);
|
||||
}
|
||||
217
src/components/OnBoarding/OnBoardingLayout.css
Normal file
217
src/components/OnBoarding/OnBoardingLayout.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
41
src/components/OnBoarding/OnBoardingLayout.tsx
Normal file
41
src/components/OnBoarding/OnBoardingLayout.tsx
Normal file
@ -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 (
|
||||
<div
|
||||
className={classnames(
|
||||
["onboarding"],
|
||||
["onboarding--first", step === 0],
|
||||
["onboarding--second", step === 1],
|
||||
["onboarding--third", step === 2]
|
||||
)}>
|
||||
<section>
|
||||
<div>
|
||||
<Logotype width={111} />
|
||||
</div>
|
||||
|
||||
{children}
|
||||
|
||||
<footer>
|
||||
<ul>
|
||||
<li {...attributes({ "aria-selected": step === 0 })}></li>
|
||||
<li {...attributes({ "aria-selected": step === 1 })}></li>
|
||||
<li {...attributes({ "aria-selected": step === 2 })}></li>
|
||||
</ul>
|
||||
</footer>
|
||||
</section>
|
||||
<BackgroundImage />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -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 <SuccessCheckIcon variant="primary"></SuccessCheckIcon>;
|
||||
|
||||
case "failure":
|
||||
return <ErrorCircleIcon />;
|
||||
|
||||
case "warning":
|
||||
return (
|
||||
<span className="onboarding-healthCheck-icon">
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM4.25 7.25V8.75H11.75V7.25H4.25Z"
|
||||
fill="#FF8447"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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 (
|
||||
<>
|
||||
<div className="index-column-section">
|
||||
<div>
|
||||
<AlphaIcon variant="error" />
|
||||
</div>
|
||||
<div className="index-alphaText">
|
||||
<p>
|
||||
<AlphaText variant="default"></AlphaText>
|
||||
</p>
|
||||
<p>
|
||||
<SimpleText className="index-version" variant="normal">
|
||||
{import.meta.env.PACKAGE_VERSION}
|
||||
</SimpleText>
|
||||
</p>
|
||||
<p>
|
||||
<SimpleText
|
||||
className="index-disclaimer"
|
||||
variant="error"
|
||||
onClick={onLegalDisclaimerOpen}>
|
||||
<a className="index-link">Legal Disclaimer</a>
|
||||
</SimpleText>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="index-column-section">
|
||||
<h3 className="index-mainTitle">
|
||||
Hello,
|
||||
<br /> Welcome to <span className="index-codex">Codex</span>{" "}
|
||||
<span className="index-vault">Vault</span>
|
||||
</h3>
|
||||
<p className="index-description">
|
||||
<SimpleText variant="light">
|
||||
Codex is a durable, decentralised data storage protocol, created so
|
||||
the world community can preserve its most important knowledge
|
||||
without risk of censorship.
|
||||
</SimpleText>
|
||||
</p>
|
||||
</div>
|
||||
<div className="index-column-section ">
|
||||
<SimpleText variant="primary">
|
||||
<a onClick={onNextStep} className="index-link index-getStarted">
|
||||
Let’s get started <ArrowRight></ArrowRight>
|
||||
</a>
|
||||
</SimpleText>
|
||||
|
||||
<Modal onClose={onLegalDisclaimerClose} open={modal}>
|
||||
<h1 className="disclaimer-title" style={{ marginTop: 0 }}>
|
||||
Disclaimer
|
||||
</h1>
|
||||
|
||||
<p className="disclaimer-text">
|
||||
The website and the content herein is not intended for public use
|
||||
and is for informational and demonstration purposes only.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p className="disclaimer-text">
|
||||
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.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p className="disclaimer-text">
|
||||
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.
|
||||
</p>
|
||||
</Modal>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -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 */
|
||||
}
|
||||
}
|
||||
@ -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<HTMLInputElement>) => {
|
||||
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<HTMLInputElement>) => {
|
||||
const [address] = Strings.splitURLAndPort(url);
|
||||
const element = e.currentTarget;
|
||||
const value = element.value;
|
||||
|
||||
setUrl(address + ":" + value);
|
||||
};
|
||||
|
||||
const onSave = () => {
|
||||
if (isInvalid === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
CodexSdk.updateURL(url)
|
||||
.then(() => queryClient.invalidateQueries())
|
||||
.then(() => codex.refetch());
|
||||
};
|
||||
|
||||
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 (
|
||||
<>
|
||||
<div className="index-column-section onboarding--deviceCheck-block">
|
||||
<div>
|
||||
<AlphaIcon variant="primary" />
|
||||
</div>
|
||||
<SimpleText variant="primary" className="onboarding-deviceCheck">
|
||||
<span>
|
||||
Connection /<br />
|
||||
Device Health Check
|
||||
</span>
|
||||
</SimpleText>
|
||||
</div>
|
||||
<div className="index-column-section">
|
||||
<h3 className="index-mainTitle onboarding-displayName">
|
||||
Nice to meet {displayName},<br />
|
||||
Let’s establish our connection.
|
||||
</h3>
|
||||
<div className="onboarding-group">
|
||||
<div className="onboarding-addressAndPort">
|
||||
<div className="onboarding-addressContainer">
|
||||
<Input
|
||||
onPaste={onPaste}
|
||||
id="url"
|
||||
type="url"
|
||||
label="Address"
|
||||
isInvalid={isInvalid}
|
||||
onChange={onAddressChange}
|
||||
value={address}
|
||||
placeholder="127.0.0.1"></Input>
|
||||
{isInvalid ? (
|
||||
<ErrorCircleIcon className="onboarding--addressSuccessIcon" />
|
||||
) : (
|
||||
<SuccessCheckIcon
|
||||
className="onboarding--addressSuccessIcon"
|
||||
variant="default"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="onboarding-portContainer">
|
||||
<Input
|
||||
inputClassName="onboarding-port"
|
||||
id="port"
|
||||
label="Port"
|
||||
type="number"
|
||||
onChange={onPortChange}
|
||||
value={port}
|
||||
placeholder="8080"></Input>
|
||||
<SuccessCheckIcon
|
||||
className="onboarding--addressSuccessIcon"
|
||||
variant="default"></SuccessCheckIcon>
|
||||
</div>
|
||||
|
||||
<RefreshIcon
|
||||
onClick={onSave}
|
||||
className={classnames(
|
||||
["onboarding-refresh"],
|
||||
[
|
||||
"onboarding-refresh--fetching",
|
||||
portForwarding.isFetching || codex.isPending,
|
||||
]
|
||||
)}></RefreshIcon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="onboarding-portForwardingHelp">
|
||||
<li>Port forwarding should be default {forwardingPortValue}.</li>
|
||||
</ul>
|
||||
|
||||
<div className="onboarding-healthChecks">
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<span className="onboarding-healthCheck-icon">
|
||||
<HealthCheckIcon />
|
||||
</span>
|
||||
|
||||
<span className="onboarding-healthCheck-itemText">
|
||||
Health Check
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={online ? "success" : "failure"}
|
||||
text="Internet connection"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={portForwarding.enabled ? "success" : "failure"}
|
||||
text="Port forwarding"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={codex.isSuccess ? "success" : "failure"}
|
||||
text="Codex connection"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={codex.isSuccess ? "success" : "warning"}
|
||||
text="Marketplace"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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<HTMLInputElement>) => {
|
||||
const value = e.currentTarget.value;
|
||||
OnBoardingUtils.setDisplayName(value);
|
||||
setDisplayName(value);
|
||||
onStepValid(!!value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="index-column-section onboarding--personalization-block">
|
||||
<div>
|
||||
<AlphaIcon variant="primary" />
|
||||
</div>
|
||||
<SimpleText variant="primary" className="onboarding-personalization">
|
||||
<span>Personalization</span>
|
||||
</SimpleText>
|
||||
</div>
|
||||
<div className="index-column-section">
|
||||
<h3 className="index-mainTitle">
|
||||
Let’s get you setup. <br />
|
||||
What do you want to be called?
|
||||
</h3>
|
||||
<div className="index-displayName">
|
||||
<Input
|
||||
onChange={onDisplayNameChange}
|
||||
label="Display name"
|
||||
id="displayName"
|
||||
value={displayName}></Input>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -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 (
|
||||
<div className="page">
|
||||
<Menu
|
||||
expanded={open}
|
||||
onClose={onClose}
|
||||
items={items}
|
||||
version={version}></Menu>
|
||||
|
||||
<main className="page-main">
|
||||
<AppBar onExpand={onExpand} />
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
.page {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.page-main {
|
||||
flex: 1;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ export function PeerCountryCell({ address, onPinAdd }: Props) {
|
||||
|
||||
return (
|
||||
<Cell>
|
||||
<div className="peerCountry">
|
||||
<div className="peer-country">
|
||||
{data ? (
|
||||
<>
|
||||
<span> {!!data && getFlagEmoji(data.country_iso)}</span>
|
||||
|
||||
@ -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 (
|
||||
<span className={"onboarding-healthCheck-icon " + className}>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM7.25225 11L12.5548 5.69675L11.4943 4.63625L7.25225 8.879L5.1305 6.75725L4.07 7.81775L7.25225 11Z"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM7.25225 11L12.5548 5.69675L11.4943 4.63625L7.25225 8.879L5.1305 6.75725L4.07 7.81775L7.25225 11Z"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { SimpleText } from "@codex-storage/marketplace-ui-components";
|
||||
import { CircleCheck } from "lucide-react";
|
||||
|
||||
export function SuccessIcon() {
|
||||
return (
|
||||
<SimpleText variant="primary">
|
||||
<span className="text--primary">
|
||||
<CircleCheck size="4rem" className="successIcon" />
|
||||
</SimpleText>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
22
src/components/UserInfo/UserInfo.css
Normal file
22
src/components/UserInfo/UserInfo.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/components/UserInfo/UserInfo.tsx
Normal file
80
src/components/UserInfo/UserInfo.tsx
Normal file
@ -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<HTMLInputElement>) => {
|
||||
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 (
|
||||
<div className="user-info">
|
||||
<div className="emoji">
|
||||
{areEmojiVisible && (
|
||||
<EmojiPicker
|
||||
emojiStyle={EmojiStyle.NATIVE}
|
||||
theme={Theme.DARK}
|
||||
lazyLoadEmojis={true}
|
||||
onEmojiClick={onEmojiClick}
|
||||
categories={
|
||||
[
|
||||
"smileys_people",
|
||||
"animals_nature",
|
||||
"food_drink",
|
||||
"travel_places",
|
||||
"activities",
|
||||
"objects",
|
||||
"symbols",
|
||||
"flags",
|
||||
] as any
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
<Input
|
||||
onChange={onDisplayNameChange}
|
||||
onClick={onDisplayEmoji}
|
||||
label="Account Emoji"
|
||||
readOnly={true}
|
||||
id="emoji"
|
||||
value={emoji}></Input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
onChange={onDisplayNameChange}
|
||||
label="Preferred name"
|
||||
id="displayName"
|
||||
value={displayName}></Input>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
34
src/components/Versions/Versions.css
Normal file
34
src/components/Versions/Versions.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/components/Versions/Versions.tsx
Normal file
28
src/components/Versions/Versions.tsx
Normal file
@ -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 (
|
||||
<div className="versions">
|
||||
<AlphaIcon variant="error" />
|
||||
<div>
|
||||
<p>Client</p>
|
||||
<small>VER. {version}</small>
|
||||
</div>
|
||||
<div>
|
||||
<p>Vault</p>
|
||||
<small>VER. {import.meta.env.PACKAGE_VERSION}</small>
|
||||
<AlphaText variant="failure" width={37}></AlphaText>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
15
src/components/WarningIcon/WarningIcon.tsx
Normal file
15
src/components/WarningIcon/WarningIcon.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
export function WarningIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM4.25 7.25V8.75H11.75V7.25H4.25Z"
|
||||
fill="#FF8447"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@ -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() {
|
||||
<div className="welcome">
|
||||
<p className="welcome-title">Welcome to Codex Marketplace</p>
|
||||
<div className="welcome-body">
|
||||
<SimpleText variant="light">
|
||||
<span>
|
||||
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!
|
||||
</SimpleText>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Link to="/dashboard/help" className="welcome-link">
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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) => (
|
||||
<Link to="/dashboard" activeOptions={{ exact: true }} {...p}>
|
||||
<span className="menu-icon">
|
||||
<HomeIcon />
|
||||
</span>
|
||||
<span className="menu-text">Dashboard</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link to="/dashboard/wallet" {...p}>
|
||||
<span className="menu-icon">
|
||||
<WalletIcon />
|
||||
</span>
|
||||
<span className="menu-text">Wallet</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link to="/dashboard/files" {...p}>
|
||||
<span className="menu-icon">
|
||||
<FilesIcon />
|
||||
</span>
|
||||
<span className="menu-text">Files</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link
|
||||
to="/dashboard/nodes"
|
||||
{...p}
|
||||
disabled={true}
|
||||
aria-disabled={true}
|
||||
data-title="Coming soon">
|
||||
<span className="menu-icon">
|
||||
<NodesIcon variant="default" />
|
||||
</span>
|
||||
<span className="menu-text">Nodes</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link
|
||||
to="/dashboard/analytics"
|
||||
{...p}
|
||||
disabled={true}
|
||||
aria-disabled={true}
|
||||
title="Coming soon"
|
||||
data-title="Coming soon">
|
||||
<span className="menu-icon">
|
||||
<AnalyticsIcon />
|
||||
</span>
|
||||
<span className="menu-text">Analytics</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link
|
||||
to="/dashboard/device"
|
||||
{...p}
|
||||
disabled={true}
|
||||
aria-disabled={true}
|
||||
title="Coming soon"
|
||||
data-title="Coming soon">
|
||||
<span className="menu-icon">
|
||||
<DeviceIcon />
|
||||
</span>
|
||||
<span className="menu-text">Devices</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "separator",
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link to="/dashboard/purchases" {...p}>
|
||||
<span className="menu-icon">
|
||||
<PurchaseIcon />
|
||||
</span>
|
||||
<span className="menu-text">Purchases</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link to="/dashboard/availabilities" {...p}>
|
||||
<span className="menu-icon">
|
||||
<HostIcon />
|
||||
</span>
|
||||
<span className="menu-text">Host</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "separator",
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link to="/dashboard/peers" {...p}>
|
||||
<span className="menu-icon">
|
||||
<PeersIcon />
|
||||
</span>
|
||||
<span className="menu-text">Peers</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link to="/dashboard/logs" {...p}>
|
||||
<span className="menu-icon">
|
||||
<LogsIcon />
|
||||
</span>
|
||||
<span className="menu-text">Log</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "space",
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link to="/dashboard/settings" {...p}>
|
||||
<span className="menu-icon">
|
||||
<SettingsIcon />
|
||||
</span>
|
||||
<span className="menu-text">Settings</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link to="/dashboard/help" {...p}>
|
||||
<span className="menu-icon">
|
||||
<HelpIcon />
|
||||
</span>
|
||||
<span className="menu-text">Help</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
Component: (p: MenuItemComponentProps) => (
|
||||
<Link to="/dashboard/disclaimer" {...p}>
|
||||
<span className="menu-icon">
|
||||
<DisclaimerIcon />
|
||||
</span>
|
||||
<span className="menu-text">Disclaimer</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
] satisfies MenuItem[];
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const onClose = () => setOpen(false);
|
||||
|
||||
const onExpand = () => setOpen(true);
|
||||
|
||||
return (
|
||||
<Page
|
||||
children={<Outlet />}
|
||||
items={items}
|
||||
version={import.meta.env.PACKAGE_VERSION}
|
||||
/>
|
||||
<div className="layout">
|
||||
<Menu expanded={open} onClose={onClose}></Menu>
|
||||
|
||||
<main>
|
||||
<AppBar onExpand={onExpand} />
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 (
|
||||
<>
|
||||
<div className="dashboard-welcomeContainer">
|
||||
<div className="dashboard-welcome-avatarContainer">
|
||||
<picture>
|
||||
<source
|
||||
srcSet="/img/avatar@4x.webp 4x,
|
||||
/img/avatar@3x.webp 3x,
|
||||
/img/avatar@2x.webp 2x,
|
||||
/img/avatar@1.5x.webp 1.5x,
|
||||
/img/avatar.webp 1x"
|
||||
sizes="(max-width: 600px) 100vw,
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
type="image/webp"
|
||||
/>
|
||||
<source
|
||||
srcSet="/img/avatar@4x.png 4x,
|
||||
/img/avatar@3x.png 3x,
|
||||
/img/avatar@2x.png 2x,
|
||||
/img/avatar@1.5x.png 1.5x,
|
||||
/img/avatar.png 1x"
|
||||
sizes="(max-width: 600px) 100vw,
|
||||
(max-width: 1200px) 50vw,
|
||||
33vw"
|
||||
type="image/png"
|
||||
/>
|
||||
<img src="/img/avatar.png" height={52} width={52} alt="Avatar" />
|
||||
</picture>
|
||||
<div>
|
||||
<div className="dashboard-welcome-avatarTitle">Welcome back,</div>
|
||||
<div className="dashboard-welcome-avatarSubtitle">{username}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dashboard-welcome-versions">
|
||||
<AlphaIcon variant="error" className="dashboard-welcome-alpha" />
|
||||
<div className="dashboard-welcome-versionContainer">
|
||||
<p className="dashboard-welcome-versionTitle">Client</p>
|
||||
<p className="dashboard-welcome-versionValue">VER. {version}</p>
|
||||
</div>
|
||||
<div className="dashboard-welcome-versionContainer">
|
||||
<p className="dashboard-welcome-versionTitle">Vault</p>
|
||||
<p className="dashboard-welcome-versionValue">
|
||||
VER. {import.meta.env.PACKAGE_VERSION}
|
||||
</p>
|
||||
<AlphaText
|
||||
variant="failure"
|
||||
className="dashboard-welcome-alphaText"
|
||||
width={37}></AlphaText>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dashboard">
|
||||
<div className="row gap">
|
||||
<div className="emoji">{emoji}</div>
|
||||
<div>
|
||||
<h3>Welcome back,</h3>
|
||||
<h4>{username}</h4>
|
||||
</div>
|
||||
</div>
|
||||
<Versions />
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<ErrorBoundary
|
||||
fallback={({ error }) => (
|
||||
|
||||
@ -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 */
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 = () => {
|
||||
<Cell>{node.peerId}</Cell>,
|
||||
<Cell>
|
||||
{node.seen ? (
|
||||
<div className="networkIndicator-point networkIndicator-point--online"></div>
|
||||
<div className="status--active">
|
||||
<SuccessCheckIcon variant="primary"></SuccessCheckIcon> Active
|
||||
</div>
|
||||
) : (
|
||||
<div className="networkIndicator-point networkIndicator-point--offline"></div>
|
||||
<div className="status--inactive">
|
||||
<ErrorCircleIcon></ErrorCircleIcon> Inactive
|
||||
</div>
|
||||
)}
|
||||
</Cell>,
|
||||
]}></Row>
|
||||
)) || [];
|
||||
|
||||
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 (
|
||||
<div className="peers">
|
||||
{/* <img
|
||||
src={`data:image/svg+xml;utf8,${encodeURIComponent(svgMap)}`}
|
||||
className="peers-map"
|
||||
/> */}
|
||||
<div>
|
||||
<ul>
|
||||
<li>Legend</li>
|
||||
<li>1-3</li>
|
||||
<li>3-5</li>
|
||||
<li>5 +</li>
|
||||
</ul>
|
||||
<div className="connections">
|
||||
<header>
|
||||
<PeersIcon></PeersIcon>
|
||||
<span>Connections</span>
|
||||
</header>
|
||||
<main style={styles}>
|
||||
<div>
|
||||
<div></div>
|
||||
<span>{actives}</span>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
<SuccessCheckIcon variant="primary"></SuccessCheckIcon>{" "}
|
||||
<span>Peer connections in Good standing. </span>
|
||||
</footer>
|
||||
</div>
|
||||
<div dangerouslySetInnerHTML={{ __html: svgMap }}></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="peers-map"
|
||||
dangerouslySetInnerHTML={{ __html: svgMap }}></div>
|
||||
|
||||
<Table headers={headers} rows={rows} className="peers-table" />
|
||||
<Table headers={headers} rows={rows} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
} */
|
||||
|
||||
@ -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: () => (
|
||||
<>
|
||||
<div className="settings">
|
||||
<div className="settings">
|
||||
<header>
|
||||
<div className="row gap">
|
||||
<Logo height={48}></Logo>
|
||||
<Logotype height={46}></Logotype>
|
||||
</div>
|
||||
<Versions></Versions>
|
||||
</header>
|
||||
<main>
|
||||
<h3>Personalization</h3>
|
||||
<ErrorBoundary
|
||||
fallback={({ error }) => (
|
||||
<ErrorPlaceholder
|
||||
@ -18,11 +28,25 @@ export const Route = createFileRoute("/dashboard/settings")({
|
||||
subtitle="Cannot retrieve the data."
|
||||
/>
|
||||
)}>
|
||||
<LogLevel />
|
||||
<UserInfo />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
|
||||
<div className="settings">
|
||||
<h3>Connection</h3>
|
||||
|
||||
<ErrorBoundary
|
||||
fallback={({ error }) => (
|
||||
<ErrorPlaceholder
|
||||
error={error}
|
||||
subtitle="Cannot retrieve the data."
|
||||
/>
|
||||
)}>
|
||||
<HealthChecks online={true} onStepValid={() => {}} />
|
||||
</ErrorBoundary>
|
||||
</main>
|
||||
|
||||
<BackgroundImage />
|
||||
|
||||
{/* <div className="settings">
|
||||
<ErrorBoundary
|
||||
fallback={({ error }) => (
|
||||
<ErrorPlaceholder
|
||||
@ -57,7 +81,7 @@ export const Route = createFileRoute("/dashboard/settings")({
|
||||
}}>
|
||||
<Debug />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* <div className="input-floating">
|
||||
<input
|
||||
@ -83,6 +107,6 @@ export const Route = createFileRoute("/dashboard/settings")({
|
||||
Floating
|
||||
</label>
|
||||
</div> */}
|
||||
</>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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 = [
|
||||
<OnBoardingStepOne onNextStep={onNextStep} />,
|
||||
<OnBoardingStepTwo onStepValid={onStepValid} />,
|
||||
<OnBoardingStepThree online={online} onStepValid={onStepValid} />,
|
||||
];
|
||||
|
||||
// const text = online ? "Network connected" : "Network disconnected";
|
||||
const onNextStep = () => navigate({ to: "/onboarding-name" });
|
||||
|
||||
return (
|
||||
<div className="index">
|
||||
<div className="index-container">
|
||||
<div className="index-column">
|
||||
<div className="index-column-section">
|
||||
<Logotype width={111} />
|
||||
</div>
|
||||
|
||||
{components[step]}
|
||||
|
||||
<div className=" ">
|
||||
<div className="index-dots">
|
||||
<span
|
||||
className={classnames(
|
||||
["index-dot"],
|
||||
["index-dot--active", step === 0]
|
||||
)}></span>
|
||||
<span
|
||||
className={classnames(
|
||||
["index-dot"],
|
||||
["index-dot--active", step === 1]
|
||||
)}></span>
|
||||
<span
|
||||
className={classnames(
|
||||
["index-dot"],
|
||||
["index-dot--active", step === 2]
|
||||
)}></span>
|
||||
<>
|
||||
<OnBoardingLayout defaultIsStepValid={false} step={0}>
|
||||
<>
|
||||
<section className="alpha">
|
||||
<AlphaIcon variant="error" />
|
||||
<div>
|
||||
<AlphaText variant="default"></AlphaText>
|
||||
<b>{import.meta.env.PACKAGE_VERSION}</b>
|
||||
<a onClick={onLegalDisclaimerOpen}>Legal Disclaimer</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="index-column">
|
||||
<OnBoardingImage />
|
||||
</div>
|
||||
<div className="index-columnRight">
|
||||
<div></div>
|
||||
{/* <div className="index-logo">
|
||||
<div className="index-network">
|
||||
<p className="index-network-text">{text}</p>
|
||||
<NetworkIcon active={online}></NetworkIcon>
|
||||
</div>
|
||||
<CodexLogo></CodexLogo>
|
||||
</div> */}
|
||||
<a
|
||||
className="index-link2"
|
||||
{...attributes({ "aria-disabled": !isStepValid })}
|
||||
onClick={onNextStep}>
|
||||
</section>
|
||||
<section className="main">
|
||||
<h1>
|
||||
Hello,
|
||||
<br /> Welcome to <b>Codex</b> <b>Vault</b>
|
||||
</h1>
|
||||
<p>
|
||||
Codex is a durable, decentralised data storage protocol, created
|
||||
so the world community can preserve its most important knowledge
|
||||
without risk of censorship.
|
||||
</p>
|
||||
</section>
|
||||
<section className="get-started">
|
||||
<a onClick={onNextStep}>
|
||||
Let’s get started <ArrowRight></ArrowRight>
|
||||
</a>
|
||||
|
||||
<Modal onClose={onLegalDisclaimerClose} open={modal}>
|
||||
<h1>Disclaimer</h1>
|
||||
|
||||
<p>
|
||||
The website and the content herein is not intended for public
|
||||
use and is for informational and demonstration purposes only.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</Modal>
|
||||
</section>
|
||||
<a className="navigation" onClick={onNextStep}>
|
||||
<ArrowRightCircle></ArrowRightCircle>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</OnBoardingLayout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
61
src/routes/onboarding-checks.tsx
Normal file
61
src/routes/onboarding-checks.tsx
Normal file
@ -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 (
|
||||
<OnBoardingLayout defaultIsStepValid={false} step={2}>
|
||||
<>
|
||||
<section className="alpha">
|
||||
<div>
|
||||
<AlphaIcon variant="primary" />
|
||||
</div>
|
||||
<p>
|
||||
Connection /<br />
|
||||
Device Health Check
|
||||
</p>
|
||||
</section>
|
||||
<section className="main">
|
||||
<h1>
|
||||
Nice to meet {displayName},<br />
|
||||
Let’s establish our connection.
|
||||
</h1>
|
||||
|
||||
<HealthChecks
|
||||
online={online}
|
||||
onStepValid={onStepValid}></HealthChecks>
|
||||
</section>
|
||||
|
||||
<a
|
||||
className="navigation"
|
||||
onClick={onNextStep}
|
||||
{...attributes({ "aria-disabled": !isStepValid })}>
|
||||
<ArrowRightCircle></ArrowRightCircle>
|
||||
</a>
|
||||
</>
|
||||
</OnBoardingLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute("/onboarding-checks")({
|
||||
component: OnBoardingChecks,
|
||||
});
|
||||
55
src/routes/onboarding-name.tsx
Normal file
55
src/routes/onboarding-name.tsx
Normal file
@ -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 (
|
||||
<OnBoardingLayout defaultIsStepValid={false} step={1}>
|
||||
<>
|
||||
<section className="alpha">
|
||||
<div>
|
||||
<AlphaIcon variant="primary" />
|
||||
</div>
|
||||
<p>Personalization</p>
|
||||
</section>
|
||||
<section className="main">
|
||||
<h1>
|
||||
Let’s get you setup. <br />
|
||||
What do you want to be called?
|
||||
</h1>
|
||||
|
||||
<UserInfo onNameChange={onNameChange} />
|
||||
</section>
|
||||
|
||||
<a
|
||||
className="navigation"
|
||||
onClick={onNextStep}
|
||||
{...attributes({ "aria-disabled": !isStepValid })}>
|
||||
<ArrowRightCircle></ArrowRightCircle>
|
||||
</a>
|
||||
</>
|
||||
</OnBoardingLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute("/onboarding-name")({
|
||||
component: OnBoardingName,
|
||||
});
|
||||
@ -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") || "🤖"
|
||||
},
|
||||
}
|
||||
@ -19,7 +19,13 @@ export default defineConfig({
|
||||
|
||||
defaultHandler(warning);
|
||||
},
|
||||
output: {
|
||||
manualChunks: {
|
||||
"emoji-picker-react": ["emoji-picker-react"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user