Display live and cached consumption on dashboard
This commit is contained in:
parent
539703b904
commit
1784a0a1f8
350
central_frontend/package-lock.json
generated
350
central_frontend/package-lock.json
generated
@ -15,6 +15,7 @@
|
||||
"@mdi/react": "^1.6.1",
|
||||
"@mui/icons-material": "^6.0.1",
|
||||
"@mui/material": "^6.0.1",
|
||||
"@mui/x-charts": "^7.15.0",
|
||||
"@mui/x-date-pickers": "^7.15.0",
|
||||
"date-and-time": "^3.5.0",
|
||||
"dayjs": "^1.11.13",
|
||||
@ -1377,6 +1378,93 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-charts": {
|
||||
"version": "7.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.15.0.tgz",
|
||||
"integrity": "sha512-eAyrLWMrFt6NKKca1vCWvVoThapGSdTzvDSY76CiA5Q1eOzZIhIriafh26QFqQLSuLYnCwHdX+gBVmCoZorcWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.4",
|
||||
"@mui/utils": "^5.16.6",
|
||||
"@mui/x-charts-vendor": "7.15.0",
|
||||
"@react-spring/rafz": "^9.7.4",
|
||||
"@react-spring/web": "^9.7.4",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.9.0",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@mui/material": "^5.15.14 || ^6.0.0",
|
||||
"@mui/system": "^5.15.14 || ^6.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-charts-vendor": {
|
||||
"version": "7.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-7.15.0.tgz",
|
||||
"integrity": "sha512-5PNzV3//gdS3jlsbvAc/kj6k7KTikjNhprXVaTIrqj9/Jm2MU0cNtmwDwDVkekXLpjeXGGT6nrIehb129GIinw==",
|
||||
"license": "MIT AND ISC",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.4",
|
||||
"@types/d3-color": "^3.1.3",
|
||||
"@types/d3-delaunay": "^6.0.4",
|
||||
"@types/d3-interpolate": "^3.0.4",
|
||||
"@types/d3-scale": "^4.0.8",
|
||||
"@types/d3-shape": "^3.1.6",
|
||||
"@types/d3-time": "^3.0.3",
|
||||
"d3-color": "^3.1.0",
|
||||
"d3-delaunay": "^6.0.4",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.2.0",
|
||||
"d3-time": "^3.1.0",
|
||||
"delaunator": "^5.0.1",
|
||||
"robust-predicates": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-charts/node_modules/@mui/utils": {
|
||||
"version": "5.16.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz",
|
||||
"integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.9",
|
||||
"@mui/types": "^7.2.15",
|
||||
"@types/prop-types": "^15.7.12",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^18.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-date-pickers": {
|
||||
"version": "7.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.15.0.tgz",
|
||||
@ -1516,6 +1604,78 @@
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-spring/animated": {
|
||||
"version": "9.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.4.tgz",
|
||||
"integrity": "sha512-7As+8Pty2QlemJ9O5ecsuPKjmO0NKvmVkRR1n6mEotFgWar8FKuQt2xgxz3RTgxcccghpx1YdS1FCdElQNexmQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-spring/shared": "~9.7.4",
|
||||
"@react-spring/types": "~9.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-spring/core": {
|
||||
"version": "9.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.4.tgz",
|
||||
"integrity": "sha512-GzjA44niEJBFUe9jN3zubRDDDP2E4tBlhNlSIkTChiNf9p4ZQlgXBg50qbXfSXHQPHak/ExYxwhipKVsQ/sUTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-spring/animated": "~9.7.4",
|
||||
"@react-spring/shared": "~9.7.4",
|
||||
"@react-spring/types": "~9.7.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/react-spring/donate"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-spring/rafz": {
|
||||
"version": "9.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.4.tgz",
|
||||
"integrity": "sha512-mqDI6rW0Ca8IdryOMiXRhMtVGiEGLIO89vIOyFQXRIwwIMX30HLya24g9z4olDvFyeDW3+kibiKwtZnA4xhldA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-spring/shared": {
|
||||
"version": "9.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.4.tgz",
|
||||
"integrity": "sha512-bEPI7cQp94dOtCFSEYpxvLxj0+xQfB5r9Ru1h8OMycsIq7zFZon1G0sHrBLaLQIWeMCllc4tVDYRTLIRv70C8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-spring/rafz": "~9.7.4",
|
||||
"@react-spring/types": "~9.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-spring/types": {
|
||||
"version": "9.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.4.tgz",
|
||||
"integrity": "sha512-iQVztO09ZVfsletMiY+DpT/JRiBntdsdJ4uqk3UJFhrhS8mIC9ZOZbmfGSRs/kdbNPQkVyzucceDicQ/3Mlj9g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-spring/web": {
|
||||
"version": "9.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.4.tgz",
|
||||
"integrity": "sha512-UMvCZp7I5HCVIleSa4BwbNxynqvj+mJjG2m20VO2yPoi2pnCYANy58flvz9v/YcXTAvsmL655FV3pm5fbr6akA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-spring/animated": "~9.7.4",
|
||||
"@react-spring/core": "~9.7.4",
|
||||
"@react-spring/shared": "~9.7.4",
|
||||
"@react-spring/types": "~9.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
|
||||
@ -1790,6 +1950,57 @@
|
||||
"@babel/types": "^7.20.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-color": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-delaunay": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||
"integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-interpolate": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-color": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-scale": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
|
||||
"integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-shape": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz",
|
||||
"integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-path": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-time": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
|
||||
"integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
@ -2304,6 +2515,121 @@
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-delaunay": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"delaunator": "5"
|
||||
},
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/date-and-time": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-3.5.0.tgz",
|
||||
@ -2338,6 +2664,15 @@
|
||||
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/delaunator": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
|
||||
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"robust-predicates": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
@ -3045,6 +3380,15 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"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==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
@ -3703,6 +4047,12 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/robust-predicates": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
|
||||
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.21.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz",
|
||||
|
@ -17,6 +17,7 @@
|
||||
"@mdi/react": "^1.6.1",
|
||||
"@mui/icons-material": "^6.0.1",
|
||||
"@mui/material": "^6.0.1",
|
||||
"@mui/x-charts": "^7.15.0",
|
||||
"@mui/x-date-pickers": "^7.15.0",
|
||||
"date-and-time": "^3.5.0",
|
||||
"dayjs": "^1.11.13",
|
||||
|
26
central_frontend/src/api/EnergyApi.ts
Normal file
26
central_frontend/src/api/EnergyApi.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Api } from "@mui/icons-material";
|
||||
import { APIClient } from "./ApiClient";
|
||||
|
||||
export class EnergyApi {
|
||||
/**
|
||||
* Get current house consumption
|
||||
*/
|
||||
static async CurrConsumption(): Promise<number> {
|
||||
const data = await APIClient.exec({
|
||||
method: "GET",
|
||||
uri: "/energy/curr_consumption",
|
||||
});
|
||||
return data.data.consumption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current cached consumption
|
||||
*/
|
||||
static async CachedConsumption(): Promise<number> {
|
||||
const data = await APIClient.exec({
|
||||
method: "GET",
|
||||
uri: "/energy/cached_consumption",
|
||||
});
|
||||
return data.data.consumption;
|
||||
}
|
||||
}
|
@ -1,3 +1,27 @@
|
||||
import { Typography } from "@mui/material";
|
||||
import { CurrConsumptionWidget } from "./HomeRoute/CurrConsumptionWidget";
|
||||
import Grid from "@mui/material/Grid2";
|
||||
import { CachedConsumptionWidget } from "./HomeRoute/CachedConsumptionWidget";
|
||||
|
||||
export function HomeRoute(): React.ReactElement {
|
||||
return <>home authenticated todo</>;
|
||||
return (
|
||||
<div style={{ flex: 1, padding: "10px" }}>
|
||||
<Typography component="h2" variant="h6" sx={{ mb: 2 }}>
|
||||
Overview
|
||||
</Typography>
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
columns={12}
|
||||
sx={{ mb: (theme) => theme.spacing(2) }}
|
||||
>
|
||||
<Grid size={{ xs: 12, sm: 6, lg: 3 }}>
|
||||
<CurrConsumptionWidget />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6, lg: 3 }}>
|
||||
<CachedConsumptionWidget />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { EnergyApi } from "../../api/EnergyApi";
|
||||
import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
|
||||
import StatCard from "../../widgets/StatCard";
|
||||
|
||||
export function CachedConsumptionWidget(): React.ReactElement {
|
||||
const snackbar = useSnackbar();
|
||||
|
||||
const [val, setVal] = React.useState<undefined | number>();
|
||||
|
||||
const refresh = async () => {
|
||||
try {
|
||||
const s = await EnergyApi.CachedConsumption();
|
||||
setVal(s);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
snackbar("Failed to refresh cached consumption!");
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
refresh();
|
||||
const i = setInterval(() => refresh(), 3000);
|
||||
|
||||
return () => clearInterval(i);
|
||||
});
|
||||
|
||||
return (
|
||||
<StatCard
|
||||
title="Cached consumption"
|
||||
data={[]}
|
||||
interval="Current data"
|
||||
trend="neutral"
|
||||
value={val?.toString() ?? "Loading"}
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { EnergyApi } from "../../api/EnergyApi";
|
||||
import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
|
||||
import StatCard from "../../widgets/StatCard";
|
||||
|
||||
export function CurrConsumptionWidget(): React.ReactElement {
|
||||
const snackbar = useSnackbar();
|
||||
|
||||
const [val, setVal] = React.useState<undefined | number>();
|
||||
|
||||
const refresh = async () => {
|
||||
try {
|
||||
const s = await EnergyApi.CurrConsumption();
|
||||
setVal(s);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
snackbar("Failed to refresh current consumption!");
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
refresh();
|
||||
const i = setInterval(() => refresh(), 3000);
|
||||
|
||||
return () => clearInterval(i);
|
||||
});
|
||||
|
||||
return (
|
||||
<StatCard
|
||||
title="Current consumption"
|
||||
data={[]}
|
||||
interval="Current data"
|
||||
trend="neutral"
|
||||
value={val?.toString() ?? "Loading"}
|
||||
/>
|
||||
);
|
||||
}
|
128
central_frontend/src/widgets/StatCard.tsx
Normal file
128
central_frontend/src/widgets/StatCard.tsx
Normal file
@ -0,0 +1,128 @@
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import Box from "@mui/material/Box";
|
||||
import Card from "@mui/material/Card";
|
||||
import CardContent from "@mui/material/CardContent";
|
||||
import Chip from "@mui/material/Chip";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { SparkLineChart } from "@mui/x-charts/SparkLineChart";
|
||||
import { areaElementClasses } from "@mui/x-charts/LineChart";
|
||||
|
||||
export type StatCardProps = {
|
||||
title: string;
|
||||
value: string;
|
||||
interval: string;
|
||||
trend: "up" | "down" | "neutral";
|
||||
data: number[];
|
||||
};
|
||||
|
||||
function getDaysInMonth(month: number, year: number) {
|
||||
const date = new Date(year, month, 0);
|
||||
const monthName = date.toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
});
|
||||
const daysInMonth = date.getDate();
|
||||
const days = [];
|
||||
let i = 1;
|
||||
while (days.length < daysInMonth) {
|
||||
days.push(`${monthName} ${i}`);
|
||||
i += 1;
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
function AreaGradient({ color, id }: { color: string; id: string }) {
|
||||
return (
|
||||
<defs>
|
||||
<linearGradient id={id} x1="50%" y1="0%" x2="50%" y2="100%">
|
||||
<stop offset="0%" stopColor={color} stopOpacity={0.3} />
|
||||
<stop offset="100%" stopColor={color} stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
);
|
||||
}
|
||||
|
||||
export default function StatCard({
|
||||
title,
|
||||
value,
|
||||
interval,
|
||||
trend,
|
||||
data,
|
||||
}: StatCardProps) {
|
||||
const theme = useTheme();
|
||||
const daysInWeek = getDaysInMonth(4, 2024);
|
||||
|
||||
const trendColors = {
|
||||
up:
|
||||
theme.palette.mode === "light"
|
||||
? theme.palette.success.main
|
||||
: theme.palette.success.dark,
|
||||
down:
|
||||
theme.palette.mode === "light"
|
||||
? theme.palette.error.main
|
||||
: theme.palette.error.dark,
|
||||
neutral:
|
||||
theme.palette.mode === "light"
|
||||
? theme.palette.grey[400]
|
||||
: theme.palette.grey[700],
|
||||
};
|
||||
|
||||
const labelColors = {
|
||||
up: "success" as const,
|
||||
down: "error" as const,
|
||||
neutral: "default" as const,
|
||||
};
|
||||
|
||||
const color = labelColors[trend];
|
||||
const chartColor = trendColors[trend];
|
||||
const trendValues = { up: "+25%", down: "-25%", neutral: "+5%" };
|
||||
|
||||
return (
|
||||
<Card variant="outlined" sx={{ height: "100%", flexGrow: 1 }}>
|
||||
<CardContent>
|
||||
<Typography component="h2" variant="subtitle2" gutterBottom>
|
||||
{title}
|
||||
</Typography>
|
||||
<Stack
|
||||
direction="column"
|
||||
sx={{ justifyContent: "space-between", flexGrow: "1", gap: 1 }}
|
||||
>
|
||||
<Stack sx={{ justifyContent: "space-between" }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{ justifyContent: "space-between", alignItems: "center" }}
|
||||
>
|
||||
<Typography variant="h4" component="p">
|
||||
{value}
|
||||
</Typography>
|
||||
<Chip size="small" color={color} label={trendValues[trend]} />
|
||||
</Stack>
|
||||
<Typography variant="caption" sx={{ color: "text.secondary" }}>
|
||||
{interval}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Box sx={{ width: "100%", height: 50 }}>
|
||||
<SparkLineChart
|
||||
colors={[chartColor]}
|
||||
data={data}
|
||||
area
|
||||
showHighlight
|
||||
showTooltip
|
||||
xAxis={{
|
||||
scaleType: "band",
|
||||
data: daysInWeek, // Use the correct property 'data' for xAxis
|
||||
}}
|
||||
sx={{
|
||||
[`& .${areaElementClasses.root}`]: {
|
||||
fill: `url(#area-gradient-${value})`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AreaGradient color={chartColor} id={`area-gradient-${value}`} />
|
||||
</SparkLineChart>
|
||||
</Box>
|
||||
</Stack>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user