forked from ddrilling/asb_cloud_front
Merge branch 'master' of https://bitbucket.org/frolovng/asb_cloud_front_react
This commit is contained in:
commit
6ca6869ea2
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"день"
|
"день"
|
||||||
]
|
],
|
||||||
|
"liveServer.settings.port": 5501
|
||||||
}
|
}
|
113
package-lock.json
generated
113
package-lock.json
generated
@ -1329,15 +1329,6 @@
|
|||||||
"@hapi/hoek": "^8.3.0"
|
"@hapi/hoek": "^8.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@hypnosphi/create-react-context": {
|
|
||||||
"version": "0.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz",
|
|
||||||
"integrity": "sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==",
|
|
||||||
"requires": {
|
|
||||||
"gud": "^1.0.0",
|
|
||||||
"warning": "^4.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@istanbuljs/load-nyc-config": {
|
"@istanbuljs/load-nyc-config": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
||||||
@ -1895,9 +1886,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
|
||||||
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"async-limiter": "~1.0.0"
|
"async-limiter": "~1.0.0"
|
||||||
}
|
}
|
||||||
@ -2379,14 +2370,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
|
||||||
"integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA=="
|
"integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA=="
|
||||||
},
|
},
|
||||||
"@types/http-proxy": {
|
|
||||||
"version": "1.17.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz",
|
|
||||||
"integrity": "sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==",
|
|
||||||
"requires": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/istanbul-lib-coverage": {
|
"@types/istanbul-lib-coverage": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
|
||||||
@ -4256,6 +4239,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.0.0-rc.1.tgz",
|
"resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.0.0-rc.1.tgz",
|
||||||
"integrity": "sha512-p3JwpvIspZKHB7cRas3xoxa5BYeCOMCuCTp/AS/44RyvO8OuZ/Pz0DWl3WK2L+BMnoK72UUm0XnGLKiMEekaYA=="
|
"integrity": "sha512-p3JwpvIspZKHB7cRas3xoxa5BYeCOMCuCTp/AS/44RyvO8OuZ/Pz0DWl3WK2L+BMnoK72UUm0XnGLKiMEekaYA=="
|
||||||
},
|
},
|
||||||
|
"chartjs-plugin-zoom": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-plugin-zoom/-/chartjs-plugin-zoom-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-1q54WOzK7FtAjkbemQeqvmFUV0btNYIQny2HbQ6Awq9wUtCz7Zmj6vIgp3C1DYMQwN0nqgpC3vnApqiwI7cSdQ==",
|
||||||
|
"requires": {
|
||||||
|
"hammerjs": "^2.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"check-types": {
|
"check-types": {
|
||||||
"version": "11.1.2",
|
"version": "11.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz",
|
||||||
@ -7353,11 +7344,6 @@
|
|||||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
|
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"gud": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
|
|
||||||
},
|
|
||||||
"gzip-size": {
|
"gzip-size": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
|
||||||
@ -7367,6 +7353,11 @@
|
|||||||
"pify": "^4.0.1"
|
"pify": "^4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"hammerjs": {
|
||||||
|
"version": "2.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||||
|
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
|
||||||
|
},
|
||||||
"handle-thing": {
|
"handle-thing": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
|
||||||
@ -7751,25 +7742,6 @@
|
|||||||
"requires-port": "^1.0.0"
|
"requires-port": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"http-proxy-middleware": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==",
|
|
||||||
"requires": {
|
|
||||||
"@types/http-proxy": "^1.17.5",
|
|
||||||
"http-proxy": "^1.18.1",
|
|
||||||
"is-glob": "^4.0.1",
|
|
||||||
"is-plain-obj": "^3.0.0",
|
|
||||||
"micromatch": "^4.0.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"is-plain-obj": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http-signature": {
|
"http-signature": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||||
@ -10392,8 +10364,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hosted-git-info": {
|
"hosted-git-info": {
|
||||||
"version": "2.8.8",
|
"version": "2.8.8",
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
|
"resolved": "",
|
||||||
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
|
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"normalize-package-data": {
|
"normalize-package-data": {
|
||||||
@ -11690,11 +11661,6 @@
|
|||||||
"ts-pnp": "^1.1.6"
|
"ts-pnp": "^1.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"popper.js": {
|
|
||||||
"version": "1.16.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
|
||||||
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
|
|
||||||
},
|
|
||||||
"portfinder": {
|
"portfinder": {
|
||||||
"version": "1.0.28",
|
"version": "1.0.28",
|
||||||
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
|
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
|
||||||
@ -13401,18 +13367,6 @@
|
|||||||
"whatwg-fetch": "^3.4.1"
|
"whatwg-fetch": "^3.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-datepicker": {
|
|
||||||
"version": "3.8.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-3.8.0.tgz",
|
|
||||||
"integrity": "sha512-iFVNEp8DJoX5yEvEiciM7sJKmLGrvE70U38KhpG13XrulNSijeHw1RZkhd/0UmuXR71dcZB/kdfjiidifstZjw==",
|
|
||||||
"requires": {
|
|
||||||
"classnames": "^2.2.6",
|
|
||||||
"date-fns": "^2.0.1",
|
|
||||||
"prop-types": "^15.7.2",
|
|
||||||
"react-onclickoutside": "^6.10.0",
|
|
||||||
"react-popper": "^1.3.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"react-dev-utils": {
|
"react-dev-utils": {
|
||||||
"version": "11.0.4",
|
"version": "11.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
|
||||||
@ -13539,25 +13493,6 @@
|
|||||||
"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=="
|
||||||
},
|
},
|
||||||
"react-onclickoutside": {
|
|
||||||
"version": "6.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.10.0.tgz",
|
|
||||||
"integrity": "sha512-7i2L3ef+0ILXpL6P+Hg304eCQswh4jl3ynwR71BSlMU49PE2uk31k8B2GkP6yE9s2D4jTGKnzuSpzWxu4YxfQQ=="
|
|
||||||
},
|
|
||||||
"react-popper": {
|
|
||||||
"version": "1.3.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz",
|
|
||||||
"integrity": "sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.1.2",
|
|
||||||
"@hypnosphi/create-react-context": "^0.3.1",
|
|
||||||
"deep-equal": "^1.1.1",
|
|
||||||
"popper.js": "^1.14.4",
|
|
||||||
"prop-types": "^15.6.1",
|
|
||||||
"typed-styles": "^0.0.7",
|
|
||||||
"warning": "^4.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"react-refresh": {
|
"react-refresh": {
|
||||||
"version": "0.8.3",
|
"version": "0.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
|
||||||
@ -15910,11 +15845,6 @@
|
|||||||
"mime-types": "~2.1.24"
|
"mime-types": "~2.1.24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typed-styles": {
|
|
||||||
"version": "0.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
|
|
||||||
"integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
|
|
||||||
},
|
|
||||||
"typedarray": {
|
"typedarray": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
@ -16768,8 +16698,7 @@
|
|||||||
},
|
},
|
||||||
"ssri": {
|
"ssri": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
|
"resolved": "",
|
||||||
"integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"figgy-pudding": "^3.5.1"
|
"figgy-pudding": "^3.5.1"
|
||||||
}
|
}
|
||||||
@ -17263,9 +17192,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
|
||||||
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"async-limiter": "~1.0.0"
|
"async-limiter": "~1.0.0"
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,11 @@
|
|||||||
"chart.js": "^3.3.0",
|
"chart.js": "^3.3.0",
|
||||||
"chartjs-adapter-moment": "^1.0.0",
|
"chartjs-adapter-moment": "^1.0.0",
|
||||||
"chartjs-plugin-datalabels": "^2.0.0-rc.1",
|
"chartjs-plugin-datalabels": "^2.0.0-rc.1",
|
||||||
|
"chartjs-plugin-zoom": "^1.1.1",
|
||||||
"craco-less": "^1.17.1",
|
"craco-less": "^1.17.1",
|
||||||
"date-fns": "^2.20.0",
|
|
||||||
"http-proxy-middleware": "^2.0.1",
|
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"pigeon-maps": "^0.19.7",
|
"pigeon-maps": "^0.19.7",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-datepicker": "^3.8.0",
|
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
|
@ -6,16 +6,6 @@
|
|||||||
"src": "favicon.ico",
|
"src": "favicon.ico",
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
"type": "image/x-icon"
|
"type": "image/x-icon"
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo192.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo512.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
// const { createProxyMiddleware } = require('http-proxy-middleware');
|
|
||||||
|
|
||||||
// module.exports = function(app) {
|
|
||||||
// app.use(
|
|
||||||
// '/api',
|
|
||||||
// createProxyMiddleware({
|
|
||||||
// target: 'http://192.168.1.70:5000',
|
|
||||||
// changeOrigin: true,
|
|
||||||
// ws:true,
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
// app.use(
|
|
||||||
// '/hubs',
|
|
||||||
// createProxyMiddleware({
|
|
||||||
// target: 'http://192.168.1.70:5000',
|
|
||||||
// changeOrigin: true,
|
|
||||||
// ws:true,
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
// };
|
|
41
src/components/AnalysisDepthToDay.jsx
Normal file
41
src/components/AnalysisDepthToDay.jsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { ChartDepthToDay } from './charts/ChartDepthToDay'
|
||||||
|
import { useParams } from "react-router-dom"
|
||||||
|
import notify from "../components/notify"
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { AnalyticsService } from "../services/api"
|
||||||
|
|
||||||
|
const lines = [
|
||||||
|
{ label: "Глубина забоя", yAccessorName: "wellDepth", color: '#f00' },
|
||||||
|
{ label: "Положение инструмента", yAccessorName: "bitDepth", color: '#ff0' }
|
||||||
|
]
|
||||||
|
|
||||||
|
export function AnalysisDepthToDay() {
|
||||||
|
let { id } = useParams()
|
||||||
|
const [depthToDayData, setDepthToDayData] = useState([])
|
||||||
|
const [loader, setLoader] = useState(false)
|
||||||
|
|
||||||
|
const handleReceiveDepthToDayData = (data) => {
|
||||||
|
setDepthToDayData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoader(true)
|
||||||
|
AnalyticsService.getWellDepthToDay(id)
|
||||||
|
.then(handleReceiveDepthToDayData)
|
||||||
|
.catch(error => {
|
||||||
|
notify(`Не удалось получить данные для Анализа Глубина-День по скважине "${id}"`,
|
||||||
|
'warning')
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
.finally(setLoader(false))
|
||||||
|
}, [id])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ChartDepthToDay
|
||||||
|
data={depthToDayData}
|
||||||
|
lines={lines}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
61
src/components/AnalysisDepthToInterval.jsx
Normal file
61
src/components/AnalysisDepthToInterval.jsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { useParams } from "react-router-dom"
|
||||||
|
import { DatePicker } from 'antd'
|
||||||
|
import notify from "../components/notify"
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { AnalyticsService } from '../services/api'
|
||||||
|
import { ChartDepthToInterval } from './charts/ChartDepthToInterval'
|
||||||
|
import { Select } from 'antd'
|
||||||
|
|
||||||
|
const { Option } = Select
|
||||||
|
|
||||||
|
const timePeriodCollection = [
|
||||||
|
{ value: '60', label: '1 минута' },
|
||||||
|
{ value: '300', label: '5 минут' },
|
||||||
|
{ value: '600', label: '10 минут' },
|
||||||
|
{ value: '1800', label: '30 минут' },
|
||||||
|
{ value: '3600', label: '1 час' },
|
||||||
|
{ value: '21600', label: '6 часов' },
|
||||||
|
{ value: '43200', label: '12 часов' },
|
||||||
|
{ value: '86400', label: '24 часа' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const { RangePicker } = DatePicker
|
||||||
|
|
||||||
|
const lines = [{ label: 'График скорость проходки-интервал', yAccessorName: "intervalDepthProgress", xAccessorName: "intervalStartDate", color: '#00f' }]
|
||||||
|
|
||||||
|
export function AnalysisDepthToInterval() {
|
||||||
|
let { id } = useParams()
|
||||||
|
const [depthToIntervalData, setDepthToIntervalData] = useState([])
|
||||||
|
const [loader, setLoader] = useState(false)
|
||||||
|
const [chartInterval, setChartInterval] = useState(600)
|
||||||
|
|
||||||
|
const children = timePeriodCollection.map((line) => <Option key={line.value}>{line.label}</Option>)
|
||||||
|
|
||||||
|
const handleReceiveDepthToIntervalData = (data) => {
|
||||||
|
setDepthToIntervalData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoader(true)
|
||||||
|
AnalyticsService.getWellDepthToInterval(id, chartInterval)
|
||||||
|
.then(handleReceiveDepthToIntervalData)
|
||||||
|
.catch(error => {
|
||||||
|
notify(`Не удалось получить данные для Анализа скорость проходки-интервал "${id}"`,
|
||||||
|
'warning')
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
.finally(setLoader(false))
|
||||||
|
}, [id, chartInterval])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Select defaultValue="600" onChange={setChartInterval}>
|
||||||
|
{children}
|
||||||
|
</Select>
|
||||||
|
<ChartDepthToInterval
|
||||||
|
data={depthToIntervalData}
|
||||||
|
lines={lines}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
60
src/components/AnalysisOperationTime.jsx
Normal file
60
src/components/AnalysisOperationTime.jsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { useParams } from "react-router-dom"
|
||||||
|
import { DatePicker, ConfigProvider } from 'antd';
|
||||||
|
import notify from "../components/notify"
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { AnalyticsService } from '../services/api'
|
||||||
|
import { ChartOperationTime } from './charts/ChartOperationTime'
|
||||||
|
import locale from "antd/lib/locale/ru_RU";
|
||||||
|
|
||||||
|
const { RangePicker } = DatePicker
|
||||||
|
|
||||||
|
const lines = [{ labelAccessorName: "processName", pieceAccessorName: "duration" }]
|
||||||
|
|
||||||
|
export function AnalysisOperationTime() {
|
||||||
|
let { id } = useParams()
|
||||||
|
const [operationTimeData, setOperationTimeData] = useState([])
|
||||||
|
const [loader, setLoader] = useState(false)
|
||||||
|
const [range, setRange] = useState([])
|
||||||
|
|
||||||
|
let begin = null
|
||||||
|
let end = null
|
||||||
|
|
||||||
|
const onChangeRange = (range) => {
|
||||||
|
setRange(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReceiveOperationTimeData = (data) => {
|
||||||
|
setOperationTimeData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoader(true)
|
||||||
|
if (range?.length > 1) {
|
||||||
|
begin = range[0].toISOString()
|
||||||
|
end = range[1].toISOString()
|
||||||
|
}
|
||||||
|
AnalyticsService.getOperationsSummary(id, begin, end)
|
||||||
|
.then(handleReceiveOperationTimeData)
|
||||||
|
.catch(error => {
|
||||||
|
notify(`Не удалось получить данные для Анализа Операция-Время по скважине "${id}" за период с ${begin} по ${end}`,
|
||||||
|
'warning')
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
.finally(setLoader(false))
|
||||||
|
}, [id, range])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ConfigProvider locale={locale}>
|
||||||
|
<RangePicker
|
||||||
|
showTime
|
||||||
|
onChange={onChangeRange}
|
||||||
|
/>
|
||||||
|
</ConfigProvider>
|
||||||
|
<ChartOperationTime
|
||||||
|
data={operationTimeData}
|
||||||
|
lines={lines}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
38
src/components/charts/ChartDepthToDay.jsx
Normal file
38
src/components/charts/ChartDepthToDay.jsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { ChartDepthToDayBase } from './ChartDepthToDayBase'
|
||||||
|
import { CreateDataset } from './ChartTimeArchive'
|
||||||
|
|
||||||
|
export const ChartDepthToDay = ({data, lines}) => {
|
||||||
|
const [depthToDayDataParams, setDepthToDayDataParams] = useState({data: {datasets: []}})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ((!lines)
|
||||||
|
|| (!data))
|
||||||
|
return
|
||||||
|
|
||||||
|
let newDatasets = lines.map(lineCfg => {
|
||||||
|
let datasets = CreateDataset(lineCfg)
|
||||||
|
if(data.length !== 0)
|
||||||
|
datasets.data = data.map(dataItem => {
|
||||||
|
return {
|
||||||
|
x: new Date(dataItem[lineCfg.xAccessorName??'date']),
|
||||||
|
y: dataItem[lineCfg.yAccessorName],
|
||||||
|
label: dataItem[lineCfg.label]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return datasets
|
||||||
|
})
|
||||||
|
|
||||||
|
let newParams = {
|
||||||
|
displayLabels: true,
|
||||||
|
data: {
|
||||||
|
datasets: newDatasets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setDepthToDayDataParams(newParams)
|
||||||
|
}, [data, lines])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChartDepthToDayBase dataParams={depthToDayDataParams} />
|
||||||
|
)
|
||||||
|
}
|
224
src/components/charts/ChartDepthToDayBase.tsx
Normal file
224
src/components/charts/ChartDepthToDayBase.tsx
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
import {useEffect, useRef, useState} from 'react';
|
||||||
|
import {
|
||||||
|
Chart,
|
||||||
|
TimeScale,
|
||||||
|
LinearScale,
|
||||||
|
Legend,
|
||||||
|
LineController,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
ChartData,
|
||||||
|
ChartTypeRegistry,
|
||||||
|
ChartOptions
|
||||||
|
} from 'chart.js'
|
||||||
|
import 'chartjs-adapter-moment';
|
||||||
|
import ChartDataLabels from 'chartjs-plugin-datalabels';
|
||||||
|
import zoomPlugin from 'chartjs-plugin-zoom';
|
||||||
|
|
||||||
|
Chart.register(TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels, zoomPlugin);
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
responsive: true,
|
||||||
|
aspectRatio: 6,
|
||||||
|
animation: false,
|
||||||
|
tooltips: {
|
||||||
|
enabled: true,
|
||||||
|
callbacks: {
|
||||||
|
label(tooltipItem:any) {
|
||||||
|
return tooltipItem.yLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
|
||||||
|
scales: {
|
||||||
|
x:{
|
||||||
|
type: 'time',
|
||||||
|
reverse: false,
|
||||||
|
time: {
|
||||||
|
stepSize: 20,
|
||||||
|
displayFormats: {
|
||||||
|
millisecond: 'HH:mm:ss.SSS',
|
||||||
|
second: 'HH:mm:ss',
|
||||||
|
minute: 'HH:mm:ss',
|
||||||
|
hour: 'DD HH:mm:ss',
|
||||||
|
day: 'MM.DD HH:mm',
|
||||||
|
week: 'yy.MM.DD HH:mm',
|
||||||
|
month: 'yyyy.MM.DD',
|
||||||
|
quarter: 'yyyy.MM.DD',
|
||||||
|
year: 'yyyy.MM',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid:{
|
||||||
|
drawTicks: false,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
z: 1,
|
||||||
|
display : true,
|
||||||
|
textStrokeColor : "#fff",
|
||||||
|
textStrokeWidth : 2,
|
||||||
|
color:"#000",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
y:{
|
||||||
|
type:'linear',
|
||||||
|
position:'top'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
elements:{
|
||||||
|
point:{
|
||||||
|
radius:0,
|
||||||
|
hoverRadius:5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins:{
|
||||||
|
legend:{
|
||||||
|
display: true,
|
||||||
|
},
|
||||||
|
datalabels: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChartTimeData = ChartData<keyof ChartTypeRegistry, {
|
||||||
|
x: Date;
|
||||||
|
label: number;
|
||||||
|
y: number;
|
||||||
|
}[], unknown>
|
||||||
|
|
||||||
|
export type ChartTimeDataParams = {
|
||||||
|
data: ChartTimeData,
|
||||||
|
xStart?: Date,
|
||||||
|
xInterval?: number,
|
||||||
|
displayLabels?: Boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChartTimeBaseProps = {
|
||||||
|
dataParams: ChartTimeDataParams,
|
||||||
|
// TODO: Create good type for options
|
||||||
|
options?: ChartOptions<keyof ChartTypeRegistry> | any,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TimeParams = {
|
||||||
|
unit: String
|
||||||
|
stepSize: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const linesPerInterval = 32
|
||||||
|
|
||||||
|
export const timeUnitByInterval = (intervalSec:number):String => {
|
||||||
|
if(intervalSec <= 60)
|
||||||
|
return 'millisecond'
|
||||||
|
|
||||||
|
if(intervalSec <= 32*60)
|
||||||
|
return 'second'
|
||||||
|
|
||||||
|
if(intervalSec <= 32*60*60)
|
||||||
|
return 'minute'
|
||||||
|
|
||||||
|
if(intervalSec <= 32*12*60*60)
|
||||||
|
return 'hour'
|
||||||
|
|
||||||
|
if(intervalSec <= 32*24*60*60)
|
||||||
|
return 'day'
|
||||||
|
|
||||||
|
if(intervalSec <= 32*7*24*60*60)
|
||||||
|
return 'week'
|
||||||
|
|
||||||
|
if(intervalSec <= 32*30.4375*24*60*60)
|
||||||
|
return 'month'
|
||||||
|
|
||||||
|
if(intervalSec <= 32*121.75*24*60*60)
|
||||||
|
return 'quarter'
|
||||||
|
else
|
||||||
|
return 'year'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const timeParamsByInterval = (intervalSec:number) :TimeParams => {
|
||||||
|
let stepSize = intervalSec
|
||||||
|
let unit = timeUnitByInterval(intervalSec)
|
||||||
|
|
||||||
|
switch(unit){
|
||||||
|
case "millisecond":
|
||||||
|
stepSize *= 1000
|
||||||
|
break;
|
||||||
|
case "second":
|
||||||
|
//stepSize *= 1
|
||||||
|
break;
|
||||||
|
case "minute":
|
||||||
|
stepSize /= 60
|
||||||
|
break;
|
||||||
|
case "hour":
|
||||||
|
stepSize /= 60*60
|
||||||
|
break;
|
||||||
|
case "day":
|
||||||
|
stepSize /= 24*60*60
|
||||||
|
break;
|
||||||
|
case "week":
|
||||||
|
stepSize /= 7*24*60*60
|
||||||
|
break;
|
||||||
|
case "month":
|
||||||
|
stepSize /= 30*24*60*60
|
||||||
|
break;
|
||||||
|
case "quarter":
|
||||||
|
stepSize /= 91*24*60*60
|
||||||
|
break;
|
||||||
|
case "year":
|
||||||
|
stepSize /= 365.25*24*60*60
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
stepSize = Math.round(stepSize/linesPerInterval)
|
||||||
|
stepSize = stepSize > 0 ? stepSize : 1;
|
||||||
|
return {unit, stepSize}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChartDepthToDayBase: React.FC<ChartTimeBaseProps> = ({options, dataParams}) => {
|
||||||
|
const chartRef = useRef<HTMLCanvasElement>(null)
|
||||||
|
const [chart, setChart] = useState<any>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if((chartRef.current)&&(!chart)) {
|
||||||
|
let thisOptions = {}
|
||||||
|
Object.assign(thisOptions, defaultOptions, options)
|
||||||
|
|
||||||
|
let newChart = new Chart(chartRef.current, {
|
||||||
|
type: 'line',
|
||||||
|
plugins: [ChartDataLabels],
|
||||||
|
options: thisOptions,
|
||||||
|
data: dataParams.data
|
||||||
|
})
|
||||||
|
setChart(newChart)
|
||||||
|
|
||||||
|
return () => chart?.destroy()
|
||||||
|
}
|
||||||
|
}, [chart, options, dataParams])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chart)
|
||||||
|
return
|
||||||
|
|
||||||
|
chart.data = dataParams.data
|
||||||
|
chart.options.aspectRatio = options?.aspectRatio
|
||||||
|
if (dataParams.xStart) {
|
||||||
|
let interval = Number(dataParams.xInterval ?? 600)
|
||||||
|
let start = new Date(dataParams.xStart)
|
||||||
|
let end = new Date(dataParams.xStart)
|
||||||
|
end.setSeconds(end.getSeconds() + interval)
|
||||||
|
let {unit, stepSize} = timeParamsByInterval(interval)
|
||||||
|
|
||||||
|
if(chart.options.scales?.x){
|
||||||
|
chart.options.scales.x.max = end.getTime()
|
||||||
|
chart.options.scales.x.min = start.getTime()
|
||||||
|
chart.options.scales.x.ticks.display = dataParams.displayLabels ?? true
|
||||||
|
chart.options.scales.x.time.unit = unit
|
||||||
|
chart.options.scales.x.time.stepSize = stepSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.update()
|
||||||
|
}, [chart, dataParams, options])
|
||||||
|
|
||||||
|
return(<canvas ref={chartRef} />)
|
||||||
|
}
|
39
src/components/charts/ChartDepthToInterval.jsx
Normal file
39
src/components/charts/ChartDepthToInterval.jsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { ChartDepthToIntervalBase } from './ChartDepthToIntervalBase'
|
||||||
|
import { CreateDataset } from './ChartTimeArchive'
|
||||||
|
|
||||||
|
export const ChartDepthToInterval = ({ lines, data }) => {
|
||||||
|
const [depthToIntervalDataParams, setDepthToIntervalDataParams] = useState({ data: { datasets: [] } })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ((!lines)
|
||||||
|
|| (!data))
|
||||||
|
return
|
||||||
|
|
||||||
|
let newDatasets = lines.map(lineCfg => {
|
||||||
|
let datasets = CreateDataset(lineCfg)
|
||||||
|
if (data.length !== 0)
|
||||||
|
datasets.data = data.map(dataItem => {
|
||||||
|
return {
|
||||||
|
x: new Date(dataItem[lineCfg.xAccessorName ?? 'intervalStartDate']),
|
||||||
|
y: dataItem[lineCfg.yAccessorName ?? 'intervalDepthProgress'],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return datasets
|
||||||
|
})
|
||||||
|
|
||||||
|
let newParams = {
|
||||||
|
displayLabels: true,
|
||||||
|
data: {
|
||||||
|
datasets: newDatasets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setDepthToIntervalDataParams(newParams)
|
||||||
|
|
||||||
|
}, [data, lines])
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
<ChartDepthToIntervalBase dataParams={depthToIntervalDataParams} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
224
src/components/charts/ChartDepthToIntervalBase.tsx
Normal file
224
src/components/charts/ChartDepthToIntervalBase.tsx
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import {
|
||||||
|
Chart,
|
||||||
|
TimeScale,
|
||||||
|
Legend,
|
||||||
|
PointElement,
|
||||||
|
ChartData,
|
||||||
|
ChartTypeRegistry,
|
||||||
|
ChartOptions,
|
||||||
|
BarController,
|
||||||
|
BarElement,
|
||||||
|
TimeSeriesScale,
|
||||||
|
LinearScale,
|
||||||
|
LineController,
|
||||||
|
} from 'chart.js'
|
||||||
|
import 'chartjs-adapter-moment'
|
||||||
|
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
||||||
|
|
||||||
|
Chart.register(TimeScale, BarController, BarElement, PointElement, TimeSeriesScale, LineController, LinearScale, Legend, ChartDataLabels)
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
responsive: true,
|
||||||
|
aspectRatio: 4,
|
||||||
|
animation: false,
|
||||||
|
tooltips: {
|
||||||
|
enabled: true,
|
||||||
|
callbacks: {
|
||||||
|
label(tooltipItem: any) {
|
||||||
|
return tooltipItem.yLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
position: 'bottom',
|
||||||
|
type: 'time',
|
||||||
|
reverse: false,
|
||||||
|
time: {
|
||||||
|
stepSize: 20,
|
||||||
|
displayFormats: {
|
||||||
|
millisecond: 'HH:mm:ss.SSS',
|
||||||
|
second: 'HH:mm:ss',
|
||||||
|
minute: 'HH:mm:ss',
|
||||||
|
hour: 'DD HH:mm:ss',
|
||||||
|
day: 'MM.DD HH:mm',
|
||||||
|
week: 'yy.MM.DD HH:mm',
|
||||||
|
month: 'yyyy.MM.DD',
|
||||||
|
quarter: 'yyyy.MM.DD',
|
||||||
|
year: 'yyyy.MM',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
drawTicks: false,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
z: 1,
|
||||||
|
display: true,
|
||||||
|
textStrokeColor: "#ffff",
|
||||||
|
textStrokeWidth: 2,
|
||||||
|
color: "#000",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
elements: {
|
||||||
|
point: {
|
||||||
|
radius: 0,
|
||||||
|
hoverRadius: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
},
|
||||||
|
datalabels: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChartTimeData = ChartData<keyof ChartTypeRegistry, {
|
||||||
|
x: Date;
|
||||||
|
label: number;
|
||||||
|
y: number;
|
||||||
|
}[], unknown>
|
||||||
|
|
||||||
|
export type ChartTimeDataParams = {
|
||||||
|
data: ChartTimeData,
|
||||||
|
xStart?: Date,
|
||||||
|
xInterval?: number,
|
||||||
|
displayLabels?: Boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChartTimeBaseProps = {
|
||||||
|
dataParams: ChartTimeDataParams,
|
||||||
|
// TODO: Create good type for options
|
||||||
|
options?: ChartOptions<keyof ChartTypeRegistry> | any,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TimeParams = {
|
||||||
|
unit: String
|
||||||
|
stepSize: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const linesPerInterval = 32
|
||||||
|
|
||||||
|
export const timeUnitByInterval = (intervalSec: number): String => {
|
||||||
|
if (intervalSec <= 60)
|
||||||
|
return 'millisecond'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 60)
|
||||||
|
return 'second'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 60 * 60)
|
||||||
|
return 'minute'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 12 * 60 * 60)
|
||||||
|
return 'hour'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 24 * 60 * 60)
|
||||||
|
return 'day'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 7 * 24 * 60 * 60)
|
||||||
|
return 'week'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 30.4375 * 24 * 60 * 60)
|
||||||
|
return 'month'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 121.75 * 24 * 60 * 60)
|
||||||
|
return 'quarter'
|
||||||
|
else
|
||||||
|
return 'year'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const timeParamsByInterval = (intervalSec: number): TimeParams => {
|
||||||
|
let stepSize = intervalSec
|
||||||
|
let unit = timeUnitByInterval(intervalSec)
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case "millisecond":
|
||||||
|
stepSize *= 1000
|
||||||
|
break;
|
||||||
|
case "second":
|
||||||
|
//stepSize *= 1
|
||||||
|
break;
|
||||||
|
case "minute":
|
||||||
|
stepSize /= 60
|
||||||
|
break;
|
||||||
|
case "hour":
|
||||||
|
stepSize /= 60 * 60
|
||||||
|
break;
|
||||||
|
case "day":
|
||||||
|
stepSize /= 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "week":
|
||||||
|
stepSize /= 7 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "month":
|
||||||
|
stepSize /= 30 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "quarter":
|
||||||
|
stepSize /= 91 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "year":
|
||||||
|
stepSize /= 365.25 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
stepSize = Math.round(stepSize / linesPerInterval)
|
||||||
|
stepSize = stepSize > 0 ? stepSize : 1;
|
||||||
|
return { unit, stepSize }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChartDepthToIntervalBase: React.FC<ChartTimeBaseProps> = ({ options, dataParams }) => {
|
||||||
|
const chartRef = useRef<HTMLCanvasElement>(null)
|
||||||
|
const [chart, setChart] = useState<any>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ((chartRef.current) && (!chart)) {
|
||||||
|
let thisOptions = {}
|
||||||
|
Object.assign(thisOptions, defaultOptions, options)
|
||||||
|
|
||||||
|
let newChart = new Chart(chartRef.current, {
|
||||||
|
type: 'bar',
|
||||||
|
plugins: [ChartDataLabels],
|
||||||
|
options: thisOptions,
|
||||||
|
data: dataParams.data
|
||||||
|
})
|
||||||
|
setChart(newChart)
|
||||||
|
|
||||||
|
return () => chart?.destroy()
|
||||||
|
}
|
||||||
|
}, [chart, options, dataParams])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chart)
|
||||||
|
return
|
||||||
|
|
||||||
|
chart.data = dataParams.data
|
||||||
|
chart.options.aspectRatio = options?.aspectRatio
|
||||||
|
if (dataParams.xStart) {
|
||||||
|
let interval = Number(dataParams.xInterval ?? 600)
|
||||||
|
let start = new Date(dataParams.xStart)
|
||||||
|
let end = new Date(dataParams.xStart)
|
||||||
|
end.setSeconds(end.getSeconds() + interval)
|
||||||
|
let { unit, stepSize } = timeParamsByInterval(interval)
|
||||||
|
|
||||||
|
if (chart.options.scales?.x) {
|
||||||
|
chart.options.scales.x.max = end.getTime()
|
||||||
|
chart.options.scales.x.min = start.getTime()
|
||||||
|
chart.options.scales.x.ticks.display = dataParams.displayLabels ?? true
|
||||||
|
chart.options.scales.x.time.unit = unit
|
||||||
|
chart.options.scales.x.time.stepSize = stepSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.update()
|
||||||
|
}, [chart, dataParams, options])
|
||||||
|
|
||||||
|
return (<canvas height='100%' width='100%' ref={chartRef} />)
|
||||||
|
}
|
67
src/components/charts/ChartOperationTime.jsx
Normal file
67
src/components/charts/ChartOperationTime.jsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { ChartOpertationTimeBase } from './ChartOperationTimeBase'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
export const CreateLabels = () => {
|
||||||
|
let labels = []
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CreateData = (lineConfig) => {
|
||||||
|
let datasets = {
|
||||||
|
label: lineConfig.label,
|
||||||
|
data: [],
|
||||||
|
backgroundColor: [
|
||||||
|
'#f00', '#ff0', '#f0f', '#0ff', '#00f', '#0f0'
|
||||||
|
],
|
||||||
|
}
|
||||||
|
return datasets
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChartOperationTime = ({ lines, data, rangeDate }) => {
|
||||||
|
const [opertationTimeDataParams, setOpertationTimeDataParams] = useState({ data: { labels: [], datasets: [] } })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ((!lines)
|
||||||
|
|| (!data))
|
||||||
|
return
|
||||||
|
|
||||||
|
let newLabels = lines.map(lineCfg => {
|
||||||
|
let labels = CreateLabels(lineCfg)
|
||||||
|
if (data.length !== 0)
|
||||||
|
labels = data.map(dataItem => {
|
||||||
|
return dataItem[lineCfg.labelAccessorName]
|
||||||
|
})
|
||||||
|
return labels
|
||||||
|
})
|
||||||
|
|
||||||
|
let newDatasets = lines.map(lineCfg => {
|
||||||
|
let datasets = CreateData(lineCfg)
|
||||||
|
if (data.length !== 0)
|
||||||
|
datasets.data = data.map(dataItem => {
|
||||||
|
return dataItem[lineCfg.pieceAccessorName]
|
||||||
|
})
|
||||||
|
return datasets
|
||||||
|
})
|
||||||
|
|
||||||
|
let interval = rangeDate ? (rangeDate[1] - rangeDate[0]) / 1000 : null
|
||||||
|
let startDate = rangeDate ? rangeDate[0] : moment()
|
||||||
|
let newParams = {
|
||||||
|
xInterval: interval,
|
||||||
|
xStart: startDate,
|
||||||
|
data: {
|
||||||
|
labels: newLabels,
|
||||||
|
datasets: newDatasets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setOpertationTimeDataParams(newParams)
|
||||||
|
|
||||||
|
console.log(newParams)
|
||||||
|
|
||||||
|
}, [data, lines, rangeDate])
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
<ChartOpertationTimeBase dataParams={opertationTimeDataParams} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
177
src/components/charts/ChartOperationTimeBase.tsx
Normal file
177
src/components/charts/ChartOperationTimeBase.tsx
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import {
|
||||||
|
Chart,
|
||||||
|
ArcElement,
|
||||||
|
TimeScale,
|
||||||
|
Legend,
|
||||||
|
PointElement,
|
||||||
|
ChartData,
|
||||||
|
ChartTypeRegistry,
|
||||||
|
ChartOptions,
|
||||||
|
DoughnutController,
|
||||||
|
} from 'chart.js'
|
||||||
|
import 'chartjs-adapter-moment'
|
||||||
|
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
||||||
|
|
||||||
|
Chart.register(TimeScale, DoughnutController, PointElement, ArcElement, Legend, ChartDataLabels)
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
responsive: true,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
position: "top",
|
||||||
|
text: "Doughnut Chart",
|
||||||
|
fontSize: 18,
|
||||||
|
fontColor: "#111"
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: "bottom",
|
||||||
|
labels: {
|
||||||
|
fontColor: "#333",
|
||||||
|
fontSize: 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChartTimeData = ChartData<keyof ChartTypeRegistry, {
|
||||||
|
x: String;
|
||||||
|
label: number;
|
||||||
|
y: number;
|
||||||
|
}[], unknown>
|
||||||
|
|
||||||
|
export type ChartTimeDataParams = {
|
||||||
|
data: ChartTimeData,
|
||||||
|
xStart?: Date,
|
||||||
|
xInterval?: number,
|
||||||
|
displayLabels?: Boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChartTimeBaseProps = {
|
||||||
|
dataParams: ChartTimeDataParams,
|
||||||
|
// TODO: Create good type for options
|
||||||
|
options?: ChartOptions<keyof ChartTypeRegistry> | any,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TimeParams = {
|
||||||
|
unit: String
|
||||||
|
stepSize: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const linesPerInterval = 32
|
||||||
|
|
||||||
|
export const timeUnitByInterval = (intervalSec: number): String => {
|
||||||
|
if (intervalSec <= 60)
|
||||||
|
return 'millisecond'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 60)
|
||||||
|
return 'second'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 60 * 60)
|
||||||
|
return 'minute'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 12 * 60 * 60)
|
||||||
|
return 'hour'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 24 * 60 * 60)
|
||||||
|
return 'day'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 7 * 24 * 60 * 60)
|
||||||
|
return 'week'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 30.4375 * 24 * 60 * 60)
|
||||||
|
return 'month'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 121.75 * 24 * 60 * 60)
|
||||||
|
return 'quarter'
|
||||||
|
else
|
||||||
|
return 'year'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const timeParamsByInterval = (intervalSec: number): TimeParams => {
|
||||||
|
let stepSize = intervalSec
|
||||||
|
let unit = timeUnitByInterval(intervalSec)
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case "millisecond":
|
||||||
|
stepSize *= 1000
|
||||||
|
break;
|
||||||
|
case "second":
|
||||||
|
//stepSize *= 1
|
||||||
|
break;
|
||||||
|
case "minute":
|
||||||
|
stepSize /= 60
|
||||||
|
break;
|
||||||
|
case "hour":
|
||||||
|
stepSize /= 60 * 60
|
||||||
|
break;
|
||||||
|
case "day":
|
||||||
|
stepSize /= 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "week":
|
||||||
|
stepSize /= 7 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "month":
|
||||||
|
stepSize /= 30 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "quarter":
|
||||||
|
stepSize /= 91 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "year":
|
||||||
|
stepSize /= 365.25 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
stepSize = Math.round(stepSize / linesPerInterval)
|
||||||
|
stepSize = stepSize > 0 ? stepSize : 1;
|
||||||
|
return { unit, stepSize }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChartOpertationTimeBase: React.FC<ChartTimeBaseProps> = ({ options, dataParams }) => {
|
||||||
|
const chartRef = useRef<HTMLCanvasElement>(null)
|
||||||
|
const [chart, setChart] = useState<any>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ((chartRef.current) && (!chart)) {
|
||||||
|
let thisOptions = {}
|
||||||
|
Object.assign(thisOptions, defaultOptions, options)
|
||||||
|
|
||||||
|
let newChart = new Chart(chartRef.current, {
|
||||||
|
type: 'doughnut',
|
||||||
|
plugins: [ChartDataLabels],
|
||||||
|
options: thisOptions,
|
||||||
|
data: dataParams.data
|
||||||
|
})
|
||||||
|
setChart(newChart)
|
||||||
|
|
||||||
|
return () => chart?.destroy()
|
||||||
|
}
|
||||||
|
}, [chart, options, dataParams])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chart)
|
||||||
|
return
|
||||||
|
|
||||||
|
chart.data = dataParams.data
|
||||||
|
chart.options.aspectRatio = options?.aspectRatio
|
||||||
|
if (dataParams.xStart) {
|
||||||
|
let interval = Number(dataParams.xInterval ?? 600)
|
||||||
|
let start = new Date(dataParams.xStart)
|
||||||
|
let end = new Date(dataParams.xStart)
|
||||||
|
end.setSeconds(end.getSeconds() + interval)
|
||||||
|
let { unit, stepSize } = timeParamsByInterval(interval)
|
||||||
|
|
||||||
|
if (chart.options.scales?.x) {
|
||||||
|
chart.options.scales.x.max = end.getTime()
|
||||||
|
chart.options.scales.x.min = start.getTime()
|
||||||
|
chart.options.scales.x.ticks.display = dataParams.displayLabels ?? true
|
||||||
|
chart.options.scales.x.time.unit = unit
|
||||||
|
chart.options.scales.x.time.stepSize = stepSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.update()
|
||||||
|
}, [chart, dataParams, options])
|
||||||
|
|
||||||
|
return (<canvas ref={chartRef} />)
|
||||||
|
}
|
@ -2,15 +2,15 @@ import moment from 'moment';
|
|||||||
import { useEffect, useState} from 'react';
|
import { useEffect, useState} from 'react';
|
||||||
import {ChartTimeBase} from './ChartTimeBase'
|
import {ChartTimeBase} from './ChartTimeBase'
|
||||||
|
|
||||||
const GetRandomColor = () => "#" + Math.floor(Math.random()*16777215).toString(16)
|
export const GetRandomColor = () => "#" + Math.floor(Math.random()*16777215).toString(16)
|
||||||
|
|
||||||
const CreateDataset = (lineConfig) => {
|
export const CreateDataset = (lineConfig) => {
|
||||||
let color = lineConfig.borderColor
|
let color = lineConfig.borderColor
|
||||||
?? lineConfig.backgroundColor
|
?? lineConfig.backgroundColor
|
||||||
?? lineConfig.color
|
?? lineConfig.color
|
||||||
?? GetRandomColor()
|
?? GetRandomColor()
|
||||||
|
|
||||||
let dataset = {
|
let datasets = {
|
||||||
label: lineConfig.label,
|
label: lineConfig.label,
|
||||||
data: [],
|
data: [],
|
||||||
backgroundColor: lineConfig.backgroundColor ?? color,
|
backgroundColor: lineConfig.backgroundColor ?? color,
|
||||||
@ -18,7 +18,7 @@ const CreateDataset = (lineConfig) => {
|
|||||||
borderWidth: lineConfig.borderWidth ?? 1,
|
borderWidth: lineConfig.borderWidth ?? 1,
|
||||||
borderDash: lineConfig.dash ?? [],
|
borderDash: lineConfig.dash ?? [],
|
||||||
}
|
}
|
||||||
return dataset
|
return datasets
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChartOptions = {
|
const ChartOptions = {
|
||||||
@ -28,7 +28,7 @@ const ChartOptions = {
|
|||||||
// display: false,
|
// display: false,
|
||||||
// maxHeight: 64,
|
// maxHeight: 64,
|
||||||
// fullSize: true,
|
// fullSize: true,
|
||||||
// posision:'chartArea',
|
// position: 'chartArea',
|
||||||
// align: 'start',
|
// align: 'start',
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
@ -12,13 +12,22 @@ import {
|
|||||||
ChartOptions} from 'chart.js'
|
ChartOptions} from 'chart.js'
|
||||||
import 'chartjs-adapter-moment';
|
import 'chartjs-adapter-moment';
|
||||||
import ChartDataLabels from 'chartjs-plugin-datalabels';
|
import ChartDataLabels from 'chartjs-plugin-datalabels';
|
||||||
|
import zoomPlugin from 'chartjs-plugin-zoom';
|
||||||
|
|
||||||
Chart.register( TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels );
|
Chart.register( TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels, zoomPlugin );
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
aspectRatio: 0.45,
|
aspectRatio: 0.45,
|
||||||
animation: false,
|
animation: false,
|
||||||
|
tooltips: {
|
||||||
|
enabled: true,
|
||||||
|
callbacks: {
|
||||||
|
label(tooltipItem:any) {
|
||||||
|
return tooltipItem.yLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
|
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
|
||||||
scales: {
|
scales: {
|
||||||
y:{
|
y:{
|
||||||
@ -68,6 +77,18 @@ const defaultOptions = {
|
|||||||
datalabels: {
|
datalabels: {
|
||||||
display: false,
|
display: false,
|
||||||
},
|
},
|
||||||
|
zoom: {
|
||||||
|
zoom: {
|
||||||
|
wheel: {
|
||||||
|
enabled: true,
|
||||||
|
modifierKey: 'alt',
|
||||||
|
},
|
||||||
|
pinch: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
mode: 'x',
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +118,7 @@ export type TimeParams = {
|
|||||||
|
|
||||||
const linesPerInterval = 32
|
const linesPerInterval = 32
|
||||||
|
|
||||||
const timeUnitByInterval = (intervalSec:number):String => {
|
export const timeUnitByInterval = (intervalSec:number):String => {
|
||||||
if(intervalSec <= 60)
|
if(intervalSec <= 60)
|
||||||
return 'millisecond'
|
return 'millisecond'
|
||||||
|
|
||||||
@ -125,7 +146,7 @@ const timeUnitByInterval = (intervalSec:number):String => {
|
|||||||
return 'year'
|
return 'year'
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeParamsByInterval = (intervalSec:number) :TimeParams => {
|
export const timeParamsByInterval = (intervalSec:number) :TimeParams => {
|
||||||
let stepSize = intervalSec
|
let stepSize = intervalSec
|
||||||
let unit = timeUnitByInterval(intervalSec)
|
let unit = timeUnitByInterval(intervalSec)
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import { useEffect, useState} from 'react';
|
|||||||
import {ChartTimeBase, ChartTimeData, ChartTimeDataParams} from './ChartTimeBase'
|
import {ChartTimeBase, ChartTimeData, ChartTimeDataParams} from './ChartTimeBase'
|
||||||
|
|
||||||
const GetRandomColor = () => "#" + Math.floor(Math.random()*16777215).toString(16)
|
const GetRandomColor = () => "#" + Math.floor(Math.random()*16777215).toString(16)
|
||||||
|
|
||||||
function GetOrCreateDatasetByLineConfig (data: ChartTimeData, lineConfig: LineConfig) {
|
function GetOrCreateDatasetByLineConfig (data: ChartTimeData, lineConfig: LineConfig) {
|
||||||
let dataset = data?.datasets.find(d=>d.label === lineConfig.label)
|
let dataset = data?.datasets.find(d=>d.label === lineConfig.label)
|
||||||
if(!dataset)
|
if(!dataset)
|
||||||
@ -110,6 +109,9 @@ export const ChartTimeOnline: React.FC<ChartTimeProps> = (props) => {
|
|||||||
align: 'left',
|
align: 'left',
|
||||||
anchor: 'center',
|
anchor: 'center',
|
||||||
clip: true
|
clip: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enable: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
src/concept/AnalisysOperationsToInterval.jsx
Normal file
44
src/concept/AnalisysOperationsToInterval.jsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { ChartOperationsToInterval } from './charts/ChartOperationsToInterval'
|
||||||
|
import { useParams } from "react-router-dom"
|
||||||
|
import notify from "../components/notify"
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { AnalyticsService } from "../services/api"
|
||||||
|
|
||||||
|
const lines = [
|
||||||
|
{ yAccessorName: "operationName", xAccessorName: "duration" },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function AnalysisOperationsToInterval() {
|
||||||
|
let { id } = useParams()
|
||||||
|
const [operationsToInterval, setOperationsToInterval] = useState([])
|
||||||
|
const [loader, setLoader] = useState(false)
|
||||||
|
|
||||||
|
const handleReceiveOperationsToIntervalData = (data) => {
|
||||||
|
setOperationsToInterval(data[0].operations)
|
||||||
|
console.log(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoader(true)
|
||||||
|
let intervalHoursTimestamp = 600
|
||||||
|
let workBeginTimestamp = 1
|
||||||
|
|
||||||
|
AnalyticsService.getOperationsToInterval(id, intervalHoursTimestamp, workBeginTimestamp)
|
||||||
|
.then(handleReceiveOperationsToIntervalData)
|
||||||
|
.catch(error => {
|
||||||
|
notify(`Не удалось получить данные для Анализа Глубина-День по скважине "${id}"`,
|
||||||
|
'warning')
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
.finally(setLoader(false))
|
||||||
|
}, [id])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ChartOperationsToInterval
|
||||||
|
data={operationsToInterval}
|
||||||
|
lines={lines}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
83
src/concept/ChartOperationsToInterval.jsx
Normal file
83
src/concept/ChartOperationsToInterval.jsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { ChartDepthToIntervalBase } from './ChartOperationsToIntervalBase'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { GetRandomColor, CreateDataset } from './ChartTimeArchive'
|
||||||
|
|
||||||
|
const CreateLabels = () => {
|
||||||
|
let labels = []
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateData = (lineConfig) => {
|
||||||
|
|
||||||
|
let datasets = {
|
||||||
|
label: lineConfig.label,
|
||||||
|
data: [],
|
||||||
|
backgroundColor: GetRandomColor()
|
||||||
|
}
|
||||||
|
return datasets
|
||||||
|
}
|
||||||
|
|
||||||
|
const labels = ['Первый']
|
||||||
|
const datasets = [
|
||||||
|
{
|
||||||
|
label: 'Dataset 1',
|
||||||
|
data: [123],
|
||||||
|
backgroundColor: "#00f",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Dataset 2',
|
||||||
|
data: [170],
|
||||||
|
backgroundColor: "#f0f",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Dataset 3',
|
||||||
|
data: [150],
|
||||||
|
backgroundColor: "#0f0",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
export const ChartOperationsToInterval = ({ data, lines }) => {
|
||||||
|
const [operationsToIntervalDataParams, setOperationsToIntervalDataParams] = useState({data: {labels: labels, datasets: datasets}})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// if ((!lines)
|
||||||
|
// || (!data))
|
||||||
|
// return
|
||||||
|
|
||||||
|
let newDatasets = lines.map(lineCfg => {
|
||||||
|
let datasets = CreateData(lineCfg)
|
||||||
|
if (data.length !== 0)
|
||||||
|
datasets = data.map(dataItem => {
|
||||||
|
return {
|
||||||
|
label: 'Dataset 1',
|
||||||
|
data: [dataItem[lineCfg.xAccessorName]],
|
||||||
|
backgroundColor: "#00f",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return datasets
|
||||||
|
})
|
||||||
|
|
||||||
|
// let newLabels = lines.map(lineCfg => {
|
||||||
|
// let labels = CreateLabels(lineCfg)
|
||||||
|
// if (data.length !== 0)
|
||||||
|
// labels = data.map(dataItem => {
|
||||||
|
// return dataItem[lineCfg.yAccessorName]
|
||||||
|
// })
|
||||||
|
// return labels
|
||||||
|
// })
|
||||||
|
|
||||||
|
let newParams = {
|
||||||
|
data: {
|
||||||
|
datasets: newDatasets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setOperationsToIntervalDataParams(newParams)
|
||||||
|
|
||||||
|
}, [data, lines])
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
<ChartDepthToIntervalBase dataParams={operationsToIntervalDataParams} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
180
src/concept/ChartOperationsToIntervalBase.tsx
Normal file
180
src/concept/ChartOperationsToIntervalBase.tsx
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import {
|
||||||
|
Chart,
|
||||||
|
TimeScale,
|
||||||
|
Legend,
|
||||||
|
PointElement,
|
||||||
|
ChartData,
|
||||||
|
ChartTypeRegistry,
|
||||||
|
ChartOptions,
|
||||||
|
BarController,
|
||||||
|
BarElement,
|
||||||
|
TimeSeriesScale,
|
||||||
|
LinearScale,
|
||||||
|
LineController,
|
||||||
|
CategoryScale
|
||||||
|
} from 'chart.js'
|
||||||
|
import 'chartjs-adapter-moment'
|
||||||
|
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
||||||
|
|
||||||
|
Chart.register(TimeScale, BarController, BarElement, PointElement, TimeSeriesScale, LineController, LinearScale, CategoryScale, Legend, ChartDataLabels)
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
plugins: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Chart.js Bar Chart - Stacked'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responsive: true,
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
stacked: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChartTimeData = ChartData<keyof ChartTypeRegistry, {
|
||||||
|
x: string;
|
||||||
|
label: string;
|
||||||
|
y: number;
|
||||||
|
}[], unknown>
|
||||||
|
|
||||||
|
export type ChartTimeDataParams = {
|
||||||
|
data: ChartTimeData,
|
||||||
|
xStart?: 0,
|
||||||
|
xInterval?: number,
|
||||||
|
displayLabels?: Boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChartTimeBaseProps = {
|
||||||
|
dataParams: ChartTimeDataParams,
|
||||||
|
// TODO: Create good type for options
|
||||||
|
options?: ChartOptions<keyof ChartTypeRegistry> | any,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TimeParams = {
|
||||||
|
unit: String
|
||||||
|
stepSize: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const linesPerInterval = 32
|
||||||
|
|
||||||
|
export const timeUnitByInterval = (intervalSec: number): String => {
|
||||||
|
if (intervalSec <= 60)
|
||||||
|
return 'millisecond'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 60)
|
||||||
|
return 'second'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 60 * 60)
|
||||||
|
return 'minute'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 12 * 60 * 60)
|
||||||
|
return 'hour'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 24 * 60 * 60)
|
||||||
|
return 'day'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 7 * 24 * 60 * 60)
|
||||||
|
return 'week'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 30.4375 * 24 * 60 * 60)
|
||||||
|
return 'month'
|
||||||
|
|
||||||
|
if (intervalSec <= 32 * 121.75 * 24 * 60 * 60)
|
||||||
|
return 'quarter'
|
||||||
|
else
|
||||||
|
return 'year'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const timeParamsByInterval = (intervalSec: number): TimeParams => {
|
||||||
|
let stepSize = intervalSec
|
||||||
|
let unit = timeUnitByInterval(intervalSec)
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case "millisecond":
|
||||||
|
stepSize *= 1000
|
||||||
|
break;
|
||||||
|
case "second":
|
||||||
|
//stepSize *= 1
|
||||||
|
break;
|
||||||
|
case "minute":
|
||||||
|
stepSize /= 60
|
||||||
|
break;
|
||||||
|
case "hour":
|
||||||
|
stepSize /= 60 * 60
|
||||||
|
break;
|
||||||
|
case "day":
|
||||||
|
stepSize /= 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "week":
|
||||||
|
stepSize /= 7 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "month":
|
||||||
|
stepSize /= 30 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "quarter":
|
||||||
|
stepSize /= 91 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
case "year":
|
||||||
|
stepSize /= 365.25 * 24 * 60 * 60
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
stepSize = Math.round(stepSize / linesPerInterval)
|
||||||
|
stepSize = stepSize > 0 ? stepSize : 1;
|
||||||
|
return { unit, stepSize }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChartDepthToIntervalBase: React.FC<ChartTimeBaseProps> = ({ options, dataParams }) => {
|
||||||
|
const chartRef = useRef<HTMLCanvasElement>(null)
|
||||||
|
const [chart, setChart] = useState<any>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ((chartRef.current) && (!chart)) {
|
||||||
|
let thisOptions = {}
|
||||||
|
Object.assign(thisOptions, defaultOptions, options)
|
||||||
|
|
||||||
|
let newChart = new Chart(chartRef.current, {
|
||||||
|
type: 'bar',
|
||||||
|
plugins: [ChartDataLabels],
|
||||||
|
options: thisOptions,
|
||||||
|
data: dataParams.data
|
||||||
|
})
|
||||||
|
setChart(newChart)
|
||||||
|
|
||||||
|
return () => chart?.destroy()
|
||||||
|
}
|
||||||
|
}, [chart, options, dataParams])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chart)
|
||||||
|
return
|
||||||
|
|
||||||
|
chart.data = dataParams.data
|
||||||
|
chart.options.aspectRatio = options?.aspectRatio
|
||||||
|
if (dataParams.xStart) {
|
||||||
|
let interval = Number(dataParams.xInterval ?? 600)
|
||||||
|
let start = new Date(dataParams.xStart)
|
||||||
|
let end = new Date(dataParams.xStart)
|
||||||
|
end.setSeconds(end.getSeconds() + interval)
|
||||||
|
let { unit, stepSize } = timeParamsByInterval(interval)
|
||||||
|
|
||||||
|
if (chart.options.scales?.x) {
|
||||||
|
chart.options.scales.x.max = end.getTime()
|
||||||
|
chart.options.scales.x.min = start.getTime()
|
||||||
|
chart.options.scales.x.ticks.display = dataParams.displayLabels ?? true
|
||||||
|
chart.options.scales.x.time.unit = unit
|
||||||
|
chart.options.scales.x.time.stepSize = stepSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.update()
|
||||||
|
}, [chart, dataParams, options])
|
||||||
|
|
||||||
|
return (<canvas ref={chartRef} />)
|
||||||
|
}
|
@ -1,11 +1,30 @@
|
|||||||
// import {UserOfWells} from "../components/UserOfWells";
|
import { AnalysisDepthToDay } from '../components/AnalysisDepthToDay'
|
||||||
|
import { AnalysisDepthToInterval } from '../components/AnalysisDepthToInterval'
|
||||||
|
import { AnalysisOperationTime } from '../components/AnalysisOperationTime'
|
||||||
|
import { Row, Col } from 'antd'
|
||||||
|
|
||||||
|
export default function Analysis() {
|
||||||
|
|
||||||
export default function Analysis(props) {
|
|
||||||
return (
|
return (
|
||||||
<div className="menu_title">
|
<>
|
||||||
<h2>Анализ</h2>
|
<Row> </Row>
|
||||||
{/*<UserOfWells data={saubData}/>*/}
|
<Row justify="space-around" align="middle">
|
||||||
<hr/>
|
<Col span={10}>
|
||||||
</div>
|
<h2>График Глубина-день</h2>
|
||||||
|
<AnalysisDepthToDay />
|
||||||
|
</Col>
|
||||||
|
<Col span={10}>
|
||||||
|
<h2>График Глубина за интервал</h2>
|
||||||
|
<AnalysisDepthToInterval />
|
||||||
|
</Col>
|
||||||
|
</Row >
|
||||||
|
<Row><div style={{height: "150px"}}> </div></Row>
|
||||||
|
<Row justify="space-around" align="middle">
|
||||||
|
<Col span={10}>
|
||||||
|
<h2>График Операция за время</h2>
|
||||||
|
<AnalysisOperationTime />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -15,7 +15,7 @@ import moment from 'moment'
|
|||||||
import notify from '../components/notify'
|
import notify from '../components/notify'
|
||||||
import LoaderPortal from '../components/LoaderPortal'
|
import LoaderPortal from '../components/LoaderPortal'
|
||||||
|
|
||||||
const { RangePicker } = DatePicker;
|
const { RangePicker } = DatePicker
|
||||||
|
|
||||||
const SaveObject = (key, obj) => {
|
const SaveObject = (key, obj) => {
|
||||||
let json = JSON.stringify(obj)
|
let json = JSON.stringify(obj)
|
||||||
|
@ -7,7 +7,7 @@ import {ClusterService} from '../services/api'
|
|||||||
import notify from '../components/notify'
|
import notify from '../components/notify'
|
||||||
|
|
||||||
const calcViewParams = (clusters) => {
|
const calcViewParams = (clusters) => {
|
||||||
if (clusters || clusters.length === 0)
|
if ((!clusters) || clusters.length === 0)
|
||||||
return {center:[60.81226, 70.0562], zoom: 5}
|
return {center:[60.81226, 70.0562], zoom: 5}
|
||||||
|
|
||||||
const center = clusters.reduce((sum, cluster) => {
|
const center = clusters.reduce((sum, cluster) => {
|
||||||
|
@ -47,7 +47,7 @@ const columns = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const filterOptions = [
|
const filterOptions = [
|
||||||
{label: 'Авария', value: 1},
|
{label: 'Важное', value: 1},
|
||||||
{label: 'Предупреждение', value: 2},
|
{label: 'Предупреждение', value: 2},
|
||||||
{label: 'Информация', value: 3},
|
{label: 'Информация', value: 3},
|
||||||
]
|
]
|
||||||
@ -72,6 +72,7 @@ export default function Messages() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const GetMessages = async () => {
|
const GetMessages = async () => {
|
||||||
setLoader(true)
|
setLoader(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let begin = null
|
let begin = null
|
||||||
let end = null
|
let end = null
|
||||||
@ -85,6 +86,12 @@ export default function Messages() {
|
|||||||
categories,
|
categories,
|
||||||
begin,
|
begin,
|
||||||
end)
|
end)
|
||||||
|
if (paginatedMessages === null){
|
||||||
|
notify(`Данных по скважине "${id}" нет`, 'warning')
|
||||||
|
setLoader(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setMessages(paginatedMessages.items.map(m => {
|
setMessages(paginatedMessages.items.map(m => {
|
||||||
return {
|
return {
|
||||||
key: m.id,
|
key: m.id,
|
||||||
@ -97,9 +104,10 @@ export default function Messages() {
|
|||||||
total: paginatedMessages.count,
|
total: paginatedMessages.count,
|
||||||
current: Math.floor(paginatedMessages.skip / pageSize),
|
current: Math.floor(paginatedMessages.skip / pageSize),
|
||||||
})
|
})
|
||||||
|
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
notify(`Не удалось загрузить сообщения по скважине "${id}"`, 'error')
|
|
||||||
console.log(ex)
|
console.log(ex)
|
||||||
|
notify(`Не удалось загрузить сообщения по скважине "${id}"`, 'error')
|
||||||
}
|
}
|
||||||
setLoader(false)
|
setLoader(false)
|
||||||
}
|
}
|
||||||
@ -108,7 +116,6 @@ export default function Messages() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
<div className='filter-group'>
|
<div className='filter-group'>
|
||||||
<h3 className='filter-group-heading'>Фильтр сообщений</h3>
|
<h3 className='filter-group-heading'>Фильтр сообщений</h3>
|
||||||
<Select
|
<Select
|
||||||
|
@ -26,11 +26,20 @@ let reportDatesRange = {
|
|||||||
to: moment("9999-12-31T23:59:59.9999999")
|
to: moment("9999-12-31T23:59:59.9999999")
|
||||||
}
|
}
|
||||||
|
|
||||||
const timePeriodNames = {
|
const timePeriodNames = [
|
||||||
600: '1 минута',
|
{label: '1 секунда', value: 1},
|
||||||
86400:'1 день',
|
{label: '10 секунд', value: 10},
|
||||||
604800:'1 неделя'
|
{label: '1 минута', value: 60},
|
||||||
}
|
{label: '5 минут', value: 300},
|
||||||
|
{label: '30 минут', value: 1800},
|
||||||
|
{label: '1 час', value: 3600},
|
||||||
|
{label: '6 часов', value: 21600},
|
||||||
|
{label: '12 часов', value: 43200},
|
||||||
|
{label: '1 день', value: 86400},
|
||||||
|
{label: '1 неделя', value: 604800}
|
||||||
|
]
|
||||||
|
|
||||||
|
const firstStep = timePeriodNames[2]["value"]
|
||||||
|
|
||||||
const imgPaths = {
|
const imgPaths = {
|
||||||
'.pdf': '/images/pdf.png',
|
'.pdf': '/images/pdf.png',
|
||||||
@ -42,7 +51,7 @@ const imgPaths = {
|
|||||||
export default function Report(props) {
|
export default function Report(props) {
|
||||||
|
|
||||||
const [rangeDate, setRangeDate] = useState([moment().subtract(1,'days'), moment()])
|
const [rangeDate, setRangeDate] = useState([moment().subtract(1,'days'), moment()])
|
||||||
const [step, setStep] = useState(600)
|
const [step, setStep] = useState(firstStep)
|
||||||
const [format, setFormat] = useState(0)
|
const [format, setFormat] = useState(0)
|
||||||
const [approxPages, setPagesCount] = useState(0)
|
const [approxPages, setPagesCount] = useState(0)
|
||||||
const [suitableReports, setSuitableReports] = useState([])
|
const [suitableReports, setSuitableReports] = useState([])
|
||||||
@ -51,6 +60,8 @@ export default function Report(props) {
|
|||||||
|
|
||||||
let wellId = useParams().id;
|
let wellId = useParams().id;
|
||||||
|
|
||||||
|
const periods = timePeriodNames.map((line) => <Option key={line.value} value={line.value}>{line.label}</Option>)
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
@ -247,12 +258,9 @@ export default function Report(props) {
|
|||||||
initialValue={step}
|
initialValue={step}
|
||||||
className="ml-30px"
|
className="ml-30px"
|
||||||
>
|
>
|
||||||
<Select
|
<Select onChange={setStep}
|
||||||
onChange={(value) => setStep(value)}
|
|
||||||
>
|
>
|
||||||
<Option value={600}>1 минута</Option>
|
{periods}
|
||||||
<Option value={86400}>1 день</Option>
|
|
||||||
<Option value={604800}>1 неделя</Option>
|
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -191,26 +191,28 @@ const columns = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const intervalSteps = [
|
const timePeriodCollection = [
|
||||||
{label: '10 минут', value: 600},
|
{value: '60', label: '1 минута'},
|
||||||
{label: '30 минут', value: 1800},
|
{value: '300', label: '5 минут'},
|
||||||
{label: '1 час', value: 3600},
|
{value: '600', label: '10 минут'},
|
||||||
{label: '6 часов', value: 21600},
|
{value: '1800', label: '30 минут'},
|
||||||
{label: '12 часов', value: 43200},
|
{value: '3600', label: '1 час'},
|
||||||
{label: '1 день', value: 86400},
|
{value: '21600', label: '6 часов'},
|
||||||
|
{value: '43200', label: '12 часов'},
|
||||||
|
{value: '86400', label: '24 часа'}
|
||||||
]
|
]
|
||||||
|
|
||||||
const defaultInterval = intervalSteps[0]["value"] // поменять тут TODO
|
const defaultChartInterval = '600'
|
||||||
|
|
||||||
export default function TelemetryView(props) {
|
export default function TelemetryView(props) {
|
||||||
let {id} = useParams()
|
let {id} = useParams()
|
||||||
const [saubData, setSaubData] = useState([])
|
const [saubData, setSaubData] = useState([])
|
||||||
const [chartInterval, setChartInterval] = useState(defaultInterval)
|
const [chartInterval, setChartInterval] = useState(defaultChartInterval)
|
||||||
const [messages, setMessages] = useState([])
|
const [messages, setMessages] = useState([])
|
||||||
|
|
||||||
const [loader, setLoader] = useState(false)
|
const [loader, setLoader] = useState(false)
|
||||||
|
|
||||||
const periods = intervalSteps.map((line) => <Option key={line.value} value={line.value}>{line.label}</Option>)
|
const options = timePeriodCollection.map((line) => <Option key={line.value}>{line.label}</Option>)
|
||||||
|
|
||||||
const handleReceiveDataSaub = (data) => {
|
const handleReceiveDataSaub = (data) => {
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -268,8 +270,8 @@ export default function TelemetryView(props) {
|
|||||||
<span style={{flexGrow: 0.1}}> </span>
|
<span style={{flexGrow: 0.1}}> </span>
|
||||||
<Col>
|
<Col>
|
||||||
Интервал:
|
Интервал:
|
||||||
<Select defaultValue={chartInterval} onChange={setChartInterval}>
|
<Select defaultValue={defaultChartInterval} onChange={setChartInterval}>
|
||||||
{periods}
|
{options}
|
||||||
</Select>
|
</Select>
|
||||||
</Col>
|
</Col>
|
||||||
<span style={{flexGrow: 1}}> </span>
|
<span style={{flexGrow: 1}}> </span>
|
||||||
|
@ -48,6 +48,21 @@ type handlerFunction = (...args: any[]) => void;
|
|||||||
|
|
||||||
type cleanFunction = (...args: any[]) => void;
|
type cleanFunction = (...args: any[]) => void;
|
||||||
|
|
||||||
|
const MakeUnsubscribeFunction = (
|
||||||
|
connection: HubConnection,
|
||||||
|
methodName: string,
|
||||||
|
groupName: (string|null)):cleanFunction => {
|
||||||
|
return async() => {
|
||||||
|
if(connection.state === HubConnectionState.Connected)
|
||||||
|
{
|
||||||
|
if(groupName)
|
||||||
|
await connection.send('RemoveFromGroup', groupName)
|
||||||
|
connection.off(methodName)
|
||||||
|
}
|
||||||
|
connection.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Subscribe on some SignalR method (topic).
|
/** Subscribe on some SignalR method (topic).
|
||||||
* @example useEffect(() => Subscribe('methodName', `${id}`, handleNewData), [id]);
|
* @example useEffect(() => Subscribe('methodName', `${id}`, handleNewData), [id]);
|
||||||
* @param {string} methodName name of the method
|
* @param {string} methodName name of the method
|
||||||
@ -66,12 +81,7 @@ const Subscribe = (
|
|||||||
connection.on(methodName, handler)
|
connection.on(methodName, handler)
|
||||||
})
|
})
|
||||||
|
|
||||||
if(groupName)
|
return MakeUnsubscribeFunction(Connections[hubUrl],methodName,groupName)
|
||||||
return () => {
|
|
||||||
Connections[hubUrl].send('RemoveFromGroup', groupName)
|
|
||||||
.finally(()=>Connections[hubUrl].off(methodName))
|
|
||||||
}
|
|
||||||
return () => Connections[hubUrl].off(methodName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Invokes some SignalR method.
|
/** Invokes some SignalR method.
|
||||||
|
@ -41,7 +41,8 @@ html {
|
|||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
//padding: 4px 24px;
|
justify-content: space-around;
|
||||||
|
gap: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header .logo {
|
.header .logo {
|
||||||
@ -55,9 +56,7 @@ html {
|
|||||||
.header .title{
|
.header .title{
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-align: start;
|
padding-left: 100px;
|
||||||
justify-content: start;
|
|
||||||
margin-left: 100px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header button{
|
.header button{
|
||||||
@ -154,9 +153,13 @@ tr.table_row_size {
|
|||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-tree-select {
|
||||||
|
width: 300px
|
||||||
|
}
|
||||||
|
|
||||||
.header-tree-select *{
|
.header-tree-select *{
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: '1.5rem';
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-tree-select{
|
.header-tree-select{
|
||||||
|
Loading…
Reference in New Issue
Block a user