ui updates
279
static/frontend/package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.7",
|
||||
"axios": "^1.9.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"tailwindcss": "^4.1.7"
|
||||
@@ -1683,6 +1684,23 @@
|
||||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
|
||||
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -1734,6 +1752,19 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -1811,6 +1842,18 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@@ -1872,6 +1915,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
||||
@@ -1881,6 +1933,20 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.155",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
|
||||
@@ -1901,6 +1967,51 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz",
|
||||
@@ -2228,6 +2339,41 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
||||
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@@ -2242,6 +2388,15 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@@ -2252,6 +2407,43 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
@@ -2278,6 +2470,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
@@ -2294,6 +2498,45 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
@@ -2731,6 +2974,36 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@@ -2958,6 +3231,12 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --host",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.7",
|
||||
"axios": "^1.9.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"tailwindcss": "^4.1.7"
|
||||
|
||||
BIN
static/frontend/public/icons/cloud.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
static/frontend/public/icons/mini/arrow-up-right-from-square.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
static/frontend/public/icons/mini/chart-line-up.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
static/frontend/public/icons/mini/clock-five.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
static/frontend/public/icons/mini/settings.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
static/frontend/public/icons/mini/wifi.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
16
static/frontend/public/icons/steam-svgrepo-com.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.56967 20.0269C4.30041 25.7964 9.65423 30 15.9906 30C23.7274 30 29.9995 23.7318 29.9995 16C29.9995 8.26803 23.7274 2 15.9906 2C8.56634 2 2.49151 7.77172 2.01172 15.0699C2.01172 17.1667 2.01172 18.0417 2.56967 20.0269Z" fill="url(#paint0_linear_87_8314)"/>
|
||||
<path d="M15.2706 12.5629L11.8426 17.5395C11.0345 17.5028 10.221 17.7314 9.54572 18.1752L2.01829 15.0784C2.01829 15.0784 1.84411 17.9421 2.56999 20.0763L7.89147 22.2707C8.15866 23.464 8.97779 24.5107 10.1863 25.0142C12.1635 25.8398 14.4433 24.8988 15.2658 22.922C15.4799 22.4052 15.5797 21.8633 15.5652 21.3225L20.5904 17.8219C23.5257 17.8219 25.9114 15.4305 25.9114 12.4937C25.9114 9.55673 23.5257 7.16748 20.5904 7.16748C17.7553 7.16748 15.1117 9.64126 15.2706 12.5629ZM14.4469 22.5783C13.8103 24.1057 12.054 24.8303 10.5273 24.1946C9.82302 23.9014 9.29128 23.3642 8.98452 22.7237L10.7167 23.4411C11.8426 23.9098 13.1343 23.3762 13.6023 22.2514C14.0718 21.1254 13.5392 19.8324 12.4139 19.3637L10.6233 18.6222C11.3142 18.3603 12.0997 18.3507 12.8336 18.6559C13.5734 18.9635 14.1475 19.5428 14.4517 20.283C14.756 21.0233 14.7548 21.8404 14.4469 22.5783ZM20.5904 16.0434C18.6364 16.0434 17.0455 14.4511 17.0455 12.4937C17.0455 10.5379 18.6364 8.94518 20.5904 8.94518C22.5457 8.94518 24.1365 10.5379 24.1365 12.4937C24.1365 14.4511 22.5457 16.0434 20.5904 16.0434ZM17.9341 12.4883C17.9341 11.0159 19.127 9.82159 20.5964 9.82159C22.0671 9.82159 23.2599 11.0159 23.2599 12.4883C23.2599 13.9609 22.0671 15.1541 20.5964 15.1541C19.127 15.1541 17.9341 13.9609 17.9341 12.4883Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_87_8314" x1="16.0056" y1="2" x2="16.0056" y2="30" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#111D2E"/>
|
||||
<stop offset="0.21248" stop-color="#051839"/>
|
||||
<stop offset="0.40695" stop-color="#0A1B48"/>
|
||||
<stop offset="0.5811" stop-color="#132E62"/>
|
||||
<stop offset="0.7376" stop-color="#144B7E"/>
|
||||
<stop offset="0.87279" stop-color="#136497"/>
|
||||
<stop offset="1" stop-color="#1387B8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 240 240" id="svg2" xmlns="http://www.w3.org/2000/svg"><style>.st0{fill:url(#path2995-1-0_1_)}.st1{fill:#c8daea}.st2{fill:#a9c9dd}.st3{fill:url(#path2991_1_)}</style><linearGradient id="path2995-1-0_1_" gradientUnits="userSpaceOnUse" x1="-683.305" y1="534.845" x2="-693.305" y2="511.512" gradientTransform="matrix(6 0 0 -6 4255 3247)"><stop offset="0" stop-color="#37aee2"/><stop offset="1" stop-color="#1e96c8"/></linearGradient><path id="path2995-1-0" class="st0" d="M240 120c0 66.3-53.7 120-120 120S0 186.3 0 120 53.7 0 120 0s120 53.7 120 120z"/><path id="path2993" class="st1" d="M98 175c-3.9 0-3.2-1.5-4.6-5.2L82 132.2 152.8 88l8.3 2.2-6.9 18.8L98 175z"/><path id="path2989" class="st2" d="M98 175c3 0 4.3-1.4 6-3 2.6-2.5 36-35 36-35l-20.5-5-19 12-2.5 30v1z"/><linearGradient id="path2991_1_" gradientUnits="userSpaceOnUse" x1="128.991" y1="118.245" x2="153.991" y2="78.245" gradientTransform="matrix(1 0 0 -1 0 242)"><stop offset="0" stop-color="#eff7fc"/><stop offset="1" stop-color="#ffffff"/></linearGradient><path id="path2991" class="st3" d="M100 144.4l48.4 35.7c5.5 3 9.5 1.5 10.9-5.1L179 82.2c2-8.1-3.1-11.7-8.4-9.3L55 117.5c-7.9 3.2-7.8 7.6-1.4 9.5l29.7 9.3L152 93c3.2-2 6.2-.9 3.8 1.3L100 144.4z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
BIN
static/frontend/public/icons/vpn.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
43
static/frontend/src/Api.jsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const API_URL = "http://192.168.2.151:8000"
|
||||
|
||||
|
||||
const createSha256Hash = async (input) => {
|
||||
try {
|
||||
// Convert string to array buffer
|
||||
const msgBuffer = new TextEncoder().encode(input);
|
||||
|
||||
// Generate hash
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
|
||||
|
||||
// Convert buffer to hex string
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
||||
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
|
||||
return hashHex;
|
||||
} catch (error) {
|
||||
console.error('Error creating SHA-256 hash:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const sendRequest = async (action,args) => {
|
||||
try {
|
||||
const TS = Math.round(Date.now()/1000);
|
||||
const hash = await createSha256Hash("<password>"+JSON.stringify(args)+TS);
|
||||
const response = await axios.post(API_URL+"/action/"+action, {args:args,hash:hash+"."+TS}, {
|
||||
timeout: 4000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error in POST request:', error);
|
||||
throw error.response?.data || error.message;
|
||||
}
|
||||
};
|
||||
|
||||
export default sendRequest;
|
||||
@@ -13,6 +13,27 @@
|
||||
box-shadow: 0 6px 12px rgba(147, 51, 234, 0.3);
|
||||
}
|
||||
|
||||
@keyframes flipRight {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
.glass:hover>.absolute>img {
|
||||
animation: flipRight 0.5s forwards;
|
||||
animation-delay: 0s;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.glass_enabled {
|
||||
background: rgba(30, 240, 135, 0.2);
|
||||
border-color: rgba(51, 234, 127, 0.7);
|
||||
@@ -42,7 +63,7 @@
|
||||
|
||||
.gauge span {
|
||||
color: #d8b4fe;
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -1,226 +1,266 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import './App.css'
|
||||
import sendRequest from "./Api.jsx";
|
||||
|
||||
|
||||
window.sendRequest = sendRequest;
|
||||
const TextTile = ({ label }) => (
|
||||
<div className="tile-content">
|
||||
<span className="text-purple-400 text-lg font-semibold">{label}</span>
|
||||
</div>
|
||||
<div className="tile-content">
|
||||
<span className="text-purple-400 text-lg font-semibold">{label}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ImageTile = ({ imageUrl, label, enabled, toggleEnabled }) => (
|
||||
<div className="tile-content cursor-pointer" onClick={toggleEnabled}>
|
||||
<img src={imageUrl} className="rounded-lg w-[6.3rem] h-[6.3rem] object-cover" alt={label} />
|
||||
<span className="text-purple-400 text-lg font-semibold">{label}</span>
|
||||
</div>
|
||||
<div className="tile-content cursor-pointer">
|
||||
<img src={imageUrl} className="rounded-lg w-[6.3rem] h-[6.3rem] object-cover" alt={label} />
|
||||
<span className="text-purple-400 text-lg font-semibold mt-2">{label}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ClockTile = ({ id }) => {
|
||||
const [time, setTime] = useState("");
|
||||
const [date, setDate] = useState("");
|
||||
const [time, setTime] = useState("");
|
||||
const [date, setDate] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const updateClock = () => {
|
||||
const now = new Date();
|
||||
const time = now.toLocaleTimeString('ru-RU', { hour12: false });
|
||||
const options = { weekday: 'short', day: 'numeric', month: 'long', year: 'numeric' };
|
||||
const date = now.toLocaleDateString('ru-RU', options)
|
||||
.replace(/,/, '')
|
||||
.replace(/(\d+)\s(\S+)/, '$1 $2')
|
||||
.replace(/г\./, '');
|
||||
setTime(time);
|
||||
setDate(date.charAt(0).toUpperCase() + date.slice(1));
|
||||
};
|
||||
updateClock();
|
||||
const interval = setInterval(updateClock, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const updateClock = () => {
|
||||
const now = new Date();
|
||||
const time = now.toLocaleTimeString('ru-RU', { hour12: false });
|
||||
const options = { weekday: 'short', day: 'numeric', month: 'long', year: 'numeric' };
|
||||
const date = now.toLocaleDateString('ru-RU', options)
|
||||
.replace(/,/, '')
|
||||
.replace(/(\d+)\s(\S+)/, '$1 $2')
|
||||
.replace(/г\./, '');
|
||||
setTime(time);
|
||||
setDate(date.charAt(0).toUpperCase() + date.slice(1));
|
||||
};
|
||||
updateClock();
|
||||
const interval = setInterval(updateClock, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="tile-content">
|
||||
<span className="text-5xl font-bold text-purple-200">{time}</span>
|
||||
<span className="text-purple-400 text-lg font-semibold">{date}</span>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="tile-content">
|
||||
<span className="text-5xl font-bold text-purple-200">{time}</span>
|
||||
<span className="text-purple-400 text-lg font-semibold mt-2">{date}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const GaugeTile = ({ gauges }) => (
|
||||
<div className="tile-content gauge-tile-content">
|
||||
<div className="flex flex-col gap-4">
|
||||
{gauges.map((g, index) => (
|
||||
<div key={index} className="flex items-center gap-4">
|
||||
<div className="gauge" style={{ "--value": `${g.value}deg` }}>
|
||||
<span>{Math.round((g.value / 360) * 100)}%</span>
|
||||
</div>
|
||||
<span className="text-purple-400 text-lg font-semibold">{g.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="tile-content gauge-tile-content">
|
||||
<div className="flex flex-col gap-4">
|
||||
{gauges.map((g, index) => (
|
||||
<div key={index} className="flex items-center gap-4">
|
||||
<div className="gauge" style={{ "--value": `${g.value}deg` }}>
|
||||
<span className='font-bold'>{Math.round((g.value / 360) * 100)}%</span>
|
||||
</div>
|
||||
<span className="text-purple-400 text-lg font-semibold">{g.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const SliderTile = ({ sliders, tileId, updateSliderValue }) => {
|
||||
return (
|
||||
<div className="tile-content">
|
||||
<span className="text-purple-400 text-lg font-semibold">Slider Controls</span>
|
||||
<div className="flex flex-col w-full gap-4">
|
||||
{sliders.map((s, index) => (
|
||||
<div key={index} className="flex flex-col items-start">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-purple-400 text-sm font-semibold">{s.label}</span>
|
||||
<span className="text-purple-300 text-sm font-normal">{s.value}</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
value={s.value}
|
||||
onChange={(e) => updateSliderValue(tileId, index, e.target.value)}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const SliderTile = ({ sliders, tileId, updateSliderValue, label }) => {
|
||||
return (
|
||||
<div className="tile-content">
|
||||
<span className="text-purple-400 text-lg font-semibold">{label}</span>
|
||||
<div className="flex flex-col w-full gap-4">
|
||||
{sliders.map((s, index) => (
|
||||
<div key={index} className="flex flex-col items-start">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-purple-400 text-sm font-semibold">{s.label}</span>
|
||||
<span className="text-purple-300 text-sm font-normal">{s.value}</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
value={s.value}
|
||||
onChange={(e) => updateSliderValue(tileId, index, e.target.value)}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const NumberTile = ({ value, label }) => (
|
||||
<div className="tile-content">
|
||||
<span className="text-5xl font-bold text-purple-200">{value}</span>
|
||||
<span className="text-purple-400 text-lg font-semibold">{label}</span>
|
||||
</div>
|
||||
<div className="tile-content">
|
||||
<span className="text-5xl font-bold text-purple-200">{value}</span>
|
||||
<span className="text-purple-400 text-lg font-semibold mt-2">{label}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
const App = () => {
|
||||
const [tiles, setTiles] = useState([
|
||||
{ id: 1, type: "text", label: "Tile 1" },
|
||||
{ id: 2, type: "gauge", gauges: [{ value: 75, label: "Metric 1" }, { value: 120, label: "Metric 2" }] },
|
||||
{ id: 3, type: "image", imageUrl: "https://cloud.viadev.su/storage/branding_media/d076be24-e369-41ca-9b24-5f94efc5ecfd.png", label: "Image 1", enabled: true },
|
||||
{ id: 4, type: "break" },
|
||||
{ id: 5, type: "text", label: "Tile 4" },
|
||||
{ id: 6, type: "image", imageUrl: "https://cloud.viadev.su/storage/branding_media/d076be24-e369-41ca-9b24-5f94efc5ecfd.png", label: "Image 2", enabled: true },
|
||||
{ id: 7, type: "clock" },
|
||||
{ id: 8, type: "slider", sliders: [{ value: 50, label: "Slider 1" }, { value: 75, label: "Slider 2" }] },
|
||||
{ id: 9, type: "text", label: "Tile 5 (Double)" },
|
||||
{ id: 10, type: "text", label: "Tile 6" },
|
||||
{ id: 11, type: "number", value: 42, label: "Data Point" }
|
||||
]);
|
||||
const [tiles, setTiles] = useState([
|
||||
{ id: 0, type: "break", label: "Система" },
|
||||
|
||||
const updateGaugeValue = (tileId, gaugeIndex) => {
|
||||
setTiles(prevTiles =>
|
||||
prevTiles.map(tile => {
|
||||
if (tile.id === tileId && tile.type === "gauge") {
|
||||
const newGauges = [...tile.gauges];
|
||||
newGauges[gaugeIndex] = {
|
||||
...newGauges[gaugeIndex],
|
||||
value: (newGauges[gaugeIndex].value + 10) % 360
|
||||
};
|
||||
return { ...tile, gauges: newGauges };
|
||||
}
|
||||
return tile;
|
||||
})
|
||||
);
|
||||
};
|
||||
{ id: 1, type: "clock", mini_icon:"/icons/mini/clock-five.png" },
|
||||
|
||||
const updateSliderValue = (tileId, sliderIndex, newValue) => {
|
||||
setTiles(prevTiles =>
|
||||
prevTiles.map(tile => {
|
||||
if (tile.id === tileId && tile.type === "slider") {
|
||||
const newSliders = [...tile.sliders];
|
||||
newSliders[sliderIndex] = {
|
||||
...newSliders[sliderIndex],
|
||||
value: parseInt(newValue)
|
||||
};
|
||||
return { ...tile, sliders: newSliders };
|
||||
}
|
||||
return tile;
|
||||
})
|
||||
);
|
||||
};
|
||||
{ id: 2, type: "gauge", gauges: [{ value: 3.60 * 0, label: "Загрузка CPU" }, { value: 3.60 * 0, label: "Занято RAM" }], tag: "system_load", mini_icon:"/icons/mini/chart-line-up.png" },
|
||||
|
||||
const updateNumberValue = (tileId) => {
|
||||
setTiles(prevTiles =>
|
||||
prevTiles.map(tile => {
|
||||
if (tile.id === tileId && tile.type === "number") {
|
||||
return { ...tile, value: tile.value + 1 };
|
||||
}
|
||||
return tile;
|
||||
})
|
||||
);
|
||||
};
|
||||
{ id: 3, type: "number", value: 0, label: "Трафик Mbit/s", tag: "net_traffic", mini_icon:"/icons/mini/wifi.png" },
|
||||
{ id: 4, type: "slider", label: "Настройки", sliders: [{ value: 50, label: "Яроксть экрана", action: "screen.chenge_brightness" }, { value: 75, label: "Громкость звука", action: "media.set_volume" }], mini_icon:"/icons/mini/settings.png" },
|
||||
|
||||
const toggleImageEnabled = (tileId) => {
|
||||
setTiles(prevTiles =>
|
||||
prevTiles.map(tile => {
|
||||
if (tile.id === tileId && tile.type === "image") {
|
||||
return { ...tile, enabled: !tile.enabled };
|
||||
}
|
||||
return tile;
|
||||
})
|
||||
);
|
||||
};
|
||||
{ id: 5, type: "break", label: "Сайты" },
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{tiles.map(tile => (
|
||||
<div key={tile.id}>
|
||||
{tile.type === "break" ? (
|
||||
<div className="w-full"></div>
|
||||
) : (
|
||||
<div
|
||||
className={`glass rounded-lg ${tile.type === "gauge" || tile.type === "slider" || tile.type === "clock" ? "w-96 h-48" : "w-48 h-48"} relative ${tile.type === "image" && tile.enabled ? "glass_enabled" : ""}`}
|
||||
>
|
||||
{tile.type === "text" && <TextTile label={tile.label} />}
|
||||
{tile.type === "image" && (
|
||||
<ImageTile
|
||||
imageUrl={tile.imageUrl}
|
||||
label={tile.label}
|
||||
enabled={tile.enabled}
|
||||
toggleEnabled={() => toggleImageEnabled(tile.id)}
|
||||
/>
|
||||
)}
|
||||
{tile.type === "clock" && <ClockTile id={tile.id} />}
|
||||
{tile.type === "gauge" && <GaugeTile gauges={tile.gauges} />}
|
||||
{tile.type === "slider" && <SliderTile sliders={tile.sliders} tileId={tile.id} updateSliderValue={updateSliderValue} />}
|
||||
{tile.type === "number" && <NumberTile value={tile.value} label={tile.label} />}
|
||||
{(tile.type === "gauge" || tile.type === "slider" || tile.type === "number") && (
|
||||
<div className="absolute top-2 right-2 flex gap-2">
|
||||
{tile.type === "gauge" && tile.gauges.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => updateGaugeValue(tile.id, index)}
|
||||
className="bg-purple-600 text-white text-xs px-2 py-1 rounded"
|
||||
>
|
||||
Update Gauge {index + 1}
|
||||
</button>
|
||||
))}
|
||||
{tile.type === "slider" && tile.sliders.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => updateSliderValue(tile.id, index, (parseInt(tile.sliders[index].value) + 10) % 101)}
|
||||
className="bg-purple-600 text-white text-xs px-2 py-1 rounded"
|
||||
>
|
||||
Increment Slider {index + 1}
|
||||
</button>
|
||||
))}
|
||||
{tile.type === "number" && (
|
||||
<button
|
||||
onClick={() => updateNumberValue(tile.id)}
|
||||
className="bg-purple-600 text-white text-xs px-2 py-1 rounded"
|
||||
>
|
||||
Increment Number
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
{ id: 6, type: "image", imageUrl: "/icons/steam-svgrepo-com.svg", label: "Steam", action: "web.open_url", action_args: { url: "https://ya.ru" }, mini_icon:"/icons/mini/arrow-up-right-from-square.png" },
|
||||
{ id: 7, type: "image", imageUrl: "/icons/telegram-logo-svgrepo-com.svg", label: "Telegram", action: "web.open_url", action_args: { url: "https://ya.ru" }, mini_icon:"/icons/mini/arrow-up-right-from-square.png" },
|
||||
{ id: 8, type: "image", imageUrl: "/icons/cloud.png", label: "Cloud", action: "web.open_url", action_args: { url: "https://cloud.viadev.su" }, mini_icon:"/icons/mini/arrow-up-right-from-square.png" },
|
||||
|
||||
{ id: 9, type: "break", label: "Управление ПК" },
|
||||
{ id: 10, type: "image", imageUrl: "/icons/vpn.png", label: "VPN", action: "v2ray.enable_vpn", action_args: {} },
|
||||
]);
|
||||
|
||||
|
||||
|
||||
const updateSliderValue = (tileId, sliderIndex, newValue) => {
|
||||
setTiles(prevTiles =>
|
||||
prevTiles.map(tile => {
|
||||
if (tile.id === tileId && tile.type === "slider") {
|
||||
const newSliders = [...tile.sliders];
|
||||
newSliders[sliderIndex] = {
|
||||
...newSliders[sliderIndex],
|
||||
value: parseInt(newValue)
|
||||
};
|
||||
return { ...tile, sliders: newSliders };
|
||||
}
|
||||
return tile;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const toggleImageEnabled = (tileId) => {
|
||||
setTiles(prevTiles =>
|
||||
prevTiles.map(tile => {
|
||||
if (tile.id === tileId && tile.type === "image") {
|
||||
return { ...tile, enabled: !tile.enabled };
|
||||
}
|
||||
return tile;
|
||||
})
|
||||
);
|
||||
};
|
||||
const setTileProps = (tileId, props) => {
|
||||
setTiles(prevTiles =>
|
||||
prevTiles.map(tile => {
|
||||
if (tile.id === tileId) {
|
||||
return { ...tile, ...props };
|
||||
}
|
||||
return tile;
|
||||
})
|
||||
);
|
||||
};
|
||||
const setTilePropsByTag = (tag, props) => {
|
||||
setTiles(prevTiles =>
|
||||
prevTiles.map(tile => {
|
||||
if (tile.tag === tag) {
|
||||
return { ...tile, ...props };
|
||||
}
|
||||
return tile;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const updateSystemStats = async () => {
|
||||
try {
|
||||
const data = await sendRequest("system.get_system_metrics", {});
|
||||
setTilePropsByTag("system_load", {
|
||||
gauges: [
|
||||
{ value: (3.60 * data.result.metrics.cpu_usage_percent).toFixed(0), label: "Загрузка CPU" },
|
||||
{ value: (3.60 * (data.result.metrics.ram_used_gb / data.result.metrics.ram_total_gb) * 100).toFixed(0), label: "Занято RAM" }
|
||||
]
|
||||
})
|
||||
setTilePropsByTag("net_traffic", {
|
||||
value: data.result.metrics.network_traffic_mbps
|
||||
})
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
updateSystemStats();
|
||||
const interval = setInterval(updateSystemStats, 2000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
|
||||
const onTileClick = async (tile) => {
|
||||
console.log("click", tile)
|
||||
|
||||
if (tile.action) {
|
||||
try {
|
||||
setTileProps(tile.id, { loading: true })
|
||||
await sendRequest(tile.action, tile.action_args);
|
||||
|
||||
if (tile.action.includes("enable")) {
|
||||
tile.enabled = true;
|
||||
tile.action = tile.action.replace("enable", "disable");
|
||||
} else if (tile.action.includes("disable")) {
|
||||
tile.enabled = false;
|
||||
tile.action = tile.action.replace("disable", "enable");
|
||||
}
|
||||
tile.loading = false;
|
||||
|
||||
setTileProps(tile.id, tile)
|
||||
} catch (e) {
|
||||
setTileProps(tile.id, { loading: false })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-6 flex-wrap gap-4">
|
||||
{tiles.map(tile => (
|
||||
<div key={tile.id} className={tile.type === "break" ? "col-span-full" : (tile.type === "gauge" || tile.type === "slider" || tile.type === "clock" || tile.type === "number" ? "w-full sm:w-auto col-span-2" : "col-span-1")}>
|
||||
{tile.type === "break" ? (
|
||||
<div className="w-full mt-2 text-purple-400 text-2xl font-bold">{tile.label}</div>
|
||||
) : (
|
||||
<div
|
||||
onClick={() => { onTileClick(tile) }}
|
||||
className={`glass rounded-lg ${tile.type === "gauge" || tile.type === "slider" || tile.type === "clock" ? "w-full h-45 lg:h-48" : "w-full h-45 lg:h-48"} relative ${tile.type === "image" && tile.enabled ? "glass_enabled" : ""}`}
|
||||
>
|
||||
{tile.loading &&
|
||||
<div className={"flex animate-[fadeIn_0.5s_ease-out] absolute inset-0 bg-black/10 backdrop-blur-[3px] flex-col items-center justify-center z-10 rounded-xl"}>
|
||||
<div className="w-10 h-10 border-4 border-purple-500 border-t-transparent rounded-full animate-spin" style={{ animationDuration: "0.5s" }} ></div>
|
||||
</div>
|
||||
}
|
||||
{!tile.loading &&
|
||||
<>
|
||||
{tile.type === "text" && <TextTile label={tile.label} />}
|
||||
{tile.type === "image" && (
|
||||
<ImageTile
|
||||
imageUrl={tile.imageUrl}
|
||||
label={tile.label}
|
||||
enabled={tile.enabled}
|
||||
toggleEnabled={() => toggleImageEnabled(tile.id)}
|
||||
/>
|
||||
)}
|
||||
{tile.type === "clock" && <ClockTile id={tile.id} />}
|
||||
{tile.type === "gauge" && <GaugeTile gauges={tile.gauges} />}
|
||||
{tile.type === "slider" && <SliderTile label={tile.label} sliders={tile.sliders} tileId={tile.id} updateSliderValue={updateSliderValue} />}
|
||||
{tile.type === "number" && <NumberTile value={tile.value} label={tile.label} />}
|
||||
{tile.mini_icon && (
|
||||
<div className="absolute top-3 right-3 flex gap-2">
|
||||
|
||||
<img src={tile.mini_icon} className='h-5 w-5'/>
|
||||
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default App
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
@import "tailwindcss";
|
||||
@import "tailwindcss";
|
||||
#root{
|
||||
width:100%;
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import './index.css'
|
||||
import App from './App.jsx'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<body className='bg-gray-900 min-h-screen p-4'>
|
||||
<body className='bg-gray-900 min-h-screen py-4 flex justify-center px-3 2xl:px-[15%]'>
|
||||
<App />
|
||||
</body>
|
||||
)
|
||||
|
||||