diff --git a/.eslintrc b/.eslintrc index 8d0b35aac583..7a4aeb6d2fd6 100644 --- a/.eslintrc +++ b/.eslintrc @@ -30,6 +30,7 @@ "LivechatDepartment" : false, "LivechatDepartmentAgents" : false, "livechatManagerRoutes" : true, + "LivechatMonitoring" : false, "LivechatPageVisited" : false, "LivechatTrigger" : false, "Logger" : false, diff --git a/package-lock.json b/package-lock.json index 6ea70282f262..17a73e3c6410 100644 --- a/package-lock.json +++ b/package-lock.json @@ -350,6 +350,11 @@ "through": "2.3.8" } }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "abab": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", @@ -2025,19 +2030,23 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "aproba": { "version": "1.2.0", - "bundled": true + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "are-we-there-yet": { "version": "1.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "requires": { "delegates": "1.0.0", "readable-stream": "2.3.5" @@ -2045,11 +2054,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" @@ -2057,57 +2068,69 @@ }, "chownr": { "version": "1.0.1", - "bundled": true + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" }, "code-point-at": { "version": "1.1.0", - "bundled": true + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "concat-map": { "version": "0.0.1", - "bundled": true + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "core-util-is": { "version": "1.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "debug": { "version": "2.6.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } }, "deep-extend": { "version": "0.4.2", - "bundled": true + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" }, "delegates": { "version": "1.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "detect-libc": { "version": "1.0.3", - "bundled": true + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, "fs-minipass": { "version": "1.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "requires": { "minipass": "2.2.4" } }, "fs.realpath": { "version": "1.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "requires": { "aproba": "1.2.0", "console-control-strings": "1.1.0", @@ -2121,7 +2144,8 @@ }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "requires": { "fs.realpath": "1.0.0", "inflight": "1.0.6", @@ -2133,25 +2157,29 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "iconv-lite": { "version": "0.4.21", - "bundled": true, + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", + "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "requires": { "safer-buffer": "2.1.2" } }, "ignore-walk": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "requires": { "minimatch": "3.0.4" } }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { "once": "1.4.0", "wrappy": "1.0.2" @@ -2159,37 +2187,44 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", - "bundled": true + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { "number-is-nan": "1.0.1" } }, "isarray": { "version": "1.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "1.1.11" } }, "minimist": { "version": "0.0.8", - "bundled": true + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minipass": { "version": "2.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", + "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "requires": { "safe-buffer": "5.1.1", "yallist": "3.0.2" @@ -2197,31 +2232,36 @@ "dependencies": { "yallist": { "version": "3.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" } } }, "minizlib": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "requires": { "minipass": "2.2.4" } }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" } }, "ms": { "version": "2.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "needle": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz", + "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", "requires": { "debug": "2.6.9", "iconv-lite": "0.4.21", @@ -2230,7 +2270,8 @@ }, "node-pre-gyp": { "version": "0.9.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.9.1.tgz", + "integrity": "sha1-8RwHUW3ZL4cZnbx+GDjqt81WyeA=", "requires": { "detect-libc": "1.0.3", "mkdirp": "0.5.1", @@ -2246,7 +2287,8 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "requires": { "abbrev": "1.1.1", "osenv": "0.1.5" @@ -2254,11 +2296,13 @@ }, "npm-bundled": { "version": "1.0.3", - "bundled": true + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", + "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==" }, "npm-packlist": { "version": "1.1.10", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz", + "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "requires": { "ignore-walk": "3.0.1", "npm-bundled": "1.0.3" @@ -2266,7 +2310,8 @@ }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { "are-we-there-yet": "1.1.4", "console-control-strings": "1.1.0", @@ -2276,30 +2321,36 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-assign": { "version": "4.1.1", - "bundled": true + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1.0.2" } }, "os-homedir": { "version": "1.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-tmpdir": { "version": "1.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "requires": { "os-homedir": "1.0.2", "os-tmpdir": "1.0.2" @@ -2307,15 +2358,18 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "process-nextick-args": { "version": "2.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "rc": { "version": "1.2.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz", + "integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=", "requires": { "deep-extend": "0.4.2", "ini": "1.3.5", @@ -2325,13 +2379,15 @@ "dependencies": { "minimist": { "version": "1.2.0", - "bundled": true + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, "readable-stream": { "version": "2.3.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", + "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -2344,38 +2400,46 @@ }, "rimraf": { "version": "2.6.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "requires": { "glob": "7.1.2" } }, "safe-buffer": { "version": "5.1.1", - "bundled": true + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "safer-buffer": { "version": "2.1.2", - "bundled": true + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sax": { "version": "1.2.4", - "bundled": true + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { "version": "5.5.0", - "bundled": true + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" }, "set-blocking": { "version": "2.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "signal-exit": { "version": "3.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "1.1.0", "is-fullwidth-code-point": "1.0.0", @@ -2384,25 +2448,29 @@ }, "string_decoder": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "requires": { "safe-buffer": "5.1.1" } }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "2.1.1" } }, "strip-json-comments": { "version": "2.0.1", - "bundled": true + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "tar": { "version": "4.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.1.tgz", + "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "requires": { "chownr": "1.0.1", "fs-minipass": "1.2.5", @@ -2415,24 +2483,28 @@ "dependencies": { "yallist": { "version": "3.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" } } }, "util-deprecate": { "version": "1.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "wide-align": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "requires": { "string-width": "1.0.2" } }, "wrappy": { "version": "1.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } }, @@ -3217,6 +3289,39 @@ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "dev": true }, + "chart.js": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.2.tgz", + "integrity": "sha512-90wl3V9xRZ8tnMvMlpcW+0Yg13BelsGS9P9t0ClaDxv/hdypHDr/YAGf+728m11P5ljwyB0ZHfPKCapZFqSqYA==", + "requires": { + "chartjs-color": "2.2.0", + "moment": "2.22.2" + } + }, + "chartjs-color": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", + "integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=", + "requires": { + "chartjs-color-string": "0.5.0", + "color-convert": "0.5.3" + }, + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + } + } + }, + "chartjs-color-string": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz", + "integrity": "sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==", + "requires": { + "color-name": "1.1.3" + } + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -3300,10 +3405,16 @@ "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -3348,7 +3459,7 @@ "mocha": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "integrity": "sha1-fYbPvPNcuCnidUwy4XNV7AUzh5Q=", "dev": true, "requires": { "browser-stdout": "1.3.0", @@ -3366,13 +3477,13 @@ "commander": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=", "dev": true }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -3400,7 +3511,7 @@ "supports-color": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", "dev": true, "requires": { "has-flag": "2.0.0" @@ -3653,12 +3764,6 @@ "simple-swizzle": "0.2.2" } }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, "colour": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", @@ -3725,7 +3830,7 @@ }, "compression": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/compression/-/compression-1.5.2.tgz", "integrity": "sha1-sDuNhub4rSloPLqN+R3cb/x3s5U=", "requires": { "accepts": "1.2.13", @@ -4373,7 +4478,7 @@ "camel-case": "3.0.0", "cli-table": "0.3.1", "co": "4.6.0", - "colors": "1.1.2", + "colors": "1.3.0", "commander": "2.15.1", "duration": "0.2.0", "fibers": "1.0.15", @@ -4385,6 +4490,14 @@ "meteor-promise": "0.8.6", "stack-chain": "1.3.7", "stacktrace-js": "1.3.1" + }, + "dependencies": { + "colors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.0.tgz", + "integrity": "sha512-EDpX3a7wHMWFA7PUHWPHNWqOxIIRSJetuwl0AS5Oi/5FMV8kWm69RTlgm00GKjBO1xFHMtBbL49yRtMMdticBw==", + "dev": true + } } }, "cuid": { @@ -6085,524 +6198,75 @@ "optional": true, "requires": { "nan": "2.10.0", - "node-pre-gyp": "0.10.0" + "node-pre-gyp": "0.10.2" }, "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", + "needle": { + "version": "2.2.0", "bundled": true, "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "debug": "2.6.9", + "iconv-lite": "0.4.23", + "sax": "1.2.4" } }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, + "node-pre-gyp": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.2.tgz", + "integrity": "sha512-16lql9QTqs6KsB9fl3neWyZm02KxIKdI9FlJjrB0y7eMTP5Nyz+xalwPbOlw3iw7EejllJPmlJSnY711PLD1ug==", "dev": true, + "optional": true, "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.8", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.4" } }, - "chownr": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", + "npm-bundled": { + "version": "1.0.3", "bundled": true, "dev": true, "optional": true }, - "debug": { - "version": "2.6.9", + "npm-packlist": { + "version": "1.1.10", "bundled": true, "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" } }, - "deep-extend": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "glob": "7.1.2" } }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.21", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": "2.1.2" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "minipass": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.1.1", - "yallist": "3.0.2" - } - }, - "minizlib": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "2.2.4" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.9", - "iconv-lite": "0.4.21", - "sax": "1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.2.0", - "nopt": "4.0.1", - "npm-packlist": "1.1.10", - "npmlog": "4.1.2", - "rc": "1.2.7", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "4.4.1" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" - } - }, - "npm-bundled": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.1.10", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.5.1", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true, - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.5.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "1.0.1", - "fs-minipass": "1.2.5", - "minipass": "2.2.4", - "minizlib": "1.1.0", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.1", - "yallist": "3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, "yallist": { "version": "3.0.2", - "bundled": true, - "dev": true + "bundled": true } } }, @@ -6829,7 +6493,7 @@ "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", "requires": { "fs.realpath": "1.0.0", "inflight": "1.0.6", @@ -7099,109 +6763,38 @@ "requires": { "lodash": "4.17.10", "nan": "2.10.0", - "node-pre-gyp": "0.10.0", + "node-pre-gyp": "0.10.2", "protobufjs": "5.0.3" }, "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" - } - }, "balanced-match": { "version": "1.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { - "version": "1.1.11", - "bundled": true, + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, - "chownr": { - "version": "1.0.1", - "bundled": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, "concat-map": { "version": "0.0.1", - "bundled": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.5.1", - "bundled": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "requires": { - "minipass": "2.3.3" - } + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "fs.realpath": { "version": "1.0.0", - "bundled": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "requires": { "fs.realpath": "1.0.0", "inflight": "1.0.6", @@ -7211,94 +6804,28 @@ "path-is-absolute": "1.0.1" } }, - "has-unicode": { - "version": "2.0.1", - "bundled": true - }, - "iconv-lite": { - "version": "0.4.23", - "bundled": true, - "requires": { - "safer-buffer": "2.1.2" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "requires": { - "minimatch": "3.0.4" - } - }, "inflight": { - "version": "1.0.6", - "bundled": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.5", - "bundled": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "1.2.0", - "bundled": true - }, - "minipass": { - "version": "2.3.3", - "bundled": true, - "requires": { - "safe-buffer": "5.1.2", - "yallist": "3.0.2" - } - }, - "minizlib": { - "version": "1.1.0", - "bundled": true, + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { - "minipass": "2.3.3" + "once": "1.4.0", + "wrappy": "1.0.2" } }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "bundled": true - } + "brace-expansion": "1.1.8" } }, - "ms": { - "version": "2.0.0", - "bundled": true - }, "needle": { "version": "2.2.1", "bundled": true, @@ -7309,8 +6836,9 @@ } }, "node-pre-gyp": { - "version": "0.10.0", - "bundled": true, + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.2.tgz", + "integrity": "sha512-16lql9QTqs6KsB9fl3neWyZm02KxIKdI9FlJjrB0y7eMTP5Nyz+xalwPbOlw3iw7EejllJPmlJSnY711PLD1ug==", "requires": { "detect-libc": "1.0.3", "mkdirp": "0.5.1", @@ -7318,18 +6846,10 @@ "nopt": "4.0.1", "npm-packlist": "1.1.10", "npmlog": "4.1.2", - "rc": "1.2.7", + "rc": "1.2.8", "rimraf": "2.6.2", "semver": "5.5.0", - "tar": "4.4.3" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "tar": "4.4.4" } }, "npm-bundled": { @@ -7344,54 +6864,18 @@ "npm-bundled": "1.0.3" } }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true - }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1.0.2" } }, - "os-homedir": { - "version": "1.0.2", - "bundled": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, "path-is-absolute": { "version": "1.0.1", - "bundled": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "protobufjs": { "version": "5.0.3", @@ -7404,118 +6888,23 @@ "yargs": "3.32.0" } }, - "rc": { - "version": "1.2.7", - "bundled": true, - "requires": { - "deep-extend": "0.5.1", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" - } - }, "rimraf": { "version": "2.6.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "requires": { "glob": "7.1.2" } }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true - }, "sax": { "version": "1.2.4", - "bundled": true - }, - "semver": { - "version": "5.5.0", - "bundled": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true - }, - "tar": { - "version": "4.4.3", - "bundled": true, - "requires": { - "chownr": "1.0.1", - "fs-minipass": "1.2.5", - "minipass": "2.3.3", - "minizlib": "1.1.0", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.2", - "yallist": "3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "requires": { - "string-width": "1.0.2" - } + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "wrappy": { "version": "1.0.2", - "bundled": true - }, - "yallist": { - "version": "3.0.2", - "bundled": true + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } }, @@ -8111,42 +7500,6 @@ "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", "dev": true }, - "html-to-text": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-3.3.0.tgz", - "integrity": "sha1-aptjxpm4hbt7qEsURr/mh2u/z7c=", - "requires": { - "he": "1.1.1", - "htmlparser2": "3.9.2", - "optimist": "0.6.1", - "underscore": "1.9.1", - "underscore.string": "3.3.4" - }, - "dependencies": { - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" - } - }, - "htmlparser2": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", - "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", - "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.3.0", - "domutils": "1.7.0", - "entities": "1.1.1", - "inherits": "2.0.3", - "readable-stream": "2.3.6" - } - } - } - }, "htmlparser2": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", @@ -8305,6 +7658,14 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.8.tgz", "integrity": "sha512-pUh+xUQQhQzevjRHHFqqcTy0/dP/kS9I8HSrUydhihjuD09W6ldVWFtIrwhXdUJHis3i2rZNqEHpZH/cbinFbg==" }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "requires": { + "minimatch": "3.0.4" + } + }, "image-size": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.2.tgz", @@ -9779,10 +9140,49 @@ "tlds": "1.199.0" }, "dependencies": { + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0.1.0", + "domelementtype": "1.3.0" + } + }, + "html-to-text": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-3.3.0.tgz", + "integrity": "sha1-aptjxpm4hbt7qEsURr/mh2u/z7c=", + "requires": { + "he": "1.1.1", + "htmlparser2": "3.9.2", + "optimist": "0.6.1", + "underscore": "1.9.1", + "underscore.string": "3.3.4" + } + }, + "htmlparser2": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "requires": { + "domelementtype": "1.3.0", + "domhandler": "2.3.0", + "domutils": "1.7.0", + "entities": "1.1.1", + "inherits": "2.0.3", + "readable-stream": "2.3.6" + } + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "tlds": { + "version": "1.199.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.199.0.tgz", + "integrity": "sha512-NM0jUhibJjEX4g0+1ETxOhuODIDpyvCC0A2BjxrTfMUMZ+uRZc6ZnJl9SmFtAW1s5iQgQIxezFpUij6/6OiRbg==" } } }, @@ -10744,7 +10144,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "requires": { "brace-expansion": "1.1.11" } @@ -11100,6 +10500,15 @@ "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -11378,8 +10787,16 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } }, "p-cancelable": { "version": "0.3.0", @@ -11563,7 +10980,7 @@ "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "integrity": "sha1-zvMdyOCho7sNEFwM2Xzzv0f0428=", "requires": { "pify": "3.0.0" } @@ -11930,7 +11347,7 @@ "postcss-import": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz", - "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", + "integrity": "sha1-Vck2LJGSmU7GiGXSJEGd8dspgfA=", "dev": true, "requires": { "postcss": "6.0.22", @@ -11979,7 +11396,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { @@ -14686,7 +14103,7 @@ "stylelint-order": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-0.8.1.tgz", - "integrity": "sha512-8mp1P2wnI9XShYXVXDsxVigE2eXnc0C2O4ktbwUvTBwjCP4xZskIbUVxp1evSG3OK4R7hXVNl/2BnJCZkrcc/w==", + "integrity": "sha1-Nfca86FZVBVODpnlZGuj1vvjT40=", "dev": true, "requires": { "lodash": "4.17.10", @@ -14734,7 +14151,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { diff --git a/package.json b/package.json index ce498fa8783b..db211abd1afe 100644 --- a/package.json +++ b/package.json @@ -132,6 +132,7 @@ "bunyan": "^1.8.12", "busboy": "^0.2.14", "cas": "https://github.com/kcbanner/node-cas/tarball/fcd27dad333223b3b75a048bce27973fb3ca0f62", + "chart.js": "^2.7.2", "clipboard": "^2.0.1", "codemirror": "^5.38.0", "coffeescript": "^1.12.7", diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 9bcd9761e738..abc641dbd5fe 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -11,6 +11,7 @@ "@username_message": "@username ", "__username__is_no_longer__role__defined_by__user_by_": "__username__ is no longer __role__ by __user_by__", "__username__was_set__role__by__user_by_": "__username__ was set __role__ by __user_by__", + "%_of_conversations": "% of Conversations", "Accept": "Accept", "Accept_incoming_livechat_requests_even_if_there_are_no_online_agents": "Accept incoming livechat requests even if there are no online agents", "Accept_with_no_online_agents": "Accept with No Online Agents", @@ -242,6 +243,7 @@ "Allow_Marketing_Emails": "Allow Marketing Emails", "Alphabetical": "Alphabetical", "Always_open_in_new_window": "Always Open in New Window", + "Analytics": "Analytics", "Analytics_features_enabled": "Features Enabled", "Analytics_features_messages_Description": "Tracks custom events related to actions a user does on messages.", "Analytics_features_rooms_Description": "Tracks custom events related to actions on a channel or group (create, leave, delete).", @@ -380,6 +382,10 @@ "Avatar_changed_successfully": "Avatar changed successfully", "Avatar_URL": "Avatar URL", "Avatar_url_invalid_or_error": "The url provided is invalid or not accessible. Please try again, but with a different url.", + "Avg_chat_duration": "Avg Chat Duration", + "Avg_first_response_time": "Avg First Response Time", + "Avg_response_time": "Avg Response Time", + "Avg_reaction_time": "Avg Reaction Time", "away": "away", "Away": "Away", "away_female": "away", @@ -398,6 +404,7 @@ "ban-user": "Ban User", "ban-user_description": "Permission to ban a user from a channel", "Beta_feature_Depends_on_Video_Conference_to_be_enabled": "Beta feature. Depends on Video Conference to be enabled.", + "Best_first_response_time": "Best first response time", "Block_User": "Block User", "Blockchain": "Blockchain", "Blockstack_Auth_Description": "Auth description", @@ -419,6 +426,8 @@ "bulk-create-c_description": "Permission to create channels in bulk", "bulk-register-user": "Bulk Create Channels", "bulk-register-user_description": "Permission to create channels in bulk", + "Busiest_day": "Busiest Day", + "Busiest_time": "Busiest Time", "busy": "busy", "Busy": "Busy", "busy_female": "busy", @@ -544,6 +553,7 @@ "Client_Secret": "Client Secret", "Clients_will_refresh_in_a_few_seconds": "Clients will refresh in a few seconds", "close": "close", + "Closed": "Closed", "Close": "Close", "close-livechat-room": "Close Livechat Room", "close-livechat-room_description": "Permission to close the current LiveChat channel", @@ -572,8 +582,10 @@ "Continue": "Continue", "Continuous_sound_notifications_for_new_livechat_room": "Continuous sound notifications for new livechat room", "Conversation": "Conversation", + "Conversations": "Conversations", "Conversation_closed": "Conversation closed: __comment__.", "Conversation_finished_message": "Conversation Finished Message", + "Conversations_per_day": "Conversations per Day", "conversation_with_s": "the conversation with %s", "Convert_Ascii_Emojis": "Convert ASCII to Emoji", "Copied": "Copied", @@ -843,6 +855,7 @@ "Current_Status": "Current Status", "Custom": "Custom", "Custom_agent": "Custom agent", + "Custom_dates": "Custom Dates", "Custom_Emoji": "Custom Emoji", "Custom_Emoji_Add": "Add New Emoji", "Custom_Emoji_Added_Successfully": "Custom emoji added successfully", @@ -1213,6 +1226,7 @@ "Files_only": "Only remove the attached files, keep messages", "Financial_Services": "Financial Services", "First_Channel_After_Login": "First Channel After Login", + "First_response_time": "First Response Time", "Flags": "Flags", "Follow_social_profiles": "Follow our social profiles, fork us on github and share your thoughts about the rocket.chat app on our trello board.", "Fonts": "Fonts", @@ -1677,6 +1691,9 @@ "Logistics": "Logistics", "Logout": "Logout", "Logout_Others": "Logout From Other Logged In Locations", + "Longest_chat_duration": "Longest Chat Duration", + "Longest_reaction_time": "Longest Reaction Time", + "Longest_response_time": "Longest Response Time", "mail-messages": "Mail Messages", "mail-messages_description": "Permission to use the mail messages option", "Mail_Message_Invalid_emails": "You have provided one or more invalid emails: %s", @@ -1947,6 +1964,7 @@ "Oops_page_not_found": "Oops, page not found", "Open": "Open", "Open_channel_user_search": "`%s` - Open Channel / User search", + "Open_conversations": "Open Conversations", "Open_days_of_the_week": "Open Days of the Week", "Open_Livechats": "Open Livechats", "Open_your_authentication_app_and_enter_the_code": "Open your authentication app and enter the code. You can also use one of your backup codes.", @@ -2038,6 +2056,8 @@ "Preferences_saved": "Preferences saved", "preview-c-room": "Preview Public Channel", "preview-c-room_description": "Permission to view the contents of a public channel before joining", + "Previous_month": "Previous Month", + "Previous_week": "Previous Week", "Privacy": "Privacy", "Private": "Private", "Private_Channel": "Private Channel", @@ -2045,6 +2065,7 @@ "Private_Groups": "Private Groups", "Private_Groups_list": "List of Private Groups", "Private_Team": "Private Team", + "Productivity": "Productivity", "Profile": "Profile", "Profile_details": "Profile Details", "Profile_picture": "Profile Picture", @@ -2101,6 +2122,7 @@ "Read_only_group": "Read Only Group", "Real_Estate": "Real Estate", "RealName_Change_Disabled": "Your Rocket.Chat administrator has disabled the changing of names", + "Real_Time_Monitoring": "Real-time Monitoring", "Reason_To_Join": "Reason to Join", "Receive_alerts": "Receive alerts", "Receive_Group_Mentions": "Receive @all and @here mentions", @@ -2540,14 +2562,18 @@ "This_email_has_already_been_used_and_has_not_been_verified__Please_change_your_password": "This email has already been used and has not been verified. Please change your password.", "This_is_a_desktop_notification": "This is a desktop notification", "This_is_a_push_test_messsage": "This is a push test message", + "This_month": "This Month", "This_room_has_been_archived_by__username_": "This room has been archived by __username__", "This_room_has_been_unarchived_by__username_": "This room has been unarchived by __username__", + "This_week": "This Week", "Thursday": "Thursday", "Time_in_seconds": "Time in seconds", "Title": "Title", "Title_bar_color": "Title bar color", "Title_bar_color_offline": "Title bar color offline", "Title_offline": "Title offline", + "To": "To", + "Today": "Today", "To_additional_emails": "To additional emails", "To_install_RocketChat_Livechat_in_your_website_copy_paste_this_code_above_the_last_body_tag_on_your_site": "To install Rocket.Chat Livechat in your website, copy & paste this code above the last </body> tag on your site.", "to_see_more_details_on_how_to_integrate": "to see more details on how to integrate.", @@ -2566,6 +2592,9 @@ "Tokens_Required_Input_Error": "Invalid typed tokens.", "Tokens_Required_Input_Placeholder": "Tokens asset names", "Topic": "Topic", + "Total_conversations": "Total Conversations", + "Total_messages": "Total Messages", + "Total_visitors": "Total Visitors", "Tourism": "Tourism", "Transcript_Enabled": "Ask Visitor if They Would Like a Transcript After Chat Closed", "Transcript_message": "Message to Show When Asking About Transcript", @@ -2816,6 +2845,7 @@ "Worldwide": "Worldwide", "Would_you_like_to_return_the_inquiry": "Would you like to return the inquiry?", "Yes": "Yes", + "Yesterday": "Yesterday", "Yes_archive_it": "Yes, archive it!", "Yes_clear_all": "Yes, clear all!", "Yes_delete_it": "Yes, delete it!", diff --git a/packages/rocketchat-livechat/client/collections/LivechatMonitoring.js b/packages/rocketchat-livechat/client/collections/LivechatMonitoring.js new file mode 100644 index 000000000000..e86134dee85a --- /dev/null +++ b/packages/rocketchat-livechat/client/collections/LivechatMonitoring.js @@ -0,0 +1 @@ +this.LivechatMonitoring = new Mongo.Collection('livechatMonitoring'); diff --git a/packages/rocketchat-livechat/client/lib/chartHandler.js b/packages/rocketchat-livechat/client/lib/chartHandler.js new file mode 100644 index 000000000000..657f6077f79b --- /dev/null +++ b/packages/rocketchat-livechat/client/lib/chartHandler.js @@ -0,0 +1,226 @@ +import Chart from 'chart.js/src/chart.js'; + +const lineChartConfiguration = ({ legends = false, anim = false, smallTicks = false }) => { + const config = { + layout: { + padding: { + top: 10, + bottom: 0, + }, + }, + legend: { + display: false, + }, + title: { + display: false, + }, + tooltips: { + enabled: true, + mode: 'point', + displayColors: false, // hide color box + }, + scales: { + xAxes: [{ + scaleLabel: { + display: false, + }, + gridLines: { + display: true, + color: 'rgba(0, 0, 0, 0.03)', + }, + }], + yAxes: [{ + scaleLabel: { + display: false, + }, + gridLines: { + display: true, + color: 'rgba(0, 0, 0, 0.03)', + }, + ticks: { + beginAtZero: true, + }, + }], + }, + hover: { + animationDuration: 0, // duration of animations when hovering an item + }, + responsive: true, + maintainAspectRatio: false, + responsiveAnimationDuration: 0, // animation duration after a resize + }; + + if (!anim) { + config.animation = { + duration: 0, // general animation time + }; + } + + if (legends) { + config.legend = { + display: true, + labels: { + boxWidth: 20, + fontSize: 8, + }, + }; + } + + if (smallTicks) { + config.scales.xAxes[0].ticks = { + fontSize: 8, + }; + + config.scales.yAxes[0].ticks = { + beginAtZero: true, + fontSize: 8, + }; + } + + return config; +}; + + +const doughnutChartConfiguration = (title) => ({ + layout: { + padding: { + top: 0, + bottom: 0, + }, + }, + legend: { + display: true, + position: 'right', + labels: { + boxWidth: 20, + fontSize: 8, + }, + }, + title: { + display: 'true', + text: title, + }, + tooltips: { + enabled: true, + mode: 'point', + displayColors: false, // hide color box + }, + // animation: { + // duration: 0 // general animation time + // }, + hover: { + animationDuration: 0, // duration of animations when hovering an item + }, + responsive: true, + maintainAspectRatio: false, + responsiveAnimationDuration: 0, // animation duration after a resize +}); + + +/** + * + * @param {Object} chart - chart element + * @param {Object} chartContext - Context of chart + * @param {Array(String)} chartLabel + * @param {Array(String)} dataLabels + * @param {Array(Array(Double))} dataPoints + */ +export const drawLineChart = (chart, chartContext, chartLabels, dataLabels, dataSets, options = {}) => { + if (!chart) { + console.log('No chart element'); + return; + } + if (chartContext) { + chartContext.destroy(); + } + const colors = [ + '#2de0a5', + '#ffd21f', + '#f5455c', + '#cbced1', + ]; + + const datasets = []; + + chartLabels.forEach(function(chartLabel, index) { + datasets.push({ + label: TAPi18n.__(chartLabel), // chart label + data: dataSets[index], // data points corresponding to data labels, x-axis points + backgroundColor: [ + colors[index], + ], + borderColor: [ + colors[index], + ], + borderWidth: 3, + fill: false, + }); + }); + + return new Chart(chart, { + type: 'line', + data: { + labels: dataLabels, // data labels, y-axis points + datasets, + }, + options: lineChartConfiguration(options), + }); +}; + +/** + * + * @param {Object} chart - chart element + * @param {Object} chartContext - Context of chart + * @param {Array(String)} dataLabels + * @param {Array(Double)} dataPoints + */ +export const drawDoughnutChart = (chart, title, chartContext, dataLabels, dataPoints) => { + if (!chart) { + return; + } + if (chartContext) { + chartContext.destroy(); + } + + return new Chart(chart, { + type: 'doughnut', + data: { + labels: dataLabels, // data labels, y-axis points + datasets: [{ + data: dataPoints, // data points corresponding to data labels, x-axis points + backgroundColor: [ + '#2de0a5', + '#ffd21f', + '#f5455c', + '#cbced1', + ], + borderWidth: 0, + }], + }, + options: doughnutChartConfiguration(title), + }); +}; + +/** + * Update chart + * @param {Object} chart [Chart context] + * @param {String} label [chart label] + * @param {Array(Double)} data [updated data] + */ +export const updateChart = (chart, label, data) => { + if (chart.data.labels.indexOf(label) === -1) { + // insert data + chart.data.labels.push(label); + chart.data.datasets.forEach((dataset, idx) => { + dataset.data.push(data[idx]); + }); + } else { + // update data + const index = chart.data.labels.indexOf(label); + chart.data.datasets.forEach((dataset, idx) => { + dataset.data[index] = data[idx]; + }); + } + + chart.update(); +}; diff --git a/packages/rocketchat-livechat/client/lib/dataHandler.js b/packages/rocketchat-livechat/client/lib/dataHandler.js new file mode 100644 index 000000000000..76ea2eb097b5 --- /dev/null +++ b/packages/rocketchat-livechat/client/lib/dataHandler.js @@ -0,0 +1,199 @@ +/** + * + * @param {Object} dbCursor cursor to minimongo result + * @return {Object} + */ +const calculateResponseTimings = (dbCursor) => { + let art = 0; + let longest = 0; + let count = 0; + + dbCursor.forEach(({ metrics }) => { + if (metrics && metrics.response && metrics.response.avg) { + art += metrics.response.avg; + count++; + } + if (metrics && metrics.response && metrics.response.ft) { + longest = Math.max(longest, metrics.response.ft); + } + }); + + const avgArt = (count) ? art / count : 0; + + return { + avg: Math.round(avgArt * 100) / 100, + longest, + }; +}; + +/** + * + * @param {Object} dbCursor cursor to minimongo result + * @return {Object} + */ +const calculateReactionTimings = (dbCursor) => { + let arnt = 0; + let longest = 0; + let count = 0; + + dbCursor.forEach(({ metrics }) => { + if (metrics && metrics.reaction && metrics.reaction.ft) { + arnt += metrics.reaction.ft; + longest = Math.max(longest, metrics.reaction.ft); + count++; + } + }); + + const avgArnt = (count) ? arnt / count : 0; + + return { + avg: Math.round(avgArnt * 100) / 100, + longest, + }; +}; + +/** + * + * @param {Object} dbCursor cursor to minimongo result + * @return {Object} + */ +const calculateDurationData = (dbCursor) => { + let total = 0; + let longest = 0; + let count = 0; + + dbCursor.forEach(({ metrics }) => { + if (metrics && metrics.chatDuration) { + total += metrics.chatDuration; + longest = Math.max(longest, metrics.chatDuration); + count++; + } + }); + + const avgCD = (count) ? total / count : 0; + + return { + avg: Math.round(avgCD * 100) / 100, + longest, + }; +}; + +/** + * return readable time format from seconds + * @param {Double} sec seconds + * @return {String} Readable string format + */ +const secondsToHHMMSS = (sec) => { + sec = parseFloat(sec); + + let hours = Math.floor(sec / 3600); + let minutes = Math.floor((sec - (hours * 3600)) / 60); + let seconds = Math.round(sec - (hours * 3600) - (minutes * 60)); + + if (hours < 10) { hours = `0${ hours }`; } + if (minutes < 10) { minutes = `0${ minutes }`; } + if (seconds < 10) { seconds = `0${ seconds }`; } + + if (hours > 0) { + return `${ hours }:${ minutes }:${ seconds }`; + } + if (minutes > 0) { + return `${ minutes }:${ seconds }`; + } + return sec; +}; + + +/** + * + * @param {Object} dbCursor cursor to minimongo result + * @return {Object} + */ +export const getTimingsChartData = (dbCursor) => { + const data = {}; + + data.reaction = calculateReactionTimings(dbCursor); + data.response = calculateResponseTimings(dbCursor); + data.chatDuration = calculateDurationData(dbCursor); + + return data; +}; + +/** + * + * @param {Object} dbCursor cursor to minimongo result + * @return {Object} + */ +export const getAgentStatusData = (dbCursor) => { + const data = { + offline: 0, + available: 0, + away: 0, + busy: 0, + }; + + dbCursor.forEach((doc) => { + if (doc.status === 'offline' || doc.status === 'away' || doc.status === 'busy') { + data[doc.status]++; + } else if (doc.status === 'online' && doc.statusLivechat === 'available') { + data[doc.statusLivechat]++; + } else { + data.offline++; + } + }); + + return data; +}; + +/** + * + * @param {Object} dbCursor cursor to minimongo result + * @return {Array(Object)} + */ +export const getConversationsOverviewData = (dbCursor) => { + let total = 0; + let totalMessages = 0; + + dbCursor.forEach(function(doc) { + total++; + if (doc.msgs) { + totalMessages += doc.msgs; + } + }); + + return [{ + title: 'Total_conversations', + value: (total) ? total : 0, + }, { + title: 'Total_messages', + value: (totalMessages) ? totalMessages : 0, + }]; +}; + + +/** + * + * @param {Object} dbCursor cursor to minimongo result + * @return {Array(Object)} + */ +export const getTimingsOverviewData = (dbCursor) => { + let total = 0; + let totalResponseTime = 0; + let totalReactionTime = 0; + + dbCursor.forEach(function(doc) { + total++; + if (doc.metrics && doc.metrics.response) { + totalResponseTime += doc.metrics.response.avg; + totalReactionTime += doc.metrics.reaction.ft; + } + }); + + return [{ + title: 'Avg_response_time', + value: (total) ? secondsToHHMMSS((totalReactionTime / total).toFixed(2)) : '-', + }, { + title: 'Avg_reaction_time', + value: (total) ? secondsToHHMMSS((totalResponseTime / total).toFixed(2)) : '-', + }]; +}; diff --git a/packages/rocketchat-livechat/client/lib/dateHandler.js b/packages/rocketchat-livechat/client/lib/dateHandler.js new file mode 100644 index 000000000000..9a0d0b6de1c2 --- /dev/null +++ b/packages/rocketchat-livechat/client/lib/dateHandler.js @@ -0,0 +1,113 @@ +import moment from 'moment'; + + +/** + * Check if given daterange matches any of pre-defined options + * @param {String} value + * @param {Date} from + * @param {Date} to + * + * @returns {String} new value + */ +export const checkDaterangeValue = (value, from, to) => { + if (moment().startOf('day').isSame(from) && moment().startOf('day').isSame(to)) { + return 'today'; + } + if (moment().startOf('day').subtract(1, 'days').isSame(from) && moment().startOf('day').subtract(1, 'days').isSame(from)) { + return 'yesterday'; + } + if (moment().startOf('week').isSame(from) && moment().endOf('week').isSame(to)) { + return 'this-week'; + } + if (moment().subtract(1, 'weeks').startOf('week').isSame(from) && moment().subtract(1, 'weeks').endOf('week').isSame(to)) { + return 'prev-week'; + } + if (moment().startOf('month').isSame(from) && moment().endOf('month').isSame(to)) { + return 'this-month'; + } + if (moment().subtract(1, 'months').startOf('month').isSame(from) && moment().subtract(1, 'months').endOf('month').isSame(to)) { + return 'prev-month'; + } + return value; +}; + +/** + * Update daterange provided + * @param {String} value + * @param {Date} from + * @param {Date} to + */ +export const setDateRange = (value, from, to) => { + if (moment(from).isAfter(moment())) { + return handleError({ details: { errorTitle: 'Invalid_dates' }, error: 'Start_date_incorrect' }); + } + + if (value && from && to) { + value = checkDaterangeValue(value, from, to); + + return { + value, + from: moment(from).format('MMM D YYYY'), + to: moment(to).format('MMM D YYYY'), + }; + } else { + return { + value: 'this-week', + from: moment().startOf('week').format('MMM D YYYY'), + to: moment().endOf('week').format('MMM D YYYY'), + }; + } +}; + +/** + * Inc/Dec ReactiVar Daterange by one unit. + * @param {Object} currentDaterange + * @param {Int} order + */ +export const updateDateRange = (currentDaterange, order) => { + // const currentDaterange = daterange.get(); + + switch (currentDaterange.value) { + case 'today': + case 'yesterday': + case 'day': + if (order === 1) { + return setDateRange('day', + moment(currentDaterange.from, 'MMM D YYYY').add(1, 'days').startOf('day'), + moment(currentDaterange.to, 'MMM D YYYY').add(1, 'days').startOf('day')); + } else { + return setDateRange('day', + moment(currentDaterange.from, 'MMM D YYYY').subtract(1, 'days').startOf('day'), + moment(currentDaterange.to, 'MMM D YYYY').subtract(1, 'days').startOf('day')); + } + // break; + case 'this-week': + case 'prev-week': + case 'week': + if (order === 1) { + return setDateRange('week', + moment(currentDaterange.from, 'MMM D YYYY').add(1, 'weeks').startOf('week'), + moment(currentDaterange.to, 'MMM D YYYY').add(1, 'weeks').endOf('week')); + } else { + return setDateRange('week', + moment(currentDaterange.from, 'MMM D YYYY').subtract(1, 'weeks').startOf('week'), + moment(currentDaterange.to, 'MMM D YYYY').subtract(1, 'weeks').endOf('week')); + } + // break; + case 'this-month': + case 'prev-month': + case 'month': + if (order === 1) { + return setDateRange('month', + moment(currentDaterange.from, 'MMM D YYYY').add(1, 'months').startOf('month'), + moment(currentDaterange.to, 'MMM D YYYY').add(1, 'months').endOf('month')); + } else { + return setDateRange('month', + moment(currentDaterange.from, 'MMM D YYYY').subtract(1, 'months').startOf('month'), + moment(currentDaterange.to, 'MMM D YYYY').subtract(1, 'months').endOf('month')); + } + // break; + case 'custom': + handleError({ details: { errorTitle: 'Navigation_didnot_work' }, error: 'You_have_selected_custom_dates' }); + } +}; diff --git a/packages/rocketchat-livechat/client/route.js b/packages/rocketchat-livechat/client/route.js index 161d390adc48..35c41475945e 100644 --- a/packages/rocketchat-livechat/client/route.js +++ b/packages/rocketchat-livechat/client/route.js @@ -19,6 +19,22 @@ AccountBox.addRoute({ pageTemplate: 'livechatCurrentChats', }, livechatManagerRoutes); +AccountBox.addRoute({ + name: 'livechat-analytics', + path: '/analytics', + sideNav: 'livechatFlex', + i18nPageTitle: 'Analytics', + pageTemplate: 'livechatAnalytics', +}, livechatManagerRoutes); + +AccountBox.addRoute({ + name: 'livechat-real-time-monitoring', + path: '/real-time-monitoring', + sideNav: 'livechatFlex', + i18nPageTitle: 'Real_Time_Monitoring', + pageTemplate: 'livechatRealTimeMonitoring', +}, livechatManagerRoutes); + AccountBox.addRoute({ name: 'livechat-users', path: '/users', @@ -145,5 +161,3 @@ AccountBox.addRoute({ i18nPageTitle: 'Livechat_Queue', pageTemplate: 'livechatQueue', }); - - diff --git a/packages/rocketchat-livechat/client/stylesheets/livechat.less b/packages/rocketchat-livechat/client/stylesheets/livechat.less index 626d12b3cead..c50056175839 100644 --- a/packages/rocketchat-livechat/client/stylesheets/livechat.less +++ b/packages/rocketchat-livechat/client/stylesheets/livechat.less @@ -688,3 +688,341 @@ vertical-align: middle; } } + +.lc-analytics-table { + display: flex; + flex-flow: column; + // width: 100%; + min-height: 300px; + height: ~"calc(100% - 280px)"; + height: ~"-webkit-calc-height(100% - 280px)"; +} + +.lc-analytics-flex-container { + position: relative; + display: flex; + flex-flow: row; + height: 100%; + // align-content: stretch; +} + +.lc-analytics-chart-col { + position: relative; + flex: 66.66666%; + max-width: 66.66666%; + padding-right: 2px; + max-height: 100%; +} + +.lc-chart-ov-content { + overflow: auto; +} + +.lc-analytics-chart-ov-col { + position: relative; + flex: 33.33333%; + max-width: 33.33333%; + // overflow: scroll; + padding-left: 2px; + height: 100%; +} + +.lc-chart-section { + position: relative; + height: 100%; + max-height: 100%; +} + +.lc-chart-section-content { + position: relative; + width: 100%; + height: 100%; + // max-height: 100%; + display: flex; + flex-direction: column; +} + +.lc-chart-options { + // flex: 1; +} + +.lc-analytics-chart-container { + // flex-grow: 100%; + flex: auto; + position: relative; + // height: inherit; +} + +.lc-analytics-header { + float: right; + & > .lc-date-picker-btn { + font-weight: inherit; + font-size: inherit; + color: #383838; + //line-height: 1; + padding-top: 23px; + } + + & > .lc-date-picker-btn { + // padding-top: 15px; + // padding-right: 25px; + // padding-left: 10px; + position: relative; + + .fade { + color: #9ea2a8; + } + } +} + +.lc-analytics-overview { + display: flex; + flex-direction: row; + margin-top: 10px; + margin-bottom: 10px; + justify-content: center; +} + +.lc-analytics-ov-col { + min-height: 20px; + flex: 33.33333%; //1; + max-width: 33.33333%; + + &.-full-width { + max-width: 100%; + } + + &:not(:last-child) { + border-right: 1px solid #E9E9E9; + } +} + +.lc-analytics-ov-case { + width: 100%; + text-align: center; + margin-bottom: 20px; + user-select: text; + + &:first-child { + margin-top: 8px; + //border-bottom: 1px solid #E9E9E9; + } + + &.-standalone { + & > .title { + margin-top: 1px; + display: inline-block; + font-size: .875rem; + font-weight: 300; + text-transform: none; + // margin-bottom: 6px; + } + & > .value { + // margin-top: 1px; + font-size: 1.75rem; + margin-bottom: 6px; + } + } + + & > .title { + margin-top: 8px; + display: inline-block; + font-weight: 300; + font-size: 0.875rem; + color: #9ea2a8; + text-transform: uppercase; + //cursor: help; + } + + & > .value { + display: inline-block; + width: 100%; + } + + & > .value { + // margin-top: 8px; + font-weight: 400; + font-size: 1.75rem; + color: #383838; + line-height: 1; + text-transform: capitalize; + } +} + +@media screen and (max-width: 925px) { + .lc-analytics-ov-case { + & > .title { + font-size: 0.5rem; + } + & > .value { + font-size: 1rem; + } + } + + .lc-analytics-flex-container { + flex-flow: column; + } + + .lc-analytics-chart-col { + flex: 100%; + max-width: 100%; + padding-right: 0px; + max-height: 100%; + padding-bottom: 2px; + } + + .lc-analytics-chart-ov-col { + padding-left: 0; + flex: 100%; + max-width: 100%; + max-height: 100%; + padding-top: 2px; + } +} + +@media screen and (max-width: 800px) { + .lc-analytics-ov-case { + & > .title { + font-size: 0.875rem; + } + & > .value { + font-size: 1.75rem; + } + } + + .lc-analytics-flex-container { + flex-flow: row; + } + + .lc-analytics-chart-col { + flex: 66.66666%; + max-width: 66.66666%; + padding-right: 2px; + max-height: 100%; + padding-bottom: 0; + } + + .lc-analytics-chart-ov-col { + padding-left: 2px; + flex: 33.33333%; + max-width: 33.33333%; + max-height: 100%; + padding-top: 0; + } +} + +@media screen and (max-width: 600px) { + .lc-analytics-ov-case { + & > .title { + font-size: 0.5rem; + } + & > .value { + font-size: 1rem; + } + } + + .lc-analytics-flex-container { + flex-flow: column; + } + + .lc-analytics-chart-col { + flex: 100%; + max-width: 100%; + padding-right: 0; + max-height: 100%; + padding-bottom: 2px; + } + + .lc-analytics-chart-ov-col { + padding-left: 0; + flex: 100%; + max-width: 100%; + max-height: 100%; + padding-top: 2px; + } +} + +.lc-monitoring-flex { + display: flex; + flex-wrap: wrap; + // flex-direction: row; + // margin-top: 10px; + // margin-bottom: 10px; + // justify-content: center; + // padding: 0 4px; +} + +.lc-monitoring-doughnut-chart { + flex: 33.33333%; + max-width: 33.33333%; + padding: 2px 2px; +} + +.lc-monitoring-line-chart { + flex: 66.66666%; + max-width: 66.66666%; + padding: 2px 2px; +} + +.lc-monitoring-line-chart-full { + flex: 66.66666%; + width: 66.66666%; + max-width: 100%; + padding: 2px 2px; +} + +.lc-monitoring-chart-container { + min-height: 250px; +} + +@media screen and (max-width: 925px) { + .lc-monitoring-doughnut-chart { + flex: 100%; + max-width: 100%; + } + + .lc-monitoring-line-chart { + flex: 100%; + max-width: 100%; + } + + .lc-monitoring-line-chart-full { + flex: 100%; + max-width: 100%; + } +} + +@media screen and (max-width: 800px) { + .lc-monitoring-doughnut-chart { + flex: 33.33333%; + max-width: 33.33333%; + } + + .lc-monitoring-line-chart { + flex: 66.66666%; + max-width: 66.66666%; + } + + .lc-monitoring-line-chart-full { + flex: 66.66666%; + width: 66.66666%; + max-width: 100%; + } +} + +@media screen and (max-width: 600px) { + .lc-monitoring-doughnut-chart { + flex: 100%; + max-width: 100%; + } + + .lc-monitoring-line-chart { + flex: 100%; + max-width: 100%; + } + + .lc-monitoring-line-chart-full { + flex: 100%; + max-width: 100%; + } +} diff --git a/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalytics.html b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalytics.html new file mode 100644 index 000000000000..ff8adcb84fa6 --- /dev/null +++ b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalytics.html @@ -0,0 +1,100 @@ + diff --git a/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalytics.js b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalytics.js new file mode 100644 index 000000000000..89e2c4932973 --- /dev/null +++ b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalytics.js @@ -0,0 +1,215 @@ +/* globals popover */ + +import moment from 'moment'; +import { drawLineChart } from '../../../lib/chartHandler'; +import { setDateRange, updateDateRange } from '../../../lib/dateHandler'; + +let templateInstance; // current template instance/context +let chartContext; // stores context of current chart, used to clean when redrawing + +const analyticsAllOptions = () => [{ + name: 'Conversations', + value: 'conversations', + chartOptions: [{ + name: 'Total_conversations', + value: 'total-conversations', + }, { + name: 'Avg_chat_duration', + value: 'avg-chat-duration', + }, { + name: 'Total_messages', + value: 'total-messages', + }], +}, { + name: 'Productivity', + value: 'productivity', + chartOptions: [{ + name: 'Avg_first_response_time', + value: 'avg-first-response-time', + }, { + name: 'Best_first_response_time', + value: 'best_first_response_time', + }, { + name: 'Avg_response_time', + value: 'avg-response-time', + }, { + name: 'Avg_reaction_time', + value: 'avg-reaction-time', + }], +}]; + +/** + * + * @param {Array} arr + * @param {Integer} chunkCount + * + * @returns {Array{Array}} Array containing arrays + */ +const chunkArray = (arr, chunkCount) => { // split array into n almost equal arrays + const chunks = []; + while (arr.length) { + const chunkSize = Math.ceil(arr.length / chunkCount--); + const chunk = arr.slice(0, chunkSize); + chunks.push(chunk); + arr = arr.slice(chunkSize); + } + return chunks; +}; + +const updateAnalyticsChart = () => { + const options = { + daterange: { + from: moment(templateInstance.daterange.get().from, 'MMM D YYYY').toISOString(), + to: moment(templateInstance.daterange.get().to, 'MMM D YYYY').toISOString(), + }, + chartOptions: templateInstance.chartOptions.get(), + }; + + Meteor.call('livechat:getAnalyticsChartData', options, function(error, result) { + if (error) { + return handleError(error); + } + + if (!(result && result.chartLabel && result.dataLabels && result.dataPoints)) { + console.log('livechat:getAnalyticsChartData => Missing Data'); + } + + chartContext = drawLineChart(document.getElementById('lc-analytics-chart'), chartContext, [result.chartLabel], result.dataLabels, [result.dataPoints]); + }); + + Meteor.call('livechat:getAgentOverviewData', options, function(error, result) { + if (error) { + return handleError(error); + } + + if (!result) { + console.log('livechat:getAgentOverviewData => Missing Data'); + } + + templateInstance.agentOverviewData.set(result); + }); +}; + +const updateAnalyticsOverview = () => { + const options = { + daterange: { + from: moment(templateInstance.daterange.get().from, 'MMM D YYYY').toISOString(), + to: moment(templateInstance.daterange.get().to, 'MMM D YYYY').toISOString(), + }, + analyticsOptions: templateInstance.analyticsOptions.get(), + }; + + Meteor.call('livechat:getAnalyticsOverviewData', options, (error, result) => { + if (error) { + return handleError(error); + } + + if (!result) { + console.log('livechat:getAnalyticsOverviewData => Missing Data'); + } + + templateInstance.analyticsOverviewData.set(chunkArray(result, 3)); + }); +}; + +Template.livechatAnalytics.helpers({ + analyticsOverviewData() { + return templateInstance.analyticsOverviewData.get(); + }, + agentOverviewData() { + return templateInstance.agentOverviewData.get(); + }, + analyticsAllOptions() { + return analyticsAllOptions(); + }, + analyticsOptions() { + return templateInstance.analyticsOptions.get(); + }, + daterange() { + return templateInstance.daterange.get(); + }, + selected(value) { + if (value === templateInstance.analyticsOptions.get().value || value === templateInstance.chartOptions.get().value) { return 'selected'; } + return false; + }, + showLeftNavButton() { + if (templateInstance.daterange.get().value === 'custom') { + return false; + } + return true; + }, + showRightNavButton() { + if (templateInstance.daterange.get().value === 'custom' || templateInstance.daterange.get().value === 'today' || templateInstance.daterange.get().value === 'this-week' || templateInstance.daterange.get().value === 'this-month') { + return false; + } + return true; + }, +}); + + +Template.livechatAnalytics.onCreated(function() { + templateInstance = Template.instance(); + + this.analyticsOverviewData = new ReactiveVar(); + this.agentOverviewData = new ReactiveVar(); + this.daterange = new ReactiveVar({}); + this.analyticsOptions = new ReactiveVar(analyticsAllOptions()[0]); // default selected first + this.chartOptions = new ReactiveVar(analyticsAllOptions()[0].chartOptions[0]); // default selected first + + this.autorun(() => { + templateInstance.daterange.set(setDateRange()); + }); +}); + +Template.livechatAnalytics.onRendered(() => { + Tracker.autorun(() => { + if (templateInstance.daterange.get() && templateInstance.analyticsOptions.get() && templateInstance.chartOptions.get()) { + updateAnalyticsOverview(); + updateAnalyticsChart(); + } + + }); + +}); + +Template.livechatAnalytics.events({ + 'click .lc-date-picker-btn'(e) { + e.preventDefault(); + const options = []; + const config = { + template: 'livechatAnalyticsDaterange', + currentTarget: e.currentTarget, + data: { + options, + daterange: templateInstance.daterange, + }, + offsetVertical: e.currentTarget.clientHeight + 10, + }; + popover.open(config); + }, + 'click .lc-daterange-prev'(e) { + e.preventDefault(); + + templateInstance.daterange.set(updateDateRange(templateInstance.daterange.get(), -1)); + }, + 'click .lc-daterange-next'(e) { + e.preventDefault(); + + templateInstance.daterange.set(updateDateRange(templateInstance.daterange.get(), 1)); + }, + 'change #lc-analytics-options'(e) { + e.preventDefault(); + + templateInstance.analyticsOptions.set(analyticsAllOptions().filter(function(obj) { + return obj.value === e.currentTarget.value; + })[0]); + templateInstance.chartOptions.set(templateInstance.analyticsOptions.get().chartOptions[0]); + }, + 'change #lc-analytics-chart-options'(e) { + e.preventDefault(); + + templateInstance.chartOptions.set(templateInstance.analyticsOptions.get().chartOptions.filter(function(obj) { + return obj.value === e.currentTarget.value; + })[0]); + }, +}); diff --git a/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsCustomDaterange.html b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsCustomDaterange.html new file mode 100644 index 000000000000..fa166b5a5b72 --- /dev/null +++ b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsCustomDaterange.html @@ -0,0 +1,15 @@ + diff --git a/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsCustomDaterange.js b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsCustomDaterange.js new file mode 100644 index 000000000000..12e9f4f99567 --- /dev/null +++ b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsCustomDaterange.js @@ -0,0 +1,39 @@ +/* globals popover */ + +import moment from 'moment'; +import { setDateRange } from '../../../lib/dateHandler'; + + +Template.livechatAnalyticsCustomDaterange.helpers({ + from() { + return moment(Template.currentData().daterange.get().from, 'MMM D YYYY').format('L'); + }, + to() { + return moment(Template.currentData().daterange.get().to, 'MMM D YYYY').format('L'); + }, +}); + +Template.livechatAnalyticsCustomDaterange.onRendered(function() { + this.$('.lc-custom-daterange').datepicker({ + autoclose: true, + todayHighlight: true, + format: moment.localeData().longDateFormat('L').toLowerCase(), + }); +}); + + +Template.livechatAnalyticsCustomDaterange.events({ + 'click .lc-custom-daterange-submit'(e) { + e.preventDefault(); + const from = document.getElementsByClassName('lc-custom-daterange-from')[0].value; + const to = document.getElementsByClassName('lc-custom-daterange-to')[0].value; + + if (moment(from).isValid() && moment(to).isValid()) { + Template.currentData().daterange.set(setDateRange('custom', moment(new Date(from)), moment(new Date(to)))); + } else { + handleError({ details: { errorTitle: 'Invalid_dates' }, error: 'Error_in_custom_dates' }); + } + + popover.close(); + }, +}); diff --git a/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsDaterange.html b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsDaterange.html new file mode 100644 index 000000000000..e6f87416ac91 --- /dev/null +++ b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsDaterange.html @@ -0,0 +1,48 @@ + diff --git a/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsDaterange.js b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsDaterange.js new file mode 100644 index 000000000000..d551d62d16d2 --- /dev/null +++ b/packages/rocketchat-livechat/client/views/app/analytics/livechatAnalyticsDaterange.js @@ -0,0 +1,55 @@ +/* globals popover */ + +import moment from 'moment'; +import { setDateRange } from '../../../lib/dateHandler'; + +Template.livechatAnalyticsDaterange.helpers({ + bold(prop) { + return (prop === Template.currentData().daterange.get().value) ? 'rc-popover__item--bold' : ''; + }, +}); + +Template.livechatAnalyticsDaterange.events({ + 'change input'(e) { + e.preventDefault(); + + const value = e.currentTarget.getAttribute('type') === 'checkbox' ? e.currentTarget.checked : e.currentTarget.value; + + popover.close(); + + switch (value) { + case 'custom': + const target = document.getElementsByClassName('lc-date-picker-btn')[0]; + const options = []; + const config = { + template: 'livechatAnalyticsCustomDaterange', + currentTarget: target, + data: { + options, + daterange: Template.currentData().daterange, + }, + offsetVertical: target.clientHeight + 10, + }; + popover.open(config); + break; + case 'today': + Template.currentData().daterange.set(setDateRange(value, moment().startOf('day'), moment().startOf('day'))); + break; + case 'yesterday': + Template.currentData().daterange.set(setDateRange(value, moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').startOf('day'))); + break; + case 'this-week': + Template.currentData().daterange.set(setDateRange(value, moment().startOf('week'), moment().endOf('week'))); + break; + case 'prev-week': + Template.currentData().daterange.set(setDateRange(value, moment().subtract(1, 'weeks').startOf('week'), moment().subtract(1, 'weeks').endOf('week'))); + break; + case 'this-month': + Template.currentData().daterange.set(setDateRange(value, moment().startOf('month'), moment().endOf('month'))); + break; + case 'prev-month': + Template.currentData().daterange.set(setDateRange(value, moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month'))); + break; + } + }, +}); diff --git a/packages/rocketchat-livechat/client/views/app/analytics/livechatRealTimeMonitoring.html b/packages/rocketchat-livechat/client/views/app/analytics/livechatRealTimeMonitoring.html new file mode 100644 index 000000000000..9d08578d885e --- /dev/null +++ b/packages/rocketchat-livechat/client/views/app/analytics/livechatRealTimeMonitoring.html @@ -0,0 +1,76 @@ + diff --git a/packages/rocketchat-livechat/client/views/app/analytics/livechatRealTimeMonitoring.js b/packages/rocketchat-livechat/client/views/app/analytics/livechatRealTimeMonitoring.js new file mode 100644 index 000000000000..2029c19f14a9 --- /dev/null +++ b/packages/rocketchat-livechat/client/views/app/analytics/livechatRealTimeMonitoring.js @@ -0,0 +1,314 @@ +import moment from 'moment'; +import { drawLineChart, drawDoughnutChart, updateChart } from '../../../lib/chartHandler'; +import { getTimingsChartData, getAgentStatusData, getConversationsOverviewData, getTimingsOverviewData } from '../../../lib/dataHandler'; + +let chartContexts = {}; // stores context of current chart, used to clean when redrawing +let templateInstance; + +const LivechatVisitors = new Mongo.Collection('livechatVisitors'); + +const initChart = { + 'lc-chats-chart'() { + return drawDoughnutChart( + document.getElementById('lc-chats-chart'), + 'Chats', + chartContexts['lc-chats-chart'], + ['Open', 'Queue', 'Closed'], [0, 0, 0]); + }, + + 'lc-agents-chart'() { + return drawDoughnutChart( + document.getElementById('lc-agents-chart'), + 'Agents', + chartContexts['lc-agents-chart'], + ['Available', 'Away', 'Busy', 'Offline'], [0, 0, 0, 0]); + }, + + 'lc-chats-per-agent-chart'() { + return drawLineChart( + document.getElementById('lc-chats-per-agent-chart'), + chartContexts['lc-chats-per-agent-chart'], + ['Open', 'Closed'], + [], [[], []], { legends: true, anim: true, smallTicks: true }); + }, + + 'lc-chats-per-dept-chart'() { + if (!document.getElementById('lc-chats-per-dept-chart')) { + return null; + } + + return drawLineChart( + document.getElementById('lc-chats-per-dept-chart'), + chartContexts['lc-chats-per-dept-chart'], + ['Open', 'Closed'], + [], [[], []], { legends: true, anim: true, smallTicks: true }); + }, + + 'lc-reaction-response-times-chart'() { + const timingLabels = []; + const initData = []; + const today = moment().startOf('day'); + for (let m = today; m.diff(moment(), 'hours') < 0; m.add(1, 'hours')) { + const hour = m.format('H'); + timingLabels.push(`${ moment(hour, ['H']).format('hA') }-${ moment((parseInt(hour) + 1) % 24, ['H']).format('hA') }`); + initData.push(0); + } + + return drawLineChart( + document.getElementById('lc-reaction-response-times-chart'), + chartContexts['lc-reaction-response-times-chart'], + ['Avg_reaction_time', 'Longest_reaction_time', 'Avg_response_time', 'Longest_response_time'], + timingLabels.slice(), + [initData.slice(), initData.slice(), initData.slice(), initData.slice()], { legends: true, anim: true, smallTicks: true }); + }, + + 'lc-chat-duration-chart'() { + const timingLabels = []; + const initData = []; + const today = moment().startOf('day'); + for (let m = today; m.diff(moment(), 'hours') < 0; m.add(1, 'hours')) { + const hour = m.format('H'); + timingLabels.push(`${ moment(hour, ['H']).format('hA') }-${ moment((parseInt(hour) + 1) % 24, ['H']).format('hA') }`); + initData.push(0); + } + + return drawLineChart( + document.getElementById('lc-chat-duration-chart'), + chartContexts['lc-chat-duration-chart'], + ['Avg_chat_duration', 'Longest_chat_duration'], + timingLabels.slice(), + [initData.slice(), initData.slice()], { legends: true, anim: true, smallTicks: true }); + }, +}; + +const initAllCharts = () => { + chartContexts['lc-chats-chart'] = initChart['lc-chats-chart'](); + chartContexts['lc-agents-chart'] = initChart['lc-agents-chart'](); + chartContexts['lc-chats-per-agent-chart'] = initChart['lc-chats-per-agent-chart'](); + chartContexts['lc-chats-per-dept-chart'] = initChart['lc-chats-per-dept-chart'](); + chartContexts['lc-reaction-response-times-chart'] = initChart['lc-reaction-response-times-chart'](); + chartContexts['lc-chat-duration-chart'] = initChart['lc-chat-duration-chart'](); +}; + +const updateChartData = (chartId, label, data) => { + // update chart + if (!chartContexts[chartId]) { + chartContexts[chartId] = initChart[chartId](); + } + + updateChart(chartContexts[chartId], label, data); +}; + +const metricsUpdated = (ts) => { + const hour = moment(ts).format('H'); + const label = `${ moment(hour, ['H']).format('hA') }-${ moment((parseInt(hour) + 1) % 24, ['H']).format('hA') }`; + + const query = { + ts: { + $gte: new Date(moment(ts).startOf('hour')), + $lt: new Date(moment(ts).add(1, 'hours').startOf('hour')), + }, + }; + + const data = getTimingsChartData(LivechatMonitoring.find(query)); + + updateChartData('lc-reaction-response-times-chart', label, [data.reaction.avg, data.reaction.longest, data.response.avg, data.response.longest]); + updateChartData('lc-chat-duration-chart', label, [data.chatDuration.avg, data.chatDuration.longest]); +}; + +const updateDepartmentsChart = (departmentId) => { + if (departmentId) { + // update for dept + const label = LivechatDepartment.findOne({ _id: departmentId }).name; + + const data = { + open: LivechatMonitoring.find({ departmentId, open: true }).count(), + closed: LivechatMonitoring.find({ departmentId, open: { $exists: false } }).count(), + }; + + updateChartData('lc-chats-per-dept-chart', label, [data.open, data.closed]); + } else { + // update for all + LivechatDepartment.find({ enabled: true }).forEach(function(dept) { + updateDepartmentsChart(dept._id); + }); + } +}; + +const updateAgentsChart = (agent) => { + if (agent) { + // update for the agent + const data = { + open: LivechatMonitoring.find({ 'servedBy.username': agent, open: true }).count(), + closed: LivechatMonitoring.find({ 'servedBy.username': agent, open: { $exists: false } }).count(), + }; + + updateChartData('lc-chats-per-agent-chart', agent, [data.open, data.closed]); + } else { + // update for all agents + AgentUsers.find().forEach(function(agent) { + if (agent.username) { + updateAgentsChart(agent.username); + } + }); + } +}; + +const updateAgentStatusChart = () => { + const statusData = getAgentStatusData(AgentUsers.find()); + + updateChartData('lc-agents-chart', 'Offline', [statusData.offline]); + updateChartData('lc-agents-chart', 'Available', [statusData.available]); + updateChartData('lc-agents-chart', 'Away', [statusData.away]); + updateChartData('lc-agents-chart', 'Busy', [statusData.busy]); +}; + +const updateChatsChart = () => { + const chats = { + open: LivechatMonitoring.find({ 'metrics.chatDuration': { $exists: false }, servedBy: { $exists: true } }).count(), + closed: LivechatMonitoring.find({ 'metrics.chatDuration': { $exists: true }, servedBy: { $exists: true } }).count(), + queue: LivechatMonitoring.find({ servedBy: { $exists: false } }).count(), + }; + + updateChartData('lc-chats-chart', 'Open', [chats.open]); + updateChartData('lc-chats-chart', 'Closed', [chats.closed]); + updateChartData('lc-chats-chart', 'Queue', [chats.queue]); +}; + +const updateConversationsOverview = () => { + const data = getConversationsOverviewData(LivechatMonitoring.find()); + + templateInstance.conversationsOverview.set(data); +}; + +const updateTimingsOverview = () => { + const data = getTimingsOverviewData(LivechatMonitoring.find()); + + templateInstance.timingOverview.set(data); +}; + +const displayDepartmentChart = (val) => { + const elem = document.getElementsByClassName('lc-chats-per-dept-chart-section')[0]; + elem.style.display = (val) ? 'block' : 'none'; +}; + +const updateVisitorsCount = () => { + templateInstance.totalVisitors.set({ + title: templateInstance.totalVisitors.get().title, + value: LivechatVisitors.find().count(), + }); +}; + +Template.livechatRealTimeMonitoring.helpers({ + showDepartmentChart() { + return templateInstance.showDepartmentChart.get(); + }, + conversationsOverview() { + return templateInstance.conversationsOverview.get(); + }, + timingOverview() { + return templateInstance.timingOverview.get(); + }, + totalVisitors() { + return templateInstance.totalVisitors.get(); + }, +}); + +Template.livechatRealTimeMonitoring.onCreated(function() { + templateInstance = Template.instance(); + this.conversationsOverview = new ReactiveVar(); + this.timingOverview = new ReactiveVar(); + this.totalVisitors = new ReactiveVar({ + title: 'Total_visitors', + value: 0, + }); + + AgentUsers.find().observeChanges({ + changed() { + updateAgentStatusChart(); + }, + added() { + updateAgentStatusChart(); + }, + }); + + LivechatVisitors.find().observeChanges({ + added() { + updateVisitorsCount(); + }, + removed() { + updateVisitorsCount(); + }, + }); + + LivechatDepartment.find({ enabled: true }).observeChanges({ + changed(id) { + displayDepartmentChart(true); + updateDepartmentsChart(id); + }, + added(id) { + displayDepartmentChart(true); + updateDepartmentsChart(id); + }, + }); + + const updateMonitoringDashboard = (id, fields) => { + const { ts } = LivechatMonitoring.findOne({ _id: id }); + + if (fields.metrics) { + // metrics changed + metricsUpdated(ts); + updateChatsChart(); + updateAgentsChart(); + updateTimingsOverview(); + updateDepartmentsChart(); + } + + if (fields.servedBy) { + // agent data changed + updateAgentsChart(fields.servedBy.username); + updateChatsChart(); + } + + if (fields.open) { + updateAgentsChart(); + updateChatsChart(); + } + + if (fields.departmentId) { + updateDepartmentsChart(fields.departmentId); + } + + if (fields.msgs) { + updateConversationsOverview(); + } + }; + + LivechatMonitoring.find().observeChanges({ + changed(id, fields) { + updateMonitoringDashboard(id, fields); + }, + added(id, fields) { + updateMonitoringDashboard(id, fields); + }, + }); +}); + +Template.livechatRealTimeMonitoring.onRendered(function() { + chartContexts = {}; // Clear chart contexts from previous loads, fixing bug when menu is reopened after changing to another. + + initAllCharts(); + + displayDepartmentChart(false); + + this.subscribe('livechat:departments'); + this.subscribe('livechat:agents'); + this.subscribe('livechat:monitoring', { + gte: moment().startOf('day').toISOString(), + lt: moment().startOf('day').add(1, 'days').toISOString(), + }); + this.subscribe('livechat:visitors', { + gte: moment().startOf('day').toISOString(), + lt: moment().startOf('day').add(1, 'days').toISOString(), + }); +}); diff --git a/packages/rocketchat-livechat/client/views/sideNav/livechatFlex.html b/packages/rocketchat-livechat/client/views/sideNav/livechatFlex.html index d64cd13bdf9c..cefaea720292 100644 --- a/packages/rocketchat-livechat/client/views/sideNav/livechatFlex.html +++ b/packages/rocketchat-livechat/client/views/sideNav/livechatFlex.html @@ -11,6 +11,8 @@

{{_ "Livechat"}}