works for v2
This commit is contained in:
Generated
+154
-35
@@ -8,18 +8,21 @@
|
||||
"name": "toneapi_webclient",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"assert": "^2.0.0",
|
||||
"chart.js": "^4.2.1",
|
||||
"chartjs-plugin-annotation": "^2.2.1",
|
||||
"chartjs-plugin-datalabels": "^2.2.0",
|
||||
"core-js": "^3.8.3",
|
||||
"pinia": "^2.0.35",
|
||||
"vue": "^3.2.13",
|
||||
"vue-chartjs": "^5.2.0",
|
||||
"vue-class-component": "^8.0.0-0",
|
||||
"vue-multiselect": "^3.0.0-beta.1",
|
||||
"vue-router": "^4.0.3",
|
||||
"vuex": "^4.0.0"
|
||||
"vue-router": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/assert": "^1.5.6",
|
||||
"@types/node": "^18.16.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
@@ -2107,6 +2110,12 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/assert": {
|
||||
"version": "1.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/assert/-/assert-1.5.6.tgz",
|
||||
"integrity": "sha512-Y7gDJiIqb9qKUHfBQYOWGngUpLORtirAVPuj/CWJrU2C6ZM4/y3XLwuwfGMF8s7QzW746LQZx23m0+1FSgjfug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
|
||||
@@ -2234,9 +2243,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
|
||||
"integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==",
|
||||
"version": "18.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.0.tgz",
|
||||
"integrity": "sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
@@ -3898,6 +3907,17 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/assert": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz",
|
||||
"integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==",
|
||||
"dependencies": {
|
||||
"es6-object-assign": "^1.1.0",
|
||||
"is-nan": "^1.2.1",
|
||||
"object-is": "^1.0.1",
|
||||
"util": "^0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/astral-regex": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
|
||||
@@ -3962,7 +3982,6 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
|
||||
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -4277,7 +4296,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
@@ -5393,7 +5411,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
|
||||
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"object-keys": "^1.1.1"
|
||||
@@ -5776,6 +5793,11 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-object-assign": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
|
||||
"integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw=="
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
@@ -7020,7 +7042,6 @@
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-callable": "^1.1.3"
|
||||
}
|
||||
@@ -7260,8 +7281,7 @@
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"node_modules/function.prototype.name": {
|
||||
"version": "1.1.5",
|
||||
@@ -7318,7 +7338,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
@@ -7442,7 +7461,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.3"
|
||||
},
|
||||
@@ -7487,7 +7505,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
},
|
||||
@@ -7517,7 +7534,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
|
||||
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.1"
|
||||
},
|
||||
@@ -7541,7 +7557,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -7553,7 +7568,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.2"
|
||||
},
|
||||
@@ -7892,8 +7906,7 @@
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/internal-slot": {
|
||||
"version": "1.0.5",
|
||||
@@ -7927,6 +7940,21 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arguments": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
|
||||
@@ -7991,7 +8019,6 @@
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -8080,6 +8107,20 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-generator-function": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
|
||||
"integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
|
||||
"dependencies": {
|
||||
"has-tostringtag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
@@ -8101,6 +8142,21 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-nan": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
|
||||
"integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"define-properties": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-negative-zero": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
|
||||
@@ -8232,7 +8288,6 @@
|
||||
"version": "1.1.10",
|
||||
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
|
||||
"integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"available-typed-arrays": "^1.0.5",
|
||||
"call-bind": "^1.0.2",
|
||||
@@ -9341,11 +9396,25 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-is": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
|
||||
"integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-keys": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
@@ -9775,6 +9844,56 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "2.0.35",
|
||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.35.tgz",
|
||||
"integrity": "sha512-P1IKKQWhxGXiiZ3atOaNI75bYlFUbRxtJdhPLX059Z7+b9Z04rnTZdSY8Aph1LA+/4QEMAYHsTQ638Wfe+6K5g==",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.5.0",
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.4.0",
|
||||
"typescript": ">=4.4.4",
|
||||
"vue": "^2.6.14 || ^3.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
},
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/vue-demi": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.0.tgz",
|
||||
"integrity": "sha512-gt58r2ogsNQeVoQ3EhoUAvUsH9xviydl0dWJj7dabBC/2L4uBId7ujtCwDRD0JhkGsV1i0CtfLAeyYKBht9oWg==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-dir": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||
@@ -12090,7 +12209,7 @@
|
||||
"version": "4.5.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
|
||||
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -12207,6 +12326,18 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util": {
|
||||
"version": "0.12.5",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
||||
"integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"is-arguments": "^1.0.4",
|
||||
"is-generator-function": "^1.0.7",
|
||||
"is-typed-array": "^1.1.3",
|
||||
"which-typed-array": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -12544,17 +12675,6 @@
|
||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vuex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
|
||||
"integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.0.0-beta.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
||||
@@ -13102,7 +13222,6 @@
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
|
||||
"integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"available-typed-arrays": "^1.0.5",
|
||||
"call-bind": "^1.0.2",
|
||||
|
||||
+5
-2
@@ -8,18 +8,21 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"assert": "^2.0.0",
|
||||
"chart.js": "^4.2.1",
|
||||
"chartjs-plugin-annotation": "^2.2.1",
|
||||
"chartjs-plugin-datalabels": "^2.2.0",
|
||||
"core-js": "^3.8.3",
|
||||
"pinia": "^2.0.35",
|
||||
"vue": "^3.2.13",
|
||||
"vue-chartjs": "^5.2.0",
|
||||
"vue-class-component": "^8.0.0-0",
|
||||
"vue-multiselect": "^3.0.0-beta.1",
|
||||
"vue-router": "^4.0.3",
|
||||
"vuex": "^4.0.0"
|
||||
"vue-router": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/assert": "^1.5.6",
|
||||
"@types/node": "^18.16.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
|
||||
+14
@@ -15,6 +15,20 @@
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Player, Weapon, Server, useKillStore } from '@/stores/kill'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup (props, ctx) {
|
||||
const store = useKillStore()
|
||||
store.fetchPlayers({})
|
||||
store.fetchServers({})
|
||||
store.fetchWeapons({})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #282a36;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="playerChart" ref="container">
|
||||
<!-- <Scatter :data="chart" :options="chartOptions" /> -->
|
||||
<Scatter :data="chart" :options="chartOptions" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -11,24 +12,34 @@ import {
|
||||
PointElement,
|
||||
LineElement,
|
||||
Tooltip,
|
||||
Legend
|
||||
Legend,
|
||||
CoreChartOptions,
|
||||
PluginChartOptions,
|
||||
ScaleChartOptions
|
||||
} from 'chart.js'
|
||||
import annotationPlugin from 'chartjs-plugin-annotation'
|
||||
import dataLabel from 'chartjs-plugin-datalabels'
|
||||
import { Store, useStore } from 'vuex'
|
||||
import { Player, Weapon } from '@/store/index'
|
||||
import { useKillStore, Player, Filters } from '@/stores/kill'
|
||||
|
||||
import { Scatter } from 'vue-chartjs'
|
||||
import { defineComponent } from 'vue'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { ChartEvent } from 'chart.js/dist/core/core.plugins'
|
||||
import { _DeepPartialObject } from 'chart.js/dist/types/utils'
|
||||
|
||||
ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend, annotationPlugin, dataLabel)
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PlayerChart',
|
||||
props: {
|
||||
filters: Object,
|
||||
filters: Object as PropType<Filters>,
|
||||
playerHighlighted: String
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
store: useKillStore(),
|
||||
refreshColors: 0
|
||||
}
|
||||
},
|
||||
emits: ['highlightPlayer'],
|
||||
components: {
|
||||
Scatter
|
||||
@@ -54,7 +65,7 @@ export default defineComponent({
|
||||
return colors
|
||||
},
|
||||
chart () {
|
||||
const chartData = {
|
||||
return {
|
||||
datasets: [
|
||||
{
|
||||
label: 'Players',
|
||||
@@ -62,29 +73,33 @@ export default defineComponent({
|
||||
borderColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? this.colors.orange : this.colors.cyan,
|
||||
backgroundColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? this.colors.orange : this.colors.cyan,
|
||||
pointRadius: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 20 : 1,
|
||||
pointStyle: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 'crossRot' : 'dot',
|
||||
pointStyle: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 'crossRot' : 'circle',
|
||||
hoverRadius: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 30 : 4,
|
||||
data: [] as { x: number, y: number }[]
|
||||
data: this.playerIdList.map((id) => ({ y: this.players[id].kills, x: this.players[id].deaths }))
|
||||
}
|
||||
]
|
||||
}
|
||||
if (!this.players) {
|
||||
return chartData
|
||||
}
|
||||
chartData.datasets[0].data = this.playerIdList.map(e => {
|
||||
if (!this.players) {
|
||||
return { x: 0, y: 0 }
|
||||
}
|
||||
return { x: this.players[e].deaths, y: this.players[e].kills }
|
||||
})
|
||||
return chartData
|
||||
},
|
||||
chartOptions () {
|
||||
chartOptions (): _DeepPartialObject<CoreChartOptions<'scatter'> & PluginChartOptions<'scatter'> & ScaleChartOptions<'scatter'>> {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
this.$props.playerHighlighted
|
||||
const options = {
|
||||
let endX = 0
|
||||
let endY = 0
|
||||
if (this.players) {
|
||||
const maxX = Math.max(...Object.values(this.players).map(e => e.deaths))
|
||||
const maxY = Math.max(...Object.values(this.players).map(e => e.kills))
|
||||
|
||||
if (maxY > maxX) {
|
||||
endX = maxX
|
||||
endY = (maxX / maxY) * maxY
|
||||
} else {
|
||||
endY = maxY
|
||||
endX = (maxY / maxX) * maxX
|
||||
}
|
||||
}
|
||||
return {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: { duration: 500 },
|
||||
layout: { autoPadding: false },
|
||||
scales: {
|
||||
@@ -124,45 +139,7 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
},
|
||||
annotation: {},
|
||||
legend: { display: false },
|
||||
datalabels: {
|
||||
formatter: (value: any, context: any) => {
|
||||
return this.players[this.playerIdList[context.dataIndex]].username
|
||||
},
|
||||
backgroundColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? this.colors.orange : null,
|
||||
borderColor: this.colors.fg,
|
||||
borderWidth: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? 1 : 0,
|
||||
borderRadius: 5,
|
||||
display: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? 'true' : 'auto',
|
||||
align: '-45',
|
||||
anchor: 'end',
|
||||
clamp: true,
|
||||
color: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? this.colors.bg : this.colors.fg
|
||||
}
|
||||
},
|
||||
onClick: (e: Event, element: any) => {
|
||||
this.$emit('highlightPlayer', element.length > 0 ? this.playerIdList[element[0].index] : undefined)
|
||||
},
|
||||
onHover: (e: any, element: any) => {
|
||||
if (!e.native.target) return
|
||||
(e.native.target as HTMLElement).style.cursor = element[0] ? 'pointer' : 'default'
|
||||
}
|
||||
}
|
||||
if (!this.players) {
|
||||
return options
|
||||
}
|
||||
const maxX = Math.max(...Object.values(this.players).map(e => e.deaths))
|
||||
const maxY = Math.max(...Object.values(this.players).map(e => e.kills))
|
||||
let endX, endY
|
||||
if (maxY > maxX) {
|
||||
endX = maxX
|
||||
endY = (maxX / maxY) * maxY
|
||||
} else {
|
||||
endY = maxY
|
||||
endX = (maxY / maxX) * maxX
|
||||
}
|
||||
options.plugins.annotation = {
|
||||
annotation: {
|
||||
annotations: {
|
||||
line: {
|
||||
type: 'line',
|
||||
@@ -175,18 +152,48 @@ export default defineComponent({
|
||||
borderDash: [1, 5]
|
||||
}
|
||||
}
|
||||
}
|
||||
return options
|
||||
},
|
||||
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) || {} },
|
||||
legend: { display: false },
|
||||
datalabels: {
|
||||
formatter: (value: any, context: any) => {
|
||||
return this.players[this.playerIdList[context.dataIndex]].username
|
||||
},
|
||||
backgroundColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? this.colors.orange : null,
|
||||
borderColor: this.colors.fg,
|
||||
borderWidth: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? 1 : 0,
|
||||
borderRadius: 5,
|
||||
display: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? 'true' : 'auto',
|
||||
align: -45,
|
||||
anchor: 'end',
|
||||
clamp: true,
|
||||
color: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? this.colors.bg : this.colors.fg
|
||||
}
|
||||
},
|
||||
onClick: (e: ChartEvent, element: any) => {
|
||||
this.$emit('highlightPlayer', element.length > 0 ? this.playerIdList[element[0].index] : undefined)
|
||||
},
|
||||
onHover: (e: any, element: any) => {
|
||||
if (!e.native.target) return
|
||||
(e.native.target as HTMLElement).style.cursor = element[0] ? 'pointer' : 'default'
|
||||
}
|
||||
}
|
||||
},
|
||||
players (): { [key: string]: Player } {
|
||||
const data = this.store.getPlayerList(this.filters || {})?.data
|
||||
if (!data) return {}
|
||||
const cut = Object.entries(data).sort((a, b) => {
|
||||
if (a[1].kills < b[1].kills) {
|
||||
return 1
|
||||
}
|
||||
if (a[1].kills > b[1].kills) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}).slice(0, 200)
|
||||
return Object.fromEntries(cut)
|
||||
},
|
||||
playerIdList (): string[] { return Object.keys(this.players) }
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
store: useStore(),
|
||||
refreshColors: 0
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
<span v-on:click="sortPlayerList('kills')" :class="sortingData.argument == 'kills' ? 'selected' : ''">K</span>
|
||||
<span v-on:click="sortPlayerList('deaths')" :class="sortingData.argument == 'deaths' ? 'selected' : ''">D</span>
|
||||
<span v-on:click="sortPlayerList('k/d')" :class="sortingData.argument == 'k/d' ? 'selected' : ''">K/D</span>
|
||||
<span v-on:click="sortPlayerList('max_kill_distance')"
|
||||
:class="sortingData.argument == 'max_kill_distance' ? 'selected' : ''">max distance</span>
|
||||
<span v-on:click="sortPlayerList('avg_kill_distance')"
|
||||
:class="sortingData.argument == 'avg_kill_distance' ? 'selected' : ''">average distance</span>
|
||||
<span v-on:click="sortPlayerList('max_distance')"
|
||||
:class="sortingData.argument == 'max_distance' ? 'selected' : ''">max distance</span>
|
||||
<span v-on:click="sortPlayerList('avg_distance')"
|
||||
:class="sortingData.argument == 'avg_distance' ? 'selected' : ''">average distance</span>
|
||||
</div>
|
||||
<div :class="'playerRow ' + (playerId === $props.playerHighlighted ? 'selected' : '')"
|
||||
v-for="(playerId, index) in playerIdList" v-bind:key="playerId" v-on:click="$emit('highlightPlayer', playerId)"
|
||||
@@ -20,32 +20,34 @@
|
||||
<div><span>{{ players[playerId].kills }}</span></div>
|
||||
<div><span>{{ players[playerId].deaths }}</span></div>
|
||||
<div><span>{{ Math.round(players[playerId].kills / Math.max(1, players[playerId].deaths) * 100) / 100 }}</span></div>
|
||||
<div><span>{{ players[playerId].max_kill_distance }}</span></div>
|
||||
<div><span>{{ Math.round(players[playerId].avg_kill_distance * 100) / 100 }}</span></div>
|
||||
<div><span>{{ players[playerId].max_distance }}</span></div>
|
||||
<div><span>{{ Math.round(((players[playerId].total_distance / players[playerId].kills) || 0) * 100) / 100 }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { Player } from '@/store/index'
|
||||
import { useStore } from 'vuex'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { useKillStore, Player, Filters } from '@/stores/kill'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
filters: Object,
|
||||
filters: { type: Object as PropType<Filters>, default: () => ({}) },
|
||||
playerHighlighted: String
|
||||
},
|
||||
emits: ['highlightPlayer'],
|
||||
data () {
|
||||
return {
|
||||
sortingData: { direction: -1, argument: 'kills' as keyof Player | 'k/d' },
|
||||
sortingData: { direction: -1, argument: 'kills' as keyof Player | 'k/d' | 'avg_distance' },
|
||||
playerIdList: [] as string[],
|
||||
store: useStore()
|
||||
store: useKillStore()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) }
|
||||
players (): { [key: string]: Player } {
|
||||
const { player: _, ...withoutPlayer } = this.filters
|
||||
return this.store.getPlayerList(withoutPlayer)?.data || {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
players: {
|
||||
@@ -55,12 +57,13 @@ export default defineComponent({
|
||||
this.sortingData.direction *= -1
|
||||
this.playerIdList = Object.keys(newval)
|
||||
this.sortPlayerList(this.sortingData.argument)
|
||||
this.playerIdList = this.playerIdList.slice(0, 500)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
playerHighlighted (newval) {
|
||||
const newElement = this.$refs['player:' + newval] as HTMLElement[]
|
||||
newElement[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
if (newElement && newElement[0]) { newElement[0].scrollIntoView({ behavior: 'smooth', block: 'center' }) }
|
||||
}
|
||||
},
|
||||
updated () {
|
||||
@@ -69,7 +72,7 @@ export default defineComponent({
|
||||
element[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
},
|
||||
methods: {
|
||||
sortPlayerList (arg: ((keyof Player) | 'k/d')) {
|
||||
sortPlayerList (arg: ((keyof Player) | 'k/d' | 'avg_distance')) {
|
||||
if (arg === this.sortingData.argument) {
|
||||
this.sortingData.direction *= -1
|
||||
} else {
|
||||
@@ -87,11 +90,14 @@ export default defineComponent({
|
||||
if (arg === 'k/d') {
|
||||
varA = this.players[a].kills / Math.max(1, this.players[a].deaths)
|
||||
varB = this.players[b].kills / Math.max(1, this.players[b].deaths)
|
||||
} else if (arg === 'avg_distance') {
|
||||
varA = (this.players[a].total_distance / this.players[a].kills) || 0
|
||||
varB = (this.players[b].total_distance / this.players[b].kills) || 0
|
||||
} else {
|
||||
varA = this.players[a][arg]
|
||||
varB = this.players[b][arg]
|
||||
}
|
||||
|
||||
if (varA === undefined || varB === undefined) return 0
|
||||
if (varA < varB) {
|
||||
return -1 * this.sortingData.direction
|
||||
}
|
||||
@@ -136,13 +142,14 @@ export default defineComponent({
|
||||
margin-right: 1rem;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.playerRow,
|
||||
.playerHeaders {
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
grid-template-columns: 4ch 20ch 6ch 6ch 6ch 10ch 1fr;
|
||||
grid-template-columns: 5ch 20ch 6ch 6ch 6ch 10ch 1fr;
|
||||
background: var(--bg-color);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
@@ -201,7 +208,7 @@ export default defineComponent({
|
||||
|
||||
.playerRow,
|
||||
.playerHeaders {
|
||||
grid-template-columns: 4ch 20ch 6ch 6ch 1fr;
|
||||
grid-template-columns: 5ch 20ch 6ch 6ch 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,28 +5,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
ArcElement,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Tooltip,
|
||||
Legend
|
||||
} from 'chart.js'
|
||||
import annotationPlugin from 'chartjs-plugin-annotation'
|
||||
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
|
||||
import dataLabel from 'chartjs-plugin-datalabels'
|
||||
import { Store, useStore } from 'vuex'
|
||||
import { Player, Weapon } from '@/store/index'
|
||||
import { Weapon, Filters, useKillStore } from '@/stores/kill'
|
||||
import { Doughnut } from 'vue-chartjs'
|
||||
import { defineComponent } from 'vue'
|
||||
import { functionExpression } from '@babel/types'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
|
||||
ChartJS.register(ArcElement, Tooltip, Legend, dataLabel)
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PlayerChart',
|
||||
props: {
|
||||
filters: Object,
|
||||
filters: Object as PropType<Filters>,
|
||||
playerHighlighted: String
|
||||
},
|
||||
emits: ['highlightPlayer'],
|
||||
@@ -36,6 +26,12 @@ export default defineComponent({
|
||||
mounted () {
|
||||
this.refreshColors++
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
store: useKillStore(),
|
||||
refreshColors: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
colors () {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
@@ -59,14 +55,26 @@ export default defineComponent({
|
||||
return colors
|
||||
},
|
||||
chart () {
|
||||
const colors = ['cyan', 'green', 'orange', 'pink', 'purple', 'red', 'yellow']
|
||||
const colors = [
|
||||
'cyan',
|
||||
'green',
|
||||
'orange',
|
||||
'pink',
|
||||
'purple',
|
||||
'red',
|
||||
'yellow'
|
||||
]
|
||||
const chartData = {
|
||||
datasets: [
|
||||
{
|
||||
label: 'Weapons',
|
||||
labels: this.sortedWeaponList || [] as string[],
|
||||
borderColor: (context: any) => this.colors.fg,
|
||||
backgroundColor: (context: any) => { return this.colors[colors[(context.dataIndex * 3) % colors.length]] },
|
||||
labels: this.sortedWeaponList || ([] as string[]),
|
||||
borderColor: () => this.colors.fg,
|
||||
backgroundColor: (context: any) => {
|
||||
return this.colors[
|
||||
colors[(context.dataIndex * 3) % colors.length]
|
||||
]
|
||||
},
|
||||
data: [] as number[]
|
||||
}
|
||||
]
|
||||
@@ -74,7 +82,7 @@ export default defineComponent({
|
||||
if (!this.sortedWeaponList) {
|
||||
return chartData
|
||||
}
|
||||
chartData.datasets[0].data = this.sortedWeaponList.map(e => {
|
||||
chartData.datasets[0].data = this.sortedWeaponList.map((e) => {
|
||||
if (!this.weapons) {
|
||||
return 0
|
||||
}
|
||||
@@ -103,10 +111,9 @@ export default defineComponent({
|
||||
borderWidth: 2,
|
||||
color: this.colors.bg,
|
||||
display: (context: any) => {
|
||||
return context.dataIndex === context.dataset.labels.length - 1 ? true : 'auto'
|
||||
},
|
||||
font: {
|
||||
weight: 'bold'
|
||||
return context.dataIndex === context.dataset.labels.length - 1
|
||||
? true
|
||||
: 'auto'
|
||||
},
|
||||
padding: 6
|
||||
},
|
||||
@@ -124,13 +131,18 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return options
|
||||
},
|
||||
weapons (): { [key: string]: Weapon } {
|
||||
return this.store.getters.getWeaponList(this.filters)
|
||||
const { weapon: _, ...withoutWeapon } = this.filters || {}
|
||||
const data = this.store.getWeaponList(withoutWeapon)?.data
|
||||
if (!data) {
|
||||
this.store.fetchWeapons(withoutWeapon)
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
},
|
||||
sortedWeaponList (): string[] {
|
||||
if (!this.weapons) return []
|
||||
@@ -146,13 +158,6 @@ export default defineComponent({
|
||||
})
|
||||
return weapons
|
||||
}
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
store: useStore(),
|
||||
refreshColors: 0
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -165,7 +170,7 @@ export default defineComponent({
|
||||
|
||||
@media only screen and (max-width: 922px) {
|
||||
canvas {
|
||||
display: none !important
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+3
-5
@@ -1,10 +1,8 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
createApp(App).use(store).use(router).mount('#app')
|
||||
const pinia = createPinia()
|
||||
|
||||
store.dispatch('fetchServers', {})
|
||||
store.dispatch('fetchPlayers', {})
|
||||
store.dispatch('fetchWeapons', {})
|
||||
createApp(App).use(pinia).use(router).mount('#app')
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
// store.ts
|
||||
import { InjectionKey } from 'vue'
|
||||
import { createStore, Store } from 'vuex'
|
||||
|
||||
export interface Player {
|
||||
deaths: number;
|
||||
username: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
max_kill_distance: number;
|
||||
// eslint-disable-next-line camelcase
|
||||
avg_kill_distance: number;
|
||||
kills: number;
|
||||
}
|
||||
|
||||
export interface Weapon {
|
||||
id?:string,
|
||||
// eslint-disable-next-line camelcase
|
||||
max_kill_distance: number;
|
||||
// eslint-disable-next-line camelcase
|
||||
avg_kill_distance: number;
|
||||
kills: number;
|
||||
}
|
||||
|
||||
export interface Server{
|
||||
name:string;
|
||||
id:number;
|
||||
description:string
|
||||
}
|
||||
// define your typings for the store state
|
||||
export interface State {
|
||||
servers: Server[]
|
||||
players: { [key: string]: { [key: string]: Player } };
|
||||
weapons: { [key: string]:{ [key: string]: Weapon }};
|
||||
}
|
||||
|
||||
// define injection key
|
||||
// eslint-disable-next-line symbol-description
|
||||
export const key: InjectionKey<Store<State>> = Symbol()
|
||||
|
||||
export default createStore<State>({
|
||||
state: {
|
||||
servers: [],
|
||||
players: {},
|
||||
weapons: {}
|
||||
},
|
||||
getters: {
|
||||
getPlayerList: (state) => ({ server, weapon, minKills, minDeaths }: { server?: number, weapon?: string, minKills:number, minDeaths:number }) => {
|
||||
let playerFilter: string[] = []
|
||||
if (state.players['']) {
|
||||
playerFilter = Object.entries(state.players['']).filter(([, value]:any) => { return (value.kills >= minKills) || (value.deaths >= minDeaths) }).map(e => e[0])
|
||||
}
|
||||
if (playerFilter.length > 0 && state.players[(server || '') + (weapon || '')]) {
|
||||
return Object.fromEntries(Object.entries(state.players[(server || '') + (weapon || '')]).filter(([key]) => playerFilter.includes(key)))
|
||||
}
|
||||
|
||||
return state.players[(server || '') + (weapon || '')]
|
||||
},
|
||||
getWeaponList: (state) => ({ server, player }: { server?: number, player?: string }) => {
|
||||
return state.weapons[(server || '') + (player || '')]
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
setPlayers (state, data: { data: { [key: string]: Player }, filters: { weapon: string, server: number } }) {
|
||||
state.players[(data.filters.server || '') + (data.filters.weapon || '')] = data.data
|
||||
},
|
||||
setWeapons (state, data:{data: {[key:string]:Weapon}, filters:{server:string, player:string}}) {
|
||||
state.weapons[(data.filters.server || '') + (data.filters.player || '')] = data.data
|
||||
},
|
||||
setServers (state, data) {
|
||||
state.servers = data
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async fetchPlayers (context, { weapon, server }) {
|
||||
const searchParams: Record<string, string> = {}
|
||||
if (weapon?.toString()) { searchParams.weapon = weapon }
|
||||
if (server?.toString()) { searchParams.server = server }
|
||||
const response = await fetch(
|
||||
'https://tone.sleepycat.date/v1/client/players?' + new URLSearchParams(searchParams)
|
||||
)
|
||||
const data = await response.json()
|
||||
Object.keys(data).forEach(e => {
|
||||
data[e].kills = Number(data[e].kills)
|
||||
data[e].deaths = Number(data[e].deaths)
|
||||
data[e].max_kill_distance = Number(data[e].max_kill_distance)
|
||||
data[e].avg_kill_distance = Number(data[e].avg_kill_distance)
|
||||
})
|
||||
|
||||
context.commit('setPlayers', { data, filters: { weapon, server } })
|
||||
},
|
||||
async fetchWeapons (context, { player, server }) {
|
||||
const searchParams: Record<string, string> = {}
|
||||
if (player?.toString()) { searchParams.player = player }
|
||||
if (server?.toString()) { searchParams.server = server }
|
||||
const response = await fetch(
|
||||
'https://tone.sleepycat.date/v1/client/weapons?' + new URLSearchParams(searchParams)
|
||||
)
|
||||
const data = await response.json()
|
||||
context.commit('setWeapons', { data, filters: { player, server } })
|
||||
},
|
||||
async fetchServers (context) {
|
||||
const response = await fetch(
|
||||
'https://tone.sleepycat.date/v1/client/servers'
|
||||
)
|
||||
const data = await response.json()
|
||||
context.commit('setServers', data)
|
||||
}
|
||||
},
|
||||
modules: {}
|
||||
})
|
||||
@@ -0,0 +1,113 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import * as assert from 'assert'
|
||||
|
||||
function removeNullEntries (a:any) {
|
||||
return Object.fromEntries(Object.entries(a).filter(e => e[1] !== undefined && e[1] !== null))
|
||||
}
|
||||
function objectEqual (a:any, b:any) {
|
||||
try {
|
||||
a = removeNullEntries(a)
|
||||
b = removeNullEntries(b)
|
||||
assert.deepStrictEqual(a, b)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
export interface Filters {
|
||||
server?: string;
|
||||
player?: string;
|
||||
weapon?: string;
|
||||
host?: string;
|
||||
map?: string;
|
||||
gamemode?: string;
|
||||
}
|
||||
|
||||
export interface Kill {
|
||||
deaths: number;
|
||||
kills: number;
|
||||
// eslint-disable-next-line camelcase
|
||||
max_distance: number;
|
||||
// eslint-disable-next-line camelcase
|
||||
total_distance: number;
|
||||
}
|
||||
export interface KillData<T extends Kill> {
|
||||
filter: Filters;
|
||||
data: { [key: string]: T };
|
||||
}
|
||||
|
||||
export interface Player extends Kill {
|
||||
username: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
deaths_while_equipped?: number;
|
||||
}
|
||||
|
||||
export interface Weapon extends Kill {
|
||||
// eslint-disable-next-line camelcase
|
||||
deaths_while_equipped: number;
|
||||
}
|
||||
|
||||
export interface Server extends Kill {
|
||||
host: number;
|
||||
}
|
||||
|
||||
// define your typings for the store state
|
||||
export interface State {
|
||||
servers: KillData<Server>[];
|
||||
players: KillData<Player>[];
|
||||
weapons: KillData<Weapon>[];
|
||||
maps: KillData<Kill>[];
|
||||
gamemodes: KillData<Kill>[];
|
||||
hosts: { [key: number]: string };
|
||||
}
|
||||
|
||||
export const useKillStore = defineStore('kill', {
|
||||
state: (): State => ({
|
||||
servers: [],
|
||||
players: [],
|
||||
weapons: [],
|
||||
maps: [],
|
||||
gamemodes: [],
|
||||
hosts: {}
|
||||
}),
|
||||
getters: {
|
||||
getPlayerList: (state) => (filters: Filters) => state.players.find((e) => objectEqual(e.filter, filters)),
|
||||
getWeaponList: (state) => (filters: Filters) => state.weapons.find((e) => objectEqual(e.filter, filters)),
|
||||
getServerList: (state) => (filters: Filters) => state.servers.find((e) => objectEqual(e.filter, filters))
|
||||
},
|
||||
actions: {
|
||||
async fetchPlayers (filter: Filters) {
|
||||
filter = removeNullEntries(filter)
|
||||
const response = await fetch(
|
||||
'https://tone.sleepycat.date/v2_test/client/players?' +
|
||||
new URLSearchParams(filter as Record<keyof Filters, string>)
|
||||
)
|
||||
const data = await response.json()
|
||||
const entry = this.players.find((e) => objectEqual(e.filter, filter))
|
||||
if (!entry) return this.players.push({ filter, data })
|
||||
entry.data = data
|
||||
},
|
||||
async fetchWeapons (filter: Filters) {
|
||||
filter = removeNullEntries(filter)
|
||||
const response = await fetch(
|
||||
'https://tone.sleepycat.date/v2_test/client/weapons?' +
|
||||
new URLSearchParams(filter as Record<keyof Filters, string>)
|
||||
)
|
||||
const data = await response.json()
|
||||
const entry = this.weapons.find((e) => objectEqual(e.filter, filter))
|
||||
if (!entry) return this.weapons.push({ filter, data })
|
||||
entry.data = data
|
||||
},
|
||||
async fetchServers (filter: Filters) {
|
||||
filter = removeNullEntries(filter)
|
||||
const response = await fetch(
|
||||
'https://tone.sleepycat.date/v2_test/client/servers?' +
|
||||
new URLSearchParams(filter as Record<keyof Filters, string>)
|
||||
)
|
||||
const data = await response.json()
|
||||
const entry = this.servers.find((e) => objectEqual(e.filter, filter))
|
||||
if (!entry) return this.servers.push({ filter, data })
|
||||
entry.data = data
|
||||
}
|
||||
}
|
||||
})
|
||||
+44
-42
@@ -12,16 +12,16 @@
|
||||
<option v-for="weaponId in sortedWeaponList" v-bind:key="weaponId" :value="weaponId">{{ weaponId }}</option>
|
||||
</select> -->
|
||||
|
||||
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Select server" v-model="models.server"
|
||||
:options="servers" :allow-empty="true" :custom-label="((e) => e.name)" @select="changeFilter({ server: $event.id })"
|
||||
@remove="changeFilter({ server: '' })"></VueMultiselect>
|
||||
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Select server" v-model="filters.server"
|
||||
:options="sortedServerList" :allow-empty="true" :custom-label="((e:Server) => e)"
|
||||
></VueMultiselect>
|
||||
|
||||
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Select weapon" v-model="models.weapon"
|
||||
:options="sortedWeaponList" :allow-empty="true" @select="changeFilter({ weapon: $event })"
|
||||
@remove="changeFilter({ weapon: '' })"></VueMultiselect>
|
||||
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Select weapon" v-model="filters.weapon"
|
||||
:options="sortedWeaponList" :allow-empty="true"
|
||||
></VueMultiselect>
|
||||
|
||||
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Search player" v-model="playerHighlighted"
|
||||
:options="sortedPlayerList" :allow-empty="true" :custom-label="((e) => players[e]?.username)"></VueMultiselect>
|
||||
:options="sortedPlayerList" :allow-empty="true" :custom-label="((e:string) => players[e]?.username)"></VueMultiselect>
|
||||
</div>
|
||||
|
||||
<div id="playerView">
|
||||
@@ -34,8 +34,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Store, useStore } from 'vuex'
|
||||
import { Player, Weapon, Server } from '@/store/index'
|
||||
import { Player, Weapon, Server, useKillStore, Filters } from '@/stores/kill'
|
||||
import PlayerList from '@/components/PlayerList.vue'
|
||||
import PlayerChart from '@/components/PlayerChart.vue'
|
||||
import WeaponChart from '@/components/WeaponChart.vue'
|
||||
@@ -46,26 +45,54 @@ import 'vue-multiselect/dist/vue-multiselect.css'
|
||||
export default defineComponent({
|
||||
name: 'PlayerView',
|
||||
components: {
|
||||
PlayerList, PlayerChart, VueMultiselect, WeaponChart
|
||||
VueMultiselect, PlayerList, PlayerChart, WeaponChart
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
models: {},
|
||||
filters: { minKills: 100, minDeaths: 100 },
|
||||
store: useStore(),
|
||||
filters: {} as Filters,
|
||||
store: useKillStore(),
|
||||
playerHighlighted: undefined
|
||||
} as { filters: { weapon?: string, server?: string }, store: Store<any>, playerHighlighted?: string }
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
servers (): Server[] { return this.store.state.servers },
|
||||
weapons (): { [key: string]: Weapon } { return this.store.getters.getWeaponList(this.filters) },
|
||||
servers (): { [key: string]: Server } {
|
||||
const { server: _, player: _1, ...withoutServer } = this.filters
|
||||
const data = this.store.getServerList(withoutServer)?.data
|
||||
if (!data) {
|
||||
this.store.fetchServers(withoutServer)
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
},
|
||||
weapons (): { [key: string]: Weapon } {
|
||||
const { weapon: _, player: _1, ...withoutWeapons } = this.filters
|
||||
const data = this.store.getWeaponList(withoutWeapons)?.data
|
||||
if (!data) {
|
||||
this.store.fetchWeapons(withoutWeapons)
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
},
|
||||
players (): { [key: string]: Player } {
|
||||
const { player: _, ...withoutPlayers } = this.filters
|
||||
const data = this.store.getPlayerList(withoutPlayers)?.data
|
||||
if (!data) {
|
||||
this.store.fetchPlayers(withoutPlayers)
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
},
|
||||
sortedWeaponList (): string[] {
|
||||
if (!this.weapons) return []
|
||||
const weapons = Object.keys(this.weapons)
|
||||
return weapons.sort()
|
||||
},
|
||||
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) },
|
||||
sortedServerList ():string[] {
|
||||
if (!this.servers) return []
|
||||
const servers = Object.keys(this.servers)
|
||||
return servers.sort()
|
||||
},
|
||||
sortedPlayerList (): string[] {
|
||||
if (!this.players) return []
|
||||
const players = Object.keys(this.players)
|
||||
@@ -73,7 +100,6 @@ export default defineComponent({
|
||||
if (!this.players) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (this.players[a].username < this.players[b].username) {
|
||||
return -1
|
||||
}
|
||||
@@ -83,30 +109,6 @@ export default defineComponent({
|
||||
return 0
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
playerHighlighted: function (newval:string) {
|
||||
this.fetchPlayerData({ player: newval, ...this.filters })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchPlayerData ({ player, server }: { player?: string, server?:string }) {
|
||||
if (!this.store.getters.getWeaponList({ player, server })) return await this.store.dispatch('fetchWeapons', { player, server })
|
||||
},
|
||||
async changeFilter ({ weapon, server }: { weapon?: string, server?: string }) {
|
||||
const filters = JSON.parse(JSON.stringify(this.filters))
|
||||
if (weapon !== undefined) filters.weapon = weapon
|
||||
if (server !== undefined) filters.server = server
|
||||
if (weapon === '') delete filters.weapon
|
||||
if (server === '') delete filters.server
|
||||
const promises = []
|
||||
if (!this.store.getters.getPlayerList(filters)) promises.push(this.store.dispatch('fetchPlayers', filters))
|
||||
if (!this.store.getters.getWeaponList(filters)) promises.push(this.store.dispatch('fetchWeapons', filters))
|
||||
promises.push(this.fetchPlayerData({ player: this.playerHighlighted, ...filters }))
|
||||
await Promise.all(promises)
|
||||
// Delay the update of filters propery after we fetch the data to the API as changing it will cause subcomponents to reload before data is fetched
|
||||
this.filters = filters
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -34,8 +34,5 @@
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user