UI: NodeList added Pie Chart and extracted Table Data
All checks were successful
build / test (pull_request) Successful in 39s

This commit is contained in:
Luis Hebendanz 2023-08-12 00:01:09 +02:00
parent 3d3dcc800b
commit de9cac534b
8 changed files with 1608 additions and 276 deletions

File diff suppressed because it is too large Load Diff

View File

@ -19,10 +19,12 @@
"classnames": "^2.3.2", "classnames": "^2.3.2",
"eslint": "8.46.0", "eslint": "8.46.0",
"eslint-config-next": "13.4.12", "eslint-config-next": "13.4.12",
"hex-rgb": "^5.0.0",
"next": "13.4.12", "next": "13.4.12",
"postcss": "8.4.27", "postcss": "8.4.27",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"recharts": "^2.7.3",
"tailwindcss": "3.3.3", "tailwindcss": "3.3.3",
"typescript": "5.1.6" "typescript": "5.1.6"
}, },
@ -929,6 +931,60 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"node_modules/@types/d3-array": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz",
"integrity": "sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A=="
},
"node_modules/@types/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA=="
},
"node_modules/@types/d3-ease": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz",
"integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA=="
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==",
"dependencies": {
"@types/d3-color": "*"
}
},
"node_modules/@types/d3-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz",
"integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg=="
},
"node_modules/@types/d3-scale": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz",
"integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==",
"dependencies": {
"@types/d3-time": "*"
}
},
"node_modules/@types/d3-shape": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz",
"integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==",
"dependencies": {
"@types/d3-path": "*"
}
},
"node_modules/@types/d3-time": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz",
"integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg=="
},
"node_modules/@types/d3-timer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz",
"integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g=="
},
"node_modules/@types/json5": { "node_modules/@types/json5": {
"version": "0.0.29", "version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@ -1669,6 +1725,11 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/css-unit-converter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz",
"integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA=="
},
"node_modules/cssesc": { "node_modules/cssesc": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@ -1685,6 +1746,116 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
}, },
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"dependencies": {
"d3-path": "^3.1.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"engines": {
"node": ">=12"
}
},
"node_modules/damerau-levenshtein": { "node_modules/damerau-levenshtein": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@ -1706,6 +1877,11 @@
} }
} }
}, },
"node_modules/decimal.js-light": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
},
"node_modules/deep-is": { "node_modules/deep-is": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@ -2379,6 +2555,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
"node_modules/execa": { "node_modules/execa": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz",
@ -2406,6 +2587,14 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
}, },
"node_modules/fast-equals": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz",
"integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
@ -2805,6 +2994,17 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/hex-rgb": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-5.0.0.tgz",
"integrity": "sha512-NQO+lgVUCtHxZ792FodgW0zflK+ozS9X9dwGp9XvvmPlH7pyxd588cn24TD3rmPm/N0AIRXF10Otah8yKqGw4w==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/hoist-non-react-statics": { "node_modules/hoist-non-react-statics": {
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@ -2879,6 +3079,14 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"engines": {
"node": ">=12"
}
},
"node_modules/is-array-buffer": { "node_modules/is-array-buffer": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
@ -3302,6 +3510,11 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -4123,6 +4336,60 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}, },
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"node_modules/react-resize-detector": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz",
"integrity": "sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==",
"dependencies": {
"lodash": "^4.17.21"
},
"peerDependencies": {
"react": "^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-smooth": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.3.tgz",
"integrity": "sha512-yl4y3XiMorss7ayF5QnBiSprig0+qFHui8uh7Hgg46QX5O+aRMRKlfGGNGLHno35JkQSvSYY8eCWkBfHfrSHfg==",
"dependencies": {
"fast-equals": "^5.0.0",
"react-transition-group": "2.9.0"
},
"peerDependencies": {
"prop-types": "^15.6.0",
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-smooth/node_modules/dom-helpers": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
"integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
"dependencies": {
"@babel/runtime": "^7.1.2"
}
},
"node_modules/react-smooth/node_modules/react-transition-group": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
"integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
"dependencies": {
"dom-helpers": "^3.4.0",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2",
"react-lifecycles-compat": "^3.0.4"
},
"peerDependencies": {
"react": ">=15.0.0",
"react-dom": ">=15.0.0"
}
},
"node_modules/react-transition-group": { "node_modules/react-transition-group": {
"version": "4.4.5", "version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
@ -4157,6 +4424,52 @@
"node": ">=8.10.0" "node": ">=8.10.0"
} }
}, },
"node_modules/recharts": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.7.3.tgz",
"integrity": "sha512-cKoO9jUZRQavn06H6Ih2EcG82zUNdQH3OEGWVCmluSDyp3d7fIpDAsbMTd8hE8+T+MD8P76iicv/J4pJspDP7A==",
"dependencies": {
"classnames": "^2.2.5",
"eventemitter3": "^4.0.1",
"lodash": "^4.17.19",
"react-is": "^16.10.2",
"react-resize-detector": "^8.0.4",
"react-smooth": "^2.0.2",
"recharts-scale": "^0.4.4",
"reduce-css-calc": "^2.1.8",
"victory-vendor": "^36.6.8"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"prop-types": "^15.6.0",
"react": "^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/recharts-scale": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
"dependencies": {
"decimal.js-light": "^2.4.1"
}
},
"node_modules/reduce-css-calc": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz",
"integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==",
"dependencies": {
"css-unit-converter": "^1.1.1",
"postcss-value-parser": "^3.3.0"
}
},
"node_modules/reduce-css-calc/node_modules/postcss-value-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
},
"node_modules/regenerator-runtime": { "node_modules/regenerator-runtime": {
"version": "0.13.11", "version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
@ -4974,6 +5287,27 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
}, },
"node_modules/victory-vendor": {
"version": "36.6.11",
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.11.tgz",
"integrity": "sha512-nT8kCiJp8dQh8g991J/R5w5eE2KnO8EAIP0xocWlh9l2okngMWglOPoMZzJvek8Q1KUc4XE/mJxTZnvOB1sTYg==",
"dependencies": {
"@types/d3-array": "^3.0.3",
"@types/d3-ease": "^3.0.0",
"@types/d3-interpolate": "^3.0.1",
"@types/d3-scale": "^4.0.2",
"@types/d3-shape": "^3.1.0",
"@types/d3-time": "^3.0.0",
"@types/d3-timer": "^3.0.0",
"d3-array": "^3.1.6",
"d3-ease": "^3.0.1",
"d3-interpolate": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-shape": "^3.1.0",
"d3-time": "^3.0.0",
"d3-timer": "^3.0.1"
}
},
"node_modules/watchpack": { "node_modules/watchpack": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",

View File

@ -23,10 +23,12 @@
"classnames": "^2.3.2", "classnames": "^2.3.2",
"eslint": "8.46.0", "eslint": "8.46.0",
"eslint-config-next": "13.4.12", "eslint-config-next": "13.4.12",
"hex-rgb": "^5.0.0",
"next": "13.4.12", "next": "13.4.12",
"postcss": "8.4.27", "postcss": "8.4.27",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"recharts": "^2.7.3",
"tailwindcss": "3.3.3", "tailwindcss": "3.3.3",
"typescript": "5.1.6" "typescript": "5.1.6"
}, },

View File

@ -27,47 +27,53 @@ import Stack from '@mui/material/Stack/Stack';
import ModeIcon from '@mui/icons-material/Mode'; import ModeIcon from '@mui/icons-material/Mode';
import ClearIcon from '@mui/icons-material/Clear'; import ClearIcon from '@mui/icons-material/Clear';
import Fade from '@mui/material/Fade/Fade'; import Fade from '@mui/material/Fade/Fade';
import NodePieChart, { PieData } from './NodePieChart';
import Grid2 from '@mui/material/Unstable_Grid2'; // Grid version 2
import { Card, CardContent, Container, FormGroup, useTheme } from '@mui/material';
import hexRgb from 'hex-rgb';
import useMediaQuery from '@mui/material/useMediaQuery';
interface Data {
export interface TableData {
name: string; name: string;
id: string; id: string;
status: boolean; status: NodeStatus;
last_seen: number; last_seen: number;
} }
function createData( export enum NodeStatus {
name: string, Online,
id: string, Offline,
status: boolean, Pending,
last_seen: number,
): Data {
if (status && last_seen > 0) {
console.error("Last seen should be 0 if status is true");
}
return {
name,
id,
status,
last_seen: last_seen,
};
} }
const rows = [
createData('Matchbox', "42:0:f21:6916:e333:c47e:4b5c:e74c", true, 0), interface HeadCell {
createData('Ahorn', "42:0:3c46:b51c:b34d:b7e1:3b02:8d24", true, 0), disablePadding: boolean;
createData('Yellow', "42:0:3c46:98ac:9c80:4f25:50e3:1d8f", false, 16.0), id: keyof TableData;
createData('Rauter', "42:0:61ea:b777:61ea:803:f885:3523", false, 6.0), label: string;
createData('Porree', "42:0:e644:4499:d034:895e:34c8:6f9a", false, 13), alignRight: boolean;
createData('Helsinki', "42:0:3c46:fd4a:acf9:e971:6036:8047", true, 0), }
createData('Kelle', "42:0:3c46:362d:a9aa:4996:c78e:839a", true, 0),
createData('Shodan', "42:0:3c46:6745:adf4:a844:26c4:bf91", true, 0.0), const headCells: readonly HeadCell[] = [
createData('Qubasa', "42:0:3c46:123e:bbea:3529:db39:6764", false, 7.0), {
createData('Green', "42:0:a46e:5af:632c:d2fe:a71d:cde0", false, 2), id: 'name',
createData('Gum', "42:0:e644:238d:3e46:c884:6ec5:16c", false, 0), alignRight: false,
createData('Xu', "42:0:ca48:c2c2:19fb:a0e9:95b9:794f", true, 0), disablePadding: false,
createData('Zaatar', "42:0:3c46:156e:10b6:3bd6:6e82:b2cd", true, 0), label: 'DISPLAY NAME & ID',
},
{
id: 'status',
alignRight: false,
disablePadding: false,
label: 'STATUS',
},
{
id: 'last_seen',
alignRight: false,
disablePadding: false,
label: 'LAST SEEN',
},
]; ];
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) { function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
@ -110,36 +116,10 @@ function stableSort<T>(array: readonly T[], comparator: (a: T, b: T) => number)
return stabilizedThis.map((el) => el[0]); return stabilizedThis.map((el) => el[0]);
} }
interface HeadCell {
disablePadding: boolean;
id: keyof Data;
label: string;
alignRight: boolean;
}
const headCells: readonly HeadCell[] = [
{
id: 'name',
alignRight: false,
disablePadding: false,
label: 'Display Name & ID',
},
{
id: 'status',
alignRight: false,
disablePadding: false,
label: 'Status',
},
{
id: 'last_seen',
alignRight: false,
disablePadding: false,
label: 'Last Seen',
},
];
interface EnhancedTableProps { interface EnhancedTableProps {
onRequestSort: (event: React.MouseEvent<unknown>, property: keyof Data) => void; onRequestSort: (event: React.MouseEvent<unknown>, property: keyof TableData) => void;
order: Order; order: Order;
orderBy: string; orderBy: string;
rowCount: number; rowCount: number;
@ -149,7 +129,7 @@ function EnhancedTableHead(props: EnhancedTableProps) {
const { order, orderBy, onRequestSort } = const { order, orderBy, onRequestSort } =
props; props;
const createSortHandler = const createSortHandler =
(property: keyof Data) => (event: React.MouseEvent<unknown>) => { (property: keyof TableData) => (event: React.MouseEvent<unknown>) => {
onRequestSort(event, property); onRequestSort(event, property);
}; };
@ -182,10 +162,161 @@ function EnhancedTableHead(props: EnhancedTableProps) {
); );
} }
interface EnhancedTableToolbarProps { interface EnhancedTableToolbarProps {
selected: string | undefined; selected: string | undefined;
tableData: TableData[];
onClear: () => void; onClear: () => void;
} }
function EnhancedTableToolbar(props: EnhancedTableToolbarProps) {
const { selected, onClear, tableData } = props;
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down('lg'));
const isSelected = selected != undefined;
const [debug, setDebug] = React.useState<boolean>(false);
const debugSx = debug ? {
'--Grid-borderWidth': '1px',
borderTop: 'var(--Grid-borderWidth) solid',
borderLeft: 'var(--Grid-borderWidth) solid',
borderColor: 'divider',
'& > div': {
borderRight: 'var(--Grid-borderWidth) solid',
borderBottom: 'var(--Grid-borderWidth) solid',
borderColor: 'divider',
}
} : {};
const pieData = React.useMemo(() => {
const online = tableData.filter((row) => row.status === NodeStatus.Online).length;
const offline = tableData.filter((row) => row.status === NodeStatus.Offline).length;
const pending = tableData.filter((row) => row.status === NodeStatus.Pending).length;
return [
{ name: 'Online', value: online, color: '#2E7D32' },
{ name: 'Offline', value: offline, color: '#db3927' },
{ name: 'Pending', value: pending, color: '#FFBB28' },
];
}, [tableData]);
const cardData = React.useMemo(() => {
let copy = pieData.filter((pieItem) => pieItem.value > 0);
const elem = { name: 'Total', value: copy.reduce((a, b) => a + b.value, 0), color: '#000000' };
copy.push(elem);
return copy;
}, [pieData]);
const cardStack = (
<Stack
sx={{ ...debugSx, paddingTop: 6 }}
height={350}
id="cardBox"
display="flex"
flexDirection="column"
justifyContent="flex-start"
flexWrap="wrap">
{cardData.map((pieItem) => (
<Card key={pieItem.name} sx={{ marginBottom: 2, marginRight: 2, width: 110, height: 110, backgroundColor: hexRgb(pieItem.color, { format: 'css', alpha: 0.18 }) }}>
<CardContent >
<Typography variant="h4" component="div" gutterBottom={true} textAlign="center">
{pieItem.value}
</Typography>
<Typography sx={{ mb: 1.5 }} color="text.secondary" textAlign="center">
{pieItem.name}
</Typography>
</CardContent>
</Card>
))}
</Stack>
);
const selectedToolbar = (
<Toolbar
sx={{
pl: { sm: 2 },
pr: { xs: 1, sm: 1 },
bgcolor: (theme) =>
alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity),
}}>
<Tooltip title="Clear">
<IconButton onClick={onClear}>
<ClearIcon />
</IconButton>
</Tooltip>
<Typography
sx={{ flex: '1 1 100%' }}
color="inherit"
style={{ fontSize: 18, marginBottom: 3, marginLeft: 3 }}
component="div"
>
{selected} selected
</Typography>
<Tooltip title="Edit">
<IconButton>
<ModeIcon />
</IconButton>
</Tooltip>
</Toolbar >
);
const unselectedToolbar = (
<Toolbar
sx={{
pl: { sm: 2 },
pr: { xs: 1, sm: 1 },
}}
>
<Box sx={{ flex: '1 1 100%' }} ></Box>
<Tooltip title="Filter list">
<IconButton>
<FilterListIcon />
</IconButton>
</Tooltip>
</Toolbar >
);
return (
<Grid2 container spacing={1} sx={debugSx}>
<Grid2 xs={6}>
<Typography
sx={{ marginLeft: 3, marginTop: 1 }}
variant="h6"
id="tableTitle"
component="div"
>
NODES
</Typography>
</Grid2>
{/* Debug Controls */}
<Grid2 xs={6} justifyContent="right" display="flex">
<FormGroup>
<FormControlLabel control={<Switch onChange={() => { setDebug(!debug) }} checked={debug} />} label="Debug" />
</FormGroup>
</Grid2>
{/* Pie Chart Grid */}
<Grid2 lg={6} sm={12} display="flex" justifyContent="center" alignItems="center">
<Box height={350} width={400}>
<NodePieChart data={pieData} showLabels={matches} />
</Box>
</Grid2>
{/* Card Stack Grid */}
<Grid2 lg={6} display="flex" sx={{ display: { lg: 'flex', sm: 'none' } }} >
{cardStack}
</Grid2>
{/*Toolbar Grid */}
<Grid2 xs={12}>
{isSelected ? selectedToolbar : unselectedToolbar}
</Grid2>
</Grid2>
);
}
function renderLastSeen(last_seen: number) { function renderLastSeen(last_seen: number) {
return ( return (
@ -208,98 +339,47 @@ function renderName(name: string, id: string) {
); );
} }
function renderStatus(status: boolean) { function renderStatus(status: NodeStatus) {
if (status) { switch (status) {
return ( case NodeStatus.Online:
<Stack direction="row" alignItems="center" gap={1}> return (
<CircleIcon color="success" style={{ fontSize: 15 }} /> <Stack direction="row" alignItems="center" gap={1}>
<Typography component="div" align="left" variant="body1"> <CircleIcon color="success" style={{ fontSize: 15 }} />
Online <Typography component="div" align="left" variant="body1">
</Typography> Online
</Stack> </Typography>
); </Stack>
);
case NodeStatus.Offline:
return (
<Stack direction="row" alignItems="center" gap={1}>
<CircleIcon color="error" style={{ fontSize: 15 }} />
<Typography component="div" align="left" variant="body1">
Offline
</Typography>
</Stack>
);
case NodeStatus.Pending:
return (
<Stack direction="row" alignItems="center" gap={1}>
<CircleIcon color="warning" style={{ fontSize: 15 }} />
<Typography component="div" align="left" variant="body1">
Pending
</Typography>
</Stack>
);
} }
return (
<Stack direction="row" alignItems="center" gap={1}>
<CircleIcon color="error" style={{ fontSize: 15 }} />
<Typography component="div" align="left" variant="body1">
Offline
</Typography>
</Stack>
);
} }
function EnhancedTableToolbar(props: EnhancedTableToolbarProps) { export interface NodeTableProps {
const { selected, onClear } = props; tableData: TableData[]
const somethingSelected = selected !== undefined;
const handleSomethingSelected = () => {
if (somethingSelected) {
return (
<Toolbar
sx={{
pl: { sm: 2 },
pr: { xs: 1, sm: 1 },
bgcolor: (theme) =>
alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity),
}}>
<Tooltip title="Clear">
<IconButton onClick={onClear}>
<ClearIcon />
</IconButton>
</Tooltip>
<Typography
sx={{ flex: '1 1 100%' }}
color="inherit"
style={{ fontSize: 18, marginBottom: 2.5, marginLeft: 3 }}
component="div"
>
{selected} selected
</Typography>
<Tooltip title="Edit">
<IconButton>
<ModeIcon />
</IconButton>
</Tooltip>
</Toolbar >
);
} else {
return (
<Toolbar
sx={{
pl: { sm: 2 },
pr: { xs: 1, sm: 1 }
}}>
<Typography
sx={{ flex: '1 1 100%' }}
variant="h6"
id="tableTitle"
component="div"
>
Nodes
</Typography>
<Tooltip title="Filter list">
<IconButton>
<FilterListIcon />
</IconButton>
</Tooltip>
</Toolbar >
);
}
};
return handleSomethingSelected();
} }
export default function EnhancedTable() { export default function NodeTable(props: NodeTableProps) {
let {tableData} = props;
const [order, setOrder] = React.useState<Order>('asc'); const [order, setOrder] = React.useState<Order>('asc');
const [orderBy, setOrderBy] = React.useState<keyof Data>('status'); const [orderBy, setOrderBy] = React.useState<keyof TableData>('status');
const [selected, setSelected] = React.useState<string | undefined>(undefined); const [selected, setSelected] = React.useState<string | undefined>(undefined);
const [page, setPage] = React.useState(0); const [page, setPage] = React.useState(0);
const [dense, setDense] = React.useState(false); const [dense, setDense] = React.useState(false);
@ -307,7 +387,7 @@ export default function EnhancedTable() {
const handleRequestSort = ( const handleRequestSort = (
event: React.MouseEvent<unknown>, event: React.MouseEvent<unknown>,
property: keyof Data, property: keyof TableData,
) => { ) => {
const isAsc = orderBy === property && order === 'asc'; const isAsc = orderBy === property && order === 'asc';
setOrder(isAsc ? 'desc' : 'asc'); setOrder(isAsc ? 'desc' : 'asc');
@ -315,7 +395,8 @@ export default function EnhancedTable() {
}; };
const handleClick = (event: React.MouseEvent<unknown>, name: string) => { const handleClick = (event: React.MouseEvent<unknown>, name: string) => {
if (selected === name) { // Speed optimization. We compare string pointers here instead of the string content.
if (selected == name) {
setSelected(undefined); setSelected(undefined);
} else { } else {
setSelected(name); setSelected(name);
@ -331,106 +412,93 @@ export default function EnhancedTable() {
setPage(0); setPage(0);
}; };
const handleChangeDense = (event: React.ChangeEvent<HTMLInputElement>) => { // Speed optimization. We compare string pointers here instead of the string content.
setDense(event.target.checked); const isSelected = (name: string) => name == selected;
};
// TODO: Make a number to increase comparison speed and ui performance
const isSelected = (name: string) => name === selected;
// Avoid a layout jump when reaching the last page with empty rows. // Avoid a layout jump when reaching the last page with empty rows.
const emptyRows = const emptyRows =
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0; page > 0 ? Math.max(0, (1 + page) * rowsPerPage - tableData.length) : 0;
const visibleRows = React.useMemo( const visibleRows = React.useMemo(
() => () =>
stableSort(rows, getComparator(order, orderBy)).slice( stableSort(tableData, getComparator(order, orderBy)).slice(
page * rowsPerPage, page * rowsPerPage,
page * rowsPerPage + rowsPerPage, page * rowsPerPage + rowsPerPage,
), ),
[order, orderBy, page, rowsPerPage], [order, orderBy, page, rowsPerPage, tableData],
); );
return ( return (
<Box sx={{ width: '100%' }}> <Paper elevation={1} sx={{ margin: 5 }}>
<Paper sx={{ width: '100%', mb: 2 }} id="test"> <Box sx={{ width: '100%' }}>
<EnhancedTableToolbar selected={selected} onClear={() => setSelected(undefined)} /> <Paper sx={{ width: '100%', mb: 2 }}>
<TableContainer> <EnhancedTableToolbar tableData={tableData} selected={selected} onClear={() => setSelected(undefined)} />
<Table <TableContainer>
sx={{ minWidth: 750 }} <Table
aria-labelledby="tableTitle" sx={{ minWidth: 750 }}
size={dense ? 'small' : 'medium'} aria-labelledby="tableTitle"
> size={dense ? 'small' : 'medium'}
<EnhancedTableHead >
order={order} <EnhancedTableHead
orderBy={orderBy} order={order}
onRequestSort={handleRequestSort} orderBy={orderBy}
rowCount={rows.length} onRequestSort={handleRequestSort}
/> rowCount={tableData.length}
<TableBody> />
{visibleRows.map((row, index) => { <TableBody>
const isItemSelected = isSelected(row.name); {visibleRows.map((row, index) => {
const labelId = `enhanced-table-checkbox-${index}`; const isItemSelected = isSelected(row.name);
const labelId = `enhanced-table-checkbox-${index}`;
return ( return (
<TableRow <TableRow
hover hover
onClick={(event) => handleClick(event, row.name)} onClick={(event) => handleClick(event, row.name)}
role="checkbox" role="checkbox"
aria-checked={isItemSelected} aria-checked={isItemSelected}
tabIndex={-1} tabIndex={-1}
key={row.name} key={row.name}
selected={isItemSelected} selected={isItemSelected}
sx={{ cursor: 'pointer' }} sx={{ cursor: 'pointer' }}
>
{/* <TableCell padding="checkbox">
<Checkbox
color="primary"
checked={isItemSelected}
inputProps={{
'aria-labelledby': labelId,
}}
/>
</TableCell> */}
<TableCell
component="th"
id={labelId}
scope="row"
> >
{renderName(row.name, row.id)} <TableCell
</TableCell> component="th"
<TableCell align="right">{renderStatus(row.status)}</TableCell> id={labelId}
<TableCell align="right">{renderLastSeen(row.last_seen)}</TableCell> scope="row"
>
{renderName(row.name, row.id)}
</TableCell>
<TableCell align="right">{renderStatus(row.status)}</TableCell>
<TableCell align="right">{renderLastSeen(row.last_seen)}</TableCell>
</TableRow>
);
})}
{emptyRows > 0 && (
<TableRow
style={{
height: (dense ? 33 : 53) * emptyRows,
}}
>
<TableCell colSpan={6} />
</TableRow> </TableRow>
); )}
})} </TableBody>
{emptyRows > 0 && ( </Table>
<TableRow </TableContainer>
style={{ {/* TODO: This creates the error Warning: Prop `id` did not match. Server: ":RspmmcqH1:" Client: ":R3j6qpj9H1:" */}
height: (dense ? 33 : 53) * emptyRows, <TablePagination
}} rowsPerPageOptions={[5, 10, 25]}
> component="div"
<TableCell colSpan={6} /> count={tableData.length}
</TableRow> rowsPerPage={rowsPerPage}
)} page={page}
</TableBody> onPageChange={handleChangePage}
</Table> onRowsPerPageChange={handleChangeRowsPerPage}
</TableContainer> />
{/* TODO: This creates the error Warning: Prop `id` did not match. Server: ":RspmmcqH1:" Client: ":R3j6qpj9H1:" */} </Paper>
<TablePagination </Box>
rowsPerPageOptions={[5, 10, 25]} </Paper>
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</Paper>
<FormControlLabel
control={<Switch checked={dense} onChange={handleChangeDense} />}
label="Dense padding"
/>
</Box>
); );
} }

View File

@ -0,0 +1,45 @@
import React, { PureComponent } from 'react';
import { PieChart, Pie, Sector, Cell, ResponsiveContainer, Legend } from 'recharts';
import { useTheme } from '@mui/material/styles';
import { Box, Color } from '@mui/material';
export interface PieData {
name: string;
value: number;
color: string;
};
interface Props {
data: PieData[];
showLabels?: boolean;
};
export default function NodePieChart(props: Props ) {
const theme = useTheme();
const {data, showLabels} = props;
return (
<Box height={350}>
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={data}
innerRadius={85}
outerRadius={120}
fill={theme.palette.primary.main}
dataKey="value"
nameKey="name"
label={showLabels}
>
{data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
<Legend verticalAlign="bottom" />
</PieChart>
</ResponsiveContainer>
</Box>
);
};

View File

@ -1,36 +0,0 @@
export default function PieData() {
return [
{
"id": "scala",
"label": "scala",
"value": 317,
"color": "hsl(3, 70%, 50%)"
},
{
"id": "rust",
"label": "rust",
"value": 489,
"color": "hsl(113, 70%, 50%)"
},
{
"id": "css",
"label": "css",
"value": 456,
"color": "hsl(17, 70%, 50%)"
},
{
"id": "elixir",
"label": "elixir",
"value": 343,
"color": "hsl(232, 70%, 50%)"
},
{
"id": "haskell",
"label": "haskell",
"value": 167,
"color": "hsl(292, 70%, 50%)"
}
];
}

View File

@ -1,17 +1,47 @@
"use client" "use client"
import { StrictMode } from "react"; import { StrictMode } from "react";
import NodeList from "./NodeList"; import NodeList, { NodeStatus, TableData } from "./NodeList";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
function createData(
name: string,
id: string,
status: NodeStatus,
last_seen: number,
): TableData {
return {
name,
id,
status,
last_seen: last_seen,
};
}
const tableData = [
createData('Matchbox', "42:0:f21:6916:e333:c47e:4b5c:e74c", NodeStatus.Pending, 0),
createData('Ahorn', "42:0:3c46:b51c:b34d:b7e1:3b02:8d24", NodeStatus.Online, 0),
createData('Yellow', "42:0:3c46:98ac:9c80:4f25:50e3:1d8f", NodeStatus.Offline, 16.0),
createData('Rauter', "42:0:61ea:b777:61ea:803:f885:3523", NodeStatus.Offline, 6.0),
createData('Porree', "42:0:e644:4499:d034:895e:34c8:6f9a", NodeStatus.Offline, 13),
createData('Helsinki', "42:0:3c46:fd4a:acf9:e971:6036:8047", NodeStatus.Online, 0),
createData('Kelle', "42:0:3c46:362d:a9aa:4996:c78e:839a", NodeStatus.Online, 0),
createData('Shodan', "42:0:3c46:6745:adf4:a844:26c4:bf91", NodeStatus.Online, 0.0),
createData('Qubasa', "42:0:3c46:123e:bbea:3529:db39:6764", NodeStatus.Offline, 7.0),
createData('Green', "42:0:a46e:5af:632c:d2fe:a71d:cde0", NodeStatus.Offline, 2),
createData('Gum', "42:0:e644:238d:3e46:c884:6ec5:16c", NodeStatus.Offline, 0),
createData('Xu', "42:0:ca48:c2c2:19fb:a0e9:95b9:794f", NodeStatus.Online, 0),
createData('Zaatar', "42:0:3c46:156e:10b6:3bd6:6e82:b2cd", NodeStatus.Online, 0),
];
export default function Page() { export default function Page() {
return ( return (
<Box> <Box sx={{ backgroundColor: "#e9ecf5", height: "100%", width: "100%" }} display="inline-block" id="rootBox">
<NodeList tableData={tableData} />
<NodeList />
</Box> </Box>
); );
} }

View File

@ -1,13 +1,16 @@
import { createTheme } from "@mui/material/styles"; import { createTheme } from "@mui/material/styles";
export const darkTheme = createTheme({ export const darkTheme = createTheme({
palette: { palette: {
mode: "dark", mode: "dark",
}, },
}); });
export const lightTheme = createTheme({ export const lightTheme = createTheme({
palette: { palette: {
mode: "light", mode: "light",
}, },
}); });