mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
Merge branch 'master' into feature-update-sso
This commit is contained in:
@@ -25,4 +25,6 @@ api-docs/public/assets/*
|
||||
server-monitor/out/scripts/prettify/*
|
||||
js-sdk/dist/logger.js
|
||||
js-sdk/dist/logger.min.js
|
||||
js-sdk/dist/fyipe.js
|
||||
js-sdk/dist/fyipe.min.js
|
||||
_test/*
|
||||
235
accounts/package-lock.json
generated
235
accounts/package-lock.json
generated
@@ -3366,11 +3366,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
|
||||
},
|
||||
"@types/mime-types": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.0.tgz",
|
||||
"integrity": "sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM="
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
@@ -3480,6 +3475,15 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz",
|
||||
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
|
||||
},
|
||||
"@types/yauzl": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
|
||||
"integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "4.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.11.1.tgz",
|
||||
@@ -4907,6 +4911,42 @@
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"bl": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
|
||||
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@@ -6888,6 +6928,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"devtools-protocol": {
|
||||
"version": "0.0.818844",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.818844.tgz",
|
||||
"integrity": "sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg=="
|
||||
},
|
||||
"diff-sequences": {
|
||||
"version": "26.6.2",
|
||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
|
||||
@@ -8310,31 +8355,36 @@
|
||||
}
|
||||
},
|
||||
"extract-zip": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
|
||||
"integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
|
||||
"requires": {
|
||||
"concat-stream": "^1.6.2",
|
||||
"debug": "^2.6.9",
|
||||
"mkdirp": "^0.5.4",
|
||||
"@types/yauzl": "^2.9.1",
|
||||
"debug": "^4.1.1",
|
||||
"get-stream": "^5.1.0",
|
||||
"yauzl": "^2.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"get-stream": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8753,6 +8803,11 @@
|
||||
"readable-stream": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
|
||||
@@ -9485,11 +9540,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
@@ -12748,6 +12803,11 @@
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
||||
},
|
||||
"move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
@@ -12864,6 +12924,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
|
||||
@@ -14908,64 +14973,61 @@
|
||||
}
|
||||
},
|
||||
"puppeteer": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-2.1.1.tgz",
|
||||
"integrity": "sha512-LWzaDVQkk1EPiuYeTOj+CZRIjda4k2s5w4MK4xoH2+kgWV/SDlkYHmxatDdtYrciHUKSXTsGgPgPP8ILVdBsxg==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.5.0.tgz",
|
||||
"integrity": "sha512-OM8ZvTXAhfgFA7wBIIGlPQzvyEETzDjeRa4mZRCRHxYL+GNH5WAuYUQdja3rpWZvkX/JKqmuVgbsxDNsDFjMEg==",
|
||||
"requires": {
|
||||
"@types/mime-types": "^2.1.0",
|
||||
"debug": "^4.1.0",
|
||||
"extract-zip": "^1.6.6",
|
||||
"devtools-protocol": "0.0.818844",
|
||||
"extract-zip": "^2.0.0",
|
||||
"https-proxy-agent": "^4.0.0",
|
||||
"mime": "^2.0.3",
|
||||
"mime-types": "^2.1.25",
|
||||
"node-fetch": "^2.6.1",
|
||||
"pkg-dir": "^4.2.0",
|
||||
"progress": "^2.0.1",
|
||||
"proxy-from-env": "^1.0.0",
|
||||
"rimraf": "^2.6.1",
|
||||
"ws": "^6.1.0"
|
||||
"rimraf": "^3.0.2",
|
||||
"tar-fs": "^2.0.0",
|
||||
"unbzip2-stream": "^1.3.3",
|
||||
"ws": "^7.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"mime": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
|
||||
"integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
||||
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"puppeteer-cluster": {
|
||||
"version": "0.21.0",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer-cluster/-/puppeteer-cluster-0.21.0.tgz",
|
||||
"integrity": "sha512-/x5mei0vXxFPpJ7iUS+xJ3rOcxxYUa2YeEyuWI9m0M5e8ammPiCXjvOsTcni+4ZAop3L2gpZFkxafPvXWOoRfg==",
|
||||
"version": "0.22.0",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer-cluster/-/puppeteer-cluster-0.22.0.tgz",
|
||||
"integrity": "sha512-hmydtMwfVM+idFIDzS8OXetnujHGre7RY3BGL+3njy9+r8Dcu3VALkZHfuBEPf6byKssTCgzxU1BvLczifXd5w==",
|
||||
"requires": {
|
||||
"debug": "^4.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
@@ -17881,6 +17943,48 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
|
||||
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"requires": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"temp-dir": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz",
|
||||
@@ -18071,8 +18175,7 @@
|
||||
"through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
||||
"dev": true
|
||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
|
||||
},
|
||||
"through2": {
|
||||
"version": "2.0.5",
|
||||
@@ -18329,6 +18432,26 @@
|
||||
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
|
||||
"dev": true
|
||||
},
|
||||
"unbzip2-stream": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
|
||||
"integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
|
||||
"requires": {
|
||||
"buffer": "^5.2.1",
|
||||
"through": "^2.3.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
"js-uuid": "0.0.6",
|
||||
"loadable-components": "^2.2.3",
|
||||
"prop-types": "^15.6.1",
|
||||
"puppeteer": "^2.1.1",
|
||||
"puppeteer-cluster": "^0.21.0",
|
||||
"puppeteer": "^5.5.0",
|
||||
"puppeteer-cluster": "^0.22.0",
|
||||
"query-string": "^5.1.1",
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
|
||||
50617
admin-dashboard/package-lock.json
generated
50617
admin-dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -20,14 +20,14 @@
|
||||
"mixpanel-browser": "^2.22.3",
|
||||
"moment": "^2.22.2",
|
||||
"prop-types": "^15.6.1",
|
||||
"puppeteer": "^2.1.1",
|
||||
"puppeteer-cluster": "^0.19.0",
|
||||
"puppeteer": "^5.5.0",
|
||||
"puppeteer-cluster": "^0.22.0",
|
||||
"react": "^16.14.0",
|
||||
"react-click-outside": "github:tj/react-click-outside",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-frontload": "^1.0.3",
|
||||
"react-ga": "^2.5.3",
|
||||
"react-json-view": "^1.19.1",
|
||||
"react-json-view": "^1.20.2",
|
||||
"react-mixpanel": "0.0.11",
|
||||
"react-redux": "^5.0.7",
|
||||
"react-router-dom": "^4.2.2",
|
||||
@@ -90,8 +90,5 @@
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"resolutions": {
|
||||
"node-fetch": "2.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,14 +139,14 @@
|
||||
text-align: center;
|
||||
color: #565656;
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
|
||||
.cancel-btn__keycode {
|
||||
width: 30px;
|
||||
height: 20px;
|
||||
line-height: 15px;
|
||||
padding: 2px;
|
||||
padding: 2px !important;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
|
||||
@@ -3578,12 +3578,23 @@ figure.bs-Number > figcaption {
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.07), 0 2px 15px rgba(84, 96, 103, 0.25);
|
||||
}
|
||||
|
||||
.ds-Modal{
|
||||
position: relative;
|
||||
z-index: 200;
|
||||
margin: 0 auto 20px;
|
||||
max-width: 670px;
|
||||
background: #f7f7f7;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.07), 0 2px 15px rgba(84, 96, 103, 0.25);
|
||||
}
|
||||
@media only screen and (max-width: 470px) {
|
||||
.bs-Modal {
|
||||
margin: 0 10px 20px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.bs-Modal:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -9099,6 +9099,14 @@ html.db-NewChrome .bs-ContentSection .bs-Tail--short {
|
||||
.db-SideNav-icon--businessSettings {
|
||||
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTguMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDI5MS45NTcgMjkxLjk1NyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMjkxLjk1NyAyOTEuOTU3OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4Ij4KPHBhdGggZD0iTTI4My4wNywxNjguODc1bC0xNy4xMDYtOS44NzZjMC40NjEtNC4yNzksMC43MDQtOC42MjIsMC43MDQtMTMuMDJzLTAuMjQzLTguNzQyLTAuNzA0LTEzLjAyMWwxNy4xMDYtOS44NzYgIGMzLjY1NS0yLjExLDYuMjctNS41MTksNy4zNjMtOS41OThjMS4wOTItNC4wNzgsMC41MzEtOC4zMzgtMS41OC0xMS45OTRsLTMyLjkwOC01Ny4wMDFjLTIuODE2LTQuODc4LTguMDY3LTcuOTA3LTEzLjcwNS03LjkwNyAgYy0yLjc1OSwwLTUuNDg1LDAuNzM0LTcuODg3LDIuMTJsLTE3LjE1NSw5LjkwNWMtNi45NzMtNS4xMTQtMTQuNTEtOS40OTctMjIuNTAzLTEzLjAzN1YxNS44MDdDMTk0LjY5NSw3LjA5MSwxODcuNjA0LDAsMTc4Ljg4OSwwICBoLTY1LjgyYy04LjcxNiwwLTE1LjgwNyw3LjA5MS0xNS44MDcsMTUuODA3VjM1LjU3Yy03Ljk5MywzLjU0LTE1LjUzMSw3LjkyNC0yMi41MDMsMTMuMDM4bC0xNy4xNTUtOS45MDQgIGMtMi40MDEtMS4zODctNS4xMjgtMi4xMjEtNy44ODctMi4xMjFjLTUuNjM4LDAtMTAuODg5LDMuMDI5LTEzLjcwNSw3LjkwN0wzLjEwMywxMDEuNDljLTIuMTExLDMuNjU1LTIuNjcyLDcuOTE2LTEuNTgsMTEuOTk0ICBjMS4wOTQsNC4wNzksMy43MDgsNy40ODcsNy4zNjMsOS41OThsMTcuMTA2LDkuODc2Yy0wLjQ2MSw0LjI3OS0wLjcwNCw4LjYyMi0wLjcwNCwxMy4wMjFzMC4yNDMsOC43NDIsMC43MDQsMTMuMDJsLTE3LjEwNiw5Ljg3NiAgYy0zLjY1NSwyLjExLTYuMjY5LDUuNTE4LTcuMzYzLDkuNTk4Yy0xLjA5Miw0LjA3OC0wLjUzMSw4LjMzOSwxLjU4LDExLjk5NGwzMi45MDgsNTcuMDAxYzIuODE2LDQuODc4LDguMDY3LDcuOTA3LDEzLjcwNSw3LjkwNyAgYzIuNzU5LDAsNS40ODUtMC43MzMsNy44ODctMi4xMmwxNy4xNTUtOS45MDVjNi45NzMsNS4xMTQsMTQuNTEsOS40OTcsMjIuNTAzLDEzLjAzN3YxOS43NjRjMCw0LjIyMiwxLjY0NCw4LjE5LDQuNjMxLDExLjE3NiAgYzIuOTg1LDIuOTg1LDYuOTU1LDQuNjMsMTEuMTc2LDQuNjNoNjUuODJjOC43MTUsMCwxNS44MDctNy4wOSwxNS44MDctMTUuODA2di0xOS43NjRjNy45OTItMy41NDEsMTUuNTMtNy45MjMsMjIuNTAyLTEzLjAzNyAgbDE3LjE1Niw5LjkwNGMyLjQwMSwxLjM4Nyw1LjEyOCwyLjEyLDcuODg3LDIuMTJjNS42MzgsMCwxMC44ODktMy4wMjksMTMuNzA1LTcuOTA3bDMyLjkwOC01Ny4wMDEgIGMyLjExMS0zLjY1NSwyLjY3Mi03LjkxNiwxLjU4LTExLjk5NEMyODkuMzQsMTc0LjM5MywyODYuNzI2LDE3MC45ODUsMjgzLjA3LDE2OC44NzV6IE0xNDUuOTc5LDIwMS42NjggIGMtMzAuNzU2LDAtNTUuNjg5LTI0LjkzNC01NS42ODktNTUuNjg5czI0LjkzNC01NS42ODksNTUuNjg5LTU1LjY4OXM1NS42ODksMjQuOTM0LDU1LjY4OSw1NS42ODlTMTc2LjczNCwyMDEuNjY4LDE0NS45NzksMjAxLjY2OHogICIgZmlsbD0iIzkwOWJhZiIvPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K');
|
||||
}
|
||||
.db-SideNav-icon--LogsIcon{
|
||||
filter: invert(66%) sepia(18%) saturate(288%) hue-rotate(175deg) brightness(93%) contrast(87%);
|
||||
background-image: url('../icons/app-log.svg');
|
||||
}
|
||||
.db-SideNav-icon--LogsIcon.db-SideNav-icon--selected{
|
||||
filter: invert(0%) sepia(100%) saturate(7459%) hue-rotate(48deg) brightness(101%) contrast(110%);
|
||||
background-image: url('../icons/app-log.svg');
|
||||
}
|
||||
.db-SideNav-icon--connect {
|
||||
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4IiB2aWV3Qm94PSIwIDAgNDg4LjE1MiA0ODguMTUyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA0ODguMTUyIDQ4OC4xNTI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPGc+Cgk8Zz4KCQk8cGF0aCBkPSJNMTc3Ljg1NCwyNjkuMzExYzAtNi4xMTUtNC45Ni0xMS4wNjktMTEuMDgtMTEuMDY5aC0zOC42NjVjLTYuMTEzLDAtMTEuMDc0LDQuOTU0LTExLjA3NCwxMS4wNjl2MzguNjYgICAgYzAsNi4xMjMsNC45NjEsMTEuMDc5LDExLjA3NCwxMS4wNzloMzguNjY1YzYuMTIsMCwxMS4wOC00Ljk1NiwxMS4wOC0xMS4wNzlWMjY5LjMxMUwxNzcuODU0LDI2OS4zMTF6IiBmaWxsPSIjOTA5YmFmIi8+CgkJPHBhdGggZD0iTTI3NC40ODMsMjY5LjMxMWMwLTYuMTE1LTQuOTYxLTExLjA2OS0xMS4wNjktMTEuMDY5aC0zOC42N2MtNi4xMTMsMC0xMS4wNzQsNC45NTQtMTEuMDc0LDExLjA2OXYzOC42NiAgICBjMCw2LjEyMyw0Ljk2MSwxMS4wNzksMTEuMDc0LDExLjA3OWgzOC42N2M2LjEwOCwwLDExLjA2OS00Ljk1NiwxMS4wNjktMTEuMDc5VjI2OS4zMTF6IiBmaWxsPSIjOTA5YmFmIi8+CgkJPHBhdGggZD0iTTM3MS4xMTcsMjY5LjMxMWMwLTYuMTE1LTQuOTYxLTExLjA2OS0xMS4wNzQtMTEuMDY5aC0zOC42NjVjLTYuMTIsMC0xMS4wOCw0Ljk1NC0xMS4wOCwxMS4wNjl2MzguNjYgICAgYzAsNi4xMjMsNC45NiwxMS4wNzksMTEuMDgsMTEuMDc5aDM4LjY2NWM2LjExMywwLDExLjA3NC00Ljk1NiwxMS4wNzQtMTEuMDc5VjI2OS4zMTF6IiBmaWxsPSIjOTA5YmFmIi8+CgkJPHBhdGggZD0iTTE3Ny44NTQsMzY1Ljk1YzAtNi4xMjUtNC45Ni0xMS4wNzUtMTEuMDgtMTEuMDc1aC0zOC42NjVjLTYuMTEzLDAtMTEuMDc0LDQuOTUtMTEuMDc0LDExLjA3NXYzOC42NTMgICAgYzAsNi4xMTksNC45NjEsMTEuMDc0LDExLjA3NCwxMS4wNzRoMzguNjY1YzYuMTIsMCwxMS4wOC00Ljk1NiwxMS4wOC0xMS4wNzRWMzY1Ljk1TDE3Ny44NTQsMzY1Ljk1eiIgZmlsbD0iIzkwOWJhZiIvPgoJCTxwYXRoIGQ9Ik0yNzQuNDgzLDM2NS45NWMwLTYuMTI1LTQuOTYxLTExLjA3NS0xMS4wNjktMTEuMDc1aC0zOC42N2MtNi4xMTMsMC0xMS4wNzQsNC45NS0xMS4wNzQsMTEuMDc1djM4LjY1MyAgICBjMCw2LjExOSw0Ljk2MSwxMS4wNzQsMTEuMDc0LDExLjA3NGgzOC42N2M2LjEwOCwwLDExLjA2OS00Ljk1NiwxMS4wNjktMTEuMDc0VjM2NS45NXoiIGZpbGw9IiM5MDliYWYiLz4KCQk8cGF0aCBkPSJNMzcxLjExNywzNjUuOTVjMC02LjEyNS00Ljk2MS0xMS4wNzUtMTEuMDY5LTExLjA3NWgtMzguNjdjLTYuMTIsMC0xMS4wOCw0Ljk1LTExLjA4LDExLjA3NXYzOC42NTMgICAgYzAsNi4xMTksNC45NiwxMS4wNzQsMTEuMDgsMTEuMDc0aDM4LjY3YzYuMTA4LDAsMTEuMDY5LTQuOTU2LDExLjA2OS0xMS4wNzRWMzY1Ljk1TDM3MS4xMTcsMzY1Ljk1eiIgZmlsbD0iIzkwOWJhZiIvPgoJCTxwYXRoIGQ9Ik00NDAuMjU0LDU0LjM1NHY1OS4wNWMwLDI2LjY5LTIxLjY1Miw0OC4xOTgtNDguMzM4LDQ4LjE5OGgtMzAuNDkzYy0yNi42ODgsMC00OC42MjctMjEuNTA4LTQ4LjYyNy00OC4xOThWNTQuMTQyICAgIGgtMTM3LjQ0djU5LjI2MmMwLDI2LjY5LTIxLjkzOCw0OC4xOTgtNDguNjIyLDQ4LjE5OEg5Ni4yMzVjLTI2LjY4NSwwLTQ4LjMzNi0yMS41MDgtNDguMzM2LTQ4LjE5OHYtNTkuMDUgICAgQzI0LjU3Niw1NS4wNTcsNS40MTEsNzQuMzU2LDUuNDExLDk4LjA3N3YzNDYuMDYxYzAsMjQuMTY3LDE5LjU4OCw0NC4wMTUsNDMuNzU1LDQ0LjAxNWgzODkuODIgICAgYzI0LjEzMSwwLDQzLjc1NS0xOS44ODksNDMuNzU1LTQ0LjAxNVY5OC4wNzdDNDgyLjc0MSw3NC4zNTYsNDYzLjU3Nyw1NS4wNTcsNDQwLjI1NCw1NC4zNTR6IE00MjYuMDkxLDQyMi41ODggICAgYzAsMTAuNDQ0LTguNDY4LDE4LjkxNy0xOC45MTYsMTguOTE3SDgwLjE0NGMtMTAuNDQ4LDAtMTguOTE2LTguNDczLTE4LjkxNi0xOC45MTdWMjQzLjgzNWMwLTEwLjQ0OCw4LjQ2Ny0xOC45MjEsMTguOTE2LTE4LjkyMSAgICBoMzI3LjAzYzEwLjQ0OCwwLDE4LjkxNiw4LjQ3MywxOC45MTYsMTguOTIxTDQyNi4wOTEsNDIyLjU4OEw0MjYuMDkxLDQyMi41ODh6IiBmaWxsPSIjOTA5YmFmIi8+CgkJPHBhdGggZD0iTTk2LjEyOCwxMjkuOTQ1aDMwLjE2MmM5LjE1NSwwLDE2LjU3OC03LjQxMiwxNi41NzgtMTYuNTY3VjE2LjU3M0MxNDIuODY4LDcuNDE3LDEzNS40NDUsMCwxMjYuMjksMEg5Ni4xMjggICAgQzg2Ljk3MiwwLDc5LjU1LDcuNDE3LDc5LjU1LDE2LjU3M3Y5Ni44MDVDNzkuNTUsMTIyLjUzMyw4Ni45NzIsMTI5Ljk0NSw5Ni4xMjgsMTI5Ljk0NXoiIGZpbGw9IiM5MDliYWYiLz4KCQk8cGF0aCBkPSJNMzYxLjAzNSwxMjkuOTQ1aDMwLjE2MmM5LjE0OSwwLDE2LjU3Mi03LjQxMiwxNi41NzItMTYuNTY3VjE2LjU3M0M0MDcuNzcsNy40MTcsNDAwLjM0NywwLDM5MS4xOTcsMGgtMzAuMTYyICAgIGMtOS4xNTQsMC0xNi41NzcsNy40MTctMTYuNTc3LDE2LjU3M3Y5Ni44MDVDMzQ0LjQ1OCwxMjIuNTMzLDM1MS44ODEsMTI5Ljk0NSwzNjEuMDM1LDEyOS45NDV6IiBmaWxsPSIjOTA5YmFmIi8+Cgk8L2c+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==')
|
||||
}
|
||||
@@ -9157,6 +9165,9 @@ html.db-NewChrome .bs-ContentSection .bs-Tail--short {
|
||||
.db-SideNav-icon--businessSettings.db-SideNav-icon--highlighted {
|
||||
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTguMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDI5MS45NTcgMjkxLjk1NyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMjkxLjk1NyAyOTEuOTU3OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4Ij4KPHBhdGggZD0iTTI4My4wNywxNjguODc1bC0xNy4xMDYtOS44NzZjMC40NjEtNC4yNzksMC43MDQtOC42MjIsMC43MDQtMTMuMDJzLTAuMjQzLTguNzQyLTAuNzA0LTEzLjAyMWwxNy4xMDYtOS44NzYgIGMzLjY1NS0yLjExLDYuMjctNS41MTksNy4zNjMtOS41OThjMS4wOTItNC4wNzgsMC41MzEtOC4zMzgtMS41OC0xMS45OTRsLTMyLjkwOC01Ny4wMDFjLTIuODE2LTQuODc4LTguMDY3LTcuOTA3LTEzLjcwNS03LjkwNyAgYy0yLjc1OSwwLTUuNDg1LDAuNzM0LTcuODg3LDIuMTJsLTE3LjE1NSw5LjkwNWMtNi45NzMtNS4xMTQtMTQuNTEtOS40OTctMjIuNTAzLTEzLjAzN1YxNS44MDdDMTk0LjY5NSw3LjA5MSwxODcuNjA0LDAsMTc4Ljg4OSwwICBoLTY1LjgyYy04LjcxNiwwLTE1LjgwNyw3LjA5MS0xNS44MDcsMTUuODA3VjM1LjU3Yy03Ljk5MywzLjU0LTE1LjUzMSw3LjkyNC0yMi41MDMsMTMuMDM4bC0xNy4xNTUtOS45MDQgIGMtMi40MDEtMS4zODctNS4xMjgtMi4xMjEtNy44ODctMi4xMjFjLTUuNjM4LDAtMTAuODg5LDMuMDI5LTEzLjcwNSw3LjkwN0wzLjEwMywxMDEuNDljLTIuMTExLDMuNjU1LTIuNjcyLDcuOTE2LTEuNTgsMTEuOTk0ICBjMS4wOTQsNC4wNzksMy43MDgsNy40ODcsNy4zNjMsOS41OThsMTcuMTA2LDkuODc2Yy0wLjQ2MSw0LjI3OS0wLjcwNCw4LjYyMi0wLjcwNCwxMy4wMjFzMC4yNDMsOC43NDIsMC43MDQsMTMuMDJsLTE3LjEwNiw5Ljg3NiAgYy0zLjY1NSwyLjExLTYuMjY5LDUuNTE4LTcuMzYzLDkuNTk4Yy0xLjA5Miw0LjA3OC0wLjUzMSw4LjMzOSwxLjU4LDExLjk5NGwzMi45MDgsNTcuMDAxYzIuODE2LDQuODc4LDguMDY3LDcuOTA3LDEzLjcwNSw3LjkwNyAgYzIuNzU5LDAsNS40ODUtMC43MzMsNy44ODctMi4xMmwxNy4xNTUtOS45MDVjNi45NzMsNS4xMTQsMTQuNTEsOS40OTcsMjIuNTAzLDEzLjAzN3YxOS43NjRjMCw0LjIyMiwxLjY0NCw4LjE5LDQuNjMxLDExLjE3NiAgYzIuOTg1LDIuOTg1LDYuOTU1LDQuNjMsMTEuMTc2LDQuNjNoNjUuODJjOC43MTUsMCwxNS44MDctNy4wOSwxNS44MDctMTUuODA2di0xOS43NjRjNy45OTItMy41NDEsMTUuNTMtNy45MjMsMjIuNTAyLTEzLjAzNyAgbDE3LjE1Niw5LjkwNGMyLjQwMSwxLjM4Nyw1LjEyOCwyLjEyLDcuODg3LDIuMTJjNS42MzgsMCwxMC44ODktMy4wMjksMTMuNzA1LTcuOTA3bDMyLjkwOC01Ny4wMDEgIGMyLjExMS0zLjY1NSwyLjY3Mi03LjkxNiwxLjU4LTExLjk5NEMyODkuMzQsMTc0LjM5MywyODYuNzI2LDE3MC45ODUsMjgzLjA3LDE2OC44NzV6IE0xNDUuOTc5LDIwMS42NjggIGMtMzAuNzU2LDAtNTUuNjg5LTI0LjkzNC01NS42ODktNTUuNjg5czI0LjkzNC01NS42ODksNTUuNjg5LTU1LjY4OXM1NS42ODksMjQuOTM0LDU1LjY4OSw1NS42ODlTMTc2LjczNCwyMDEuNjY4LDE0NS45NzksMjAxLjY2OHogICIgZmlsbD0iIzY3NzJlNSIvPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K');
|
||||
}
|
||||
.db-SideNav-icon--LogsIcon.db-SideNav-icon--highlighted {
|
||||
background-image: url('../icons/app-log.svg');
|
||||
}
|
||||
.db-SideNav-icon--connect.db-SideNav-icon--highlighted {
|
||||
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4IiB2aWV3Qm94PSIwIDAgNDg4LjE1MiA0ODguMTUyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA0ODguMTUyIDQ4OC4xNTI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPGc+Cgk8Zz4KCQk8cGF0aCBkPSJNMTc3Ljg1NCwyNjkuMzExYzAtNi4xMTUtNC45Ni0xMS4wNjktMTEuMDgtMTEuMDY5aC0zOC42NjVjLTYuMTEzLDAtMTEuMDc0LDQuOTU0LTExLjA3NCwxMS4wNjl2MzguNjYgICAgYzAsNi4xMjMsNC45NjEsMTEuMDc5LDExLjA3NCwxMS4wNzloMzguNjY1YzYuMTIsMCwxMS4wOC00Ljk1NiwxMS4wOC0xMS4wNzlWMjY5LjMxMUwxNzcuODU0LDI2OS4zMTF6IiBmaWxsPSIjNjc3MmU1Ii8+CgkJPHBhdGggZD0iTTI3NC40ODMsMjY5LjMxMWMwLTYuMTE1LTQuOTYxLTExLjA2OS0xMS4wNjktMTEuMDY5aC0zOC42N2MtNi4xMTMsMC0xMS4wNzQsNC45NTQtMTEuMDc0LDExLjA2OXYzOC42NiAgICBjMCw2LjEyMyw0Ljk2MSwxMS4wNzksMTEuMDc0LDExLjA3OWgzOC42N2M2LjEwOCwwLDExLjA2OS00Ljk1NiwxMS4wNjktMTEuMDc5VjI2OS4zMTF6IiBmaWxsPSIjNjc3MmU1Ii8+CgkJPHBhdGggZD0iTTM3MS4xMTcsMjY5LjMxMWMwLTYuMTE1LTQuOTYxLTExLjA2OS0xMS4wNzQtMTEuMDY5aC0zOC42NjVjLTYuMTIsMC0xMS4wOCw0Ljk1NC0xMS4wOCwxMS4wNjl2MzguNjYgICAgYzAsNi4xMjMsNC45NiwxMS4wNzksMTEuMDgsMTEuMDc5aDM4LjY2NWM2LjExMywwLDExLjA3NC00Ljk1NiwxMS4wNzQtMTEuMDc5VjI2OS4zMTF6IiBmaWxsPSIjNjc3MmU1Ii8+CgkJPHBhdGggZD0iTTE3Ny44NTQsMzY1Ljk1YzAtNi4xMjUtNC45Ni0xMS4wNzUtMTEuMDgtMTEuMDc1aC0zOC42NjVjLTYuMTEzLDAtMTEuMDc0LDQuOTUtMTEuMDc0LDExLjA3NXYzOC42NTMgICAgYzAsNi4xMTksNC45NjEsMTEuMDc0LDExLjA3NCwxMS4wNzRoMzguNjY1YzYuMTIsMCwxMS4wOC00Ljk1NiwxMS4wOC0xMS4wNzRWMzY1Ljk1TDE3Ny44NTQsMzY1Ljk1eiIgZmlsbD0iIzY3NzJlNSIvPgoJCTxwYXRoIGQ9Ik0yNzQuNDgzLDM2NS45NWMwLTYuMTI1LTQuOTYxLTExLjA3NS0xMS4wNjktMTEuMDc1aC0zOC42N2MtNi4xMTMsMC0xMS4wNzQsNC45NS0xMS4wNzQsMTEuMDc1djM4LjY1MyAgICBjMCw2LjExOSw0Ljk2MSwxMS4wNzQsMTEuMDc0LDExLjA3NGgzOC42N2M2LjEwOCwwLDExLjA2OS00Ljk1NiwxMS4wNjktMTEuMDc0VjM2NS45NXoiIGZpbGw9IiM2NzcyZTUiLz4KCQk8cGF0aCBkPSJNMzcxLjExNywzNjUuOTVjMC02LjEyNS00Ljk2MS0xMS4wNzUtMTEuMDY5LTExLjA3NWgtMzguNjdjLTYuMTIsMC0xMS4wOCw0Ljk1LTExLjA4LDExLjA3NXYzOC42NTMgICAgYzAsNi4xMTksNC45NiwxMS4wNzQsMTEuMDgsMTEuMDc0aDM4LjY3YzYuMTA4LDAsMTEuMDY5LTQuOTU2LDExLjA2OS0xMS4wNzRWMzY1Ljk1TDM3MS4xMTcsMzY1Ljk1eiIgZmlsbD0iIzY3NzJlNSIvPgoJCTxwYXRoIGQ9Ik00NDAuMjU0LDU0LjM1NHY1OS4wNWMwLDI2LjY5LTIxLjY1Miw0OC4xOTgtNDguMzM4LDQ4LjE5OGgtMzAuNDkzYy0yNi42ODgsMC00OC42MjctMjEuNTA4LTQ4LjYyNy00OC4xOThWNTQuMTQyICAgIGgtMTM3LjQ0djU5LjI2MmMwLDI2LjY5LTIxLjkzOCw0OC4xOTgtNDguNjIyLDQ4LjE5OEg5Ni4yMzVjLTI2LjY4NSwwLTQ4LjMzNi0yMS41MDgtNDguMzM2LTQ4LjE5OHYtNTkuMDUgICAgQzI0LjU3Niw1NS4wNTcsNS40MTEsNzQuMzU2LDUuNDExLDk4LjA3N3YzNDYuMDYxYzAsMjQuMTY3LDE5LjU4OCw0NC4wMTUsNDMuNzU1LDQ0LjAxNWgzODkuODIgICAgYzI0LjEzMSwwLDQzLjc1NS0xOS44ODksNDMuNzU1LTQ0LjAxNVY5OC4wNzdDNDgyLjc0MSw3NC4zNTYsNDYzLjU3Nyw1NS4wNTcsNDQwLjI1NCw1NC4zNTR6IE00MjYuMDkxLDQyMi41ODggICAgYzAsMTAuNDQ0LTguNDY4LDE4LjkxNy0xOC45MTYsMTguOTE3SDgwLjE0NGMtMTAuNDQ4LDAtMTguOTE2LTguNDczLTE4LjkxNi0xOC45MTdWMjQzLjgzNWMwLTEwLjQ0OCw4LjQ2Ny0xOC45MjEsMTguOTE2LTE4LjkyMSAgICBoMzI3LjAzYzEwLjQ0OCwwLDE4LjkxNiw4LjQ3MywxOC45MTYsMTguOTIxTDQyNi4wOTEsNDIyLjU4OEw0MjYuMDkxLDQyMi41ODh6IiBmaWxsPSIjNjc3MmU1Ii8+CgkJPHBhdGggZD0iTTk2LjEyOCwxMjkuOTQ1aDMwLjE2MmM5LjE1NSwwLDE2LjU3OC03LjQxMiwxNi41NzgtMTYuNTY3VjE2LjU3M0MxNDIuODY4LDcuNDE3LDEzNS40NDUsMCwxMjYuMjksMEg5Ni4xMjggICAgQzg2Ljk3MiwwLDc5LjU1LDcuNDE3LDc5LjU1LDE2LjU3M3Y5Ni44MDVDNzkuNTUsMTIyLjUzMyw4Ni45NzIsMTI5Ljk0NSw5Ni4xMjgsMTI5Ljk0NXoiIGZpbGw9IiM2NzcyZTUiLz4KCQk8cGF0aCBkPSJNMzYxLjAzNSwxMjkuOTQ1aDMwLjE2MmM5LjE0OSwwLDE2LjU3Mi03LjQxMiwxNi41NzItMTYuNTY3VjE2LjU3M0M0MDcuNzcsNy40MTcsNDAwLjM0NywwLDM5MS4xOTcsMGgtMzAuMTYyICAgIGMtOS4xNTQsMC0xNi41NzcsNy40MTctMTYuNTc3LDE2LjU3M3Y5Ni44MDVDMzQ0LjQ1OCwxMjIuNTMzLDM1MS44ODEsMTI5Ljk0NSwzNjEuMDM1LDEyOS45NDV6IiBmaWxsPSIjNjc3MmU1Ii8+Cgk8L2c+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==')
|
||||
}
|
||||
@@ -9206,6 +9217,9 @@ html.db-NewChrome .bs-ContentSection .bs-Tail--short {
|
||||
.db-SideNav-icon--businessSettings.db-SideNav-icon--selected {
|
||||
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDg5Ni4wMjUgODk2LjAyNSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgODk2LjAyNSA4OTYuMDI1OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgY2xhc3M9IiI+PGc+PGc+PHBhdGggaWQ9InNldHRpbmdzXzFfIiBkPSJNODYzLjI0LDM4Mi43NzFsLTg4Ljc1OS0xNC44MDdjLTYuNDUxLTI2LjM3NC0xNS44NTctNTEuNTg1LTI4LjEwNy03NS4wOTlsNTYuODIxLTcwLjQ1MiAgIGMxMi4wODUtMTQuODg5LDExLjUzNi0zNi4zMTItMS4yMDUtNTAuNjgybC0zNS4zMDEtMzkuNzI5Yy0xMi43OTYtMTQuMzU1LTM0LjAxNi0xNy4zOTEtNTAuMjAyLTcuMTY1bC03NS45MDYsNDcuNzE2ICAgYy0zMy4zODYtMjMuMzI2LTcxLjIwNC00MC41NTEtMTEyLTUwLjU0NmwtMTQuODUtODkuMjM1Yy0zLjExNi0xOC44OTUtMTkuNDY3LTMyLjc1OS0zOC42NjEtMzIuNzU5aC01My4xOTggICBjLTE5LjE1NSwwLTM1LjU2MSwxMy44NjQtMzguNjA4LDMyLjc1OWwtMTQuOTMxLDg5LjI2M2MtMzMuNzI5LDguMjU4LTY1LjM1MywyMS41ODgtOTQuMjEzLDM5LjE0NGwtNzIuMTg4LTUxLjUxOCAgIGMtMTUuNTU4LTExLjExNS0zNi45MjctOS4zNzctNTAuNTA0LDQuMTcxbC0zNy41ODMsMzcuNjFjLTEzLjU0OCwxMy41NzctMTUuMjg2LDM0Ljk0Ni00LjE0Miw1MC41MDRsNTEuNjM4LDcyLjMyNiAgIGMtMTcuMzkxLDI4LjY0Mi0zMC41ODQsNjAuMDg2LTM4Ljg0MSw5My41MTVsLTg5Ljc0MywxNC45ODVDMTMuODkxLDM4NS44ODgsMCw0MDIuMjQsMCw0MjEuNDM1djUzLjE1NiAgIGMwLDE5LjE5MywxMy44OTEsMzUuNTQ3LDMyLjc1NywzOC42NjNsODkuNzQzLDE0Ljk4NWM2Ljc4MSwyNy41MDgsMTYuNjI1LDUzLjc4NCwyOS43MDksNzguMTQ3TDk1LjY0Nyw2NzYuNDQgICBjLTEyLjA0NCwxNC44NzUtMTEuNTM4LDM2LjMxMiwxLjIwMyw1MC42NjlsMzUuMjc0LDM5LjczYzEyLjc5NywxNC4zODIsMzQuMDI4LDE3LjM2Myw1MC4yMTYsNy4xNjNsNzctNDguMzcgICBjMzIuNTgxLDIyLjI4NSw2OS40NCwzOC42NjQsMTA4Ljk5Myw0OC4zN2wxNC45MzEsODkuMjVjMy4wNDgsMTguODk2LDE5LjQ1MywzMi43NiwzOC42MDgsMzIuNzZoNTMuMTk4ICAgYzE5LjE5NCwwLDM1LjU0NS0xMy44NjMsMzguNjYxLTMyLjc1OWwxNC44NzUtODkuMjVjMzMuMzA4LTguMTQ3LDY0LjUzMS0yMS4yNDUsOTMuMTM0LTM4LjVsNzUuMTk2LDUzLjcwNSAgIGMxNS41MywxMS4xNTUsMzYuOTE1LDkuNDA1LDUwLjQ3OC00LjE4NmwzNy41OTgtMzcuNTk3YzEzLjUzMi0xMy41MzYsMTUuMzY1LTM0Ljg5Myw0LjEyNy01MC40NzlsLTUzLjUzNi03NS4wNTkgICBjMTcuNDQxLTI4LjczOCwzMC43MDQtNjAuMjM4LDM4LjkwOS05My44MTZsODguNzU4LTE0LjgyYzE4LjkyMS0zLjExNiwzMi43NTYtMTkuNDY5LDMyLjc1Ni0zOC42NjN2LTUzLjE1NiAgIEM4OTUuOTk4LDQwMi4yNCw4ODIuMTYzLDM4NS44ODgsODYzLjI0LDM4Mi43NzF6IE00NDkuNDIsNjE2LjAxM2MtOTIuNzY0LDAtMTY4LTc1LjI1LTE2OC0xNjhjMC05Mi43NjQsNzUuMjM2LTE2OCwxNjgtMTY4ICAgYzkyLjc0OCwwLDE2Ny45OTgsNzUuMjM2LDE2Ny45OTgsMTY4QzYxNy40MTgsNTQwLjc2Myw1NDIuMTY4LDYxNi4wMTMsNDQ5LjQyLDYxNi4wMTN6IiBkYXRhLW9yaWdpbmFsPSIjMDAwMDAwIiBjbGFzcz0iYWN0aXZlLXBhdGgiIHN0eWxlPSJmaWxsOiMwMDAwMDAiIGRhdGEtb2xkX2NvbG9yPSIjMDAwMDAwIj48L3BhdGg+PC9nPjwvZz4gPC9zdmc+');
|
||||
}
|
||||
.db-SideNav-icon--LogsIcon.db-SideNav-icon--selected {
|
||||
background-image: url('../icons/app-log.svg');
|
||||
}
|
||||
.db-SideNav-icon--connect.db-SideNav-icon--selected {
|
||||
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDQ4OC4xNTIgNDg4LjE1MiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDg4LjE1MiA0ODguMTUyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgY2xhc3M9IiI+PGc+PGc+PGc+PHBhdGggZD0iTTE3Ny44NTQsMjY5LjMxMWMwLTYuMTE1LTQuOTYtMTEuMDY5LTExLjA4LTExLjA2OWgtMzguNjY1Yy02LjExMywwLTExLjA3NCw0Ljk1NC0xMS4wNzQsMTEuMDY5djM4LjY2ICAgIGMwLDYuMTIzLDQuOTYxLDExLjA3OSwxMS4wNzQsMTEuMDc5aDM4LjY2NWM2LjEyLDAsMTEuMDgtNC45NTYsMTEuMDgtMTEuMDc5VjI2OS4zMTFMMTc3Ljg1NCwyNjkuMzExeiIgZGF0YS1vcmlnaW5hbD0iIzAwMDAwMCIgY2xhc3M9ImFjdGl2ZS1wYXRoIiBzdHlsZT0iZmlsbDojMDA4MGE4IiBkYXRhLW9sZF9jb2xvcj0iIzAwODBhOCI+PC9wYXRoPjxwYXRoIGQ9Ik0yNzQuNDgzLDI2OS4zMTFjMC02LjExNS00Ljk2MS0xMS4wNjktMTEuMDY5LTExLjA2OWgtMzguNjdjLTYuMTEzLDAtMTEuMDc0LDQuOTU0LTExLjA3NCwxMS4wNjl2MzguNjYgICAgYzAsNi4xMjMsNC45NjEsMTEuMDc5LDExLjA3NCwxMS4wNzloMzguNjdjNi4xMDgsMCwxMS4wNjktNC45NTYsMTEuMDY5LTExLjA3OVYyNjkuMzExeiIgZGF0YS1vcmlnaW5hbD0iIzAwMDAwMCIgY2xhc3M9ImFjdGl2ZS1wYXRoIiBzdHlsZT0iZmlsbDojMDA4MGE4IiBkYXRhLW9sZF9jb2xvcj0iIzAwODBhOCI+PC9wYXRoPjxwYXRoIGQ9Ik0zNzEuMTE3LDI2OS4zMTFjMC02LjExNS00Ljk2MS0xMS4wNjktMTEuMDc0LTExLjA2OWgtMzguNjY1Yy02LjEyLDAtMTEuMDgsNC45NTQtMTEuMDgsMTEuMDY5djM4LjY2ICAgIGMwLDYuMTIzLDQuOTYsMTEuMDc5LDExLjA4LDExLjA3OWgzOC42NjVjNi4xMTMsMCwxMS4wNzQtNC45NTYsMTEuMDc0LTExLjA3OVYyNjkuMzExeiIgZGF0YS1vcmlnaW5hbD0iIzAwMDAwMCIgY2xhc3M9ImFjdGl2ZS1wYXRoIiBzdHlsZT0iZmlsbDojMDA4MGE4IiBkYXRhLW9sZF9jb2xvcj0iIzAwODBhOCI+PC9wYXRoPjxwYXRoIGQ9Ik0xNzcuODU0LDM2NS45NWMwLTYuMTI1LTQuOTYtMTEuMDc1LTExLjA4LTExLjA3NWgtMzguNjY1Yy02LjExMywwLTExLjA3NCw0Ljk1LTExLjA3NCwxMS4wNzV2MzguNjUzICAgIGMwLDYuMTE5LDQuOTYxLDExLjA3NCwxMS4wNzQsMTEuMDc0aDM4LjY2NWM2LjEyLDAsMTEuMDgtNC45NTYsMTEuMDgtMTEuMDc0VjM2NS45NUwxNzcuODU0LDM2NS45NXoiIGRhdGEtb3JpZ2luYWw9IiMwMDAwMDAiIGNsYXNzPSJhY3RpdmUtcGF0aCIgc3R5bGU9ImZpbGw6IzAwODBhOCIgZGF0YS1vbGRfY29sb3I9IiMwMDgwYTgiPjwvcGF0aD48cGF0aCBkPSJNMjc0LjQ4MywzNjUuOTVjMC02LjEyNS00Ljk2MS0xMS4wNzUtMTEuMDY5LTExLjA3NWgtMzguNjdjLTYuMTEzLDAtMTEuMDc0LDQuOTUtMTEuMDc0LDExLjA3NXYzOC42NTMgICAgYzAsNi4xMTksNC45NjEsMTEuMDc0LDExLjA3NCwxMS4wNzRoMzguNjdjNi4xMDgsMCwxMS4wNjktNC45NTYsMTEuMDY5LTExLjA3NFYzNjUuOTV6IiBkYXRhLW9yaWdpbmFsPSIjMDAwMDAwIiBjbGFzcz0iYWN0aXZlLXBhdGgiIHN0eWxlPSJmaWxsOiMwMDgwYTgiIGRhdGEtb2xkX2NvbG9yPSIjMDA4MGE4Ij48L3BhdGg+PHBhdGggZD0iTTM3MS4xMTcsMzY1Ljk1YzAtNi4xMjUtNC45NjEtMTEuMDc1LTExLjA2OS0xMS4wNzVoLTM4LjY3Yy02LjEyLDAtMTEuMDgsNC45NS0xMS4wOCwxMS4wNzV2MzguNjUzICAgIGMwLDYuMTE5LDQuOTYsMTEuMDc0LDExLjA4LDExLjA3NGgzOC42N2M2LjEwOCwwLDExLjA2OS00Ljk1NiwxMS4wNjktMTEuMDc0VjM2NS45NUwzNzEuMTE3LDM2NS45NXoiIGRhdGEtb3JpZ2luYWw9IiMwMDAwMDAiIGNsYXNzPSJhY3RpdmUtcGF0aCIgc3R5bGU9ImZpbGw6IzAwODBhOCIgZGF0YS1vbGRfY29sb3I9IiMwMDgwYTgiPjwvcGF0aD48cGF0aCBkPSJNNDQwLjI1NCw1NC4zNTR2NTkuMDVjMCwyNi42OS0yMS42NTIsNDguMTk4LTQ4LjMzOCw0OC4xOThoLTMwLjQ5M2MtMjYuNjg4LDAtNDguNjI3LTIxLjUwOC00OC42MjctNDguMTk4VjU0LjE0MiAgICBoLTEzNy40NHY1OS4yNjJjMCwyNi42OS0yMS45MzgsNDguMTk4LTQ4LjYyMiw0OC4xOThIOTYuMjM1Yy0yNi42ODUsMC00OC4zMzYtMjEuNTA4LTQ4LjMzNi00OC4xOTh2LTU5LjA1ICAgIEMyNC41NzYsNTUuMDU3LDUuNDExLDc0LjM1Niw1LjQxMSw5OC4wNzd2MzQ2LjA2MWMwLDI0LjE2NywxOS41ODgsNDQuMDE1LDQzLjc1NSw0NC4wMTVoMzg5LjgyICAgIGMyNC4xMzEsMCw0My43NTUtMTkuODg5LDQzLjc1NS00NC4wMTVWOTguMDc3QzQ4Mi43NDEsNzQuMzU2LDQ2My41NzcsNTUuMDU3LDQ0MC4yNTQsNTQuMzU0eiBNNDI2LjA5MSw0MjIuNTg4ICAgIGMwLDEwLjQ0NC04LjQ2OCwxOC45MTctMTguOTE2LDE4LjkxN0g4MC4xNDRjLTEwLjQ0OCwwLTE4LjkxNi04LjQ3My0xOC45MTYtMTguOTE3VjI0My44MzVjMC0xMC40NDgsOC40NjctMTguOTIxLDE4LjkxNi0xOC45MjEgICAgaDMyNy4wM2MxMC40NDgsMCwxOC45MTYsOC40NzMsMTguOTE2LDE4LjkyMUw0MjYuMDkxLDQyMi41ODhMNDI2LjA5MSw0MjIuNTg4eiIgZGF0YS1vcmlnaW5hbD0iIzAwMDAwMCIgY2xhc3M9ImFjdGl2ZS1wYXRoIiBzdHlsZT0iZmlsbDojMDA4MGE4IiBkYXRhLW9sZF9jb2xvcj0iIzAwODBhOCI+PC9wYXRoPjxwYXRoIGQ9Ik05Ni4xMjgsMTI5Ljk0NWgzMC4xNjJjOS4xNTUsMCwxNi41NzgtNy40MTIsMTYuNTc4LTE2LjU2N1YxNi41NzNDMTQyLjg2OCw3LjQxNywxMzUuNDQ1LDAsMTI2LjI5LDBIOTYuMTI4ICAgIEM4Ni45NzIsMCw3OS41NSw3LjQxNyw3OS41NSwxNi41NzN2OTYuODA1Qzc5LjU1LDEyMi41MzMsODYuOTcyLDEyOS45NDUsOTYuMTI4LDEyOS45NDV6IiBkYXRhLW9yaWdpbmFsPSIjMDAwMDAwIiBjbGFzcz0iYWN0aXZlLXBhdGgiIHN0eWxlPSJmaWxsOiMwMDgwYTgiIGRhdGEtb2xkX2NvbG9yPSIjMDA4MGE4Ij48L3BhdGg+PHBhdGggZD0iTTM2MS4wMzUsMTI5Ljk0NWgzMC4xNjJjOS4xNDksMCwxNi41NzItNy40MTIsMTYuNTcyLTE2LjU2N1YxNi41NzNDNDA3Ljc3LDcuNDE3LDQwMC4zNDcsMCwzOTEuMTk3LDBoLTMwLjE2MiAgICBjLTkuMTU0LDAtMTYuNTc3LDcuNDE3LTE2LjU3NywxNi41NzN2OTYuODA1QzM0NC40NTgsMTIyLjUzMywzNTEuODgxLDEyOS45NDUsMzYxLjAzNSwxMjkuOTQ1eiIgZGF0YS1vcmlnaW5hbD0iIzAwMDAwMCIgY2xhc3M9ImFjdGl2ZS1wYXRoIiBzdHlsZT0iZmlsbDojMDA4MGE4IiBkYXRhLW9sZF9jb2xvcj0iIzAwODBhOCI+PC9wYXRoPjwvZz48L2c+PC9nPiA8L3N2Zz4=')
|
||||
}
|
||||
@@ -10635,6 +10649,26 @@ table {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/********************** CallLogsContentViewModal ***********************/
|
||||
.db-CallLogsContentViewModal-ContentViewerWrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.db-CallLogsContentViewModal-ContentViewerWrapper > div {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.db-CallLogsContentViewModal-ContentViewerContainer:first-child {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.db-CallLogsContentViewModal-ContentViewer {
|
||||
min-height: 300px;
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.price-list-3c {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto;
|
||||
|
||||
262
admin-dashboard/src/actions/callLogs.js
Normal file
262
admin-dashboard/src/actions/callLogs.js
Normal file
@@ -0,0 +1,262 @@
|
||||
import { getApi, postApi, deleteApi } from '../api';
|
||||
import * as types from '../constants/callLogs';
|
||||
import errors from '../errors';
|
||||
|
||||
// Fetch All Call Logs
|
||||
export const fetchCallLogsRequest = () => {
|
||||
return {
|
||||
type: types.FETCH_CALLLOGS_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchCallLogsSuccess = callLogs => {
|
||||
return {
|
||||
type: types.FETCH_CALLLOGS_SUCCESS,
|
||||
payload: callLogs,
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchCallLogsError = error => {
|
||||
return {
|
||||
type: types.FETCH_CALLLOGS_FAILURE,
|
||||
payload: error,
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchCallLogs = (skip, limit) => async dispatch => {
|
||||
skip = skip ? parseInt(skip) : 0;
|
||||
limit = limit ? parseInt(limit) : 10;
|
||||
|
||||
dispatch(fetchCallLogsRequest());
|
||||
|
||||
try {
|
||||
const response = await getApi(`call-logs?skip=${skip}&limit=${limit}`);
|
||||
const data = response.data;
|
||||
|
||||
dispatch(fetchCallLogsSuccess(data));
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
let errorMsg;
|
||||
if (error && error.response && error.response.data)
|
||||
errorMsg = error.response.data;
|
||||
if (error && error.data) {
|
||||
errorMsg = error.data;
|
||||
}
|
||||
if (error && error.message) {
|
||||
errorMsg = error.message;
|
||||
} else {
|
||||
errorMsg = 'Network Error';
|
||||
}
|
||||
dispatch(fetchCallLogsError(errors(errorMsg)));
|
||||
}
|
||||
};
|
||||
|
||||
// Search Call Logs.
|
||||
export const searchCallLogsRequest = () => {
|
||||
return {
|
||||
type: types.SEARCH_CALLLOGS_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export const searchCallLogsSuccess = callLogs => {
|
||||
return {
|
||||
type: types.SEARCH_CALLLOGS_SUCCESS,
|
||||
payload: callLogs,
|
||||
};
|
||||
};
|
||||
|
||||
export const searchCallLogsError = error => {
|
||||
return {
|
||||
type: types.SEARCH_CALLLOGS_FAILURE,
|
||||
payload: error,
|
||||
};
|
||||
};
|
||||
|
||||
export const searchCallLogs = (filter, skip, limit) => async dispatch => {
|
||||
const values = {
|
||||
filter,
|
||||
};
|
||||
|
||||
dispatch(searchCallLogsRequest());
|
||||
|
||||
try {
|
||||
const response = await postApi(
|
||||
`call-logs/search?skip=${skip}&limit=${limit}`,
|
||||
values
|
||||
);
|
||||
const data = response.data;
|
||||
|
||||
dispatch(searchCallLogsSuccess(data));
|
||||
return response;
|
||||
} catch (error) {
|
||||
let errorMsg;
|
||||
if (error && error.response && error.response.data)
|
||||
errorMsg = error.response.data;
|
||||
if (error && error.data) {
|
||||
errorMsg = error.data;
|
||||
}
|
||||
if (error && error.message) {
|
||||
errorMsg = error.message;
|
||||
} else {
|
||||
errorMsg = 'Network Error';
|
||||
}
|
||||
dispatch(searchCallLogsError(errors(errorMsg)));
|
||||
}
|
||||
};
|
||||
|
||||
// Delete All Call Logs
|
||||
export const deleteCallLogsRequest = () => {
|
||||
return {
|
||||
type: types.DELETE_ALL_CALLLOGS_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteCallLogsSuccess = message => {
|
||||
return {
|
||||
type: types.DELETE_ALL_CALLLOGS_SUCCESS,
|
||||
payload: message,
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteCallLogsError = error => {
|
||||
return {
|
||||
type: types.DELETE_ALL_CALLLOGS_FAILURE,
|
||||
payload: error,
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteCallLogs = () => async dispatch => {
|
||||
dispatch(deleteCallLogsRequest());
|
||||
|
||||
try {
|
||||
const response = await deleteApi(`call-logs`);
|
||||
const message = response.data.message;
|
||||
|
||||
dispatch(deleteCallLogsSuccess(message));
|
||||
} catch (error) {
|
||||
let errorMsg;
|
||||
if (error && error.response && error.response.data)
|
||||
errorMsg = error.response.data;
|
||||
if (error && error.data) {
|
||||
errorMsg = error.data;
|
||||
}
|
||||
if (error && error.message) {
|
||||
errorMsg = error.message;
|
||||
} else {
|
||||
errorMsg = 'Network Error';
|
||||
}
|
||||
dispatch(deleteCallLogsError(errors(errorMsg)));
|
||||
}
|
||||
};
|
||||
|
||||
// fetch callLogStatus
|
||||
|
||||
export function fetchCallLogStatusRequest(promise) {
|
||||
return {
|
||||
type: types.FETCH_CALLLOG_STATUS_REQUEST,
|
||||
payload: promise,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchCallLogStatusError(error) {
|
||||
return {
|
||||
type: types.FETCH_CALLLOG_STATUS_FAILED,
|
||||
payload: error,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchCallLogStatusSuccess(callLogStatus) {
|
||||
return {
|
||||
type: types.FETCH_CALLLOG_STATUS_SUCCESS,
|
||||
payload: callLogStatus,
|
||||
};
|
||||
}
|
||||
|
||||
export const resetFetchCallLogStatus = () => {
|
||||
return {
|
||||
type: types.FETCH_CALLLOG_STATUS_RESET,
|
||||
};
|
||||
};
|
||||
|
||||
// Calls the API to fetch callLogStatus
|
||||
export const fetchCallLogStatus = () => async dispatch => {
|
||||
dispatch(fetchCallLogStatusRequest());
|
||||
|
||||
try {
|
||||
const response = await getApi('globalConfig/callLogMonitoringStatus');
|
||||
dispatch(fetchCallLogStatusSuccess(response.data));
|
||||
return response;
|
||||
} catch (error) {
|
||||
let errorMsg;
|
||||
if (error && error.response && error.response.data)
|
||||
errorMsg = error.response.data;
|
||||
if (error && error.data) {
|
||||
errorMsg = error.data;
|
||||
}
|
||||
if (error && error.message) {
|
||||
errorMsg = error.message;
|
||||
} else {
|
||||
errorMsg = 'Network Error';
|
||||
}
|
||||
dispatch(fetchCallLogStatusError(errors(errorMsg)));
|
||||
return 'error';
|
||||
}
|
||||
};
|
||||
|
||||
// change callLogStatus
|
||||
|
||||
export function changeCallLogStatusRequest(promise) {
|
||||
return {
|
||||
type: types.CHANGE_CALLLOG_STATUS_REQUEST,
|
||||
payload: promise,
|
||||
};
|
||||
}
|
||||
|
||||
export function changeCallLogStatusError(error) {
|
||||
return {
|
||||
type: types.CHANGE_CALLLOG_STATUS_FAILED,
|
||||
payload: error,
|
||||
};
|
||||
}
|
||||
|
||||
export function changeCallLogStatusSuccess(callLogStatus) {
|
||||
return {
|
||||
type: types.CHANGE_CALLLOG_STATUS_SUCCESS,
|
||||
payload: callLogStatus,
|
||||
};
|
||||
}
|
||||
|
||||
export const resetConfirmCallLogStatus = () => {
|
||||
return {
|
||||
type: types.CHANGE_CALLLOG_STATUS_RESET,
|
||||
};
|
||||
};
|
||||
|
||||
// Calls the API to change callLogStatus
|
||||
export const callLogStatusChange = values => async dispatch => {
|
||||
dispatch(changeCallLogStatusRequest());
|
||||
|
||||
try {
|
||||
const response = await postApi('globalConfig/', [
|
||||
{ name: 'callLogMonitoringStatus', value: values.status },
|
||||
]);
|
||||
const data = response.data;
|
||||
dispatch(changeCallLogStatusSuccess(data));
|
||||
return data;
|
||||
} catch (error) {
|
||||
let errorMsg;
|
||||
if (error && error.response && error.response.data)
|
||||
errorMsg = error.response.data;
|
||||
if (error && error.data) {
|
||||
errorMsg = error.data;
|
||||
}
|
||||
if (error && error.message) {
|
||||
errorMsg = error.message;
|
||||
} else {
|
||||
errorMsg = 'Network Error';
|
||||
}
|
||||
dispatch(changeCallLogStatusError(errors(errorMsg)));
|
||||
return 'error';
|
||||
}
|
||||
};
|
||||
@@ -94,7 +94,6 @@ export class DashboardApp extends Component {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{/* <ClickOutside onClickOutside={this.closeModal} /> */}
|
||||
<ClickOutside onClickOutside={this.hideProfileMenu}>
|
||||
<ProfileMenu visible={this.props.profile.menuVisible} />
|
||||
</ClickOutside>
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import ReactJson from 'react-json-view';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
|
||||
class AuditLogsJsonViewModal extends Component {
|
||||
componentDidMount() {
|
||||
@@ -40,98 +41,111 @@ class AuditLogsJsonViewModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>API Request and Response</span>
|
||||
</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>
|
||||
API Request and Response
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<div className="jsonViwer Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="db-AuditLogsJsonViewModal-JsonViewerWrapper">
|
||||
<div className="db-AuditLogsJsonViewModal-JsonViewerContainer">
|
||||
<div className="Text-fontWeight--medium">
|
||||
Request
|
||||
<div className="bs-Modal-content">
|
||||
<div className="jsonViwer Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="db-AuditLogsJsonViewModal-JsonViewerWrapper">
|
||||
<div className="db-AuditLogsJsonViewModal-JsonViewerContainer">
|
||||
<div className="Text-fontWeight--medium">
|
||||
Request
|
||||
</div>
|
||||
<div className="db-AuditLogsJsonViewModal-JsonViewer">
|
||||
<ReactJson
|
||||
src={reqLog}
|
||||
name="Request"
|
||||
enableClipboard={false}
|
||||
displayObjectSize={
|
||||
false
|
||||
}
|
||||
displayDataTypes={false}
|
||||
indentWidth={2}
|
||||
collapsed={1}
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="db-AuditLogsJsonViewModal-JsonViewer">
|
||||
<ReactJson
|
||||
src={reqLog}
|
||||
name="Request"
|
||||
enableClipboard={false}
|
||||
displayObjectSize={false}
|
||||
displayDataTypes={false}
|
||||
indentWidth={2}
|
||||
collapsed={1}
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="AuditLogsJsonViewModal-JsonViewerContainer">
|
||||
<div className="Text-fontWeight--medium">
|
||||
Response
|
||||
</div>
|
||||
<div className="db-AuditLogsJsonViewModal-JsonViewer">
|
||||
<ReactJson
|
||||
src={resLog}
|
||||
name="Response"
|
||||
enableClipboard={false}
|
||||
displayObjectSize={false}
|
||||
displayDataTypes={false}
|
||||
indentWidth={2}
|
||||
collapsed={1}
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
}}
|
||||
/>
|
||||
<div className="AuditLogsJsonViewModal-JsonViewerContainer">
|
||||
<div className="Text-fontWeight--medium">
|
||||
Response
|
||||
</div>
|
||||
<div className="db-AuditLogsJsonViewModal-JsonViewer">
|
||||
<ReactJson
|
||||
src={resLog}
|
||||
name="Response"
|
||||
enableClipboard={false}
|
||||
displayObjectSize={
|
||||
false
|
||||
}
|
||||
displayDataTypes={false}
|
||||
indentWidth={2}
|
||||
collapsed={1}
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { closeModal } from '../../actions/modal';
|
||||
import { deleteAuditLogs } from '../../actions/auditLogs';
|
||||
@@ -47,79 +48,86 @@ class DeleteConfirmationModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete Audit Log</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete Audit Log</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Do you want to delete all the logs?
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Do you want to delete all the logs?
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
id="cancelAuditDelete"
|
||||
className={`bs-Button btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={deleteRequest}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={this.handleDelete}
|
||||
disabled={deleteRequest}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={!deleteRequest}>
|
||||
<span>Delete Logs</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={deleteRequest}>
|
||||
<span>
|
||||
<FormLoader />
|
||||
<button
|
||||
id="cancelAuditDelete"
|
||||
className={`bs-Button btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={deleteRequest}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</ShouldRender>
|
||||
</button>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={this.handleDelete}
|
||||
disabled={deleteRequest}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={!deleteRequest}>
|
||||
<span>Delete Logs</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={deleteRequest}>
|
||||
<span>
|
||||
<FormLoader />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
class CallLogsContentViewModal extends Component {
|
||||
componentDidMount() {
|
||||
window.addEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
handleKeyboard = e => {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
return this.props.closeThisDialog();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isRequesting, error, closeThisDialog, content } = this.props;
|
||||
|
||||
return (
|
||||
<div className="db-CallLogsContentViewModal ModalLayer-wash Box-root Flex-flex Flex-alignItems--flexStart Flex-justifyContent--center">
|
||||
<div
|
||||
className="ModalLayer-contents"
|
||||
tabIndex={-1}
|
||||
style={{ marginTop: 40 }}
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal">
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Content</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<div className="jsonViwer Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="db-CallLogsContentViewModal-ContentViewerWrapper">
|
||||
<div className="db-CallLogsContentViewModal-ContentViewerContainer">
|
||||
{content ? (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: content,
|
||||
}}
|
||||
></div>
|
||||
) : (
|
||||
<span>
|
||||
The Call Body is Empty.
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CallLogsContentViewModal.displayName = 'CallLogsContentViewModal';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
isRequesting:
|
||||
state.callLogs &&
|
||||
state.callLogs.callLogs &&
|
||||
state.callLogs.callLogs.requesting,
|
||||
error:
|
||||
state.callLogs &&
|
||||
state.callLogs.callLogs &&
|
||||
state.callLogs.callLogs.error,
|
||||
};
|
||||
};
|
||||
|
||||
CallLogsContentViewModal.propTypes = {
|
||||
isRequesting: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
closeThisDialog: PropTypes.func,
|
||||
error: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
content: PropTypes.string,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(CallLogsContentViewModal);
|
||||
@@ -0,0 +1,125 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
class CallLogsErrorViewModal extends Component {
|
||||
componentDidMount() {
|
||||
window.addEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
handleKeyboard = e => {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
return this.props.closeThisDialog();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isRequesting, error, closeThisDialog, content } = this.props;
|
||||
return (
|
||||
<div className="db-CallLogsContentViewModal ModalLayer-wash Box-root Flex-flex Flex-alignItems--flexStart Flex-justifyContent--center">
|
||||
<div
|
||||
className="ModalLayer-contents"
|
||||
tabIndex={-1}
|
||||
style={{ marginTop: 40 }}
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="ds-Modal">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Error</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<div className="db-CallLogsContentViewModal-ContentViewerWrapper">
|
||||
<div className="db-CallLogsContentViewModal-ContentViewerContainer">
|
||||
<span>{content}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CallLogsErrorViewModal.displayName = 'CallLogsErrorViewModal';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
isRequesting:
|
||||
state.callLogs &&
|
||||
state.callLogs.callLogs &&
|
||||
state.callLogs.callLogs.requesting,
|
||||
error:
|
||||
state.callLogs &&
|
||||
state.callLogs.callLogs &&
|
||||
state.callLogs.callLogs.error,
|
||||
};
|
||||
};
|
||||
|
||||
CallLogsErrorViewModal.propTypes = {
|
||||
isRequesting: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
closeThisDialog: PropTypes.func,
|
||||
content: PropTypes.string,
|
||||
error: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(CallLogsErrorViewModal);
|
||||
478
admin-dashboard/src/components/callLogs/CallLogsList.js
Normal file
478
admin-dashboard/src/components/callLogs/CallLogsList.js
Normal file
@@ -0,0 +1,478 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { ListLoader } from '../basic/Loader';
|
||||
import { openModal, closeModal } from '../../actions/modal';
|
||||
import CallLogsContentViewModal from './CallLogsContentViewModal';
|
||||
import CallLogsErrorViewModal from './CallLogsErrorViewModal';
|
||||
import DeleteConfirmationModal from './DeleteConfirmationModal';
|
||||
import { history } from '../../store';
|
||||
|
||||
export class CallLogsList extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { deleteModalId: uuid.v4() };
|
||||
}
|
||||
|
||||
handleDelete = () => {
|
||||
const { openModal } = this.props;
|
||||
const { deleteModalId } = this.state;
|
||||
openModal({
|
||||
id: deleteModalId,
|
||||
content: DeleteConfirmationModal,
|
||||
});
|
||||
};
|
||||
|
||||
handleKeyBoard = e => {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
return this.props.closeModal({ id: this.state.deleteModalId });
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (
|
||||
this.props.callLogs &&
|
||||
this.props.callLogs.skip &&
|
||||
typeof this.props.callLogs.skip === 'string'
|
||||
) {
|
||||
this.props.callLogs.skip = parseInt(this.props.callLogs.skip, 10);
|
||||
}
|
||||
if (
|
||||
this.props.callLogs &&
|
||||
this.props.callLogs.limit &&
|
||||
typeof this.props.callLogs.limit === 'string'
|
||||
) {
|
||||
this.props.callLogs.limit = parseInt(this.props.callLogs.limit, 10);
|
||||
}
|
||||
if (!this.props.callLogs.skip) this.props.callLogs.skip = 0;
|
||||
if (!this.props.callLogs.limit) this.props.callLogs.limit = 0;
|
||||
|
||||
let canNext =
|
||||
this.props.callLogs &&
|
||||
this.props.callLogs.count &&
|
||||
this.props.callLogs.count >
|
||||
this.props.callLogs.skip + this.props.callLogs.limit
|
||||
? true
|
||||
: false;
|
||||
let canPrev =
|
||||
this.props.callLogs && this.props.callLogs.skip <= 0 ? false : true;
|
||||
|
||||
if (
|
||||
this.props.callLogs &&
|
||||
(this.props.requesting || !this.props.callLogs.callLogs)
|
||||
) {
|
||||
canNext = false;
|
||||
canPrev = false;
|
||||
}
|
||||
return (
|
||||
<div onKeyDown={this.handleKeyBoard}>
|
||||
<div style={{ overflow: 'hidden', overflowX: 'auto' }}>
|
||||
<table className="Table">
|
||||
<thead className="Table-body">
|
||||
<tr className="Table-row db-ListViewItem db-ListViewItem-header">
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--dark Text-display--inline Text-fontSize--13 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--upper Text-wrap--wrap">
|
||||
<span>Status</span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--dark Text-display--inline Text-fontSize--13 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--upper Text-wrap--wrap">
|
||||
<span>Project Name</span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-align--left Text-color--dark Text-display--block Text-fontSize--13 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--upper Text-wrap--wrap">
|
||||
<span>From</span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--dark Text-display--inline Text-fontSize--13 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--upper Text-wrap--wrap">
|
||||
<span>To</span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--dark Text-display--inline Text-fontSize--13 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--upper Text-wrap--wrap">
|
||||
<span>Actions</span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="Table-body">
|
||||
{this.props.requesting ? (
|
||||
<Fragment>
|
||||
<tr className="Table-row db-ListViewItem bs-ActionsParent db-ListViewItem--hasLink">
|
||||
<td
|
||||
colSpan={7}
|
||||
className="Table-cell Table-cell--align--right Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-link">
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="Box-root">
|
||||
<ListLoader />
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
) : this.props.callLogs &&
|
||||
this.props.callLogs.callLogs &&
|
||||
this.props.callLogs.callLogs.length > 0 ? (
|
||||
this.props.callLogs.callLogs.map(callLog => {
|
||||
return (
|
||||
<tr
|
||||
key={callLog._id}
|
||||
className="Table-row db-ListViewItem bs-ActionsParent"
|
||||
>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--wrap db-ListViewItem-cell db-ListViewItem-cell--breakWord"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--cyan Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="Box-root Margin-right--16">
|
||||
<div
|
||||
className={`Badge Badge--color--${
|
||||
callLog.status ===
|
||||
'Success'
|
||||
? 'green'
|
||||
: 'red'
|
||||
} Box-root Flex-inlineFlex Flex-alignItems--center Padding-horizontal--8 Padding-vertical--2`}
|
||||
>
|
||||
<span
|
||||
className={`Badge-text Text-color--${
|
||||
callLog.status ===
|
||||
'Success'
|
||||
? 'green'
|
||||
: 'red'
|
||||
} Text-display--inline Text-fontSize--12 Text-fontWeight--bold Text-lineHeight--16 Text-typeface--upper Text-wrap--noWrap`}
|
||||
>
|
||||
<span>
|
||||
{callLog.status
|
||||
? callLog.status
|
||||
: 'N/A'}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--wrap db-ListViewItem-cell"
|
||||
style={{
|
||||
height: '1px',
|
||||
cursor: 'pointer',
|
||||
textDecoration: callLog.projectId
|
||||
? 'underline'
|
||||
: null,
|
||||
}}
|
||||
onClick={() => {
|
||||
history.push(
|
||||
'/admin/projects/' +
|
||||
callLog.projectId
|
||||
._id
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="db-ListViewItem-link">
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="Box-root">
|
||||
<span>
|
||||
{callLog.projectId
|
||||
? callLog
|
||||
.projectId
|
||||
.name
|
||||
: 'N/A'}
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--wrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-link">
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="Box-root">
|
||||
<span>
|
||||
{callLog.from
|
||||
? callLog.from
|
||||
: 'N/A'}
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--wrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-link">
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="Box-root Flex-flex">
|
||||
<span>
|
||||
{callLog.to
|
||||
? callLog.to
|
||||
: 'N/A'}
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-link">
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="Box-root">
|
||||
<span>
|
||||
<button
|
||||
onClick={() => {
|
||||
this.props.openModal(
|
||||
{
|
||||
id: uuid.v4(),
|
||||
onConfirm: () => {
|
||||
return Promise.resolve();
|
||||
},
|
||||
content: props => (
|
||||
<CallLogsContentViewModal
|
||||
{...props}
|
||||
content={
|
||||
callLog.content
|
||||
}
|
||||
/>
|
||||
),
|
||||
}
|
||||
);
|
||||
}}
|
||||
id="view"
|
||||
className="bs-Button"
|
||||
>
|
||||
<span>
|
||||
View
|
||||
Content
|
||||
</span>
|
||||
</button>
|
||||
{callLog.error ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
this.props.openModal(
|
||||
{
|
||||
id: uuid.v4(),
|
||||
onConfirm: () => {
|
||||
return Promise.resolve();
|
||||
},
|
||||
content: props => (
|
||||
<CallLogsErrorViewModal
|
||||
{...props}
|
||||
content={
|
||||
callLog.error
|
||||
}
|
||||
/>
|
||||
),
|
||||
}
|
||||
);
|
||||
}}
|
||||
id="view"
|
||||
className="bs-Button"
|
||||
>
|
||||
<span>
|
||||
View
|
||||
Error
|
||||
</span>
|
||||
</button>
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<tr></tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div
|
||||
id="logsStatus"
|
||||
style={{ textAlign: 'center', marginTop: '10px' }}
|
||||
>
|
||||
{this.props.callLogs &&
|
||||
(!this.props.callLogs.callLogs ||
|
||||
!this.props.callLogs.callLogs.length) &&
|
||||
!this.props.requesting &&
|
||||
!this.props.callLogs.error
|
||||
? "We don't have any logs yet"
|
||||
: null}
|
||||
{this.props.callLogs && this.props.callLogs.error
|
||||
? this.props.callLogs.error
|
||||
: null}
|
||||
</div>
|
||||
<div className="Box-root Flex-flex Flex-alignItems--center Flex-justifyContent--spaceBetween">
|
||||
<div className="Box-root Flex-flex Flex-alignItems--center Padding-all--20">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<span>
|
||||
<span
|
||||
id="log-count"
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{this.props.callLogs &&
|
||||
this.props.callLogs.count
|
||||
? this.props.callLogs.count +
|
||||
(this.props.callLogs &&
|
||||
this.props.callLogs.count > 1
|
||||
? ' Logs'
|
||||
: ' Log')
|
||||
: null}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="Box-root Padding-horizontal--20 Padding-vertical--16">
|
||||
<div className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart">
|
||||
<div className="Box-root Margin-right--8">
|
||||
<button
|
||||
id="btnPrev"
|
||||
onClick={() => {
|
||||
this.props.prevClicked(
|
||||
this.props.callLogs.skip,
|
||||
this.props.callLogs.limit
|
||||
);
|
||||
}}
|
||||
className={
|
||||
'Button bs-ButtonLegacy' +
|
||||
(canPrev ? '' : 'Is--disabled')
|
||||
}
|
||||
disabled={!canPrev}
|
||||
data-db-analytics-name="list_view.pagination.previous"
|
||||
type="button"
|
||||
>
|
||||
<div className="Button-fill bs-ButtonLegacy-fill Box-root Box-background--white Flex-inlineFlex Flex-alignItems--center Flex-direction--row Padding-horizontal--8 Padding-vertical--4">
|
||||
<span className="Button-label Text-color--default Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--noWrap">
|
||||
<span>Previous</span>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<button
|
||||
id="btnNext"
|
||||
onClick={() => {
|
||||
this.props.nextClicked(
|
||||
this.props.callLogs.skip,
|
||||
this.props.callLogs.limit
|
||||
);
|
||||
}}
|
||||
className={
|
||||
'Button bs-ButtonLegacy' +
|
||||
(canNext ? '' : 'Is--disabled')
|
||||
}
|
||||
disabled={!canNext}
|
||||
data-db-analytics-name="list_view.pagination.next"
|
||||
type="button"
|
||||
>
|
||||
<div className="Button-fill bs-ButtonLegacy-fill Box-root Box-background--white Flex-inlineFlex Flex-alignItems--center Flex-direction--row Padding-horizontal--8 Padding-vertical--4">
|
||||
<span className="Button-label Text-color--default Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--noWrap">
|
||||
<span>Next</span>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
{/* <div className="Box-root">
|
||||
<button
|
||||
id="deleteLog"
|
||||
onClick={this.handleDelete}
|
||||
className={'Button bs-ButtonLegacy'}
|
||||
// data-db-analytics-name="list_view.pagination.next"
|
||||
type="button"
|
||||
disabled={this.props.requesting}
|
||||
>
|
||||
<div className="Button-fill bs-ButtonLegacy-fill Box-root Box-background--white Flex-inlineFlex Flex-alignItems--center Flex-direction--row Padding-horizontal--8 Padding-vertical--4">
|
||||
<span className="Button-label Text-color--default Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--noWrap">
|
||||
<span>Delete All Logs</span>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return bindActionCreators({ openModal, closeModal }, dispatch);
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
users: state.user.users.users,
|
||||
deleteRequest: state.callLogs.callLogs.deleteRequest,
|
||||
};
|
||||
}
|
||||
|
||||
CallLogsList.displayName = 'ProjectList';
|
||||
|
||||
CallLogsList.propTypes = {
|
||||
nextClicked: PropTypes.func.isRequired,
|
||||
prevClicked: PropTypes.func.isRequired,
|
||||
closeModal: PropTypes.func.isRequired,
|
||||
callLogs: PropTypes.oneOfType([
|
||||
PropTypes.object,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
requesting: PropTypes.bool,
|
||||
openModal: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(CallLogsList);
|
||||
@@ -0,0 +1,165 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { closeModal } from '../../actions/modal';
|
||||
import { deleteCallLogs } from '../../actions/callLogs';
|
||||
import { FormLoader } from '../basic/Loader';
|
||||
|
||||
class DeleteConfirmationModal extends Component {
|
||||
componentDidMount() {
|
||||
window.addEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
handleKeyboard = e => {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
return this.props.closeThisDialog();
|
||||
case 'Enter':
|
||||
return this.handleDelete();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
handleDelete = () => {
|
||||
const { error, deleteCallLogs, closeModal, modalId } = this.props;
|
||||
deleteCallLogs().then(() => {
|
||||
if (!error) {
|
||||
return closeModal({ id: modalId });
|
||||
}
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const { closeThisDialog, deleteRequest, error } = this.props;
|
||||
|
||||
return (
|
||||
<div className="ModalLayer-wash Box-root Flex-flex Flex-alignItems--flexStart Flex-justifyContent--center">
|
||||
<div
|
||||
className="ModalLayer-contents"
|
||||
tabIndex={-1}
|
||||
style={{ marginTop: 40 }}
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete Call Log</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Do you want to delete all the logs?
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
id="cancelCallDelete"
|
||||
className={`bs-Button btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={deleteRequest}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={this.handleDelete}
|
||||
disabled={deleteRequest}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={!deleteRequest}>
|
||||
<span>Delete Logs</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={deleteRequest}>
|
||||
<span>
|
||||
<FormLoader />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
deleteRequest: state.callLogs.callLogs.deleteRequest,
|
||||
error: state.callLogs.callLogs.error,
|
||||
modalId: state.modal.modals[0].id,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ closeModal, deleteCallLogs }, dispatch);
|
||||
|
||||
DeleteConfirmationModal.displayName = 'Delete Confirmation Modal';
|
||||
|
||||
DeleteConfirmationModal.propTypes = {
|
||||
closeThisDialog: PropTypes.func,
|
||||
deleteRequest: PropTypes.bool,
|
||||
error: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
closeModal: PropTypes.func,
|
||||
deleteCallLogs: PropTypes.func,
|
||||
modalId: PropTypes.string,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(DeleteConfirmationModal);
|
||||
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { closeModal } from '../../actions/modal';
|
||||
import { deleteEmailLogs } from '../../actions/emailLogs';
|
||||
@@ -47,79 +48,86 @@ class DeleteConfirmationModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete Email Log</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete Email Log</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Do you want to delete all the logs?
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Do you want to delete all the logs?
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
id="cancelEmailDelete"
|
||||
className={`bs-Button btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={deleteRequest}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={this.handleDelete}
|
||||
disabled={deleteRequest}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={!deleteRequest}>
|
||||
<span>Delete Logs</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={deleteRequest}>
|
||||
<span>
|
||||
<FormLoader />
|
||||
<button
|
||||
id="cancelEmailDelete"
|
||||
className={`bs-Button btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={deleteRequest}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</ShouldRender>
|
||||
</button>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={this.handleDelete}
|
||||
disabled={deleteRequest}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={!deleteRequest}>
|
||||
<span>Delete Logs</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={deleteRequest}>
|
||||
<span>
|
||||
<FormLoader />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
class EmailLogsContentViewModal extends Component {
|
||||
componentDidMount() {
|
||||
@@ -31,74 +32,81 @@ class EmailLogsContentViewModal extends Component {
|
||||
style={{ marginTop: 40 }}
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--large">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Content</span>
|
||||
</span>
|
||||
<div className="ds-Modal">
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Content</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<div className="jsonViwer Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="db-EmailLogsContentViewModal-ContentViewerWrapper">
|
||||
<div className="db-EmailLogsContentViewModal-ContentViewerContainer">
|
||||
{content ? (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: content,
|
||||
}}
|
||||
></div>
|
||||
) : (
|
||||
<span>
|
||||
The Email Body is Empty.
|
||||
</span>
|
||||
)}
|
||||
<div className="bs-Modal-content">
|
||||
<div className="jsonViwer Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="db-EmailLogsContentViewModal-ContentViewerWrapper">
|
||||
<div className="db-EmailLogsContentViewModal-ContentViewerContainer">
|
||||
{content ? (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: content,
|
||||
}}
|
||||
></div>
|
||||
) : (
|
||||
<span>
|
||||
The Email Body is Empty.
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
class EmailLogsErrorViewModal extends Component {
|
||||
componentDidMount() {
|
||||
window.addEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
handleKeyboard = e => {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
return this.props.closeThisDialog();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isRequesting, error, closeThisDialog, content } = this.props;
|
||||
return (
|
||||
<div className="db-EmailLogsContentViewModal ModalLayer-wash Box-root Flex-flex Flex-alignItems--flexStart Flex-justifyContent--center">
|
||||
<div
|
||||
className="ModalLayer-contents"
|
||||
tabIndex={-1}
|
||||
style={{ marginTop: 40 }}
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="ds-Modal">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Error</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<div className="db-EmailLogsContentViewModal-ContentViewerWrapper">
|
||||
<div className="db-EmailLogsContentViewModal-ContentViewerContainer">
|
||||
<span>{content}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EmailLogsErrorViewModal.displayName = 'EmailLogsErrorViewModal';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
isRequesting:
|
||||
state.emailLogs &&
|
||||
state.emailLogs.emailLogs &&
|
||||
state.emailLogs.emailLogs.requesting,
|
||||
error:
|
||||
state.emailLogs &&
|
||||
state.emailLogs.emailLogs &&
|
||||
state.emailLogs.emailLogs.error,
|
||||
};
|
||||
};
|
||||
|
||||
EmailLogsErrorViewModal.propTypes = {
|
||||
isRequesting: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
closeThisDialog: PropTypes.func,
|
||||
content: PropTypes.string,
|
||||
error: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(EmailLogsErrorViewModal);
|
||||
@@ -7,6 +7,7 @@ import uuid from 'uuid';
|
||||
import { ListLoader } from '../basic/Loader';
|
||||
import { openModal, closeModal } from '../../actions/modal';
|
||||
import EmailLogsContentViewModal from './EmailLogsContentViewModal';
|
||||
import EmailLogsErrorViewModal from './EmailLogsErrorViewModal';
|
||||
import DeleteConfirmationModal from './DeleteConfirmationModal';
|
||||
|
||||
export class EmailLogsList extends Component {
|
||||
@@ -129,6 +130,14 @@ export class EmailLogsList extends Component {
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--dark Text-display--inline Text-fontSize--13 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--upper Text-wrap--wrap"></span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="Table-body">
|
||||
@@ -292,6 +301,50 @@ export class EmailLogsList extends Component {
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{emailLog.error ? (
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-link">
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="Box-root">
|
||||
<span>
|
||||
<button
|
||||
onClick={() => {
|
||||
this.props.openModal(
|
||||
{
|
||||
id: uuid.v4(),
|
||||
onConfirm: () => {
|
||||
return Promise.resolve();
|
||||
},
|
||||
content: props => (
|
||||
<EmailLogsErrorViewModal
|
||||
{...props}
|
||||
content={
|
||||
emailLog.error
|
||||
}
|
||||
/>
|
||||
),
|
||||
}
|
||||
);
|
||||
}}
|
||||
id="view"
|
||||
className="bs-Button"
|
||||
>
|
||||
<span>
|
||||
View
|
||||
Error
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
) : null}
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import ClickOutside from 'react-click-outside';
|
||||
class About extends Component {
|
||||
componentDidMount() {
|
||||
window.addEventListener('keydown', this.handleKeyBoard);
|
||||
@@ -22,8 +22,36 @@ class About extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { versions } = this.props;
|
||||
const { versions, closeThisDialog, probes } = this.props;
|
||||
const currentYear = new Date().getFullYear();
|
||||
let probeVersion = null;
|
||||
if (probes && probes.length > 0) {
|
||||
probeVersion = probes.map((probe, i) => {
|
||||
return (
|
||||
<tr key={i}>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
{probe.probeName} Version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{probe.version ? (
|
||||
<strong id="probe-version">
|
||||
{probe.version}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ModalLayer-wash Box-root Flex-flex Flex-alignItems--flexStart Flex-justifyContent--center">
|
||||
@@ -33,247 +61,260 @@ class About extends Component {
|
||||
style={{ marginTop: 40 }}
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>About</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style={{
|
||||
paddingBottom: '10px',
|
||||
}}
|
||||
colSpan={2}
|
||||
>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Fyipe is a product of{' '}
|
||||
<a
|
||||
href="https://hackerbay.io"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
HackerBay, Inc.
|
||||
</a>
|
||||
. HackerBay, Inc. is a
|
||||
United States Delaware C
|
||||
Corporation.
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Server Version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{versions.server ? (
|
||||
<strong id="server-version">
|
||||
{versions.server}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Admin Dashboard Version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{versions.adminDashboard ? (
|
||||
<strong id="admin-dashboard-version">
|
||||
{
|
||||
versions.adminDashboard
|
||||
}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Dashboard Version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{versions.dashboard ? (
|
||||
<strong id="dashboard-version">
|
||||
{versions.dashboard}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Docs Version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{versions.docs ? (
|
||||
<strong id="docs-version">
|
||||
{versions.docs}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Helm chart version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{versions.helm ? (
|
||||
<strong id="helm-version">
|
||||
{versions.helm}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
style={{ paddingTop: '20px' }}
|
||||
colSpan={2}
|
||||
>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<a
|
||||
href="https://fyipe.com/legal"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Legal Center
|
||||
</a>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
|
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
style={{
|
||||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="https://fyipe.com/legal/terms"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Terms of Use
|
||||
</a>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
|
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
style={{
|
||||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="https://fyipe.com/legal/privacy"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
|
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
style={{
|
||||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="https://fyipe.com/legal/sla"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
SLA
|
||||
</a>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Copyright © {currentYear} HackerBay, Inc.
|
||||
</span>
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<button
|
||||
className="bs-Button bs-DeprecatedButton bs-Button--grey btn__modal"
|
||||
type="button"
|
||||
onClick={this.props.closeThisDialog}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>About</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style={{
|
||||
paddingBottom: '10px',
|
||||
}}
|
||||
colSpan={2}
|
||||
>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Fyipe is a product of{' '}
|
||||
<a
|
||||
href="https://hackerbay.io"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
HackerBay, Inc.
|
||||
</a>
|
||||
. HackerBay, Inc. is a
|
||||
United States Delaware C
|
||||
Corporation.
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Server Version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{versions.server ? (
|
||||
<strong id="server-version">
|
||||
{
|
||||
versions.server
|
||||
}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Admin Dashboard Version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{versions.adminDashboard ? (
|
||||
<strong id="admin-dashboard-version">
|
||||
{
|
||||
versions.adminDashboard
|
||||
}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Dashboard Version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{versions.dashboard ? (
|
||||
<strong id="dashboard-version">
|
||||
{
|
||||
versions.dashboard
|
||||
}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Docs Version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{versions.docs ? (
|
||||
<strong id="docs-version">
|
||||
{versions.docs}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Helm Chart Version
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: '15px',
|
||||
}}
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{versions.helm ? (
|
||||
<strong id="helm-version">
|
||||
{versions.helm}
|
||||
</strong>
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{probeVersion}
|
||||
<tr>
|
||||
<td
|
||||
style={{
|
||||
paddingTop: '20px',
|
||||
}}
|
||||
colSpan={2}
|
||||
>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<a
|
||||
href="https://fyipe.com/legal"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Legal Center
|
||||
</a>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft:
|
||||
'10px',
|
||||
}}
|
||||
>
|
||||
|
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
style={{
|
||||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="https://fyipe.com/legal/terms"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Terms of Use
|
||||
</a>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft:
|
||||
'10px',
|
||||
}}
|
||||
>
|
||||
|
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
style={{
|
||||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="https://fyipe.com/legal/privacy"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
<span
|
||||
style={{
|
||||
paddingLeft:
|
||||
'10px',
|
||||
}}
|
||||
>
|
||||
|
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
style={{
|
||||
paddingLeft: '10px',
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="https://fyipe.com/legal/sla"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
SLA
|
||||
</a>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Copyright © {currentYear} HackerBay,
|
||||
Inc.
|
||||
</span>
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<button
|
||||
className="bs-Button bs-DeprecatedButton bs-Button--grey btn__modal"
|
||||
type="button"
|
||||
onClick={this.props.closeThisDialog}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -286,12 +327,17 @@ About.displayName = 'AboutModal';
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
versions: state.version.versions,
|
||||
probes: state.probe.probes.data,
|
||||
};
|
||||
};
|
||||
|
||||
About.propTypes = {
|
||||
closeThisDialog: PropTypes.func.isRequired,
|
||||
versions: PropTypes.object,
|
||||
probes: PropTypes.oneOfType([
|
||||
PropTypes.object,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(About);
|
||||
|
||||
@@ -7,11 +7,13 @@ import { openNotificationMenu } from '../../actions/notification';
|
||||
import { API_URL, User } from '../../config';
|
||||
import { openSideNav } from '../../actions/page';
|
||||
import { getVersion } from '../../actions/version';
|
||||
import { getProbes } from '../../actions/probe';
|
||||
|
||||
class TopContent extends Component {
|
||||
componentDidMount() {
|
||||
const { getVersion } = this.props;
|
||||
getVersion();
|
||||
this.props.getProbes(0, 10);
|
||||
}
|
||||
showProfileMenu = e => {
|
||||
this.props.showProfileMenu(e.clientX);
|
||||
@@ -135,7 +137,13 @@ const mapStateToProps = state => {
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators(
|
||||
{ showProfileMenu, openNotificationMenu, openSideNav, getVersion },
|
||||
{
|
||||
showProfileMenu,
|
||||
openNotificationMenu,
|
||||
openSideNav,
|
||||
getVersion,
|
||||
getProbes,
|
||||
},
|
||||
dispatch
|
||||
);
|
||||
|
||||
@@ -154,6 +162,7 @@ TopContent.propTypes = {
|
||||
]),
|
||||
length: PropTypes.number,
|
||||
map: PropTypes.func,
|
||||
getProbes: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
TopContent.contextTypes = {
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { Field, reduxForm } from 'redux-form';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import { FormLoader } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { RenderField } from '../basic/RenderField';
|
||||
@@ -60,138 +61,155 @@ class ProbeAddModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div
|
||||
className="bs-Modal-header-copy"
|
||||
style={{
|
||||
marginBottom: '10px',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Add New Probe</span>
|
||||
</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div
|
||||
className="bs-Modal-header-copy"
|
||||
style={{
|
||||
marginBottom: '10px',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Add New Probe</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
id="frmIncident"
|
||||
onSubmit={handleSubmit(this.submitForm)}
|
||||
>
|
||||
<div className="bs-Modal-content bs-u-paddingless">
|
||||
<div className="bs-Modal-block bs-u-paddingless">
|
||||
<div className="bs-Modal-content">
|
||||
<span className="bs-Fieldset">
|
||||
<div className="bs-Fieldset-rows">
|
||||
<div className="bs-Fieldset-row">
|
||||
<label className="bs-Fieldset-label">
|
||||
<span>Probe Name</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="text"
|
||||
name="probe_name"
|
||||
id="probe_name"
|
||||
placeholder="US WEST"
|
||||
disabled={disabled}
|
||||
validate={
|
||||
ValidateField.text
|
||||
}
|
||||
autoFocus={true}
|
||||
/>
|
||||
<form
|
||||
id="frmIncident"
|
||||
onSubmit={handleSubmit(this.submitForm)}
|
||||
>
|
||||
<div className="bs-Modal-content bs-u-paddingless">
|
||||
<div className="bs-Modal-block bs-u-paddingless">
|
||||
<div className="bs-Modal-content">
|
||||
<span className="bs-Fieldset">
|
||||
<div className="bs-Fieldset-rows">
|
||||
<div className="bs-Fieldset-row">
|
||||
<label className="bs-Fieldset-label">
|
||||
<span>
|
||||
Probe Name
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="text"
|
||||
name="probe_name"
|
||||
id="probe_name"
|
||||
placeholder="US WEST"
|
||||
disabled={
|
||||
disabled
|
||||
}
|
||||
validate={
|
||||
ValidateField.text
|
||||
}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label className="bs-Fieldset-label">
|
||||
<span>
|
||||
Probe Key
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="text"
|
||||
name="probe_key"
|
||||
id="probe_key"
|
||||
placeholder="abcde-qw345-awqert-456yu"
|
||||
disabled={
|
||||
disabled
|
||||
}
|
||||
validate={
|
||||
ValidateField.text
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label className="bs-Fieldset-label">
|
||||
<span>Probe Key</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="text"
|
||||
name="probe_key"
|
||||
id="probe_key"
|
||||
placeholder="abcde-qw345-awqert-456yu"
|
||||
disabled={disabled}
|
||||
validate={
|
||||
ValidateField.text
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender
|
||||
if={
|
||||
addProbeState && addProbeState.error
|
||||
}
|
||||
>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{addProbeState.error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender
|
||||
if={
|
||||
addProbeState &&
|
||||
addProbeState.error
|
||||
}
|
||||
>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{
|
||||
addProbeState.error
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className="bs-Button bs-DeprecatedButton btn__modal"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
resetAddProbe();
|
||||
closeThisDialog();
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="add_probe"
|
||||
className="bs-Button bs-DeprecatedButton bs-Button--blue btn__modal"
|
||||
disabled={disabled}
|
||||
type="submit"
|
||||
>
|
||||
{addProbeState &&
|
||||
!addProbeState.requesting && (
|
||||
<>
|
||||
<span>Create</span>
|
||||
<span className="create-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{addProbeState &&
|
||||
addProbeState.requesting && (
|
||||
<FormLoader />
|
||||
)}
|
||||
</button>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className="bs-Button bs-DeprecatedButton btn__modal"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
resetAddProbe();
|
||||
closeThisDialog();
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="add_probe"
|
||||
className="bs-Button bs-DeprecatedButton bs-Button--blue btn__modal"
|
||||
disabled={disabled}
|
||||
type="submit"
|
||||
>
|
||||
{addProbeState &&
|
||||
!addProbeState.requesting && (
|
||||
<>
|
||||
<span>Create</span>
|
||||
<span className="create-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{addProbeState &&
|
||||
addProbeState.requesting && (
|
||||
<FormLoader />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import { Spinner } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { closeModal } from '../../actions/modal';
|
||||
@@ -48,74 +49,82 @@ class ProbeDeleteModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete Probe</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete Probe</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Are you sure you want to delete this
|
||||
probe?
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Are you sure you want to delete this probe?
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={this.handleDelete}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Delete Probe</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={this.handleDelete}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Delete Probe</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import { Spinner } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
|
||||
@@ -41,74 +42,82 @@ class ProjectBlockModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Block Project</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Block Project</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Are you sure you want to block this
|
||||
project?
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Are you sure you want to block this project?
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={confirmThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Block Project</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={confirmThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Block Project</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import { Spinner } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
|
||||
@@ -41,75 +42,82 @@ class ProjectDeleteModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete Project</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete Project</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Are you sure you want to delete this
|
||||
project?
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Are you sure you want to delete this
|
||||
project?
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={confirmThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Delete Project</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={confirmThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Delete Project</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -158,26 +158,28 @@ export class ProjectList extends Component {
|
||||
project.users.find(
|
||||
user => user.role === 'Owner'
|
||||
) || {};
|
||||
const usersDetail =
|
||||
project.users.length - 1 > 0
|
||||
? project.users.length - 1 > 1
|
||||
? `${
|
||||
projectOwner.name
|
||||
} and ${project.users
|
||||
.length - 1} others`
|
||||
: `${projectOwner.name} and 1 other`
|
||||
: 'Not Added Yet';
|
||||
let usersDetail;
|
||||
if (project.users.length > 0) {
|
||||
if (project.users.length === 1) {
|
||||
usersDetail = `${projectOwner.name}`;
|
||||
} else if (
|
||||
project.users.length === 2
|
||||
) {
|
||||
usersDetail = `${projectOwner.name} and 1 other`;
|
||||
} else {
|
||||
usersDetail = `${
|
||||
projectOwner.name
|
||||
} and ${project.users.length -
|
||||
1} others`;
|
||||
}
|
||||
} else {
|
||||
usersDetail = 'Not Added Yet';
|
||||
}
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={project._id}
|
||||
className="Table-row db-ListViewItem bs-ActionsParent db-ListViewItem--hasLink"
|
||||
onClick={() => {
|
||||
history.push(
|
||||
'/admin/projects/' +
|
||||
project._id
|
||||
);
|
||||
}}
|
||||
id={`project_${index}`}
|
||||
>
|
||||
<td
|
||||
@@ -185,6 +187,14 @@ export class ProjectList extends Component {
|
||||
style={{
|
||||
height: '1px',
|
||||
minWidth: '270px',
|
||||
textDecoration:
|
||||
'underline',
|
||||
}}
|
||||
onClick={() => {
|
||||
history.push(
|
||||
'/admin/projects/' +
|
||||
project._id
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
@@ -201,7 +211,21 @@ export class ProjectList extends Component {
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--right Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
style={{
|
||||
height: '1px',
|
||||
textDecoration:
|
||||
'underline',
|
||||
}}
|
||||
onClick={() => {
|
||||
if (
|
||||
projectOwner.userId
|
||||
) {
|
||||
history.push(
|
||||
'/admin/users/' +
|
||||
projectOwner.userId
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="db-ListViewItem-link">
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { PropTypes } from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
|
||||
const MessageModal = props => {
|
||||
@@ -21,48 +22,50 @@ const MessageModal = props => {
|
||||
className="bs-Modal bs-Modal--medium"
|
||||
style={{ minWidth: '400px' }}
|
||||
>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>
|
||||
{testError
|
||||
? 'Test Failed'
|
||||
: 'Test Email Sent'}
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>
|
||||
{testError
|
||||
? 'Test Failed'
|
||||
: 'Test Email Sent'}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="Flex-flex bs-Modal-content">
|
||||
<ShouldRender if={testError}>
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
{testError}
|
||||
</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={!testError}>
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
We've successfully sent a test email to{' '}
|
||||
{email}. If you do not see it, please check
|
||||
spam.
|
||||
</span>
|
||||
</ShouldRender>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className="bs-Button"
|
||||
onClick={closeThisDialog}
|
||||
>
|
||||
<span>Ok</span>
|
||||
</button>
|
||||
<div className="Flex-flex bs-Modal-content">
|
||||
<ShouldRender if={testError}>
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
{testError}
|
||||
</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={!testError}>
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
We've successfully sent a test email
|
||||
to {email}. If you do not see it, please
|
||||
check spam.
|
||||
</span>
|
||||
</ShouldRender>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className="bs-Button"
|
||||
onClick={closeThisDialog}
|
||||
>
|
||||
<span>Ok</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
183
admin-dashboard/src/components/settings/callLog.js
Normal file
183
admin-dashboard/src/components/settings/callLog.js
Normal file
@@ -0,0 +1,183 @@
|
||||
import React, { Component } from 'react';
|
||||
import { FormLoader } from '../basic/Loader';
|
||||
import { Field, reduxForm } from 'redux-form';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
fetchCallLogStatus,
|
||||
callLogStatusChange,
|
||||
} from '../../actions/callLogs';
|
||||
|
||||
class CallLog extends Component {
|
||||
async componentDidMount() {
|
||||
await this.props.fetchCallLogStatus();
|
||||
}
|
||||
toggleComponent = ({ input: { value, onChange } }) => (
|
||||
<label className="Toggler-wrap">
|
||||
<input
|
||||
className="btn-toggler"
|
||||
checked={value}
|
||||
onChange={onChange}
|
||||
type="checkbox"
|
||||
name="callStatusToggler"
|
||||
id="callStatusToggler"
|
||||
/>
|
||||
<span className="TogglerBtn-slider round"></span>
|
||||
</label>
|
||||
);
|
||||
submitForm = values => {
|
||||
this.props.callLogStatusChange({ status: values.callStatusToggler });
|
||||
};
|
||||
render() {
|
||||
const { changeCallLogStatus, handleSubmit } = this.props;
|
||||
return (
|
||||
<div
|
||||
id="fyipeCallLog"
|
||||
onKeyDown={this.handleKeyBoard}
|
||||
className="bs-ContentSection Card-root Card-shadow--medium"
|
||||
>
|
||||
<div className="Box-root">
|
||||
<div className="bs-ContentSection-content Box-root Box-divider--surface-bottom-1 Padding-horizontal--20 Padding-vertical--16">
|
||||
<div className="Box-root">
|
||||
<div className="Flex-flex Flex-alignItems-center Flex-justifyContent--spaceBetween">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Call Logs Settings</span>
|
||||
</span>
|
||||
</div>
|
||||
<p>
|
||||
<span>
|
||||
Here you can enable or disable call logs
|
||||
being monitored on your Fyipe projects.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
id="call-log-toggle-form"
|
||||
onSubmit={handleSubmit(this.submitForm)}
|
||||
>
|
||||
<div className="bs-ContentSection-content Box-root Box-background--offset Box-divider--surface-bottom-1 Padding-horizontal--8 Padding-vertical--2">
|
||||
<div>
|
||||
<div className="bs-Fieldset-wrapper Box-root Margin-bottom--2">
|
||||
<fieldset className="bs-Fieldset">
|
||||
<div className="bs-Fieldset-rows">
|
||||
<div className="bs-Fieldset-row">
|
||||
<label className="bs-Fieldset-label">
|
||||
Enable Call Logs
|
||||
</label>
|
||||
<div
|
||||
className="bs-Fieldset-fields"
|
||||
style={{
|
||||
paddingTop: 3,
|
||||
}}
|
||||
>
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
name="callStatusToggler"
|
||||
id="callStatusToggler"
|
||||
component={
|
||||
this.toggleComponent
|
||||
}
|
||||
disabled={
|
||||
changeCallLogStatus &&
|
||||
changeCallLogStatus.requesting
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bs-ContentSection-footer bs-ContentSection-content Box-root Box-background--white Flex-flex Flex-alignItems--center Flex-justifyContent--spaceBetween Padding-horizontal--20 Padding-vertical--12">
|
||||
<span className="db-SettingsForm-footerMessage">
|
||||
{!changeCallLogStatus.requesting &&
|
||||
changeCallLogStatus.error && (
|
||||
<div
|
||||
id="errors"
|
||||
className="bs-Tail-copy"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{
|
||||
changeCallLogStatus.error
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
<div>
|
||||
<button
|
||||
className="bs-Button bs-Button--blue"
|
||||
disabled={
|
||||
changeCallLogStatus &&
|
||||
changeCallLogStatus.requesting
|
||||
}
|
||||
type="submit"
|
||||
id="callLogSubmit"
|
||||
>
|
||||
{changeCallLogStatus.requesting ? (
|
||||
<FormLoader />
|
||||
) : (
|
||||
<span>Save Settings</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CallLog.displayName = 'CallLog';
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return bindActionCreators(
|
||||
{ fetchCallLogStatus, callLogStatusChange },
|
||||
dispatch
|
||||
);
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const callLogStatus = state.callLogs.callLogStatus;
|
||||
const changeCallLogStatus = state.callLogs.changeCallLogStatus;
|
||||
return {
|
||||
settings: state.settings,
|
||||
callLogStatus,
|
||||
changeCallLogStatus,
|
||||
initialValues: {
|
||||
callStatusToggler: callLogStatus.data
|
||||
? callLogStatus.data.value
|
||||
: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
const ReduxFormComponent = reduxForm({
|
||||
form: 'call-log-toggle-form',
|
||||
enableReinitialize: true,
|
||||
})(CallLog);
|
||||
|
||||
CallLog.propTypes = {
|
||||
changeCallLogStatus: PropTypes.object,
|
||||
handleSubmit: PropTypes.func,
|
||||
fetchCallLogStatus: PropTypes.func,
|
||||
callLogStatusChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ReduxFormComponent);
|
||||
@@ -140,15 +140,6 @@ export class Component extends React.Component {
|
||||
await this.props.fetchSettings(settingsType);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { settings } = this.props;
|
||||
if (settings && settings.testSuccess) {
|
||||
if (window.location.href.indexOf('localhost') <= -1) {
|
||||
this.context.mixpanel.track('Sent SMTP settings');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleTestSmtp = e => {
|
||||
e.preventDefault();
|
||||
const { testSmtp, smtpForm } = this.props;
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { reduxForm, Field } from 'redux-form';
|
||||
import PropTypes from 'prop-types';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { Validate } from '../../config';
|
||||
import { RenderField } from '../basic/RenderField';
|
||||
@@ -26,112 +27,119 @@ const SmtpTestModal = ({
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Test SMTP Settings</span>
|
||||
</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Test SMTP Settings</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-header-copy Margin-top--8">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
If your SMTP settings are correct, a
|
||||
test email will be sent to this email
|
||||
address.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-header-copy Margin-top--8">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
If your SMTP settings are correct, a test
|
||||
email will be sent to this email address.
|
||||
</span>
|
||||
<div className="bs-ContentSection-content Box-root Box-background--offset Box-divider--surface-bottom-1 Padding-horizontal--8 Padding-vertical--2">
|
||||
<div>
|
||||
<div className="bs-Fieldset-wrapper Box-root Margin-bottom--2">
|
||||
<fieldset className="bs-Fieldset">
|
||||
<div className="bs-Fieldset-rows">
|
||||
<div className="Margin-bottom--20 Margin-top--20">
|
||||
<div
|
||||
style={{
|
||||
paddingTop: 3,
|
||||
}}
|
||||
>
|
||||
<Field
|
||||
className="db-BusinessSettings-input-wide TextInput bs-TextInput"
|
||||
type="text"
|
||||
name="test-email"
|
||||
id="testEmail"
|
||||
placeholder="Enter an email"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
disabled={testing}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-ContentSection-content Box-root Box-background--offset Box-divider--surface-bottom-1 Padding-horizontal--8 Padding-vertical--2">
|
||||
<div>
|
||||
<div className="bs-Fieldset-wrapper Box-root Margin-bottom--2">
|
||||
<fieldset className="bs-Fieldset">
|
||||
<div className="bs-Fieldset-rows">
|
||||
<div className="Margin-bottom--20 Margin-top--20">
|
||||
<div
|
||||
style={{
|
||||
paddingTop: 3,
|
||||
}}
|
||||
>
|
||||
<Field
|
||||
className="db-BusinessSettings-input-wide TextInput bs-TextInput"
|
||||
type="text"
|
||||
name="test-email"
|
||||
id="testEmail"
|
||||
placeholder="Enter an email"
|
||||
component={RenderField}
|
||||
disabled={testing}
|
||||
/>
|
||||
<div
|
||||
className="bs-Modal-footer"
|
||||
style={{ wordBreak: 'break-word' }}
|
||||
>
|
||||
<div className="Flex-flex Flex-direction--row Flex-justifyContent--flexEnd Table-cell--width--maximized">
|
||||
<ShouldRender if={testError}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{testError}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</ShouldRender>
|
||||
<button
|
||||
id="cancelSmtpTest"
|
||||
className={`bs-Button ${testing &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={testing}
|
||||
style={{ height: '33px' }}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmSmtpTest"
|
||||
className={`bs-Button bs-Button--blue ${testing &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={() => {
|
||||
// prevent form submission if form field is empty or invalid
|
||||
if (!smtp.values) return;
|
||||
if (
|
||||
smtp.syncErrors &&
|
||||
smtp.syncErrors['test-email']
|
||||
)
|
||||
return;
|
||||
|
||||
confirmThisDialog(smtp.values);
|
||||
}}
|
||||
disabled={testing}
|
||||
style={{ height: '33px' }}
|
||||
>
|
||||
{testing ? (
|
||||
<FormLoader />
|
||||
) : (
|
||||
<span>Test</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="bs-Modal-footer"
|
||||
style={{ wordBreak: 'break-word' }}
|
||||
>
|
||||
<div className="Flex-flex Flex-direction--row Flex-justifyContent--flexEnd Table-cell--width--maximized">
|
||||
<ShouldRender if={testError}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{ marginTop: '2px' }}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{testError}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
id="cancelSmtpTest"
|
||||
className={`bs-Button ${testing &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={testing}
|
||||
style={{ height: '33px' }}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmSmtpTest"
|
||||
className={`bs-Button bs-Button--blue ${testing &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={() => {
|
||||
// prevent form submission if form field is empty or invalid
|
||||
if (!smtp.values) return;
|
||||
if (
|
||||
smtp.syncErrors &&
|
||||
smtp.syncErrors['test-email']
|
||||
)
|
||||
return;
|
||||
|
||||
confirmThisDialog(smtp.values);
|
||||
}}
|
||||
disabled={testing}
|
||||
style={{ height: '33px' }}
|
||||
>
|
||||
{testing ? (
|
||||
<FormLoader />
|
||||
) : (
|
||||
<span>Test</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { closeModal } from '../../actions/modal';
|
||||
import { deleteSmsLogs } from '../../actions/smsLogs';
|
||||
@@ -47,79 +48,86 @@ class DeleteConfirmationModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete SMS Log</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete SMS Log</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Do you want to delete all the logs?
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Do you want to delete all the logs?
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
id="cancelSmsDelete"
|
||||
className={`bs-Button btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={deleteRequest}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={this.handleDelete}
|
||||
disabled={deleteRequest}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={!deleteRequest}>
|
||||
<span>Delete Logs</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={deleteRequest}>
|
||||
<span>
|
||||
<FormLoader />
|
||||
<button
|
||||
id="cancelSmsDelete"
|
||||
className={`bs-Button btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={deleteRequest}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</ShouldRender>
|
||||
</button>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${deleteRequest &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={this.handleDelete}
|
||||
disabled={deleteRequest}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={!deleteRequest}>
|
||||
<span>Delete Logs</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={deleteRequest}>
|
||||
<span>
|
||||
<FormLoader />
|
||||
</span>
|
||||
</ShouldRender>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
class SmsLogsContentViewModal extends Component {
|
||||
componentDidMount() {
|
||||
@@ -32,67 +33,80 @@ class SmsLogsContentViewModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--large">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Content</span>
|
||||
</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Content</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<div className="jsonViwer Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="db-SmsLogsContentViewModal-ContentViewerWrapper">
|
||||
<div className="db-SmsLogsContentViewModal-ContentViewerContainer">
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: content,
|
||||
}}
|
||||
></div>
|
||||
<div className="bs-Modal-content">
|
||||
<div className="jsonViwer Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="db-SmsLogsContentViewModal-ContentViewerWrapper">
|
||||
<div className="db-SmsLogsContentViewModal-ContentViewerContainer">
|
||||
{content ? (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: content,
|
||||
}}
|
||||
></div>
|
||||
) : (
|
||||
<span>
|
||||
The SMS Body is Empty.
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
125
admin-dashboard/src/components/smsLogs/SmsLogsErrorViewModal.js
Normal file
125
admin-dashboard/src/components/smsLogs/SmsLogsErrorViewModal.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
class SmsLogsErrorViewModal extends Component {
|
||||
componentDidMount() {
|
||||
window.addEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
handleKeyboard = e => {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
return this.props.closeThisDialog();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isRequesting, error, closeThisDialog, content } = this.props;
|
||||
return (
|
||||
<div className="db-SmsLogsContentViewModal ModalLayer-wash Box-root Flex-flex Flex-alignItems--flexStart Flex-justifyContent--center">
|
||||
<div
|
||||
className="ModalLayer-contents"
|
||||
tabIndex={-1}
|
||||
style={{ marginTop: 40 }}
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="ds-Modal">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Error</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<div className="db-SmsLogsContentViewModal-ContentViewerWrapper">
|
||||
<div className="db-SmsLogsContentViewModal-ContentViewerContainer">
|
||||
<span>{content}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<span>Close</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SmsLogsErrorViewModal.displayName = 'SmsLogsErrorViewModal';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
isRequesting:
|
||||
state.smsLogs &&
|
||||
state.smsLogs.smsLogs &&
|
||||
state.smsLogs.smsLogs.requesting,
|
||||
error:
|
||||
state.smsLogs &&
|
||||
state.smsLogs.smsLogs &&
|
||||
state.smsLogs.smsLogs.error,
|
||||
};
|
||||
};
|
||||
|
||||
SmsLogsErrorViewModal.propTypes = {
|
||||
isRequesting: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
closeThisDialog: PropTypes.func,
|
||||
content: PropTypes.string,
|
||||
error: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(SmsLogsErrorViewModal);
|
||||
@@ -8,6 +8,9 @@ import { ListLoader } from '../basic/Loader';
|
||||
import { openModal, closeModal } from '../../actions/modal';
|
||||
import DeleteConfirmationModal from './DeleteConfirmationModal';
|
||||
import SmsLogsContentViewModal from './SmsLogsContentViewModal';
|
||||
import SmsLogsErrorViewModal from './SmsLogsErrorViewModal';
|
||||
|
||||
import { history } from '../../store';
|
||||
|
||||
export class SmsLogsList extends Component {
|
||||
constructor(props) {
|
||||
@@ -74,6 +77,16 @@ export class SmsLogsList extends Component {
|
||||
<table className="Table">
|
||||
<thead className="Table-body">
|
||||
<tr className="Table-row db-ListViewItem db-ListViewItem-header">
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--dark Text-display--inline Text-fontSize--13 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--upper Text-wrap--wrap">
|
||||
<span>Status</span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--noWrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
@@ -90,7 +103,7 @@ export class SmsLogsList extends Component {
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--dark Text-display--inline Text-fontSize--13 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--upper Text-wrap--wrap">
|
||||
<span>User name</span>
|
||||
<span>Users</span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
@@ -146,17 +159,67 @@ export class SmsLogsList extends Component {
|
||||
key={smsLog._id}
|
||||
className="Table-row db-ListViewItem bs-ActionsParent"
|
||||
>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--wrap db-ListViewItem-cell db-ListViewItem-cell--breakWord"
|
||||
style={{
|
||||
height: '1px',
|
||||
}}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--cyan Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="Box-root Margin-right--16">
|
||||
<div
|
||||
className={`Badge Badge--color--${
|
||||
smsLog.status ===
|
||||
'Success'
|
||||
? 'green'
|
||||
: 'red'
|
||||
} Box-root Flex-inlineFlex Flex-alignItems--center Padding-horizontal--8 Padding-vertical--2`}
|
||||
>
|
||||
<span
|
||||
className={`Badge-text Text-color--${
|
||||
smsLog.status ===
|
||||
'Success'
|
||||
? 'green'
|
||||
: 'red'
|
||||
} Text-display--inline Text-fontSize--12 Text-fontWeight--bold Text-lineHeight--16 Text-typeface--upper Text-wrap--noWrap`}
|
||||
>
|
||||
<span>
|
||||
{smsLog.status
|
||||
? smsLog.status
|
||||
: 'N/A'}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--wrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
style={{
|
||||
height: '1px',
|
||||
cursor: 'pointer',
|
||||
textDecoration: smsLog.projectId
|
||||
? 'underline'
|
||||
: null,
|
||||
}}
|
||||
onClick={() => {
|
||||
history.push(
|
||||
'/admin/projects/' +
|
||||
smsLog.projectId._id
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="db-ListViewItem-link">
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<span className="db-ListViewItem-text Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="Box-root">
|
||||
<span>
|
||||
{smsLog.projectId
|
||||
? smsLog.projectId
|
||||
? smsLog
|
||||
.projectId
|
||||
.name
|
||||
: 'N/A'}
|
||||
</span>
|
||||
</div>
|
||||
@@ -166,14 +229,31 @@ export class SmsLogsList extends Component {
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--wrap db-ListViewItem-cell db-ListViewItem-cell--breakWord"
|
||||
style={{ height: '1px' }}
|
||||
style={{
|
||||
height: '1px',
|
||||
cursor: 'pointer',
|
||||
textDecoration: smsLog.userId
|
||||
? 'underline'
|
||||
: null,
|
||||
}}
|
||||
onClick={() => {
|
||||
if (smsLog.userId) {
|
||||
history.push(
|
||||
'/admin/users/' +
|
||||
smsLog.userId
|
||||
._id
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
<span className="db-ListViewItem-text Text-color--cyan Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<div className="Box-root Margin-right--16">
|
||||
<span>
|
||||
{smsLog.userId
|
||||
? smsLog.userId
|
||||
? smsLog
|
||||
.userId
|
||||
.name
|
||||
: 'N/A'}
|
||||
</span>
|
||||
</div>
|
||||
@@ -182,7 +262,9 @@ export class SmsLogsList extends Component {
|
||||
</td>
|
||||
<td
|
||||
className="Table-cell Table-cell--align--left Table-cell--verticalAlign--top Table-cell--width--minimized Table-cell--wrap--wrap db-ListViewItem-cell"
|
||||
style={{ height: '1px' }}
|
||||
style={{
|
||||
height: '1px',
|
||||
}}
|
||||
>
|
||||
<div className="db-ListViewItem-link">
|
||||
<div className="db-ListViewItem-cellContent Box-root Padding-all--8">
|
||||
@@ -234,6 +316,35 @@ export class SmsLogsList extends Component {
|
||||
Content
|
||||
</span>
|
||||
</button>
|
||||
{smsLog.error ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
this.props.openModal(
|
||||
{
|
||||
id: uuid.v4(),
|
||||
onConfirm: () => {
|
||||
return Promise.resolve();
|
||||
},
|
||||
content: props => (
|
||||
<SmsLogsErrorViewModal
|
||||
{...props}
|
||||
content={
|
||||
smsLog.error
|
||||
}
|
||||
/>
|
||||
),
|
||||
}
|
||||
);
|
||||
}}
|
||||
id="view"
|
||||
className="bs-Button"
|
||||
>
|
||||
<span>
|
||||
View
|
||||
Error
|
||||
</span>
|
||||
</button>
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { PropTypes } from 'prop-types';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
|
||||
const MessageModal = props => {
|
||||
const { closeThisDialog } = props;
|
||||
@@ -16,29 +17,31 @@ const MessageModal = props => {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Info</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Info</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Only the user can turn on 2FA not the admin
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Only the user can turn on 2FA not the admin
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className="bs-Button"
|
||||
onClick={closeThisDialog}
|
||||
>
|
||||
<span>Ok</span>
|
||||
</button>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className="bs-Button"
|
||||
onClick={closeThisDialog}
|
||||
>
|
||||
<span>Ok</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { Field, reduxForm } from 'redux-form';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import { FormLoader } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { RenderField } from '../basic/RenderField';
|
||||
@@ -62,230 +63,256 @@ class UserAddModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div
|
||||
className="bs-Modal-header-copy"
|
||||
style={{
|
||||
marginBottom: '10px',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Add New User</span>
|
||||
</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div
|
||||
className="bs-Modal-header-copy"
|
||||
style={{
|
||||
marginBottom: '10px',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Add New User</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
id="frmUser"
|
||||
onSubmit={handleSubmit(this.submitForm)}
|
||||
>
|
||||
<div className="bs-Modal-content bs-u-paddingless">
|
||||
<div className="bs-Modal-block bs-u-paddingless">
|
||||
<div className="bs-Modal-content">
|
||||
<span className="bs-Fieldset">
|
||||
<div className="bs-Fieldset-rows">
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>Email</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="email"
|
||||
name="email"
|
||||
id="email"
|
||||
placeholder="jeff@example.com"
|
||||
required="required"
|
||||
disabled={disabled}
|
||||
autoFocus={true}
|
||||
/>
|
||||
<form
|
||||
id="frmUser"
|
||||
onSubmit={handleSubmit(this.submitForm)}
|
||||
>
|
||||
<div className="bs-Modal-content bs-u-paddingless">
|
||||
<div className="bs-Modal-block bs-u-paddingless">
|
||||
<div className="bs-Modal-content">
|
||||
<span className="bs-Fieldset">
|
||||
<div className="bs-Fieldset-rows">
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>Email</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="email"
|
||||
name="email"
|
||||
id="email"
|
||||
placeholder="jeff@example.com"
|
||||
required="required"
|
||||
disabled={
|
||||
disabled
|
||||
}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>
|
||||
Full Name
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="text"
|
||||
name="name"
|
||||
id="name"
|
||||
placeholder="Jeff Smith"
|
||||
required="required"
|
||||
disabled={
|
||||
disabled
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="companyName"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>
|
||||
Company Name
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="text"
|
||||
name="companyName"
|
||||
id="companyName"
|
||||
placeholder="Company Name"
|
||||
disabled={
|
||||
disabled
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="companyPhoneNumber"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>
|
||||
Company Phone
|
||||
Number
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="tel"
|
||||
name="companyPhoneNumber"
|
||||
id="companyPhoneNumber"
|
||||
placeholder="+1-123-456-7890"
|
||||
disabled={
|
||||
disabled
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="password"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>
|
||||
Password
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="password"
|
||||
name="password"
|
||||
id="password"
|
||||
placeholder="Password"
|
||||
required="required"
|
||||
disabled={
|
||||
disabled
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="confirmPassword"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>
|
||||
Confirm Password
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="password"
|
||||
name="confirmPassword"
|
||||
id="confirmPassword"
|
||||
placeholder="Confirm Password"
|
||||
required="required"
|
||||
disabled={
|
||||
disabled
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>Full Name</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="text"
|
||||
name="name"
|
||||
id="name"
|
||||
placeholder="Jeff Smith"
|
||||
required="required"
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="companyName"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>
|
||||
Company Name
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="text"
|
||||
name="companyName"
|
||||
id="companyName"
|
||||
placeholder="Company Name"
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="companyPhoneNumber"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>
|
||||
Company Phone Number
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="tel"
|
||||
name="companyPhoneNumber"
|
||||
id="companyPhoneNumber"
|
||||
placeholder="+1-123-456-7890"
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="password"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>Password</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="password"
|
||||
name="password"
|
||||
id="password"
|
||||
placeholder="Password"
|
||||
required="required"
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
htmlFor="confirmPassword"
|
||||
className="bs-Fieldset-label"
|
||||
>
|
||||
<span>
|
||||
Confirm Password
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="password"
|
||||
name="confirmPassword"
|
||||
id="confirmPassword"
|
||||
placeholder="Confirm Password"
|
||||
required="required"
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender
|
||||
if={addUserState && addUserState.error}
|
||||
>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{addUserState.error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender
|
||||
if={
|
||||
addUserState &&
|
||||
addUserState.error
|
||||
}
|
||||
>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{addUserState.error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className="bs-Button bs-DeprecatedButton btn__modal"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
resetAddUser();
|
||||
closeThisDialog();
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="add_user_btn"
|
||||
className="bs-Button bs-DeprecatedButton bs-Button--blue btn__modal"
|
||||
disabled={disabled}
|
||||
type="submit"
|
||||
>
|
||||
{addUserState &&
|
||||
!addUserState.requesting && (
|
||||
<>
|
||||
<span>Create</span>
|
||||
<span className="create-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{addUserState &&
|
||||
addUserState.requesting && (
|
||||
<FormLoader />
|
||||
)}
|
||||
</button>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className="bs-Button bs-DeprecatedButton btn__modal"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
resetAddUser();
|
||||
closeThisDialog();
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="add_user_btn"
|
||||
className="bs-Button bs-DeprecatedButton bs-Button--blue btn__modal"
|
||||
disabled={disabled}
|
||||
type="submit"
|
||||
>
|
||||
{addUserState &&
|
||||
!addUserState.requesting && (
|
||||
<>
|
||||
<span>Create</span>
|
||||
<span className="create-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{addUserState &&
|
||||
addUserState.requesting && (
|
||||
<FormLoader />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import { Spinner } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
|
||||
@@ -41,74 +42,82 @@ class UserBlockModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Block User</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Block User</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Are you sure you want to block this
|
||||
user?
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Are you sure you want to block this user?
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={confirmThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Block User</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={confirmThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Block User</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import { Spinner } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
|
||||
@@ -41,74 +42,82 @@ class UserDeleteModal extends Component {
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete User</span>
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Delete User</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Are you sure you want to delete this
|
||||
user?
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Are you sure you want to delete this user?
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop: '2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender if={error}>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={confirmThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Delete User</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="confirmDelete"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
onClick={confirmThisDialog}
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Delete User</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
26
admin-dashboard/src/constants/callLogs.js
Normal file
26
admin-dashboard/src/constants/callLogs.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// Fetch All Call Log List
|
||||
export const FETCH_CALLLOGS_REQUEST = 'FETCH_CALLLOGS_REQUEST';
|
||||
export const FETCH_CALLLOGS_SUCCESS = 'FETCH_CALLLOGS_SUCCESS';
|
||||
export const FETCH_CALLLOGS_FAILURE = 'FETCH_CALLLOGS_FAILURE';
|
||||
|
||||
// Search Call Logs
|
||||
export const SEARCH_CALLLOGS_REQUEST = 'SEARCH_CALLLOGS_REQUEST';
|
||||
export const SEARCH_CALLLOGS_SUCCESS = 'SEARCH_CALLLOGS_SUCCESS';
|
||||
export const SEARCH_CALLLOGS_FAILURE = 'SEARCH_CALLLOGS_FAILURE';
|
||||
|
||||
// Delete All Logs
|
||||
export const DELETE_ALL_CALLLOGS_REQUEST = 'DELETE_ALL_CALLLOGS_REQUEST';
|
||||
export const DELETE_ALL_CALLLOGS_SUCCESS = 'DELETE_ALL_CALLLOGS_SUCCESS';
|
||||
export const DELETE_ALL_CALLLOGS_FAILURE = 'DELETE_ALL_CALLLOGS_FAILURE';
|
||||
|
||||
// Fetch call log status
|
||||
export const FETCH_CALLLOG_STATUS_SUCCESS = 'FETCH_CALLLOG_STATUS_SUCCESS';
|
||||
export const FETCH_CALLLOG_STATUS_FAILED = 'FETCH_CALLLOG_STATUS_FAILED';
|
||||
export const FETCH_CALLLOG_STATUS_REQUEST = 'FETCH_CALLLOG_STATUS_REQUEST';
|
||||
export const FETCH_CALLLOG_STATUS_RESET = 'FETCH_CALLLOG_STATUS_RESET';
|
||||
|
||||
// change call log status
|
||||
export const CHANGE_CALLLOG_STATUS_SUCCESS = 'CHANGE_CALLLOG_STATUS_SUCCESS';
|
||||
export const CHANGE_CALLLOG_STATUS_FAILED = 'CHANGE_CALLLOG_STATUS_FAILED';
|
||||
export const CHANGE_CALLLOG_STATUS_REQUEST = 'CHANGE_CALLLOG_STATUS_REQUEST';
|
||||
export const CHANGE_CALLLOG_STATUS_RESET = 'CHANGE_CALLLOG_STATUS_RESET';
|
||||
226
admin-dashboard/src/pages/CallLogs.js
Normal file
226
admin-dashboard/src/pages/CallLogs.js
Normal file
@@ -0,0 +1,226 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import CallLogsList from '../components/callLogs/CallLogsList';
|
||||
import {
|
||||
fetchCallLogs,
|
||||
searchCallLogs,
|
||||
fetchCallLogStatus,
|
||||
} from '../actions/callLogs';
|
||||
import Dashboard from '../components/Dashboard';
|
||||
import { Link } from 'react-router-dom';
|
||||
import AlertPanel from '../components/basic/AlertPanel';
|
||||
import ShouldRender from '../components/basic/ShouldRender';
|
||||
class CallLogs extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
searchBox: null,
|
||||
};
|
||||
}
|
||||
|
||||
prevClicked = (skip, limit) => {
|
||||
const { searchBox } = this.state;
|
||||
const { fetchCallLogs, searchCallLogs } = this.props;
|
||||
|
||||
if (searchBox && searchBox !== '') {
|
||||
searchCallLogs(
|
||||
searchBox,
|
||||
(skip || 0) > (limit || 10) ? skip - limit : 0,
|
||||
10
|
||||
);
|
||||
} else {
|
||||
fetchCallLogs((skip || 0) > (limit || 10) ? skip - limit : 0, 10);
|
||||
}
|
||||
};
|
||||
|
||||
nextClicked = (skip, limit) => {
|
||||
const { searchBox } = this.state;
|
||||
const { fetchCallLogs, searchCallLogs } = this.props;
|
||||
|
||||
if (searchBox && searchBox !== '') {
|
||||
searchCallLogs(searchBox, skip + limit, 10);
|
||||
} else {
|
||||
fetchCallLogs(skip + limit, 10);
|
||||
}
|
||||
};
|
||||
|
||||
ready = () => {
|
||||
this.props.fetchCallLogs();
|
||||
this.props.fetchCallLogStatus();
|
||||
};
|
||||
|
||||
onChange = e => {
|
||||
const value = e.target.value;
|
||||
const { searchCallLogs } = this.props;
|
||||
|
||||
this.setState({ searchBox: value });
|
||||
searchCallLogs(value, 0, 10);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { callLogStatus } = this.props;
|
||||
return (
|
||||
<Dashboard ready={this.ready}>
|
||||
<div
|
||||
id="fyipeCallLog"
|
||||
onKeyDown={this.handleKeyBoard}
|
||||
className="Box-root Margin-vertical--12"
|
||||
>
|
||||
<div>
|
||||
<div>
|
||||
<div className="db-BackboneViewContainer">
|
||||
<div
|
||||
className="customers-list-view react-view popover-container"
|
||||
style={{
|
||||
position: 'relative',
|
||||
overflow: 'visible',
|
||||
}}
|
||||
></div>
|
||||
<div className="bs-BIM">
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<div className="bs-ContentSection Card-root Card-shadow--medium">
|
||||
<div className="Box-root">
|
||||
<div className="ContentHeader Box-root Box-background--white Box-divider--surface-bottom-1 Flex-flex Flex-direction--column Padding-horizontal--20 Padding-vertical--16">
|
||||
<div className="Box-root Flex-flex Flex-direction--row Flex-justifyContent--spaceBetween">
|
||||
<div className="ContentHeader-center Box-root Flex-flex Flex-direction--column Flex-justifyContent--center">
|
||||
<span className="ContentHeader-title Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--medium Text-lineHeight--28 Text-typeface--base Text-wrap--wrap">
|
||||
<span
|
||||
style={{
|
||||
textTransform:
|
||||
'capitalize',
|
||||
}}
|
||||
>
|
||||
Call Logs
|
||||
</span>
|
||||
</span>
|
||||
<span className="ContentHeader-description Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
|
||||
<span>
|
||||
Here is a
|
||||
complete
|
||||
list of Call
|
||||
logs.
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{/* <div className="ContentHeader-end Box-root Flex-flex Flex-alignItems--center Margin-left--16">
|
||||
<div className="Box-root">
|
||||
<div className="ContentHeader-end Box-root Flex-flex Flex-alignItems--center Margin-left--16">
|
||||
<div>
|
||||
<input
|
||||
id="searchCallLog"
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
placeholder="Search Logs"
|
||||
onChange={
|
||||
this
|
||||
.onChange
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ContentHeader Box-root Box-background--white Box-divider--surface-bottom-1 Flex-flex Flex-direction--column">
|
||||
<ShouldRender
|
||||
if={
|
||||
callLogStatus.data &&
|
||||
!callLogStatus.data
|
||||
.value
|
||||
}
|
||||
>
|
||||
<AlertPanel
|
||||
className=""
|
||||
message={
|
||||
<span id="callLogDisabled">
|
||||
You are
|
||||
currently
|
||||
not storing
|
||||
any call
|
||||
logs at the
|
||||
moment.
|
||||
Click{' '}
|
||||
<Link
|
||||
className="Border-bottom--white Text-fontWeight--bold Text-color--white"
|
||||
to="/admin/settings/call-logs"
|
||||
id="callLogSetting"
|
||||
>
|
||||
here
|
||||
</Link>{' '}
|
||||
to turn it
|
||||
on.
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</ShouldRender>
|
||||
</div>
|
||||
</div>
|
||||
<CallLogsList
|
||||
callLogs={
|
||||
this.props.callLogs || {}
|
||||
}
|
||||
prevClicked={this.prevClicked}
|
||||
nextClicked={this.nextClicked}
|
||||
userId={this.props.userId}
|
||||
requesting={
|
||||
this.props.requesting
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dashboard>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CallLogs.displayName = 'CallLogs';
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return bindActionCreators(
|
||||
{
|
||||
fetchCallLogs,
|
||||
searchCallLogs,
|
||||
fetchCallLogStatus,
|
||||
},
|
||||
dispatch
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const callLogs = state.callLogs.callLogs;
|
||||
const searchCallLogs = state.callLogs.searchCallLogs;
|
||||
const requesting =
|
||||
callLogs && searchCallLogs
|
||||
? callLogs.requesting || searchCallLogs.requesting
|
||||
? true
|
||||
: false
|
||||
: false;
|
||||
const callLogStatus = state.callLogs.callLogStatus;
|
||||
const changeCallLogStatus = state.callLogs.changeCallLogStatus;
|
||||
return {
|
||||
callLogs,
|
||||
requesting,
|
||||
callLogStatus,
|
||||
changeCallLogStatus,
|
||||
};
|
||||
};
|
||||
CallLogs.propTypes = {
|
||||
fetchCallLogs: PropTypes.func.isRequired,
|
||||
searchCallLogs: PropTypes.func.isRequired,
|
||||
requesting: PropTypes.bool,
|
||||
callLogs: PropTypes.object,
|
||||
userId: PropTypes.string,
|
||||
fetchCallLogStatus: PropTypes.func.isRequired,
|
||||
callLogStatus: PropTypes.object,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(CallLogs);
|
||||
@@ -8,6 +8,7 @@ import Sso from '../components/settings/sso';
|
||||
import SsoDefaultRoles from '../components/settings/ssoDefaultRoles';
|
||||
import AuditLog from '../components/settings/auditLog';
|
||||
import EmailLog from '../components/settings/emailLog';
|
||||
import CallLog from '../components/settings/callLog';
|
||||
import SmsLog from '../components/settings/smsLog';
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
@@ -26,6 +27,8 @@ const getChild = key => {
|
||||
);
|
||||
case '/admin/settings/audit-logs':
|
||||
return <AuditLog />;
|
||||
case '/admin/settings/call-logs':
|
||||
return <CallLog />;
|
||||
case '/admin/settings/email-logs':
|
||||
return <EmailLog />;
|
||||
case '/admin/settings/sms-logs':
|
||||
|
||||
@@ -5,6 +5,7 @@ import Project from './Project';
|
||||
import Probes from './Probes';
|
||||
import AuditLogs from './AuditLogs';
|
||||
import EmailLogs from './EmailLogs';
|
||||
import CallLogs from './CallLogs';
|
||||
import SmsLogs from './SmsLogs';
|
||||
import Settings from './Settings';
|
||||
import License from './License';
|
||||
@@ -18,6 +19,7 @@ export default {
|
||||
AuditLogs,
|
||||
EmailLogs,
|
||||
SmsLogs,
|
||||
CallLogs,
|
||||
Settings,
|
||||
License,
|
||||
};
|
||||
|
||||
253
admin-dashboard/src/reducers/callLogs.js
Normal file
253
admin-dashboard/src/reducers/callLogs.js
Normal file
@@ -0,0 +1,253 @@
|
||||
import {
|
||||
FETCH_CALLLOGS_REQUEST,
|
||||
FETCH_CALLLOGS_SUCCESS,
|
||||
FETCH_CALLLOGS_FAILURE,
|
||||
SEARCH_CALLLOGS_REQUEST,
|
||||
SEARCH_CALLLOGS_SUCCESS,
|
||||
SEARCH_CALLLOGS_FAILURE,
|
||||
DELETE_ALL_CALLLOGS_REQUEST,
|
||||
DELETE_ALL_CALLLOGS_SUCCESS,
|
||||
DELETE_ALL_CALLLOGS_FAILURE,
|
||||
FETCH_CALLLOG_STATUS_FAILED,
|
||||
FETCH_CALLLOG_STATUS_REQUEST,
|
||||
FETCH_CALLLOG_STATUS_SUCCESS,
|
||||
FETCH_CALLLOG_STATUS_RESET,
|
||||
CHANGE_CALLLOG_STATUS_FAILED,
|
||||
CHANGE_CALLLOG_STATUS_REQUEST,
|
||||
CHANGE_CALLLOG_STATUS_RESET,
|
||||
CHANGE_CALLLOG_STATUS_SUCCESS,
|
||||
} from '../constants/callLogs';
|
||||
|
||||
const INITIAL_STATE = {
|
||||
callLogs: {
|
||||
error: null,
|
||||
requesting: false,
|
||||
success: false,
|
||||
callLogs: [],
|
||||
count: null,
|
||||
limit: null,
|
||||
skip: null,
|
||||
deleteRequest: false,
|
||||
deleted: false,
|
||||
},
|
||||
searchCallLogs: {
|
||||
requesting: false,
|
||||
error: null,
|
||||
success: false,
|
||||
},
|
||||
callLogStatus: {
|
||||
error: null,
|
||||
requesting: false,
|
||||
success: false,
|
||||
data: null,
|
||||
},
|
||||
changeCallLogStatus: {
|
||||
error: null,
|
||||
requesting: false,
|
||||
success: false,
|
||||
},
|
||||
};
|
||||
|
||||
export default function project(state = INITIAL_STATE, action) {
|
||||
switch (action.type) {
|
||||
// Fetch callLogs list
|
||||
case FETCH_CALLLOGS_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
callLogs: {
|
||||
requesting: true,
|
||||
error: null,
|
||||
success: false,
|
||||
},
|
||||
});
|
||||
|
||||
case FETCH_CALLLOGS_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
callLogs: {
|
||||
requesting: false,
|
||||
error: null,
|
||||
success: true,
|
||||
callLogs: action.payload.data,
|
||||
count: action.payload.count,
|
||||
limit: action.payload.limit,
|
||||
skip: action.payload.skip,
|
||||
deleteRequest: false,
|
||||
deleted: false,
|
||||
},
|
||||
});
|
||||
|
||||
case FETCH_CALLLOGS_FAILURE:
|
||||
return Object.assign({}, state, {
|
||||
callLogs: {
|
||||
requesting: false,
|
||||
error: action.payload,
|
||||
success: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Search CallLog list.
|
||||
case SEARCH_CALLLOGS_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
searchCallLogs: {
|
||||
requesting: true,
|
||||
error: null,
|
||||
success: false,
|
||||
},
|
||||
});
|
||||
|
||||
case SEARCH_CALLLOGS_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
callLogs: {
|
||||
requesting: false,
|
||||
error: null,
|
||||
success: true,
|
||||
callLogs: action.payload.data,
|
||||
count: action.payload.count,
|
||||
limit: action.payload.limit,
|
||||
skip: action.payload.skip,
|
||||
deleteRequest: false,
|
||||
deleted: true,
|
||||
},
|
||||
searchCallLogs: {
|
||||
requesting: false,
|
||||
error: null,
|
||||
success: true,
|
||||
},
|
||||
});
|
||||
|
||||
case SEARCH_CALLLOGS_FAILURE:
|
||||
return Object.assign({}, state, {
|
||||
searchCallLogs: {
|
||||
requesting: false,
|
||||
error: action.payload,
|
||||
success: false,
|
||||
},
|
||||
});
|
||||
|
||||
//Delete all Call logs
|
||||
case DELETE_ALL_CALLLOGS_REQUEST:
|
||||
return {
|
||||
...state,
|
||||
callLogs: {
|
||||
...state.callLogs,
|
||||
error: null,
|
||||
success: false,
|
||||
deleteRequest: true,
|
||||
},
|
||||
};
|
||||
|
||||
case DELETE_ALL_CALLLOGS_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
callLogs: {
|
||||
requesting: false,
|
||||
error: null,
|
||||
success: true,
|
||||
deleteRequest: false,
|
||||
deleted: true,
|
||||
callLogs: [],
|
||||
count: null,
|
||||
limit: null,
|
||||
skip: null,
|
||||
},
|
||||
});
|
||||
|
||||
case DELETE_ALL_CALLLOGS_FAILURE:
|
||||
return {
|
||||
...state,
|
||||
callLogs: {
|
||||
...state.callLogs,
|
||||
error: action.payload,
|
||||
success: false,
|
||||
deleteRequest: false,
|
||||
},
|
||||
};
|
||||
case FETCH_CALLLOG_STATUS_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
callLogStatus: {
|
||||
error: null,
|
||||
requesting: true,
|
||||
success: false,
|
||||
data: null,
|
||||
},
|
||||
});
|
||||
|
||||
case FETCH_CALLLOG_STATUS_SUCCESS: {
|
||||
return Object.assign({}, state, {
|
||||
callLogStatus: {
|
||||
error: null,
|
||||
requesting: false,
|
||||
success: true,
|
||||
data: {
|
||||
...action.payload,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
case FETCH_CALLLOG_STATUS_FAILED:
|
||||
return Object.assign({}, state, {
|
||||
callLogStatus: {
|
||||
...state.callLogStatus,
|
||||
error: action.payload,
|
||||
requesting: false,
|
||||
success: false,
|
||||
},
|
||||
});
|
||||
|
||||
case FETCH_CALLLOG_STATUS_RESET:
|
||||
return Object.assign({}, state, {
|
||||
callLogStatus: {
|
||||
error: null,
|
||||
requesting: false,
|
||||
success: false,
|
||||
data: null,
|
||||
},
|
||||
});
|
||||
case CHANGE_CALLLOG_STATUS_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
changeCallLogStatus: {
|
||||
error: null,
|
||||
requesting: true,
|
||||
success: false,
|
||||
},
|
||||
});
|
||||
|
||||
case CHANGE_CALLLOG_STATUS_SUCCESS: {
|
||||
return Object.assign({}, state, {
|
||||
callLogStatus: {
|
||||
error: null,
|
||||
requesting: false,
|
||||
success: true,
|
||||
data: {
|
||||
...action.payload,
|
||||
},
|
||||
},
|
||||
changeCallLogStatus: {
|
||||
error: null,
|
||||
requesting: false,
|
||||
success: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
case CHANGE_CALLLOG_STATUS_FAILED:
|
||||
return Object.assign({}, state, {
|
||||
changeCallLogStatus: {
|
||||
error: action.payload,
|
||||
requesting: false,
|
||||
success: false,
|
||||
},
|
||||
});
|
||||
|
||||
case CHANGE_CALLLOG_STATUS_RESET:
|
||||
return Object.assign({}, state, {
|
||||
changeEmailLogStatus: {
|
||||
error: null,
|
||||
requesting: false,
|
||||
success: false,
|
||||
},
|
||||
});
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import project from './project';
|
||||
import probe from './probe';
|
||||
import auditLogs from './auditLogs';
|
||||
import emailLogs from './emailLogs';
|
||||
import callLogs from './callLogs';
|
||||
import smsLogs from './smsLogs';
|
||||
import settings from './settings';
|
||||
import license from './license';
|
||||
@@ -27,6 +28,7 @@ const appReducer = combineReducers({
|
||||
project,
|
||||
probe,
|
||||
auditLogs,
|
||||
callLogs,
|
||||
emailLogs,
|
||||
smsLogs,
|
||||
settings,
|
||||
|
||||
@@ -16,6 +16,7 @@ const {
|
||||
License,
|
||||
EmailLogs,
|
||||
SmsLogs,
|
||||
CallLogs,
|
||||
} = pages;
|
||||
|
||||
export const groups = [
|
||||
@@ -73,35 +74,63 @@ export const groups = [
|
||||
index: 3,
|
||||
shortcut: 'f+b',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
group: 'Logs',
|
||||
visible: true,
|
||||
routes: [
|
||||
{
|
||||
title: 'Audit Logs',
|
||||
path: '/admin/audit-logs',
|
||||
icon: 'appLog',
|
||||
component: AuditLogs,
|
||||
title: 'Logs',
|
||||
exact: true,
|
||||
visible: true,
|
||||
subRoutes: [],
|
||||
index: 4,
|
||||
shortcut: 'f+a',
|
||||
},
|
||||
{
|
||||
title: 'Email Logs',
|
||||
path: '/admin/email-logs',
|
||||
icon: 'emailIcon',
|
||||
component: EmailLogs,
|
||||
visible: true,
|
||||
subRoutes: [],
|
||||
index: 5,
|
||||
shortcut: 'f+e',
|
||||
},
|
||||
{
|
||||
title: 'SMS Logs',
|
||||
path: '/admin/sms-logs',
|
||||
icon: 'smsIcon',
|
||||
component: SmsLogs,
|
||||
visible: true,
|
||||
subRoutes: [],
|
||||
index: 6,
|
||||
shortcut: 'f+k',
|
||||
path: '/admin/audit-logs',
|
||||
icon: 'LogsIcon',
|
||||
component: AuditLogs,
|
||||
index: 1,
|
||||
subRoutes: [
|
||||
{
|
||||
title: 'Audit Logs',
|
||||
path: '/admin/audit-logs',
|
||||
icon: 'appLog',
|
||||
component: AuditLogs,
|
||||
visible: true,
|
||||
subRoutes: [],
|
||||
index: 4,
|
||||
shortcut: 'f+a',
|
||||
},
|
||||
{
|
||||
title: 'Call Logs',
|
||||
path: '/admin/call-logs',
|
||||
icon: 'emailIcon',
|
||||
component: CallLogs,
|
||||
visible: true,
|
||||
subRoutes: [],
|
||||
index: 5,
|
||||
shortcut: 'f+w',
|
||||
},
|
||||
{
|
||||
title: 'Email Logs',
|
||||
path: '/admin/email-logs',
|
||||
icon: 'emailIcon',
|
||||
component: EmailLogs,
|
||||
visible: true,
|
||||
subRoutes: [],
|
||||
index: 6,
|
||||
shortcut: 'f+e',
|
||||
},
|
||||
{
|
||||
title: 'SMS Logs',
|
||||
path: '/admin/sms-logs',
|
||||
icon: 'smsIcon',
|
||||
component: SmsLogs,
|
||||
visible: true,
|
||||
subRoutes: [],
|
||||
index: 7,
|
||||
shortcut: 'f+k',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -174,6 +203,16 @@ export const groups = [
|
||||
index: 5,
|
||||
shortcut: 'f+g',
|
||||
},
|
||||
{
|
||||
title: 'Call Log',
|
||||
path: '/admin/settings/call-logs',
|
||||
icon: 'emailIcon',
|
||||
component: Settings,
|
||||
visible: true,
|
||||
subRoutes: [],
|
||||
index: 6,
|
||||
shortcut: 'f+h',
|
||||
},
|
||||
{
|
||||
title: 'Email Log',
|
||||
path: '/admin/settings/email-logs',
|
||||
@@ -181,7 +220,7 @@ export const groups = [
|
||||
component: Settings,
|
||||
visible: true,
|
||||
subRoutes: [],
|
||||
index: 6,
|
||||
index: 7,
|
||||
shortcut: 'f+i',
|
||||
},
|
||||
{
|
||||
@@ -191,7 +230,7 @@ export const groups = [
|
||||
component: Settings,
|
||||
visible: true,
|
||||
subRoutes: [],
|
||||
index: 7,
|
||||
index: 8,
|
||||
shortcut: 'f+c',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -107,11 +107,18 @@ describe('About Modal (IS_SAAS_SERVICE=false)', () => {
|
||||
'#admin-dashboard-version',
|
||||
elem => elem.textContent
|
||||
);
|
||||
|
||||
const probeVersion = await page.$eval(
|
||||
'#probe-version',
|
||||
elem => elem.textContent
|
||||
);
|
||||
|
||||
expect(serverVersion).toBeDefined();
|
||||
expect(docsVersion).toBeDefined();
|
||||
expect(helmVersion).toBeDefined();
|
||||
expect(dashboardVersion).toBeDefined();
|
||||
expect(adminDashboardVersion).toBeDefined();
|
||||
expect(probeVersion).toBeDefined();
|
||||
});
|
||||
},
|
||||
operationTimeOut
|
||||
|
||||
@@ -32,6 +32,7 @@ router.post('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
alertVia: data.alertVia,
|
||||
userId: userId,
|
||||
incidentId: data.incidentId,
|
||||
eventType: data.eventType,
|
||||
});
|
||||
return sendItemResponse(req, res, alert);
|
||||
} catch (error) {
|
||||
|
||||
110
backend/backend/api/callLogs.js
Normal file
110
backend/backend/api/callLogs.js
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
*
|
||||
* Copyright HackerBay, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const CallLogsService = require('../services/callLogsService');
|
||||
const getUser = require('../middlewares/user').getUser;
|
||||
const isUserMasterAdmin = require('../middlewares/user').isUserMasterAdmin;
|
||||
|
||||
const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
const sendListResponse = require('../middlewares/response').sendListResponse;
|
||||
const { sendItemResponse } = require('../middlewares/response');
|
||||
|
||||
router.get('/', getUser, isUserMasterAdmin, async function(req, res) {
|
||||
try {
|
||||
const query = {};
|
||||
const skip = req.query.skip;
|
||||
const limit = req.query.limit;
|
||||
const callLogs = await CallLogsService.findBy(query, limit, skip);
|
||||
const count = await CallLogsService.countBy(query);
|
||||
return sendListResponse(req, res, callLogs, count);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/', getUser, isUserMasterAdmin, async (req, res) => {
|
||||
try {
|
||||
const data = req.body;
|
||||
|
||||
if (!data) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Values should not be null',
|
||||
});
|
||||
}
|
||||
if (!data.status || !data.status.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Call Log Status is required',
|
||||
});
|
||||
}
|
||||
if (!data.from || !data.from.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Call Log Sender name is required',
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.to || !data.to.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Call Log Recipient name is required',
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.projectId || !data.projectId.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Call Log ProjectId is required',
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.content || !data.content.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Call Log Content is required',
|
||||
});
|
||||
}
|
||||
const callLog = await CallLogsService.create(data);
|
||||
return sendItemResponse(req, res, callLog);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/search', getUser, isUserMasterAdmin, async function(req, res) {
|
||||
try {
|
||||
const filter = req.body.filter;
|
||||
const skip = req.query.skip;
|
||||
const limit = req.query.limit;
|
||||
|
||||
const {
|
||||
searchedCallLogs,
|
||||
totalSearchCount,
|
||||
} = await CallLogsService.search({ filter, skip, limit });
|
||||
|
||||
return sendListResponse(req, res, searchedCallLogs, totalSearchCount);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/', getUser, isUserMasterAdmin, async function(req, res) {
|
||||
try {
|
||||
const query = {};
|
||||
|
||||
const msg = await CallLogsService.hardDeleteBy({ query });
|
||||
|
||||
return sendItemResponse(req, res, msg);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -17,6 +17,7 @@ const LogService = require('../services/logService');
|
||||
const ApplicationSecurityLogService = require('../services/applicationSecurityLogService');
|
||||
const ContainerSecurityLogService = require('../services/containerSecurityLogService');
|
||||
const ErrorTrackerService = require('../services/errorTrackerService');
|
||||
const IssueService = require('../services/issueService');
|
||||
|
||||
const router = express.Router();
|
||||
const isUserAdmin = require('../middlewares/project').isUserAdmin;
|
||||
@@ -329,6 +330,36 @@ router.get(
|
||||
return newElement;
|
||||
})
|
||||
);
|
||||
|
||||
// fetch error trackers
|
||||
const errorTrackers = await ErrorTrackerService.getErrorTrackersByComponentId(
|
||||
componentId,
|
||||
limit,
|
||||
skip
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
errorTrackers.map(async errorTracker => {
|
||||
let errorStatus = 'No Errors yet';
|
||||
const issues = await IssueService.findBy(
|
||||
{ errorTrackerId: errorTracker._id },
|
||||
1,
|
||||
0
|
||||
);
|
||||
if (issues.length > 0) errorStatus = 'Listening for Errors';
|
||||
const newElement = {
|
||||
_id: errorTracker._id,
|
||||
name: errorTracker.name,
|
||||
type: 'error tracker',
|
||||
createdAt: errorTracker.createdAt,
|
||||
icon: 'errorTracking',
|
||||
status: errorStatus,
|
||||
};
|
||||
// add it to the total resources
|
||||
totalResources.push(newElement);
|
||||
return newElement;
|
||||
})
|
||||
);
|
||||
// return response
|
||||
return sendItemResponse(req, res, {
|
||||
totalResources,
|
||||
|
||||
@@ -285,13 +285,38 @@ router.post('/:errorTrackerId/track', isErrorTrackerValid, async function(
|
||||
// if it doesnt exist, create the issue and use its details
|
||||
if (!issue) {
|
||||
issue = await IssueService.create(data);
|
||||
} else {
|
||||
// issue exist but checked if it is resolved so to uresolve it
|
||||
if (issue.resolved) {
|
||||
const updateData = {
|
||||
resolved: false,
|
||||
resolvedAt: '',
|
||||
resolvedById: null,
|
||||
};
|
||||
const query = {
|
||||
_id: issue._id,
|
||||
errorTrackerId,
|
||||
};
|
||||
await IssueService.updateOneBy(query, updateData);
|
||||
}
|
||||
}
|
||||
// if it exist, use the issue details
|
||||
// since it now exist, use the issue details
|
||||
data.issueId = issue._id;
|
||||
data.fingerprintHash = issue.fingerprintHash;
|
||||
|
||||
// create the error event
|
||||
const errorEvent = await ErrorEventService.create(data);
|
||||
await RealTimeService.sendErrorEventCreated(errorEvent);
|
||||
|
||||
// get the issue in the format that the fronnted will want for the real time update
|
||||
const errorTrackerIssue = await ErrorEventService.findDistinct(
|
||||
{ _id: data.issueId, errorTrackerId: data.errorTrackerId },
|
||||
1,
|
||||
0
|
||||
);
|
||||
|
||||
issue = errorTrackerIssue.totalErrorEvents[0];
|
||||
|
||||
await RealTimeService.sendErrorEventCreated({ errorEvent, issue });
|
||||
return sendItemResponse(req, res, errorEvent);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
|
||||
@@ -53,9 +53,10 @@ router.post('/', getUser, isUserMasterAdmin, async function(req, res) {
|
||||
!value &&
|
||||
name !== 'auditLogMonitoringStatus' &&
|
||||
name !== 'emailLogMonitoringStatus' &&
|
||||
name !== 'smsLogMonitoringStatus'
|
||||
name !== 'smsLogMonitoringStatus' &&
|
||||
name !== 'callLogMonitoringStatus'
|
||||
) {
|
||||
// Audit or Email or SMS Log Status can be 'false'
|
||||
// Audit or Email or SMS or Call Log Status can be 'false'
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Value must be present.',
|
||||
@@ -157,6 +158,15 @@ router.get('/:name', getUser, isUserMasterAdmin, async function(req, res) {
|
||||
};
|
||||
await GlobalConfigService.create(smsLogConfig);
|
||||
}
|
||||
|
||||
// If Call logs status was fetched and it doesnt exist, we need to create it
|
||||
if (!globalConfig && req.params.name === 'callLogMonitoringStatus') {
|
||||
const callLogConfig = {
|
||||
name: 'callLogMonitoringStatus',
|
||||
value: true,
|
||||
};
|
||||
await GlobalConfigService.create(callLogConfig);
|
||||
}
|
||||
globalConfig = await GlobalConfigService.findOneBy({
|
||||
name: req.params.name,
|
||||
});
|
||||
|
||||
@@ -25,6 +25,7 @@ const getSubProjects = require('../middlewares/subProject').getSubProjects;
|
||||
const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
const sendListResponse = require('../middlewares/response').sendListResponse;
|
||||
const sendItemResponse = require('../middlewares/response').sendItemResponse;
|
||||
const subscriberAlertService = require('../services/subscriberAlertService');
|
||||
|
||||
// Route
|
||||
// Description: Creating incident.
|
||||
@@ -284,13 +285,49 @@ router.post(
|
||||
async function(req, res) {
|
||||
try {
|
||||
const userId = req.user ? req.user.id : null;
|
||||
const projectId = req.params.projectId;
|
||||
// Call the IncidentService
|
||||
const incident = await IncidentService.acknowledge(
|
||||
req.params.incidentId,
|
||||
userId,
|
||||
req.user.name
|
||||
);
|
||||
return sendItemResponse(req, res, incident);
|
||||
let incidentMessages = await IncidentMessageService.findBy({
|
||||
incidentId: incident._id,
|
||||
type: 'internal',
|
||||
});
|
||||
const timeline = await IncidentTimelineService.findBy({
|
||||
incidentId: incident._id,
|
||||
});
|
||||
const alerts = await AlertService.findBy({
|
||||
query: { incidentId: incident._id },
|
||||
});
|
||||
const subscriberAlerts = await subscriberAlertService.findBy({
|
||||
incidentId: incident._id,
|
||||
projectId,
|
||||
});
|
||||
const subAlerts = uniqByKeepFirst(
|
||||
subscriberAlerts,
|
||||
it => it.identification
|
||||
);
|
||||
incidentMessages = [
|
||||
...incidentMessages,
|
||||
...timeline,
|
||||
...alerts,
|
||||
...subAlerts,
|
||||
];
|
||||
incidentMessages.sort((a, b) => b.createdAt - a.createdAt);
|
||||
const filteredMsg = incidentMessages.filter(
|
||||
a =>
|
||||
a.status !== 'internal notes added' &&
|
||||
a.status !== 'internal notes updated'
|
||||
);
|
||||
const result = {
|
||||
data: filteredMsg,
|
||||
incident,
|
||||
type: 'internal',
|
||||
};
|
||||
return sendItemResponse(req, res, result);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
@@ -309,13 +346,49 @@ router.post(
|
||||
async function(req, res) {
|
||||
try {
|
||||
const userId = req.user ? req.user.id : null;
|
||||
const projectId = req.params.projectId;
|
||||
// Call the IncidentService
|
||||
const incident = await IncidentService.resolve(
|
||||
req.params.incidentId,
|
||||
userId
|
||||
);
|
||||
let incidentMessages = await IncidentMessageService.findBy({
|
||||
incidentId: incident._id,
|
||||
type: 'internal',
|
||||
});
|
||||
const timeline = await IncidentTimelineService.findBy({
|
||||
incidentId: incident._id,
|
||||
});
|
||||
const alerts = await AlertService.findBy({
|
||||
query: { incidentId: incident._id },
|
||||
});
|
||||
const subscriberAlerts = await subscriberAlertService.findBy({
|
||||
incidentId: incident._id,
|
||||
projectId,
|
||||
});
|
||||
const subAlerts = uniqByKeepFirst(
|
||||
subscriberAlerts,
|
||||
it => it.identification
|
||||
);
|
||||
incidentMessages = [
|
||||
...incidentMessages,
|
||||
...timeline,
|
||||
...alerts,
|
||||
...subAlerts,
|
||||
];
|
||||
incidentMessages.sort((a, b) => b.createdAt - a.createdAt);
|
||||
const filteredMsg = incidentMessages.filter(
|
||||
a =>
|
||||
a.status !== 'internal notes added' &&
|
||||
a.status !== 'internal notes updated'
|
||||
);
|
||||
const result = {
|
||||
data: filteredMsg,
|
||||
incident,
|
||||
type: 'internal',
|
||||
};
|
||||
|
||||
return sendItemResponse(req, res, incident);
|
||||
return sendItemResponse(req, res, result);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
@@ -530,10 +603,52 @@ router.post(
|
||||
status,
|
||||
});
|
||||
|
||||
incidentMessage = await IncidentMessageService.findOneBy({
|
||||
_id: incidentMessage._id,
|
||||
incidentId: incidentMessage.incidentId,
|
||||
const alerts = await AlertService.findBy({
|
||||
query: { incidentId: incident._id },
|
||||
});
|
||||
const subscriberAlerts = await subscriberAlertService.findBy({
|
||||
incidentId: incident._id,
|
||||
projectId: req.params.projectId,
|
||||
});
|
||||
|
||||
if (
|
||||
data.type === 'internal' ||
|
||||
(data.type === 'internal' &&
|
||||
data.incident_state === 'update')
|
||||
) {
|
||||
let incidentMessages = await IncidentMessageService.findBy({
|
||||
incidentId: incident._id,
|
||||
type: data.type,
|
||||
});
|
||||
const timeline = await IncidentTimelineService.findBy({
|
||||
incidentId: incident._id,
|
||||
});
|
||||
const subAlerts = uniqByKeepFirst(
|
||||
subscriberAlerts,
|
||||
it => it.identification
|
||||
);
|
||||
incidentMessages = [
|
||||
...incidentMessages,
|
||||
...timeline,
|
||||
...alerts,
|
||||
...subAlerts,
|
||||
];
|
||||
incidentMessages.sort((a, b) => b.createdAt - a.createdAt);
|
||||
const filteredMsg = incidentMessages.filter(
|
||||
a =>
|
||||
a.status !== 'internal notes added' &&
|
||||
a.status !== 'internal notes updated'
|
||||
);
|
||||
incidentMessage = {
|
||||
type: data.type,
|
||||
data: filteredMsg,
|
||||
};
|
||||
} else {
|
||||
incidentMessage = await IncidentMessageService.findOneBy({
|
||||
_id: incidentMessage._id,
|
||||
incidentId: incidentMessage.incidentId,
|
||||
});
|
||||
}
|
||||
}
|
||||
return sendItemResponse(req, res, incidentMessage);
|
||||
} catch (error) {
|
||||
@@ -571,7 +686,11 @@ router.delete(
|
||||
isAuthorized,
|
||||
async function(req, res) {
|
||||
try {
|
||||
const { incidentId, incidentMessageId } = req.params;
|
||||
const { incidentId, incidentMessageId, projectId } = req.params;
|
||||
const checkMsg = await IncidentMessageService.findOneBy({
|
||||
_id: incidentMessageId,
|
||||
});
|
||||
let result;
|
||||
const incidentMessage = await IncidentMessageService.deleteBy(
|
||||
{
|
||||
_id: incidentMessageId,
|
||||
@@ -587,9 +706,47 @@ router.delete(
|
||||
createdById: req.user.id,
|
||||
status,
|
||||
});
|
||||
const alerts = await AlertService.findBy({
|
||||
query: { incidentId: incidentId },
|
||||
});
|
||||
const subscriberAlerts = await subscriberAlertService.findBy({
|
||||
incidentId: incidentId,
|
||||
projectId,
|
||||
});
|
||||
|
||||
await RealTimeService.deleteIncidentNote(incidentMessage);
|
||||
return sendItemResponse(req, res, incidentMessage);
|
||||
if (checkMsg.type === 'investigation') {
|
||||
result = incidentMessage;
|
||||
} else {
|
||||
let incidentMessages = await IncidentMessageService.findBy({
|
||||
incidentId,
|
||||
type: checkMsg.type,
|
||||
});
|
||||
const timeline = await IncidentTimelineService.findBy({
|
||||
incidentId,
|
||||
});
|
||||
const subAlerts = uniqByKeepFirst(
|
||||
subscriberAlerts,
|
||||
it => it.identification
|
||||
);
|
||||
incidentMessages = [
|
||||
...incidentMessages,
|
||||
...timeline,
|
||||
...alerts,
|
||||
...subAlerts,
|
||||
];
|
||||
incidentMessages.sort((a, b) => b.createdAt - a.createdAt);
|
||||
const filteredMsg = incidentMessages.filter(
|
||||
a =>
|
||||
a.status !== 'internal notes added' &&
|
||||
a.status !== 'internal notes updated'
|
||||
);
|
||||
result = {
|
||||
type: checkMsg.type,
|
||||
data: filteredMsg,
|
||||
};
|
||||
}
|
||||
return sendItemResponse(req, res, result);
|
||||
} else {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 404,
|
||||
@@ -611,17 +768,57 @@ router.get(
|
||||
type = 'internal';
|
||||
}
|
||||
try {
|
||||
let incidentMessages, result;
|
||||
const incidentId = req.params.incidentId;
|
||||
const incidentMessages = await IncidentMessageService.findBy(
|
||||
{ incidentId, type },
|
||||
req.query.skip || 0,
|
||||
req.query.limit || 10
|
||||
);
|
||||
const projectId = req.params.projectId;
|
||||
if (type === 'investigation') {
|
||||
incidentMessages = await IncidentMessageService.findBy(
|
||||
{ incidentId, type },
|
||||
req.query.skip || 0,
|
||||
req.query.limit || 10
|
||||
);
|
||||
} else {
|
||||
incidentMessages = await IncidentMessageService.findBy({
|
||||
incidentId,
|
||||
type,
|
||||
});
|
||||
}
|
||||
const timeline = await IncidentTimelineService.findBy({
|
||||
incidentId,
|
||||
});
|
||||
const alerts = await AlertService.findBy({
|
||||
query: { incidentId: incidentId },
|
||||
});
|
||||
const subscriberAlerts = await subscriberAlertService.findBy({
|
||||
incidentId: incidentId,
|
||||
projectId,
|
||||
});
|
||||
const count = await IncidentMessageService.countBy({
|
||||
incidentId,
|
||||
type,
|
||||
});
|
||||
return sendListResponse(req, res, incidentMessages, count);
|
||||
if (type === 'investigation') {
|
||||
result = incidentMessages;
|
||||
} else {
|
||||
const subAlerts = uniqByKeepFirst(
|
||||
subscriberAlerts,
|
||||
it => it.identification
|
||||
);
|
||||
incidentMessages = [
|
||||
...incidentMessages,
|
||||
...timeline,
|
||||
...alerts,
|
||||
...subAlerts,
|
||||
];
|
||||
incidentMessages.sort((a, b) => b.createdAt - a.createdAt);
|
||||
const filteredMsg = incidentMessages.filter(
|
||||
a =>
|
||||
a.status !== 'internal notes added' &&
|
||||
a.status !== 'internal notes updated'
|
||||
);
|
||||
result = filteredMsg;
|
||||
}
|
||||
return sendListResponse(req, res, result, count);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
@@ -709,4 +906,12 @@ router.get(
|
||||
}
|
||||
);
|
||||
|
||||
function uniqByKeepFirst(a, key) {
|
||||
const seen = new Set();
|
||||
return a.filter(item => {
|
||||
const k = key(item);
|
||||
return seen.has(k) ? false : seen.add(k);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -143,11 +143,6 @@ router.delete(
|
||||
// process incoming http request from post request
|
||||
router.post('/:projectId/request/:requestId', async function(req, res) {
|
||||
try {
|
||||
// target value key on request body, request query or request headers
|
||||
// more may be added in the future
|
||||
const externalFilter =
|
||||
req.body.value || req.query.value || req.headers.value;
|
||||
|
||||
// request object for use in variables
|
||||
const request = {
|
||||
body: { ...req.body },
|
||||
@@ -156,7 +151,7 @@ router.post('/:projectId/request/:requestId', async function(req, res) {
|
||||
};
|
||||
|
||||
const { projectId, requestId } = req.params;
|
||||
const data = { projectId, requestId, filter: externalFilter, request };
|
||||
const data = { projectId, requestId, request };
|
||||
|
||||
const response = await IncomingRequestService.handleIncomingRequestAction(
|
||||
data
|
||||
@@ -170,20 +165,15 @@ router.post('/:projectId/request/:requestId', async function(req, res) {
|
||||
// process incoming http request from get request
|
||||
router.get('/:projectId/request/:requestId', async function(req, res) {
|
||||
try {
|
||||
// target value key on request body, request query or request headers
|
||||
// more may be added in the future
|
||||
// request body won't be available on get request
|
||||
const externalFilter = req.query.value || req.headers.value;
|
||||
|
||||
// request object for use in variables
|
||||
// request body won't be available for a get request
|
||||
const request = {
|
||||
body: { ...req.body },
|
||||
query: { ...req.query },
|
||||
headers: { ...req.headers },
|
||||
};
|
||||
|
||||
const { projectId, requestId } = req.params;
|
||||
const data = { projectId, requestId, filter: externalFilter, request };
|
||||
const data = { projectId, requestId, request };
|
||||
|
||||
const response = await IncomingRequestService.handleIncomingRequestAction(
|
||||
data
|
||||
|
||||
@@ -542,53 +542,68 @@ router.post(
|
||||
|
||||
const {
|
||||
stat: validUp,
|
||||
reasons: upFailedReasons,
|
||||
successReasons: upSuccessReasons,
|
||||
failedReasons: upFailedReasons,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.up
|
||||
? ProbeService.conditions(
|
||||
monitor.type,
|
||||
monitor.criteria.up,
|
||||
data
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, failedReasons: [], successReasons: [] });
|
||||
const {
|
||||
stat: validDegraded,
|
||||
reasons: degradedFailedReasons,
|
||||
successReasons: degradedSuccessReasons,
|
||||
failedReasons: degradedFailedReasons,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.degraded
|
||||
? ProbeService.conditions(
|
||||
monitor.type,
|
||||
monitor.criteria.degraded,
|
||||
data
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, failedReasons: [], successReasons: [] });
|
||||
const {
|
||||
stat: validDown,
|
||||
reasons: downFailedReasons,
|
||||
successReasons: downSuccessReasons,
|
||||
failedReasons: downFailedReasons,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.down
|
||||
? ProbeService.conditions(
|
||||
monitor.type,
|
||||
monitor.criteria.down,
|
||||
data
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, failedReasons: [], successReasons: [] });
|
||||
|
||||
if (validDown) {
|
||||
data.status = 'offline';
|
||||
data.reason = downFailedReasons;
|
||||
if (validUp) {
|
||||
data.status = 'online';
|
||||
data.reason = upSuccessReasons;
|
||||
} else if (validDegraded) {
|
||||
data.status = 'degraded';
|
||||
data.reason = degradedFailedReasons;
|
||||
} else if (validUp) {
|
||||
data.status = 'online';
|
||||
data.reason = upFailedReasons;
|
||||
data.reason = [...degradedSuccessReasons, ...upFailedReasons];
|
||||
} else if (validDown) {
|
||||
data.status = 'offline';
|
||||
data.reason = [
|
||||
...downSuccessReasons,
|
||||
...degradedFailedReasons,
|
||||
...upFailedReasons,
|
||||
];
|
||||
} else {
|
||||
data.status = 'offline';
|
||||
data.reason = [
|
||||
...degradedFailedReasons,
|
||||
...downFailedReasons,
|
||||
...degradedFailedReasons,
|
||||
...upFailedReasons,
|
||||
];
|
||||
}
|
||||
|
||||
const index = data.reason.indexOf('Request Timed out');
|
||||
if (index > -1) {
|
||||
data.reason = data.reason.filter(
|
||||
item => !item.includes('Response Time is')
|
||||
);
|
||||
}
|
||||
data.reason = data.reason.filter(
|
||||
(item, pos, self) => self.indexOf(item) === pos
|
||||
);
|
||||
const log = await ProbeService.saveMonitorLog(data);
|
||||
|
||||
return sendItemResponse(req, res, log);
|
||||
|
||||
@@ -8,6 +8,7 @@ const express = require('express');
|
||||
const ProbeService = require('../services/probeService');
|
||||
const MonitorService = require('../services/monitorService');
|
||||
const ProjectService = require('../services/projectService');
|
||||
const LighthouseLogService = require('../services/lighthouseLogService');
|
||||
const ApplicationSecurityService = require('../services/applicationSecurityService');
|
||||
const ContainerSecurityService = require('../services/containerSecurityService');
|
||||
const router = express.Router();
|
||||
@@ -116,7 +117,6 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
log,
|
||||
reason,
|
||||
data = {};
|
||||
|
||||
if (type === 'incomingHttpRequest') {
|
||||
const newMonitor = await MonitorService.findOneBy({
|
||||
_id: monitor._id,
|
||||
@@ -127,7 +127,8 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
if (type === 'api' || type === 'url') {
|
||||
const {
|
||||
stat: validUp,
|
||||
reasons: upFailedReasons,
|
||||
successReasons: upSuccessReasons,
|
||||
failedReasons: upFailedReasons,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.up
|
||||
? ProbeService.conditions(
|
||||
monitor.type,
|
||||
@@ -136,10 +137,11 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
resp,
|
||||
rawResp
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, successReasons: [], failedReasons: [] });
|
||||
const {
|
||||
stat: validDegraded,
|
||||
reasons: degradedFailedReasons,
|
||||
successReasons: degradedSuccessReasons,
|
||||
failedReasons: degradedFailedReasons,
|
||||
} = await (monitor &&
|
||||
monitor.criteria &&
|
||||
monitor.criteria.degraded
|
||||
@@ -150,10 +152,11 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
resp,
|
||||
rawResp
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, successReasons: [], failedReasons: [] });
|
||||
const {
|
||||
stat: validDown,
|
||||
reasons: downFailedReasons,
|
||||
successReasons: downSuccessReasons,
|
||||
failedReasons: downFailedReasons,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.down
|
||||
? ProbeService.conditions(
|
||||
monitor.type,
|
||||
@@ -162,33 +165,36 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
resp,
|
||||
rawResp
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, successReasons: [], failedReasons: [] });
|
||||
|
||||
if (validDown) {
|
||||
status = 'offline';
|
||||
reason = downFailedReasons;
|
||||
if (validUp) {
|
||||
status = 'online';
|
||||
reason = upSuccessReasons;
|
||||
} else if (validDegraded) {
|
||||
status = 'degraded';
|
||||
reason = degradedFailedReasons;
|
||||
} else if (validUp) {
|
||||
status = 'online';
|
||||
reason = upFailedReasons;
|
||||
reason = [...degradedSuccessReasons, ...upFailedReasons];
|
||||
} else if (validDown) {
|
||||
status = 'offline';
|
||||
reason = [
|
||||
...downSuccessReasons,
|
||||
...degradedFailedReasons,
|
||||
...upFailedReasons,
|
||||
];
|
||||
} else {
|
||||
status = 'offline';
|
||||
reason = [
|
||||
...degradedFailedReasons,
|
||||
...downFailedReasons,
|
||||
...degradedFailedReasons,
|
||||
...upFailedReasons,
|
||||
];
|
||||
}
|
||||
|
||||
data.status = status;
|
||||
data.reason = reason;
|
||||
}
|
||||
if (type === 'script') {
|
||||
const {
|
||||
stat: validUp,
|
||||
reasons: upFailedReasons,
|
||||
reasons: upSuccessReasons,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.up
|
||||
? ProbeService.scriptConditions(
|
||||
res,
|
||||
@@ -198,7 +204,7 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
: { stat: false, reasons: [] });
|
||||
const {
|
||||
stat: validDegraded,
|
||||
reasons: degradedFailedReasons,
|
||||
reasons: degradedSuccessReasons,
|
||||
} = await (monitor &&
|
||||
monitor.criteria &&
|
||||
monitor.criteria.degraded
|
||||
@@ -210,7 +216,7 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
: { stat: false, reasons: [] });
|
||||
const {
|
||||
stat: validDown,
|
||||
reasons: downFailedReasons,
|
||||
reasons: downSuccessReasons,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.down
|
||||
? ProbeService.scriptConditions(
|
||||
res,
|
||||
@@ -221,19 +227,18 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
|
||||
if (validDown) {
|
||||
status = 'failed';
|
||||
reason = upFailedReasons;
|
||||
reason = upSuccessReasons;
|
||||
} else if (validDegraded) {
|
||||
status = 'degraded';
|
||||
reason = upFailedReasons;
|
||||
reason = upSuccessReasons;
|
||||
} else if (validUp) {
|
||||
status = 'success';
|
||||
reason = [...degradedFailedReasons, ...downFailedReasons];
|
||||
reason = [...degradedSuccessReasons, ...downSuccessReasons];
|
||||
} else {
|
||||
status = 'failed';
|
||||
reason = upFailedReasons;
|
||||
reason = upSuccessReasons;
|
||||
}
|
||||
resp.status = null;
|
||||
|
||||
data.status = status;
|
||||
data.reason = reason;
|
||||
}
|
||||
@@ -246,20 +251,21 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
}
|
||||
if (type === 'server-monitor') {
|
||||
data = serverData;
|
||||
|
||||
const {
|
||||
stat: validUp,
|
||||
reasons: upFailedReasons,
|
||||
successReasons: upSuccessReasons,
|
||||
failedReasons: upFailedReasons,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.up
|
||||
? ProbeService.conditions(
|
||||
monitor.type,
|
||||
monitor.criteria.up,
|
||||
data
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, successReasons: [], failedReasons: [] });
|
||||
const {
|
||||
stat: validDegraded,
|
||||
reasons: degradedFailedReasons,
|
||||
successReasons: degradedSuccessReasons,
|
||||
failedReasons: degradedFailedReasons,
|
||||
} = await (monitor &&
|
||||
monitor.criteria &&
|
||||
monitor.criteria.degraded
|
||||
@@ -268,32 +274,40 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
monitor.criteria.degraded,
|
||||
data
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, successReasons: [], failedReasons: [] });
|
||||
const {
|
||||
stat: validDown,
|
||||
reasons: downFailedReasons,
|
||||
successReasons: downSuccessReasons,
|
||||
failedReasons: downFailedReasons,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.down
|
||||
? ProbeService.conditions(
|
||||
monitor.type,
|
||||
monitor.criteria.down,
|
||||
data
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, successReasons: [], failedReasons: [] });
|
||||
|
||||
if (validDown) {
|
||||
data.status = 'offline';
|
||||
data.reason = downFailedReasons;
|
||||
if (validUp) {
|
||||
data.status = 'online';
|
||||
data.reason = upSuccessReasons;
|
||||
} else if (validDegraded) {
|
||||
data.status = 'degraded';
|
||||
data.reason = degradedFailedReasons;
|
||||
} else if (validUp) {
|
||||
data.status = 'online';
|
||||
data.reason = upFailedReasons;
|
||||
data.reason = [
|
||||
...degradedSuccessReasons,
|
||||
...upFailedReasons,
|
||||
];
|
||||
} else if (validDown) {
|
||||
data.status = 'offline';
|
||||
data.reason = [
|
||||
...downSuccessReasons,
|
||||
...degradedFailedReasons,
|
||||
...upFailedReasons,
|
||||
];
|
||||
} else {
|
||||
data.status = 'offline';
|
||||
data.reason = [
|
||||
...degradedFailedReasons,
|
||||
...downFailedReasons,
|
||||
...degradedFailedReasons,
|
||||
...upFailedReasons,
|
||||
];
|
||||
}
|
||||
@@ -325,14 +339,35 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
|
||||
data.monitorId = req.params.monitorId || monitor._id;
|
||||
data.probeId = req.probe && req.probe.id ? req.probe.id : null;
|
||||
|
||||
data.reason =
|
||||
data && data.reason && data.reason.length
|
||||
? data.reason.filter(
|
||||
(item, pos, self) => self.indexOf(item) === pos
|
||||
)
|
||||
: data.reason;
|
||||
const index =
|
||||
data.reason && data.reason.indexOf('Request Timed out');
|
||||
if (index > -1) {
|
||||
data.reason =
|
||||
data && data.reason && data.reason.length
|
||||
? data.reason.filter(
|
||||
item => !item.includes('Response Time is')
|
||||
)
|
||||
: data.reason;
|
||||
}
|
||||
if (data.lighthouseScanStatus) {
|
||||
if (data.lighthouseScanStatus === 'scanning') {
|
||||
await MonitorService.updateOneBy(
|
||||
{ _id: data.monitorId },
|
||||
{
|
||||
lighthouseScanStatus: data.lighthouseScanStatus,
|
||||
}
|
||||
},
|
||||
{ fetchLightHouse: true }
|
||||
);
|
||||
await LighthouseLogService.updateAllLighthouseLogs(
|
||||
data.monitor.projectId,
|
||||
data.monitorId,
|
||||
{ scanning: true }
|
||||
);
|
||||
} else {
|
||||
await MonitorService.updateOneBy(
|
||||
@@ -346,6 +381,7 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
}
|
||||
} else {
|
||||
if (data.lighthouseData) {
|
||||
data.scanning = false;
|
||||
log = await ProbeService.saveLighthouseLog(data);
|
||||
} else {
|
||||
log = await ProbeService.saveMonitorLog(data);
|
||||
|
||||
@@ -20,8 +20,7 @@ router.get('/', getUser, isUserMasterAdmin, async function(req, res) {
|
||||
const query = {};
|
||||
const skip = req.query.skip;
|
||||
const limit = req.query.limit;
|
||||
|
||||
const smsLogs = await SmsLogsService.findBy(query, skip, limit);
|
||||
const smsLogs = await SmsLogsService.findBy(query, limit, skip);
|
||||
const count = await SmsLogsService.countBy(query);
|
||||
return sendListResponse(req, res, smsLogs, count);
|
||||
} catch (error) {
|
||||
@@ -29,6 +28,56 @@ router.get('/', getUser, isUserMasterAdmin, async function(req, res) {
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/', getUser, isUserMasterAdmin, async (req, res) => {
|
||||
try {
|
||||
const data = req.body;
|
||||
|
||||
if (!data) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Values should not be null',
|
||||
});
|
||||
}
|
||||
if (!data.status || !data.status.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'SMS Log Status is required',
|
||||
});
|
||||
}
|
||||
if (!data.userId || !data.userId.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'SMS Log UserId is required',
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.sentTo || !data.sendTo.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Email Log Recipient name is required',
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.projectId || !data.projectId.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'SMS Log ProjectId is required',
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.content || !data.content.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'SMS Log Content is required',
|
||||
});
|
||||
}
|
||||
const smsLog = await SmsLogsService.create(data);
|
||||
return sendItemResponse(req, res, smsLog);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/search', getUser, isUserMasterAdmin, async function(req, res) {
|
||||
try {
|
||||
const filter = req.body.filter;
|
||||
|
||||
@@ -527,6 +527,7 @@ router.post('/totp/verifyToken', async function(req, res) {
|
||||
},
|
||||
tempEmail: user.tempEmail || null,
|
||||
tempAlertPhoneNumber: user.tempAlertPhoneNumber || null,
|
||||
role: user.role || null,
|
||||
};
|
||||
return sendItemResponse(req, res, userObj);
|
||||
} catch (error) {
|
||||
@@ -607,6 +608,7 @@ router.post('/verify/backupCode', async function(req, res) {
|
||||
},
|
||||
tempEmail: user.tempEmail || null,
|
||||
tempAlertPhoneNumber: user.tempAlertPhoneNumber || null,
|
||||
role: user.role || null,
|
||||
};
|
||||
return sendItemResponse(req, res, userObj);
|
||||
} catch (error) {
|
||||
@@ -1066,6 +1068,7 @@ router.get('/profile', getUser, async function(req, res) {
|
||||
},
|
||||
tempEmail: user.tempEmail || null,
|
||||
tempAlertPhoneNumber: user.tempAlertPhoneNumber || null,
|
||||
role: user.role || null,
|
||||
};
|
||||
return sendItemResponse(req, res, userObj);
|
||||
} catch (error) {
|
||||
|
||||
@@ -11,6 +11,8 @@ module.exports = [
|
||||
'{{projectId}} : Unique identifier for the current project.',
|
||||
"{{trackEmailAsViewedUrl}} : Include this in your email to track emails and know when they're opened.",
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
emailType: 'Subscriber Incident Created',
|
||||
subject:
|
||||
@@ -553,6 +555,8 @@ span.st-Delink.st-Delink--footer a {
|
||||
'{{projectId}} : Unique identifier for the current project.',
|
||||
"{{trackEmailAsViewedUrl}} : Include this in your email to track emails and know when they're opened.",
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
emailType: 'Subscriber Incident Acknowldeged',
|
||||
subject:
|
||||
@@ -1095,6 +1099,8 @@ body[override] table.st-Button td.st-Button-area span.st-Button-internal{
|
||||
"{{trackEmailAsViewedUrl}} : Include this in your email to track emails and know when they're opened.",
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incidentNoteStatus}} : Status of the incident note',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
emailType: 'Investigation note is created',
|
||||
subject: 'An update on an active incident for {{monitorName}}',
|
||||
@@ -1653,6 +1659,8 @@ body[override] table.st-Button td.st-Button-area span.st-Button-internal{
|
||||
'{{projectId}} : Unique identifier for the current project.',
|
||||
"{{trackEmailAsViewedUrl}} : Include this in your email to track emails and know when they're opened.",
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
emailType: 'Subscriber Incident Resolved',
|
||||
subject:
|
||||
|
||||
@@ -9,6 +9,8 @@ module.exports = {
|
||||
'{{projectId}} : Unique identifier for the current project.',
|
||||
"{{trackEmailAsViewedUrl}} : Include this in your email to track emails and know when they're opened.",
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
'Subscriber Incident Acknowldeged': [
|
||||
'{{userName}} : User display name.',
|
||||
@@ -20,6 +22,8 @@ module.exports = {
|
||||
'{{projectId}} : Unique identifier for the current project.',
|
||||
"{{trackEmailAsViewedUrl}} : Include this in your email to track emails and know when they're opened.",
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
'Subscriber Incident Resolved': [
|
||||
'{{userName}} : User display name.',
|
||||
@@ -31,6 +35,8 @@ module.exports = {
|
||||
'{{projectId}} : Unique identifier for the current project.',
|
||||
"{{trackEmailAsViewedUrl}} : Include this in your email to track emails and know when they're opened.",
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
'Team Member Incident': [
|
||||
'{{ incidentTime }} : Time at which this incident occured.',
|
||||
|
||||
@@ -7,6 +7,8 @@ module.exports = [
|
||||
'{{incidentType}} : Type of incident. Online, offline or degraded.',
|
||||
'{{componentName}} : Name of the component the monitor belongs to',
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
smsType: 'Subscriber Incident Created',
|
||||
body:
|
||||
@@ -20,6 +22,8 @@ module.exports = [
|
||||
'{{incidentType}} : Type of incident. Online, offline or degraded.',
|
||||
'{{componentName}} : Name of the component the monitor belongs to',
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
smsType: 'Subscriber Incident Acknowldeged',
|
||||
body:
|
||||
@@ -33,6 +37,8 @@ module.exports = [
|
||||
'{{incidentType}} : Type of incident. Online, offline or degraded.',
|
||||
'{{componentName}} : Name of the component the monitor belongs to',
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
smsType: 'Subscriber Incident Resolved',
|
||||
body:
|
||||
@@ -46,6 +52,8 @@ module.exports = [
|
||||
'{{incidentType}} : Type of incident. Online, offline or degraded.',
|
||||
'{{componentName}} : Name of the component the monitor belongs to',
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
smsType: 'Investigation note is created',
|
||||
body:
|
||||
|
||||
@@ -6,6 +6,8 @@ module.exports = {
|
||||
'{{incidentType}} : Type of incident.',
|
||||
'{{componentName}} : Name of the component the monitor belongs to',
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
'Subscriber Incident Acknowldeged': [
|
||||
'{{incidentTime}} : Time at which this incident occured.',
|
||||
@@ -14,6 +16,8 @@ module.exports = {
|
||||
'{{incidentType}} : Type of incident.',
|
||||
'{{componentName}} : Name of the component the monitor belongs to',
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
'Subscriber Incident Resolved': [
|
||||
'{{incidentTime}} : Time at which this incident occured.',
|
||||
@@ -22,5 +26,7 @@ module.exports = {
|
||||
'{{incidentType}} : Type of incident.',
|
||||
'{{componentName}} : Name of the component the monitor belongs to',
|
||||
'{{statusPageUrl}} : URL of the Status Page your subscriber can go to. ',
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
};
|
||||
|
||||
@@ -131,6 +131,16 @@ module.exports = {
|
||||
return statusPageId;
|
||||
},
|
||||
|
||||
getStatusPageUrl: function(req) {
|
||||
const statusPageUrl =
|
||||
req.params.url ||
|
||||
req.query.url ||
|
||||
req.headers['url'] ||
|
||||
req.body.url;
|
||||
|
||||
return statusPageUrl;
|
||||
},
|
||||
|
||||
isValidMonitor: async function(req, res, next) {
|
||||
const id = req.params.id;
|
||||
let monitor = await MonitorService.findBy({
|
||||
|
||||
@@ -6,13 +6,22 @@ const ipaddr = require('ipaddr.js');
|
||||
const _this = {
|
||||
ipWhitelist: async function(req, res, next) {
|
||||
const statusPageId = apiMiddleware.getStatusPageId(req);
|
||||
const statusPage = await StatusPageService.findOneBy({
|
||||
_id: statusPageId,
|
||||
});
|
||||
const statusPageUrl = apiMiddleware.getStatusPageUrl(req);
|
||||
let statusPage;
|
||||
|
||||
if (statusPageId && statusPageId.length && statusPageId !== 'null') {
|
||||
statusPage = await StatusPageService.findOneBy({
|
||||
_id: statusPageId,
|
||||
});
|
||||
} else {
|
||||
statusPage = await StatusPageService.findOneBy({
|
||||
domains: { $elemMatch: { domain: statusPageUrl } },
|
||||
});
|
||||
}
|
||||
|
||||
if (!statusPage.enableIpWhitelist) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const ipWhitelist = statusPage.ipWhitelist
|
||||
? [...statusPage.ipWhitelist]
|
||||
: [];
|
||||
|
||||
@@ -10,7 +10,7 @@ const CLUSTER_KEY = process.env.CLUSTER_KEY;
|
||||
module.exports = {
|
||||
isAuthorizedProbe: async function(req, res, next) {
|
||||
try {
|
||||
let probeKey, probeName, clusterKey;
|
||||
let probeKey, probeName, clusterKey, probeVersion;
|
||||
|
||||
if (req.params.probeKey) {
|
||||
probeKey = req.params.probeKey;
|
||||
@@ -58,6 +58,18 @@ module.exports = {
|
||||
clusterKey = req.body.clusterKey;
|
||||
}
|
||||
|
||||
if (req.params.probeVersion) {
|
||||
probeVersion = req.params.probeVersion;
|
||||
} else if (req.query.probeVersion) {
|
||||
probeVersion = req.query.probeVersion;
|
||||
} else if (req.headers['probeversion']) {
|
||||
probeVersion = req.headers['probeversion'];
|
||||
} else if (req.headers['probeversion']) {
|
||||
probeVersion = req.headers['probeversion'];
|
||||
} else if (req.body.probeVersion) {
|
||||
probeVersion = req.body.probeVersion;
|
||||
}
|
||||
|
||||
let probe = null;
|
||||
|
||||
if (clusterKey && clusterKey === CLUSTER_KEY) {
|
||||
@@ -81,6 +93,7 @@ module.exports = {
|
||||
probe = await ProbeService.create({
|
||||
probeKey,
|
||||
probeName,
|
||||
probeVersion,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -93,10 +106,25 @@ module.exports = {
|
||||
{ probeKey }
|
||||
);
|
||||
}
|
||||
|
||||
req.probe = {};
|
||||
req.probe.id = probe._id;
|
||||
await ProbeService.updateProbeStatus(probe._id);
|
||||
|
||||
//Update probe version
|
||||
const probeValue = await ProbeService.findOneBy({
|
||||
probeKey,
|
||||
probeName,
|
||||
});
|
||||
|
||||
if (!probeValue.version || probeValue.version !== probeVersion) {
|
||||
await ProbeService.updateOneBy(
|
||||
{
|
||||
probeName,
|
||||
},
|
||||
{ version: probeVersion }
|
||||
);
|
||||
}
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
ErrorService.log('probeAuthorization.isAuthorizedProbe', error);
|
||||
|
||||
19
backend/backend/models/callLogs.js
Normal file
19
backend/backend/models/callLogs.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const mongoose = require('../config/db');
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
const callLogsSchema = new Schema({
|
||||
from: String,
|
||||
to: String,
|
||||
projectId: { type: String, ref: 'Project' },
|
||||
createdAt: { type: Date, default: Date.now },
|
||||
deleted: { type: Boolean, default: false },
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
},
|
||||
|
||||
deletedById: { type: String, ref: 'User' },
|
||||
content: String,
|
||||
status: String,
|
||||
error: String,
|
||||
});
|
||||
module.exports = mongoose.model('callLogs', callLogsSchema);
|
||||
@@ -16,8 +16,8 @@ const schema = new Schema({
|
||||
template: String,
|
||||
status: String,
|
||||
content: String,
|
||||
error: String,
|
||||
deleted: { type: Boolean, default: false },
|
||||
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
},
|
||||
|
||||
@@ -22,19 +22,6 @@ const incomingRequestSchema = new Schema(
|
||||
url: String,
|
||||
deleted: { type: Boolean, default: false },
|
||||
deletedAt: Date,
|
||||
filterCriteria: String,
|
||||
filterCondition: {
|
||||
type: String,
|
||||
enum: [
|
||||
'equalTo',
|
||||
'notEqualTo',
|
||||
'lessThan',
|
||||
'greaterThan',
|
||||
'greaterThanOrEqualTo',
|
||||
'lessThanOrEqualTo',
|
||||
],
|
||||
},
|
||||
filterText: Schema.Types.Mixed, // expected to store both string and number
|
||||
incidentTitle: String,
|
||||
incidentType: { type: String },
|
||||
incidentPriority: {
|
||||
@@ -43,6 +30,24 @@ const incomingRequestSchema = new Schema(
|
||||
},
|
||||
incidentDescription: String,
|
||||
customFields: [{ fieldName: String, fieldValue: Schema.Types.Mixed }],
|
||||
filterMatch: String,
|
||||
filters: [
|
||||
{
|
||||
filterCriteria: String,
|
||||
filterCondition: {
|
||||
type: String,
|
||||
enum: [
|
||||
'equalTo',
|
||||
'notEqualTo',
|
||||
'lessThan',
|
||||
'greaterThan',
|
||||
'greaterThanOrEqualTo',
|
||||
'lessThanOrEqualTo',
|
||||
],
|
||||
},
|
||||
filterText: Schema.Types.Mixed,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
@@ -15,5 +15,6 @@ const lighthouseLogSchema = new Schema({
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
scanning: Boolean,
|
||||
});
|
||||
module.exports = mongoose.model('LighthouseLog', lighthouseLogSchema);
|
||||
|
||||
@@ -7,6 +7,8 @@ const monitorLogSchema = new Schema({
|
||||
status: String, // status based on criteria.
|
||||
responseTime: Number, // time taken for ping.
|
||||
responseStatus: Number, // status code of ping.
|
||||
responseBody: String, //response body of ping
|
||||
responseHeader: Object, //response header(s) of ping
|
||||
cpuLoad: Number, // cpu load.
|
||||
avgCpuLoad: Number, // average cpu load from server.
|
||||
cpuCores: Number, // number of cpu cores.
|
||||
|
||||
@@ -11,6 +11,7 @@ const probeSchema = new Schema({
|
||||
createdAt: { type: Date, default: Date.now },
|
||||
probeKey: { type: String },
|
||||
probeName: { type: String },
|
||||
version: { type: String },
|
||||
lastAlive: { type: Date, default: Date.now },
|
||||
deleted: { type: Boolean, default: false },
|
||||
deletedAt: { type: Date },
|
||||
|
||||
@@ -6,11 +6,15 @@ const smsCountSchema = new Schema({
|
||||
sentTo: String,
|
||||
createdAt: { type: Date, default: Date.now },
|
||||
projectId: { type: String, ref: 'Project' },
|
||||
parentProjectId: { type: String, ref: 'Project' },
|
||||
deleted: { type: Boolean, default: false },
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
},
|
||||
|
||||
deletedById: { type: String, ref: 'User' },
|
||||
content: String,
|
||||
status: String,
|
||||
error: String,
|
||||
});
|
||||
module.exports = mongoose.model('SmsCount', smsCountSchema);
|
||||
|
||||
@@ -32,5 +32,7 @@ const subscriberAlertSchema = new Schema({
|
||||
},
|
||||
|
||||
deletedById: { type: String, ref: 'User' },
|
||||
totalSubscribers: { type: Number },
|
||||
identification: { type: Number },
|
||||
});
|
||||
module.exports = mongoose.model('SubscriberAlert', subscriberAlertSchema);
|
||||
|
||||
@@ -596,6 +596,16 @@ module.exports = {
|
||||
!areEmailAlertsEnabledInGlobalSettings &&
|
||||
!hasCustomSmtpSettings
|
||||
) {
|
||||
let errorMessageText;
|
||||
if (!hasGlobalSmtpSettings && !hasCustomSmtpSettings) {
|
||||
errorMessageText =
|
||||
'SMTP Settings not found on Admin Dashboard';
|
||||
} else if (
|
||||
hasGlobalSmtpSettings &&
|
||||
!areEmailAlertsEnabledInGlobalSettings
|
||||
) {
|
||||
errorMessageText = 'Alert Disabled on Admin Dashboard';
|
||||
}
|
||||
return await _this.create({
|
||||
projectId: incident.projectId,
|
||||
monitorId,
|
||||
@@ -608,13 +618,7 @@ module.exports = {
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
eventType,
|
||||
errorMessage:
|
||||
!hasGlobalSmtpSettings && !hasCustomSmtpSettings
|
||||
? 'SMTP Settings not found on Admin Dashboard'
|
||||
: hasGlobalSmtpSettings &&
|
||||
!areEmailAlertsEnabledInGlobalSettings
|
||||
? 'Alert Disabled on Admin Dashboard'
|
||||
: 'Error.',
|
||||
errorMessage: errorMessageText,
|
||||
});
|
||||
}
|
||||
const incidentcreatedBy =
|
||||
@@ -674,6 +678,8 @@ module.exports = {
|
||||
incidentId: incident._id,
|
||||
eventType,
|
||||
alertStatus: 'Cannot Send',
|
||||
error: true,
|
||||
errorMessage: e.message,
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -818,6 +824,15 @@ module.exports = {
|
||||
(!project.alertEnable || !areAlertsEnabledGlobally)) ||
|
||||
(!IS_SAAS_SERVICE && !areAlertsEnabledGlobally))
|
||||
) {
|
||||
let errorMessageText;
|
||||
if (!hasGlobalTwilioSettings) {
|
||||
errorMessageText =
|
||||
'Twilio Settings not found on Admin Dashboard';
|
||||
} else if (!areAlertsEnabledGlobally) {
|
||||
errorMessageText = 'Alert Disabled on Admin Dashboard';
|
||||
} else if (IS_SAAS_SERVICE && !project.alertEnable) {
|
||||
errorMessageText = 'Alert Disabled for this project';
|
||||
}
|
||||
return await _this.create({
|
||||
projectId: incident.projectId,
|
||||
schedule: schedule._id,
|
||||
@@ -830,13 +845,7 @@ module.exports = {
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
eventType,
|
||||
errorMessage: !hasGlobalTwilioSettings
|
||||
? 'Twilio Settings not found on Admin Dashboard'
|
||||
: !areAlertsEnabledGlobally
|
||||
? 'Alert Disabled on Admin Dashboard'
|
||||
: IS_SAAS_SERVICE && !project.alertEnable
|
||||
? 'Alert Disabled for this project'
|
||||
: 'Error',
|
||||
errorMessage: errorMessageText,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -847,6 +856,17 @@ module.exports = {
|
||||
);
|
||||
if (!doesPhoneNumberComplyWithHighRiskConfig) {
|
||||
const countryType = getCountryType(user.alertPhoneNumber);
|
||||
let errorMessageText;
|
||||
if (countryType === 'us') {
|
||||
errorMessageText =
|
||||
'Calls for numbers inside US not enabled for this project';
|
||||
} else if (countryType === 'non-us') {
|
||||
errorMessageText =
|
||||
'Calls for numbers outside US not enabled for this project';
|
||||
} else {
|
||||
errorMessageText =
|
||||
'Calls to High Risk country not enabled for this project';
|
||||
}
|
||||
return await _this.create({
|
||||
projectId: incident.projectId,
|
||||
monitorId,
|
||||
@@ -859,12 +879,7 @@ module.exports = {
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
eventType,
|
||||
errorMessage:
|
||||
countryType === 'us'
|
||||
? 'Calls for numbers inside US not enabled for this project'
|
||||
: countryType === 'non-us'
|
||||
? 'Calls for numbers outside US not enabled for this project'
|
||||
: 'Calls to High Risk country not enabled for this project',
|
||||
errorMessage: errorMessageText,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -914,7 +929,7 @@ module.exports = {
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
eventType,
|
||||
errorMessage: 'Error',
|
||||
errorMessage: alertStatus.message,
|
||||
});
|
||||
} else if (alertStatus) {
|
||||
alert = await _this.create({
|
||||
@@ -1003,6 +1018,17 @@ module.exports = {
|
||||
(!project.alertEnable || !areAlertsEnabledGlobally)) ||
|
||||
(!IS_SAAS_SERVICE && !areAlertsEnabledGlobally))
|
||||
) {
|
||||
let errorMessageText;
|
||||
if (!hasGlobalTwilioSettings) {
|
||||
errorMessageText =
|
||||
'Twilio Settings not found on Admin Dashboard';
|
||||
} else if (!areAlertsEnabledGlobally) {
|
||||
errorMessageText = 'Alert Disabled on Admin Dashboard';
|
||||
} else if (IS_SAAS_SERVICE && !project.alertEnable) {
|
||||
errorMessageText = 'Alert Disabled for this project';
|
||||
} else {
|
||||
errorMessageText = 'Error';
|
||||
}
|
||||
return await _this.create({
|
||||
projectId: incident.projectId,
|
||||
schedule: schedule._id,
|
||||
@@ -1015,13 +1041,7 @@ module.exports = {
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
eventType,
|
||||
errorMessage: !hasGlobalTwilioSettings
|
||||
? 'Twilio Settings not found on Admin Dashboard'
|
||||
: !areAlertsEnabledGlobally
|
||||
? 'Alert Disabled on Admin Dashboard'
|
||||
: IS_SAAS_SERVICE && !project.alertEnable
|
||||
? 'Alert Disabled for this project'
|
||||
: 'Error',
|
||||
errorMessage: errorMessageText,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1032,6 +1052,17 @@ module.exports = {
|
||||
);
|
||||
if (!doesPhoneNumberComplyWithHighRiskConfig) {
|
||||
const countryType = getCountryType(user.alertPhoneNumber);
|
||||
let errorMessageText;
|
||||
if (countryType === 'us') {
|
||||
errorMessageText =
|
||||
'SMS for numbers inside US not enabled for this project';
|
||||
} else if (countryType === 'non-us') {
|
||||
errorMessageText =
|
||||
'SMS for numbers outside US not enabled for this project';
|
||||
} else {
|
||||
errorMessageText =
|
||||
'SMS to High Risk country not enabled for this project';
|
||||
}
|
||||
return await _this.create({
|
||||
projectId: incident.projectId,
|
||||
monitorId,
|
||||
@@ -1044,12 +1075,7 @@ module.exports = {
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
eventType,
|
||||
errorMessage:
|
||||
countryType === 'us'
|
||||
? 'SMS for numbers inside US not enabled for this project'
|
||||
: countryType === 'non-us'
|
||||
? 'SMS for numbers outside US not enabled for this project'
|
||||
: 'SMS to High Risk country not enabled for this project',
|
||||
errorMessage: errorMessageText,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1120,9 +1146,7 @@ module.exports = {
|
||||
});
|
||||
if (IS_SAAS_SERVICE && !hasCustomTwilioSettings) {
|
||||
// calculate charge per 160 chars
|
||||
// numSegments is the number of segments the sms will be divided into
|
||||
// numSegments is provided by twilio
|
||||
const segments = Number(sendResult.numSegments);
|
||||
const segments = calcSmsSegments(sendResult.body);
|
||||
const balanceStatus = await PaymentService.chargeAlertAndGetProjectBalance(
|
||||
user._id,
|
||||
project,
|
||||
@@ -1205,6 +1229,7 @@ module.exports = {
|
||||
) {
|
||||
try {
|
||||
const _this = this;
|
||||
const uuid = new Date().getTime();
|
||||
const monitor = await MonitorService.findOneBy({
|
||||
_id: incident.monitorId._id,
|
||||
});
|
||||
@@ -1234,7 +1259,9 @@ module.exports = {
|
||||
note: data.content,
|
||||
incidentState: data.incident_state,
|
||||
statusNoteStatus,
|
||||
}
|
||||
},
|
||||
subscribers.length,
|
||||
uuid
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1247,6 +1274,7 @@ module.exports = {
|
||||
sendCreatedIncidentToSubscribers: async function(incident, component) {
|
||||
try {
|
||||
const _this = this;
|
||||
const uuid = new Date().getTime();
|
||||
if (incident) {
|
||||
const monitorId = incident.monitorId._id
|
||||
? incident.monitorId._id
|
||||
@@ -1270,7 +1298,10 @@ module.exports = {
|
||||
subscriber,
|
||||
incident,
|
||||
'Subscriber Incident Created',
|
||||
enabledStatusPage
|
||||
enabledStatusPage,
|
||||
null,
|
||||
subscribers.length,
|
||||
uuid
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -1279,7 +1310,9 @@ module.exports = {
|
||||
incident,
|
||||
'Subscriber Incident Created',
|
||||
null,
|
||||
component
|
||||
component,
|
||||
subscribers.length,
|
||||
uuid
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1422,7 +1455,7 @@ module.exports = {
|
||||
}
|
||||
} else {
|
||||
if (escalation.email) {
|
||||
_this.sendAcknowledgeEmailAlert({
|
||||
await _this.sendAcknowledgeEmailAlert({
|
||||
incident,
|
||||
user,
|
||||
project,
|
||||
@@ -1493,6 +1526,16 @@ module.exports = {
|
||||
!areEmailAlertsEnabledInGlobalSettings &&
|
||||
!hasCustomSmtpSettings
|
||||
) {
|
||||
let errorMessageText;
|
||||
if (!hasGlobalSmtpSettings && !hasCustomSmtpSettings) {
|
||||
errorMessageText =
|
||||
'SMTP Settings not found on Admin Dashboard';
|
||||
} else if (
|
||||
hasGlobalSmtpSettings &&
|
||||
!areEmailAlertsEnabledInGlobalSettings
|
||||
) {
|
||||
errorMessageText = 'Alert Disabled on Admin Dashboard';
|
||||
}
|
||||
return await _this.create({
|
||||
projectId: incident.projectId,
|
||||
monitorId: monitor._id,
|
||||
@@ -1505,13 +1548,7 @@ module.exports = {
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
eventType,
|
||||
errorMessage:
|
||||
!hasGlobalSmtpSettings && !hasCustomSmtpSettings
|
||||
? 'SMTP Settings not found on Admin Dashboard'
|
||||
: hasGlobalSmtpSettings &&
|
||||
!areEmailAlertsEnabledInGlobalSettings
|
||||
? 'Alert Disabled on Admin Dashboard'
|
||||
: 'Error.',
|
||||
errorMessage: errorMessageText,
|
||||
});
|
||||
}
|
||||
const incidentcreatedBy =
|
||||
@@ -1590,6 +1627,8 @@ module.exports = {
|
||||
incidentId: incident._id,
|
||||
eventType,
|
||||
alertStatus: 'Cannot Send',
|
||||
error: true,
|
||||
errorMessage: e.message,
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -1723,7 +1762,7 @@ module.exports = {
|
||||
}
|
||||
} else {
|
||||
if (escalation.email) {
|
||||
_this.sendResolveEmailAlert({
|
||||
await _this.sendResolveEmailAlert({
|
||||
incident,
|
||||
user,
|
||||
project,
|
||||
@@ -1790,6 +1829,16 @@ module.exports = {
|
||||
!areEmailAlertsEnabledInGlobalSettings &&
|
||||
!hasCustomSmtpSettings
|
||||
) {
|
||||
let errorMessageText;
|
||||
if (!hasGlobalSmtpSettings && !hasCustomSmtpSettings) {
|
||||
errorMessageText =
|
||||
'SMTP Settings not found on Admin Dashboard';
|
||||
} else if (
|
||||
hasGlobalSmtpSettings &&
|
||||
!areEmailAlertsEnabledInGlobalSettings
|
||||
) {
|
||||
errorMessageText = 'Alert Disabled on Admin Dashboard';
|
||||
}
|
||||
return await _this.create({
|
||||
projectId: incident.projectId,
|
||||
monitorId: monitor._id,
|
||||
@@ -1802,13 +1851,7 @@ module.exports = {
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
eventType,
|
||||
errorMessage:
|
||||
!hasGlobalSmtpSettings && !hasCustomSmtpSettings
|
||||
? 'SMTP Settings not found on Admin Dashboard'
|
||||
: hasGlobalSmtpSettings &&
|
||||
!areEmailAlertsEnabledInGlobalSettings
|
||||
? 'Alert Disabled on Admin Dashboard'
|
||||
: 'Error.',
|
||||
errorMessage: errorMessageText,
|
||||
});
|
||||
}
|
||||
const incidentcreatedBy =
|
||||
@@ -1886,6 +1929,8 @@ module.exports = {
|
||||
incidentId: incident._id,
|
||||
eventType,
|
||||
alertStatus: 'Cannot Send',
|
||||
error: true,
|
||||
errorMessage: e.message,
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -1893,6 +1938,7 @@ module.exports = {
|
||||
sendAcknowledgedIncidentToSubscribers: async function(incident) {
|
||||
try {
|
||||
const _this = this;
|
||||
const uuid = new Date().getTime();
|
||||
if (incident) {
|
||||
const monitorId = incident.monitorId._id
|
||||
? incident.monitorId._id
|
||||
@@ -1915,14 +1961,21 @@ module.exports = {
|
||||
subscriber,
|
||||
incident,
|
||||
'Subscriber Incident Acknowldeged',
|
||||
enabledStatusPage
|
||||
enabledStatusPage,
|
||||
{},
|
||||
subscribers.length,
|
||||
uuid
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await _this.sendSubscriberAlert(
|
||||
subscriber,
|
||||
incident,
|
||||
'Subscriber Incident Acknowldeged'
|
||||
'Subscriber Incident Acknowldeged',
|
||||
null,
|
||||
{},
|
||||
subscribers.length,
|
||||
uuid
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1939,6 +1992,7 @@ module.exports = {
|
||||
sendResolvedIncidentToSubscribers: async function(incident) {
|
||||
try {
|
||||
const _this = this;
|
||||
const uuid = new Date().getTime();
|
||||
if (incident) {
|
||||
const monitorId = incident.monitorId._id
|
||||
? incident.monitorId._id
|
||||
@@ -1961,14 +2015,21 @@ module.exports = {
|
||||
subscriber,
|
||||
incident,
|
||||
'Subscriber Incident Resolved',
|
||||
enabledStatusPage
|
||||
enabledStatusPage,
|
||||
{},
|
||||
subscribers.length,
|
||||
uuid
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await _this.sendSubscriberAlert(
|
||||
subscriber,
|
||||
incident,
|
||||
'Subscriber Incident Resolved'
|
||||
'Subscriber Incident Resolved',
|
||||
null,
|
||||
{},
|
||||
subscribers.length,
|
||||
uuid
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1987,7 +2048,9 @@ module.exports = {
|
||||
incident,
|
||||
templateType = 'Subscriber Incident Created',
|
||||
statusPage,
|
||||
{ note, incidentState, statusNoteStatus } = {}
|
||||
{ note, incidentState, statusNoteStatus } = {},
|
||||
totalSubscribers,
|
||||
id
|
||||
) {
|
||||
try {
|
||||
const _this = this;
|
||||
@@ -2031,6 +2094,21 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
const monitorCustomFields = {},
|
||||
incidentCustomFields = {};
|
||||
monitor.customFields.forEach(
|
||||
field =>
|
||||
(monitorCustomFields[field.fieldName] = field.fieldValue)
|
||||
);
|
||||
incident.customFields.forEach(
|
||||
field =>
|
||||
(incidentCustomFields[field.fieldName] = field.fieldValue)
|
||||
);
|
||||
const customFields = {
|
||||
monitor: { customFields: monitorCustomFields },
|
||||
incident: { customFields: incidentCustomFields },
|
||||
};
|
||||
|
||||
let webhookNotificationSent = true;
|
||||
|
||||
if (subscriber.alertVia === AlertType.Webhook) {
|
||||
@@ -2038,24 +2116,33 @@ module.exports = {
|
||||
isStatusPageNoteAlert &&
|
||||
!project.enableInvestigationNoteNotificationWebhook;
|
||||
|
||||
let eventType;
|
||||
if (investigationNoteNotificationWebhookDisabled) {
|
||||
if (isStatusPageNoteAlert) {
|
||||
eventType = statusPageNoteAlertEventType;
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Acknowldeged'
|
||||
) {
|
||||
eventType = 'acknowledged';
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Resolved'
|
||||
) {
|
||||
eventType = 'resolved';
|
||||
} else {
|
||||
eventType = 'identified';
|
||||
}
|
||||
return await SubscriberAlertService.create({
|
||||
projectId: incident.projectId,
|
||||
incidentId: incident._id,
|
||||
subscriberId: subscriber._id,
|
||||
alertVia: AlertType.Webhook,
|
||||
eventType: isStatusPageNoteAlert
|
||||
? statusPageNoteAlertEventType
|
||||
: templateType ===
|
||||
'Subscriber Incident Acknowldeged'
|
||||
? 'acknowledged'
|
||||
: templateType === 'Subscriber Incident Resolved'
|
||||
? 'resolved'
|
||||
: 'identified',
|
||||
eventType: eventType,
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
errorMessage:
|
||||
'Investigation Note Webhook Notification Disabled',
|
||||
totalSubscribers,
|
||||
id,
|
||||
});
|
||||
}
|
||||
const downTimeString = IncidentUtility.calculateHumanReadableDownTime(
|
||||
@@ -2079,20 +2166,28 @@ module.exports = {
|
||||
alertStatus = null;
|
||||
throw error;
|
||||
} finally {
|
||||
if (isStatusPageNoteAlert) {
|
||||
eventType = statusPageNoteAlertEventType;
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Acknowldeged'
|
||||
) {
|
||||
eventType = 'acknowledged';
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Resolved'
|
||||
) {
|
||||
eventType = 'resolved';
|
||||
} else {
|
||||
eventType = 'identified';
|
||||
}
|
||||
SubscriberAlertService.create({
|
||||
projectId: incident.projectId,
|
||||
incidentId: incident._id,
|
||||
subscriberId: subscriber._id,
|
||||
alertVia: AlertType.Webhook,
|
||||
alertStatus: alertStatus,
|
||||
eventType: isStatusPageNoteAlert
|
||||
? statusPageNoteAlertEventType
|
||||
: templateType ===
|
||||
'Subscriber Incident Acknowldeged'
|
||||
? 'acknowledged'
|
||||
: templateType === 'Subscriber Incident Resolved'
|
||||
? 'resolved'
|
||||
: 'identified',
|
||||
eventType: eventType,
|
||||
totalSubscribers,
|
||||
id,
|
||||
}).catch(error => {
|
||||
ErrorService.log(
|
||||
'AlertService.sendSubscriberAlert',
|
||||
@@ -2124,54 +2219,74 @@ module.exports = {
|
||||
isStatusPageNoteAlert &&
|
||||
!project.enableInvestigationNoteNotificationEmail;
|
||||
|
||||
let errorMessageText, eventType;
|
||||
if (
|
||||
(!areEmailAlertsEnabledInGlobalSettings &&
|
||||
!hasCustomSmtpSettings) ||
|
||||
investigationNoteNotificationEmailDisabled
|
||||
) {
|
||||
if (!hasGlobalSmtpSettings && !hasCustomSmtpSettings) {
|
||||
errorMessageText =
|
||||
'SMTP Settings not found on Admin Dashboard';
|
||||
} else if (
|
||||
hasGlobalSmtpSettings &&
|
||||
!areEmailAlertsEnabledInGlobalSettings
|
||||
) {
|
||||
errorMessageText = 'Alert Disabled on Admin Dashboard';
|
||||
} else if (investigationNoteNotificationEmailDisabled) {
|
||||
errorMessageText =
|
||||
'Investigation Note Email Notification Disabled';
|
||||
}
|
||||
if (isStatusPageNoteAlert) {
|
||||
eventType = statusPageNoteAlertEventType;
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Acknowldeged'
|
||||
) {
|
||||
eventType = 'acknowledged';
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Resolved'
|
||||
) {
|
||||
eventType = 'resolved';
|
||||
} else {
|
||||
eventType = 'identified';
|
||||
}
|
||||
return await SubscriberAlertService.create({
|
||||
projectId: incident.projectId,
|
||||
incidentId: incident._id,
|
||||
subscriberId: subscriber._id,
|
||||
alertVia: AlertType.Email,
|
||||
eventType: isStatusPageNoteAlert
|
||||
? statusPageNoteAlertEventType
|
||||
: templateType ===
|
||||
'Subscriber Incident Acknowldeged'
|
||||
? 'acknowledged'
|
||||
: templateType === 'Subscriber Incident Resolved'
|
||||
? 'resolved'
|
||||
: 'identified',
|
||||
eventType: eventType,
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
errorMessage:
|
||||
!hasGlobalSmtpSettings && !hasCustomSmtpSettings
|
||||
? 'SMTP Settings not found on Admin Dashboard'
|
||||
: hasGlobalSmtpSettings &&
|
||||
!areEmailAlertsEnabledInGlobalSettings
|
||||
? 'Alert Disabled on Admin Dashboard'
|
||||
: investigationNoteNotificationEmailDisabled
|
||||
? 'Investigation Note Email Notification Disabled'
|
||||
: 'Error',
|
||||
errorMessage: errorMessageText,
|
||||
totalSubscribers,
|
||||
id,
|
||||
});
|
||||
}
|
||||
const emailTemplate = await EmailTemplateService.findOneBy({
|
||||
projectId: incident.projectId,
|
||||
emailType: templateType,
|
||||
});
|
||||
if (isStatusPageNoteAlert) {
|
||||
eventType = statusPageNoteAlertEventType;
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Acknowldeged'
|
||||
) {
|
||||
eventType = 'acknowledged';
|
||||
} else if (templateType === 'Subscriber Incident Resolved') {
|
||||
eventType = 'resolved';
|
||||
} else {
|
||||
eventType = 'identified';
|
||||
}
|
||||
const subscriberAlert = await SubscriberAlertService.create({
|
||||
projectId: incident.projectId,
|
||||
incidentId: incident._id,
|
||||
subscriberId: subscriber._id,
|
||||
alertVia: AlertType.Email,
|
||||
alertStatus: 'Pending',
|
||||
eventType: isStatusPageNoteAlert
|
||||
? statusPageNoteAlertEventType
|
||||
: templateType === 'Subscriber Incident Acknowldeged'
|
||||
? 'acknowledged'
|
||||
: templateType === 'Subscriber Incident Resolved'
|
||||
? 'resolved'
|
||||
: 'identified',
|
||||
eventType: eventType,
|
||||
totalSubscribers,
|
||||
id,
|
||||
});
|
||||
const alertId = subscriberAlert._id;
|
||||
const trackEmailAsViewedUrl = `${global.apiHost}/subscriberAlert/${incident.projectId}/${alertId}/viewed`;
|
||||
@@ -2193,7 +2308,8 @@ module.exports = {
|
||||
trackEmailAsViewedUrl,
|
||||
component.name,
|
||||
statusPageUrl,
|
||||
project.replyAddress
|
||||
project.replyAddress,
|
||||
customFields
|
||||
);
|
||||
|
||||
alertStatus = 'Sent';
|
||||
@@ -2210,7 +2326,8 @@ module.exports = {
|
||||
trackEmailAsViewedUrl,
|
||||
component.name,
|
||||
statusPageUrl,
|
||||
project.replyAddress
|
||||
project.replyAddress,
|
||||
customFields
|
||||
);
|
||||
|
||||
alertStatus = 'Sent';
|
||||
@@ -2235,7 +2352,8 @@ module.exports = {
|
||||
trackEmailAsViewedUrl,
|
||||
component.name,
|
||||
statusPageUrl,
|
||||
project.replyAddress
|
||||
project.replyAddress,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Sent';
|
||||
} else {
|
||||
@@ -2251,7 +2369,8 @@ module.exports = {
|
||||
trackEmailAsViewedUrl,
|
||||
component.name,
|
||||
statusPageUrl,
|
||||
project.replyAddress
|
||||
project.replyAddress,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Sent';
|
||||
}
|
||||
@@ -2273,7 +2392,8 @@ module.exports = {
|
||||
component.name,
|
||||
note,
|
||||
statusUrl,
|
||||
statusNoteStatus
|
||||
statusNoteStatus,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Sent';
|
||||
} else {
|
||||
@@ -2291,7 +2411,8 @@ module.exports = {
|
||||
trackEmailAsViewedUrl,
|
||||
component.name,
|
||||
statusPageUrl,
|
||||
project.replyAddress
|
||||
project.replyAddress,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Sent';
|
||||
} else {
|
||||
@@ -2307,7 +2428,8 @@ module.exports = {
|
||||
trackEmailAsViewedUrl,
|
||||
component.name,
|
||||
statusPageUrl,
|
||||
project.replyAddress
|
||||
project.replyAddress,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Sent';
|
||||
}
|
||||
@@ -2355,6 +2477,33 @@ module.exports = {
|
||||
(!IS_SAAS_SERVICE && !areAlertsEnabledGlobally))) ||
|
||||
investigationNoteNotificationSMSDisabled
|
||||
) {
|
||||
let errorMessageText, eventType;
|
||||
if (!hasGlobalTwilioSettings) {
|
||||
errorMessageText =
|
||||
'Twilio Settings not found on Admin Dashboard';
|
||||
} else if (!areAlertsEnabledGlobally) {
|
||||
errorMessageText = 'Alert Disabled on Admin Dashboard';
|
||||
} else if (IS_SAAS_SERVICE && !project.alertEnable) {
|
||||
errorMessageText = 'Alert Disabled for this project';
|
||||
} else if (investigationNoteNotificationSMSDisabled) {
|
||||
errorMessageText =
|
||||
'Investigation Note SMS Notification Disabled';
|
||||
} else {
|
||||
errorMessageText = 'Error';
|
||||
}
|
||||
if (isStatusPageNoteAlert) {
|
||||
eventType = statusPageNoteAlertEventType;
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Acknowldeged'
|
||||
) {
|
||||
eventType = 'acknowledged';
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Resolved'
|
||||
) {
|
||||
eventType = 'resolved';
|
||||
} else {
|
||||
eventType = 'identified';
|
||||
}
|
||||
return await SubscriberAlertService.create({
|
||||
projectId: incident.projectId,
|
||||
incidentId: incident._id,
|
||||
@@ -2362,23 +2511,10 @@ module.exports = {
|
||||
alertVia: AlertType.SMS,
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
errorMessage: !hasGlobalTwilioSettings
|
||||
? 'Twilio Settings not found on Admin Dashboard'
|
||||
: !areAlertsEnabledGlobally
|
||||
? 'Alert Disabled on Admin Dashboard'
|
||||
: IS_SAAS_SERVICE && !project.alertEnable
|
||||
? 'Alert Disabled for this project'
|
||||
: investigationNoteNotificationSMSDisabled
|
||||
? 'Investigation Note SMS Notification Disabled'
|
||||
: 'Error',
|
||||
eventType: isStatusPageNoteAlert
|
||||
? statusPageNoteAlertEventType
|
||||
: templateType ===
|
||||
'Subscriber Incident Acknowldeged'
|
||||
? 'acknowledged'
|
||||
: templateType === 'Subscriber Incident Resolved'
|
||||
? 'resolved'
|
||||
: 'identified',
|
||||
errorMessage: errorMessageText,
|
||||
eventType: eventType,
|
||||
totalSubscribers,
|
||||
id,
|
||||
});
|
||||
}
|
||||
const countryCode = await _this.mapCountryShortNameToCountryCode(
|
||||
@@ -2399,6 +2535,30 @@ module.exports = {
|
||||
);
|
||||
if (!doesPhoneNumberComplyWithHighRiskConfig) {
|
||||
const countryType = getCountryType(contactPhone);
|
||||
let errorMessageText, eventType;
|
||||
if (countryType === 'us') {
|
||||
errorMessageText =
|
||||
'SMS for numbers inside US not enabled for this project';
|
||||
} else if (countryType === 'non-us') {
|
||||
errorMessageText =
|
||||
'SMS for numbers outside US not enabled for this project';
|
||||
} else {
|
||||
errorMessageText =
|
||||
'SMS to High Risk country not enabled for this project';
|
||||
}
|
||||
if (isStatusPageNoteAlert) {
|
||||
eventType = statusPageNoteAlertEventType;
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Acknowldeged'
|
||||
) {
|
||||
eventType = 'acknowledged';
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Resolved'
|
||||
) {
|
||||
eventType = 'resolved';
|
||||
} else {
|
||||
eventType = 'identified';
|
||||
}
|
||||
return await SubscriberAlertService.create({
|
||||
projectId: incident.projectId,
|
||||
incidentId: incident._id,
|
||||
@@ -2406,21 +2566,10 @@ module.exports = {
|
||||
alertVia: AlertType.SMS,
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
errorMessage:
|
||||
countryType === 'us'
|
||||
? 'SMS for numbers inside US not enabled for this project'
|
||||
: countryType === 'non-us'
|
||||
? 'SMS for numbers outside US not enabled for this project'
|
||||
: 'SMS to High Risk country not enabled for this project',
|
||||
eventType: isStatusPageNoteAlert
|
||||
? statusPageNoteAlertEventType
|
||||
: templateType ===
|
||||
'Subscriber Incident Acknowldeged'
|
||||
? 'acknowledged'
|
||||
: templateType ===
|
||||
'Subscriber Incident Resolved'
|
||||
? 'resolved'
|
||||
: 'identified',
|
||||
errorMessage: errorMessageText,
|
||||
eventType: eventType,
|
||||
totalSubscribers,
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2430,6 +2579,20 @@ module.exports = {
|
||||
contactPhone,
|
||||
AlertType.SMS
|
||||
);
|
||||
let eventType;
|
||||
if (isStatusPageNoteAlert) {
|
||||
eventType = statusPageNoteAlertEventType;
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Acknowldeged'
|
||||
) {
|
||||
eventType = 'acknowledged';
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Resolved'
|
||||
) {
|
||||
eventType = 'resolved';
|
||||
} else {
|
||||
eventType = 'identified';
|
||||
}
|
||||
|
||||
if (!status.success) {
|
||||
return await SubscriberAlertService.create({
|
||||
@@ -2440,15 +2603,9 @@ module.exports = {
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
errorMessage: status.message,
|
||||
eventType: isStatusPageNoteAlert
|
||||
? statusPageNoteAlertEventType
|
||||
: templateType ===
|
||||
'Subscriber Incident Acknowldeged'
|
||||
? 'acknowledged'
|
||||
: templateType ===
|
||||
'Subscriber Incident Resolved'
|
||||
? 'resolved'
|
||||
: 'identified',
|
||||
eventType: eventType,
|
||||
totalSubscribers,
|
||||
id,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2458,19 +2615,27 @@ module.exports = {
|
||||
projectId: incident.projectId,
|
||||
smsType: templateType,
|
||||
});
|
||||
let eventType;
|
||||
if (isStatusPageNoteAlert) {
|
||||
eventType = statusPageNoteAlertEventType;
|
||||
} else if (
|
||||
templateType === 'Subscriber Incident Acknowldeged'
|
||||
) {
|
||||
eventType = 'acknowledged';
|
||||
} else if (templateType === 'Subscriber Incident Resolved') {
|
||||
eventType = 'resolved';
|
||||
} else {
|
||||
eventType = 'identified';
|
||||
}
|
||||
const subscriberAlert = await SubscriberAlertService.create({
|
||||
projectId: incident.projectId,
|
||||
incidentId: incident._id,
|
||||
subscriberId: subscriber._id,
|
||||
alertVia: AlertType.SMS,
|
||||
alertStatus: 'Pending',
|
||||
eventType: isStatusPageNoteAlert
|
||||
? statusPageNoteAlertEventType
|
||||
: templateType === 'Subscriber Incident Acknowldeged'
|
||||
? 'acknowledged'
|
||||
: templateType === 'Subscriber Incident Resolved'
|
||||
? 'resolved'
|
||||
: 'identified',
|
||||
eventType: eventType,
|
||||
totalSubscribers,
|
||||
id,
|
||||
});
|
||||
const alertId = subscriberAlert._id;
|
||||
|
||||
@@ -2488,7 +2653,8 @@ module.exports = {
|
||||
project.name,
|
||||
incident.projectId,
|
||||
component.name,
|
||||
statusPageUrl
|
||||
statusPageUrl,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Success';
|
||||
} else {
|
||||
@@ -2501,7 +2667,8 @@ module.exports = {
|
||||
project.name,
|
||||
incident.projectId,
|
||||
component.name,
|
||||
statusPageUrl
|
||||
statusPageUrl,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Success';
|
||||
}
|
||||
@@ -2522,7 +2689,8 @@ module.exports = {
|
||||
project.name,
|
||||
incident.projectId,
|
||||
component.name,
|
||||
statusPageUrl
|
||||
statusPageUrl,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Success';
|
||||
} else {
|
||||
@@ -2535,7 +2703,8 @@ module.exports = {
|
||||
project.name,
|
||||
incident.projectId,
|
||||
component.name,
|
||||
statusPageUrl
|
||||
statusPageUrl,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Success';
|
||||
}
|
||||
@@ -2554,7 +2723,8 @@ module.exports = {
|
||||
project.name,
|
||||
incident.projectId,
|
||||
component.name,
|
||||
statusUrl
|
||||
statusUrl,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Success';
|
||||
} else {
|
||||
@@ -2569,7 +2739,8 @@ module.exports = {
|
||||
project.name,
|
||||
incident.projectId,
|
||||
component.name,
|
||||
statusPageUrl
|
||||
statusPageUrl,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Success';
|
||||
} else {
|
||||
@@ -2582,7 +2753,8 @@ module.exports = {
|
||||
project.name,
|
||||
incident.projectId,
|
||||
component.name,
|
||||
statusPageUrl
|
||||
statusPageUrl,
|
||||
customFields
|
||||
);
|
||||
alertStatus = 'Success';
|
||||
}
|
||||
@@ -2617,9 +2789,7 @@ module.exports = {
|
||||
!hasCustomTwilioSettings
|
||||
) {
|
||||
// charge sms per 160 chars
|
||||
// numSegments is the number of segments an sms can be divided into
|
||||
// numSegments is provided by twilio
|
||||
const segments = Number(sendResult.numSegments);
|
||||
const segments = calcSmsSegments(sendResult.body);
|
||||
const balanceStatus = await PaymentService.chargeAlertAndGetProjectBalance(
|
||||
owner.userId,
|
||||
project,
|
||||
@@ -2650,7 +2820,7 @@ module.exports = {
|
||||
{
|
||||
alertStatus: null,
|
||||
error: true,
|
||||
errorMessage: 'Error',
|
||||
errorMessage: error.message,
|
||||
}
|
||||
);
|
||||
throw error;
|
||||
@@ -2846,6 +3016,17 @@ module.exports = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @description calculates the number of segments an sms is divided into
|
||||
* @param {string} sms the body of the sms sent
|
||||
* @returns an interger
|
||||
*/
|
||||
function calcSmsSegments(sms) {
|
||||
let smsLength = sms.length;
|
||||
smsLength = Number(smsLength);
|
||||
return Math.ceil(smsLength / 160);
|
||||
}
|
||||
|
||||
const AlertModel = require('../models/alert');
|
||||
const ProjectService = require('./projectService');
|
||||
const PaymentService = require('./paymentService');
|
||||
|
||||
111
backend/backend/services/callLogsService.js
Normal file
111
backend/backend/services/callLogsService.js
Normal file
@@ -0,0 +1,111 @@
|
||||
module.exports = {
|
||||
findBy: async function(query, limit, skip, sort) {
|
||||
try {
|
||||
if (!skip) skip = 0;
|
||||
|
||||
if (!limit) limit = 0;
|
||||
|
||||
if (typeof skip === 'string') skip = parseInt(skip);
|
||||
|
||||
if (typeof limit === 'string') limit = parseInt(limit);
|
||||
|
||||
if (!query) {
|
||||
query = {};
|
||||
}
|
||||
|
||||
if (!sort) {
|
||||
sort = { createdAt: 'desc' };
|
||||
}
|
||||
|
||||
if (!query.deleted) query.deleted = false;
|
||||
const items = await CallLogsModel.find(query)
|
||||
.limit(limit)
|
||||
.skip(skip)
|
||||
.sort(sort)
|
||||
.populate('projectId', 'name');
|
||||
return items;
|
||||
} catch (error) {
|
||||
ErrorService.log('callLogsService.findBy', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
create: async function(from, to, projectId, content, status, error) {
|
||||
try {
|
||||
let item = new CallLogsModel();
|
||||
|
||||
item.from = from;
|
||||
item.to = to;
|
||||
item.projectId = projectId;
|
||||
item.content = content;
|
||||
item.status = status;
|
||||
item.error = error;
|
||||
item = await item.save();
|
||||
|
||||
return item;
|
||||
} catch (error) {
|
||||
ErrorService.log('callLogsService.create', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
countBy: async function(query) {
|
||||
try {
|
||||
if (!query) {
|
||||
query = {};
|
||||
}
|
||||
|
||||
if (!query.deleted) query.deleted = false;
|
||||
const count = await CallLogsModel.countDocuments(query);
|
||||
return count;
|
||||
} catch (error) {
|
||||
ErrorService.log('callLogsService.countBy', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
deleteBy: async function(query, userId) {
|
||||
try {
|
||||
if (!query) {
|
||||
query = {};
|
||||
}
|
||||
|
||||
query.deleted = false;
|
||||
const items = await CallLogsModel.findOneAndUpdate(query, {
|
||||
$set: {
|
||||
deleted: true,
|
||||
deletedAt: Date.now(),
|
||||
deletedById: userId,
|
||||
},
|
||||
});
|
||||
return items;
|
||||
} catch (error) {
|
||||
ErrorService.log('callLogsService.findOneAndUpdate', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
hardDeleteBy: async function({ query }) {
|
||||
try {
|
||||
await CallLogsModel.deleteMany(query);
|
||||
} catch (error) {
|
||||
ErrorService.log('callLogs.hardDeleteBy', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
search: async function({ filter, skip, limit }) {
|
||||
const _this = this;
|
||||
const query = {
|
||||
to: { $regex: new RegExp(filter), $options: 'i' },
|
||||
};
|
||||
|
||||
const searchedCallLogs = await _this.findBy({ query, skip, limit });
|
||||
const totalSearchCount = await _this.countBy({ query });
|
||||
|
||||
return { searchedCallLogs, totalSearchCount };
|
||||
},
|
||||
};
|
||||
|
||||
const CallLogsModel = require('../models/callLogs');
|
||||
const ErrorService = require('./errorService');
|
||||
@@ -37,6 +37,7 @@ module.exports = {
|
||||
body,
|
||||
template,
|
||||
content,
|
||||
error,
|
||||
}) {
|
||||
try {
|
||||
let item = new EmailStatusModel();
|
||||
@@ -48,6 +49,7 @@ module.exports = {
|
||||
item.body = body;
|
||||
item.template = template;
|
||||
item.content = content;
|
||||
item.error = error;
|
||||
item = await item.save();
|
||||
|
||||
return item;
|
||||
|
||||
@@ -109,7 +109,7 @@ module.exports = {
|
||||
// get all unique hashes by error tracker Id
|
||||
const errorTrackerIssues = await IssueService.findBy(
|
||||
query,
|
||||
limit,
|
||||
0,
|
||||
skip
|
||||
);
|
||||
|
||||
@@ -117,7 +117,10 @@ module.exports = {
|
||||
let index = 0;
|
||||
|
||||
// if the next index is available in the issue tracker, proceed
|
||||
while (errorTrackerIssues[index]) {
|
||||
while (
|
||||
errorTrackerIssues[index] &&
|
||||
totalErrorEvents.length < limit
|
||||
) {
|
||||
const issue = errorTrackerIssues[index];
|
||||
|
||||
if (issue) {
|
||||
@@ -168,6 +171,10 @@ module.exports = {
|
||||
// increment index
|
||||
index = index + 1;
|
||||
}
|
||||
// sort total error events by latest occurence date
|
||||
totalErrorEvents.sort((eventA, eventB) =>
|
||||
moment(eventB.latestOccurennce).isAfter(eventA.latestOccurennce)
|
||||
);
|
||||
let dateRange = { startDate: '', endDate: '' };
|
||||
// set the date time range
|
||||
if (query.createdAt) {
|
||||
@@ -184,6 +191,17 @@ module.exports = {
|
||||
endDate: totalErrorEvents[0].latestOccurennce,
|
||||
})
|
||||
: null;
|
||||
errorTrackerIssues.length > 0
|
||||
? (dateRange = {
|
||||
startDate:
|
||||
errorTrackerIssues[errorTrackerIssues.length - 1]
|
||||
.createdAt,
|
||||
endDate:
|
||||
totalErrorEvents.length > 0
|
||||
? totalErrorEvents[0].latestOccurennce
|
||||
: errorTrackerIssues[0].createdAt,
|
||||
})
|
||||
: null;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -327,3 +345,4 @@ const ErrorEventModel = require('../models/errorEvent');
|
||||
const ErrorService = require('./errorService');
|
||||
const IssueService = require('./issueService');
|
||||
const IssueMemberService = require('./issueMemberService');
|
||||
const moment = require('moment');
|
||||
|
||||
@@ -509,8 +509,10 @@ module.exports = {
|
||||
status: 'acknowledged',
|
||||
});
|
||||
|
||||
AlertService.sendAcknowledgedIncidentToSubscribers(incident);
|
||||
AlertService.sendAcknowledgedIncidentMail(incident);
|
||||
await AlertService.sendAcknowledgedIncidentToSubscribers(
|
||||
incident
|
||||
);
|
||||
await AlertService.sendAcknowledgedIncidentMail(incident);
|
||||
|
||||
WebHookService.sendIntegrationNotification(
|
||||
incident.projectId,
|
||||
@@ -638,7 +640,7 @@ module.exports = {
|
||||
status: 'resolved',
|
||||
});
|
||||
|
||||
_this.sendIncidentResolvedNotification(incident, name);
|
||||
await _this.sendIncidentResolvedNotification(incident, name);
|
||||
RealTimeService.incidentResolved(incident);
|
||||
ZapierService.pushToZapier('incident_resolve', incident);
|
||||
|
||||
@@ -753,8 +755,8 @@ module.exports = {
|
||||
);
|
||||
|
||||
// send notificaton to subscribers
|
||||
AlertService.sendResolvedIncidentToSubscribers(incident);
|
||||
AlertService.sendResolveIncidentMail(incident);
|
||||
await AlertService.sendResolvedIncidentToSubscribers(incident);
|
||||
await AlertService.sendResolveIncidentMail(incident);
|
||||
|
||||
const msg = `${
|
||||
resolvedincident.monitorId.name
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@ module.exports = {
|
||||
Log.bestPractices = data.bestPractices;
|
||||
Log.seo = data.seo;
|
||||
Log.pwa = data.pwa;
|
||||
Log.scanning = data.scanning;
|
||||
|
||||
const savedLog = await Log.save();
|
||||
|
||||
@@ -47,6 +48,26 @@ module.exports = {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
updateManyBy: async function(query, data) {
|
||||
try {
|
||||
if (!query) {
|
||||
query = {};
|
||||
}
|
||||
|
||||
const lighthouseLog = await LighthouseLogModel.updateMany(
|
||||
query,
|
||||
{ $set: data },
|
||||
{
|
||||
new: true,
|
||||
}
|
||||
);
|
||||
|
||||
return lighthouseLog;
|
||||
} catch (error) {
|
||||
ErrorService.log('lighthouseLogService.updateManyBy', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async findBy(query, limit, skip) {
|
||||
try {
|
||||
@@ -183,6 +204,27 @@ module.exports = {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
async updateAllLighthouseLogs(projectId, monitorId, query) {
|
||||
try {
|
||||
await this.updateManyBy({ monitorId: monitorId }, query);
|
||||
const logs = await this.findLastestScan({
|
||||
monitorId,
|
||||
url: null,
|
||||
limit: 5,
|
||||
skip: 0,
|
||||
});
|
||||
await RealTimeService.updateAllLighthouseLog(projectId, {
|
||||
monitorId,
|
||||
logs,
|
||||
});
|
||||
} catch (error) {
|
||||
ErrorService.log(
|
||||
'lighthouseLogService.updateAllLighthouseLog',
|
||||
error
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const LighthouseLogModel = require('../models/lighthouseLog');
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,12 +2,26 @@ module.exports = {
|
||||
create: async function(data) {
|
||||
try {
|
||||
const Log = new MonitorLogModel();
|
||||
|
||||
let responseBody = '';
|
||||
if (data.resp && data.resp.body) {
|
||||
if (typeof data.resp.body === 'object') {
|
||||
responseBody = JSON.stringify(data.resp.body);
|
||||
} else {
|
||||
responseBody = data.resp.body;
|
||||
}
|
||||
} else {
|
||||
responseBody = '';
|
||||
}
|
||||
Log.monitorId = data.monitorId;
|
||||
Log.probeId = data.probeId;
|
||||
Log.status = data.status;
|
||||
Log.responseTime = data.responseTime;
|
||||
Log.responseStatus = data.responseStatus;
|
||||
Log.responseBody = responseBody;
|
||||
Log.responseHeader =
|
||||
data.rawResp && data.rawResp.headers
|
||||
? data.rawResp.headers
|
||||
: {};
|
||||
Log.cpuLoad = data.cpuLoad;
|
||||
Log.avgCpuLoad = data.avgCpuLoad;
|
||||
Log.cpuCores = data.cpuCores;
|
||||
|
||||
@@ -87,6 +87,7 @@ module.exports = {
|
||||
monitor.monitorSla = data.monitorSla;
|
||||
monitor.incidentCommunicationSla =
|
||||
data.incidentCommunicationSla;
|
||||
monitor.customFields = data.customFields;
|
||||
monitor.createdById = data.createdById;
|
||||
if (data.type === 'url' || data.type === 'api') {
|
||||
monitor.data = {};
|
||||
@@ -195,7 +196,6 @@ module.exports = {
|
||||
);
|
||||
}
|
||||
const monitor = await this.findOneBy(query);
|
||||
|
||||
await RealTimeService.monitorEdit(monitor);
|
||||
|
||||
return monitor;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -105,19 +105,7 @@ module.exports = {
|
||||
query = {};
|
||||
}
|
||||
query.deleted = false;
|
||||
const project = await ProjectModel.findOneAndUpdate(
|
||||
query,
|
||||
{
|
||||
$set: {
|
||||
deleted: true,
|
||||
deletedById: userId,
|
||||
deletedAt: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
new: true,
|
||||
}
|
||||
);
|
||||
let project = await ProjectModel.findOne(query);
|
||||
if (project) {
|
||||
if (project.stripeSubscriptionId) {
|
||||
await PaymentService.removeSubscription(
|
||||
@@ -161,6 +149,32 @@ module.exports = {
|
||||
{ projectId: project._id },
|
||||
userId
|
||||
);
|
||||
|
||||
const components = await componentService.findBy({
|
||||
projectId: project._id,
|
||||
});
|
||||
await Promise.all(
|
||||
components.map(async component => {
|
||||
await componentService.deleteBy(
|
||||
{ _id: component._id },
|
||||
userId
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
project = await ProjectModel.findOneAndUpdate(
|
||||
query,
|
||||
{
|
||||
$set: {
|
||||
deleted: true,
|
||||
deletedById: userId,
|
||||
deletedAt: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
new: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
return project;
|
||||
} catch (error) {
|
||||
@@ -743,3 +757,4 @@ const StatusPageService = require('./statusPageService');
|
||||
const slugify = require('slugify');
|
||||
const generate = require('nanoid/generate');
|
||||
const { IS_SAAS_SERVICE } = require('../config/server');
|
||||
const componentService = require('./componentService');
|
||||
|
||||
@@ -691,6 +691,31 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
updateAllLighthouseLog: async (projectId, data) => {
|
||||
try {
|
||||
if (!global || !global.io) {
|
||||
return;
|
||||
}
|
||||
|
||||
const project = await ProjectService.findOneBy({ _id: projectId });
|
||||
const parentProjectId = project
|
||||
? project.parentProjectId
|
||||
? project.parentProjectId._id
|
||||
: project._id
|
||||
: projectId;
|
||||
console.log(projectId, data, 'project id');
|
||||
|
||||
global.io.emit(`updateAllLighthouseLog-${parentProjectId}`, {
|
||||
projectId,
|
||||
monitorId: data.monitorId,
|
||||
data,
|
||||
});
|
||||
} catch (error) {
|
||||
ErrorService.log('realTimeService.updateAllLighthouseLog', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
updateMonitorStatus: async (data, projectId) => {
|
||||
try {
|
||||
if (!global || !global.io) {
|
||||
@@ -970,14 +995,14 @@ module.exports = {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
sendErrorEventCreated: async errorEvent => {
|
||||
sendErrorEventCreated: async data => {
|
||||
try {
|
||||
if (!global || !global.io) {
|
||||
return;
|
||||
}
|
||||
const errorTrackerId = errorEvent.errorTrackerId._id;
|
||||
const errorTrackerId = data.errorEvent.errorTrackerId._id;
|
||||
|
||||
global.io.emit(`createErrorEvent-${errorTrackerId}`, errorEvent);
|
||||
global.io.emit(`createErrorEvent-${errorTrackerId}`, data);
|
||||
} catch (error) {
|
||||
ErrorService.log('realTimeService.sendErrorEventCreated', error);
|
||||
throw error;
|
||||
|
||||
@@ -17,7 +17,8 @@ module.exports = {
|
||||
.sort([['createdAt', -1]])
|
||||
.limit(limit)
|
||||
.skip(skip)
|
||||
.populate('userIds', 'name');
|
||||
.populate('userId', 'name')
|
||||
.populate('projectId', 'name');
|
||||
return SmsCount;
|
||||
} catch (error) {
|
||||
ErrorService.log('smsCountService.findBy', error);
|
||||
@@ -34,7 +35,8 @@ module.exports = {
|
||||
if (!query.deleted) query.deleted = false;
|
||||
const SmsCount = await SmsCountModel.findOne(query)
|
||||
.sort([['createdAt', -1]])
|
||||
.populate('userIds', 'name');
|
||||
.populate('userId', 'name')
|
||||
.populate('projectId', 'name');
|
||||
return SmsCount;
|
||||
} catch (error) {
|
||||
ErrorService.log('smsCountService.findOneBy', error);
|
||||
@@ -42,12 +44,15 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
create: async function(userId, sentTo, projectId) {
|
||||
create: async function(userId, sentTo, projectId, content, status, error) {
|
||||
try {
|
||||
const smsCountModel = new SmsCountModel();
|
||||
smsCountModel.userId = userId || null;
|
||||
smsCountModel.sentTo = sentTo || null;
|
||||
smsCountModel.projectId = projectId || null;
|
||||
smsCountModel.content = content || null;
|
||||
smsCountModel.status = status || null;
|
||||
smsCountModel.error = error || null;
|
||||
const smsCount = await smsCountModel.save();
|
||||
return smsCount;
|
||||
} catch (error) {
|
||||
|
||||
@@ -8,6 +8,8 @@ module.exports = {
|
||||
subscriberAlertModel.alertVia = data.alertVia || null;
|
||||
subscriberAlertModel.alertStatus = data.alertStatus || null;
|
||||
subscriberAlertModel.eventType = data.eventType || null;
|
||||
subscriberAlertModel.totalSubscribers = data.totalSubscribers || 0;
|
||||
subscriberAlertModel.identification = data.id || 0;
|
||||
if (data.error) {
|
||||
subscriberAlertModel.error = data.error;
|
||||
subscriberAlertModel.errorMessage = data.errorMessage;
|
||||
|
||||
@@ -12,6 +12,7 @@ const defaultSmsTemplates = require('../config/smsTemplate');
|
||||
const GlobalConfigService = require('./globalConfigService');
|
||||
const UserService = require('./userService');
|
||||
const SmsCountService = require('./smsCountService');
|
||||
const CallLogsService = require('./callLogsService');
|
||||
const AlertService = require('./alertService');
|
||||
const { IS_TESTING } = require('../config/server');
|
||||
|
||||
@@ -62,17 +63,17 @@ const _this = {
|
||||
incidentType,
|
||||
projectId
|
||||
) {
|
||||
let smsBody;
|
||||
try {
|
||||
const options = {
|
||||
body: `Fyipe Alert: Monitor ${monitorName} is ${incidentType}. Please acknowledge or resolve this incident on Fyipe Dashboard.`,
|
||||
to: number,
|
||||
};
|
||||
|
||||
smsBody = options.body;
|
||||
const customTwilioSettings = await _this.findByOne({
|
||||
projectId,
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
if (customTwilioSettings) {
|
||||
options.from = customTwilioSettings.phoneNumber;
|
||||
const incidentSMSAction = new incidentSMSActionModel();
|
||||
@@ -87,6 +88,13 @@ const _this = {
|
||||
customTwilioSettings.authToken
|
||||
);
|
||||
const message = await twilioClient.messages.create(options);
|
||||
await SmsCountService.create(
|
||||
userId,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Success'
|
||||
);
|
||||
return message;
|
||||
} else {
|
||||
const creds = await _this.getSettings();
|
||||
@@ -98,6 +106,14 @@ const _this = {
|
||||
if (!creds['sms-enabled']) {
|
||||
const error = new Error('SMS Not Enabled');
|
||||
error.code = 400;
|
||||
await SmsCountService.create(
|
||||
userId,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
return error;
|
||||
}
|
||||
const alertLimit = await AlertService.checkPhoneAlertsLimit(
|
||||
@@ -113,12 +129,27 @@ const _this = {
|
||||
incidentSMSAction.name = name;
|
||||
await incidentSMSAction.save();
|
||||
const message = await twilioClient.messages.create(options);
|
||||
await SmsCountService.create(
|
||||
userId,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Success'
|
||||
);
|
||||
return message;
|
||||
} else {
|
||||
const error = new Error(
|
||||
'Alerts limit reached for the day.'
|
||||
);
|
||||
error.code = 400;
|
||||
await SmsCountService.create(
|
||||
userId,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@@ -127,6 +158,14 @@ const _this = {
|
||||
'twillioService.sendIncidentCreatedMessage',
|
||||
error
|
||||
);
|
||||
await SmsCountService.create(
|
||||
userId,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -140,8 +179,10 @@ const _this = {
|
||||
projectName,
|
||||
projectId,
|
||||
componentName,
|
||||
statusPageUrl
|
||||
statusPageUrl,
|
||||
customFields
|
||||
) {
|
||||
let smsBody;
|
||||
try {
|
||||
let { template } = await _this.getTemplate(
|
||||
smsTemplate,
|
||||
@@ -154,8 +195,10 @@ const _this = {
|
||||
incidentType: incident.incidentType,
|
||||
componentName,
|
||||
statusPageUrl,
|
||||
...customFields,
|
||||
};
|
||||
template = template(data);
|
||||
smsBody = template;
|
||||
const customTwilioSettings = await _this.findByOne({
|
||||
projectId,
|
||||
enabled: true,
|
||||
@@ -174,13 +217,27 @@ const _this = {
|
||||
);
|
||||
|
||||
const message = await twilioClient.messages.create(options);
|
||||
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Success'
|
||||
);
|
||||
return message;
|
||||
} else {
|
||||
const creds = await _this.getSettings();
|
||||
if (!creds['sms-enabled']) {
|
||||
const error = new Error('SMS Not Enabled');
|
||||
error.code = 400;
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
return error;
|
||||
}
|
||||
const options = {
|
||||
@@ -200,11 +257,26 @@ const _this = {
|
||||
|
||||
if (alertLimit) {
|
||||
const message = await twilioClient.messages.create(options);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Success'
|
||||
);
|
||||
return message;
|
||||
} else {
|
||||
const error = new Error(
|
||||
'Alerts limit reached for the day.'
|
||||
);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
error.code = 400;
|
||||
return error;
|
||||
}
|
||||
@@ -214,6 +286,14 @@ const _this = {
|
||||
'twillioService.sendIncidentCreatedMessageToSubscriber',
|
||||
error
|
||||
);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -227,8 +307,10 @@ const _this = {
|
||||
projectName,
|
||||
projectId,
|
||||
componentName,
|
||||
statusUrl
|
||||
statusUrl,
|
||||
customFields
|
||||
) {
|
||||
let smsBody;
|
||||
try {
|
||||
let { template } = await _this.getTemplate(
|
||||
smsTemplate,
|
||||
@@ -241,8 +323,10 @@ const _this = {
|
||||
incidentType: incident.incidentType,
|
||||
componentName,
|
||||
statusPageUrl: statusUrl,
|
||||
...customFields,
|
||||
};
|
||||
template = template(data);
|
||||
smsBody = template;
|
||||
const customTwilioSettings = await _this.findByOne({
|
||||
projectId,
|
||||
enabled: true,
|
||||
@@ -261,13 +345,27 @@ const _this = {
|
||||
);
|
||||
|
||||
const message = await twilioClient.messages.create(options);
|
||||
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Success'
|
||||
);
|
||||
return message;
|
||||
} else {
|
||||
const creds = await _this.getSettings();
|
||||
if (!creds['sms-enabled']) {
|
||||
const error = new Error('SMS Not Enabled');
|
||||
error.code = 400;
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
return error;
|
||||
}
|
||||
const options = {
|
||||
@@ -287,11 +385,26 @@ const _this = {
|
||||
|
||||
if (alertLimit) {
|
||||
const message = await twilioClient.messages.create(options);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Success'
|
||||
);
|
||||
return message;
|
||||
} else {
|
||||
const error = new Error(
|
||||
'Alerts limit reached for the day.'
|
||||
);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
error.code = 400;
|
||||
return error;
|
||||
}
|
||||
@@ -301,6 +414,14 @@ const _this = {
|
||||
'twillioService.sendInvestigationNoteToSubscribers',
|
||||
error
|
||||
);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -314,8 +435,10 @@ const _this = {
|
||||
projectName,
|
||||
projectId,
|
||||
componentName,
|
||||
statusPageUrl
|
||||
statusPageUrl,
|
||||
customFields
|
||||
) {
|
||||
let smsBody;
|
||||
try {
|
||||
const _this = this;
|
||||
let { template } = await _this.getTemplate(
|
||||
@@ -329,8 +452,10 @@ const _this = {
|
||||
incidentType: incident.incidentType,
|
||||
componentName,
|
||||
statusPageUrl,
|
||||
...customFields,
|
||||
};
|
||||
template = template(data);
|
||||
smsBody = template;
|
||||
const customTwilioSettings = await _this.findByOne({
|
||||
projectId,
|
||||
enabled: true,
|
||||
@@ -347,12 +472,27 @@ const _this = {
|
||||
customTwilioSettings.authToken
|
||||
);
|
||||
const message = await twilioClient.messages.create(options);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Success'
|
||||
);
|
||||
return message;
|
||||
} else {
|
||||
const creds = await _this.getSettings();
|
||||
if (!creds['sms-enabled']) {
|
||||
const error = new Error('SMS Not Enabled');
|
||||
error.code = 400;
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
return error;
|
||||
}
|
||||
const options = {
|
||||
@@ -372,11 +512,26 @@ const _this = {
|
||||
|
||||
if (alertLimit) {
|
||||
const message = await twilioClient.messages.create(options);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Success'
|
||||
);
|
||||
return message;
|
||||
} else {
|
||||
const error = new Error(
|
||||
'Alerts limit reached for the day.'
|
||||
);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
error.code = 400;
|
||||
return error;
|
||||
}
|
||||
@@ -386,6 +541,14 @@ const _this = {
|
||||
'twillioService.sendIncidentAcknowldegedMessageToSubscriber',
|
||||
error
|
||||
);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -399,8 +562,10 @@ const _this = {
|
||||
projectName,
|
||||
projectId,
|
||||
componentName,
|
||||
statusPageUrl
|
||||
statusPageUrl,
|
||||
customFields
|
||||
) {
|
||||
let smsBody;
|
||||
try {
|
||||
const _this = this;
|
||||
let { template } = await _this.getTemplate(
|
||||
@@ -414,8 +579,10 @@ const _this = {
|
||||
incidentType: incident.incidentType,
|
||||
componentName,
|
||||
statusPageUrl,
|
||||
...customFields,
|
||||
};
|
||||
template = template(data);
|
||||
smsBody = template;
|
||||
const customTwilioSettings = await _this.findByOne({
|
||||
projectId,
|
||||
enabled: true,
|
||||
@@ -432,12 +599,27 @@ const _this = {
|
||||
customTwilioSettings.authToken
|
||||
);
|
||||
const message = await twilioClient.messages.create(options);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Success'
|
||||
);
|
||||
return message;
|
||||
} else {
|
||||
const creds = await _this.getSettings();
|
||||
if (!creds['sms-enabled']) {
|
||||
const error = new Error('SMS Not Enabled');
|
||||
error.code = 400;
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
return error;
|
||||
}
|
||||
const options = {
|
||||
@@ -457,11 +639,26 @@ const _this = {
|
||||
|
||||
if (alertLimit) {
|
||||
const message = await twilioClient.messages.create(options);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Success'
|
||||
);
|
||||
return message;
|
||||
} else {
|
||||
const error = new Error(
|
||||
'Alerts limit reached for the day.'
|
||||
);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
error.code = 400;
|
||||
return error;
|
||||
}
|
||||
@@ -471,6 +668,14 @@ const _this = {
|
||||
'twillioService.sendIncidentResolvedMessageToSubscriber',
|
||||
error
|
||||
);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
number,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -489,6 +694,13 @@ const _this = {
|
||||
);
|
||||
|
||||
const message = await twilioClient.messages.create(options);
|
||||
await SmsCountService.create(
|
||||
null,
|
||||
options.to,
|
||||
null,
|
||||
options.body,
|
||||
'Success'
|
||||
);
|
||||
|
||||
return message;
|
||||
} catch (error) {
|
||||
@@ -512,9 +724,9 @@ const _this = {
|
||||
accessToken,
|
||||
incidentId,
|
||||
projectId,
|
||||
redialCount,
|
||||
incidentType
|
||||
) {
|
||||
let callBody;
|
||||
try {
|
||||
const message =
|
||||
'<Say voice="alice">This is an alert from Fyipe. Your monitor ' +
|
||||
@@ -524,6 +736,7 @@ const _this = {
|
||||
'. Please go to Fyipe Dashboard or Mobile app to acknowledge or resolve this incident.</Say>';
|
||||
const hangUp = '<Hangup />';
|
||||
const twiml = '<Response> ' + message + hangUp + '</Response>';
|
||||
callBody = twiml;
|
||||
const options = {
|
||||
twiml: twiml,
|
||||
to: number,
|
||||
@@ -540,6 +753,13 @@ const _this = {
|
||||
customTwilioSettings.authToken
|
||||
);
|
||||
const call = await twilioClient.calls.create(options);
|
||||
await CallLogsService.create(
|
||||
'+15005550006',
|
||||
number,
|
||||
projectId,
|
||||
callBody,
|
||||
'Success'
|
||||
);
|
||||
return call;
|
||||
} else {
|
||||
const creds = await _this.getSettings();
|
||||
@@ -550,6 +770,14 @@ const _this = {
|
||||
if (!creds['call-enabled']) {
|
||||
const error = new Error('Call Not Enabled');
|
||||
error.code = 400;
|
||||
await CallLogsService.create(
|
||||
'+15005550006',
|
||||
number,
|
||||
projectId,
|
||||
callBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -560,6 +788,13 @@ const _this = {
|
||||
options.from = creds.phone;
|
||||
if (twilioClient) {
|
||||
const call = await twilioClient.calls.create(options);
|
||||
await CallLogsService.create(
|
||||
'+15005550006',
|
||||
number,
|
||||
projectId,
|
||||
callBody,
|
||||
'Success'
|
||||
);
|
||||
return call;
|
||||
}
|
||||
} else {
|
||||
@@ -567,11 +802,27 @@ const _this = {
|
||||
'Alerts limit reached for the day.'
|
||||
);
|
||||
error.code = 400;
|
||||
await CallLogsService.create(
|
||||
'+15005550006',
|
||||
number,
|
||||
projectId,
|
||||
callBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
ErrorService.log('twillioService.sendIncidentCreatedCall', error);
|
||||
await CallLogsService.create(
|
||||
'+15005550006',
|
||||
number,
|
||||
projectId,
|
||||
callBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -597,6 +848,7 @@ const _this = {
|
||||
projectId,
|
||||
validationResult
|
||||
) {
|
||||
let smsBody;
|
||||
try {
|
||||
const customTwilioSettings = await _this.findByOne({
|
||||
projectId,
|
||||
@@ -612,6 +864,7 @@ const _this = {
|
||||
.substr(2, 6);
|
||||
if (customTwilioSettings) {
|
||||
const template = `Your verification code: ${alertPhoneVerificationCode}`;
|
||||
smsBody = template;
|
||||
const options = {
|
||||
body: template,
|
||||
from: customTwilioSettings.phoneNumber,
|
||||
@@ -652,13 +905,20 @@ const _this = {
|
||||
throw error;
|
||||
}
|
||||
const template = `Your verification code: ${alertPhoneVerificationCode}`;
|
||||
smsBody = template;
|
||||
const options = {
|
||||
body: template,
|
||||
from: creds.phone,
|
||||
to,
|
||||
};
|
||||
await twilioClient.messages.create(options);
|
||||
await SmsCountService.create(userId, to, projectId);
|
||||
await SmsCountService.create(
|
||||
userId,
|
||||
to,
|
||||
projectId,
|
||||
options.body,
|
||||
'Success'
|
||||
);
|
||||
await UserService.updateOneBy(
|
||||
{ _id: userId },
|
||||
{
|
||||
@@ -672,12 +932,28 @@ const _this = {
|
||||
const error = new Error(
|
||||
'Alerts limit reached for the day.'
|
||||
);
|
||||
await SmsCountService.create(
|
||||
userId,
|
||||
to,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
ErrorService.log('twillioService.sendVerificationSMS', error);
|
||||
await SmsCountService.create(
|
||||
userId,
|
||||
to,
|
||||
projectId,
|
||||
smsBody,
|
||||
'Error',
|
||||
error.message
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -43,11 +43,11 @@ module.exports = {
|
||||
|
||||
const job = async monitor => {
|
||||
try {
|
||||
const { stat: validUp, reasons } = await (monitor &&
|
||||
const { stat: validUp, successReasons } = await (monitor &&
|
||||
monitor.criteria &&
|
||||
monitor.criteria.up
|
||||
? ProbeService.conditions(monitor.type, monitor.criteria.up)
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, successReasons: [] });
|
||||
const { stat: validDown } = await (monitor &&
|
||||
monitor.criteria &&
|
||||
monitor.criteria.down
|
||||
@@ -57,7 +57,9 @@ const job = async monitor => {
|
||||
await ProbeService.saveMonitorLog({
|
||||
monitorId: monitor._id,
|
||||
status: 'offline',
|
||||
reason: reasons,
|
||||
reason: await successReasons.filter(function(item, pos, self) {
|
||||
return self.indexOf(item) == pos;
|
||||
}),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
7636
backend/package-lock.json
generated
7636
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -204,6 +204,7 @@ app.use(['/version', '/api/version'], require('./backend/api/version'));
|
||||
app.use(['/tutorial', '/api/tutorial'], require('./backend/api/tutorial'));
|
||||
app.use(['/audit-logs', '/api/audit-logs'], require('./backend/api/auditLogs'));
|
||||
app.use(['/email-logs', '/api/email-logs'], require('./backend/api/emailLogs'));
|
||||
app.use(['/call-logs', '/api/call-logs'], require('./backend/api/callLogs'));
|
||||
app.use(['/sms-logs', '/api/sms-logs'], require('./backend/api/smsLogs'));
|
||||
app.use(['/component', '/api/component'], require('./backend/api/component'));
|
||||
app.use(
|
||||
|
||||
176
backend/test/customField.test.js
Normal file
176
backend/test/customField.test.js
Normal file
@@ -0,0 +1,176 @@
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
process.env.PORT = 3020;
|
||||
process.env.IS_SAAS_SERVICE = true;
|
||||
const chai = require('chai');
|
||||
const expect = require('chai').expect;
|
||||
const userData = require('./data/user');
|
||||
const app = require('../server');
|
||||
chai.use(require('chai-http'));
|
||||
const request = chai.request.agent(app);
|
||||
const GlobalConfig = require('./utils/globalConfig');
|
||||
const { createUser } = require('./utils/userSignUp');
|
||||
const VerificationTokenModel = require('../backend/models/verificationToken');
|
||||
const AirtableService = require('../backend/services/airtableService');
|
||||
const UserService = require('../backend/services/userService');
|
||||
const ProjectService = require('../backend/services/projectService');
|
||||
const ComponentService = require('../backend/services/componentService');
|
||||
const IncidentCustomFieldService = require('../backend/services/customFieldService');
|
||||
|
||||
describe('Incident Custom Field API', function() {
|
||||
const timeout = 30000;
|
||||
let projectId, userId, token, authorization, customFieldId;
|
||||
|
||||
const incidentFieldText = {
|
||||
fieldName: 'inTextField',
|
||||
fieldType: 'text',
|
||||
},
|
||||
incidentFieldNumber = {
|
||||
fieldName: 'inNumField',
|
||||
fieldType: 'number',
|
||||
};
|
||||
|
||||
this.timeout(timeout);
|
||||
before(function(done) {
|
||||
GlobalConfig.initTestConfig().then(function() {
|
||||
createUser(request, userData.user, function(err, res) {
|
||||
const project = res.body.project;
|
||||
projectId = project._id;
|
||||
userId = res.body.id;
|
||||
|
||||
VerificationTokenModel.findOne({ userId }, function(
|
||||
err,
|
||||
verificationToken
|
||||
) {
|
||||
request
|
||||
.get(`/user/confirmation/${verificationToken.token}`)
|
||||
.redirects(0)
|
||||
.end(function() {
|
||||
request
|
||||
.post('/user/login')
|
||||
.send({
|
||||
email: userData.user.email,
|
||||
password: userData.user.password,
|
||||
})
|
||||
.end(function(err, res) {
|
||||
token = res.body.tokens.jwtAccessToken;
|
||||
authorization = `Basic ${token}`;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
await GlobalConfig.removeTestConfig();
|
||||
await ProjectService.hardDeleteBy({ _id: projectId });
|
||||
await UserService.hardDeleteBy({
|
||||
email: userData.user.email,
|
||||
});
|
||||
await ComponentService.hardDeleteBy({ projectId });
|
||||
await IncidentCustomFieldService.hardDeleteBy({ projectId });
|
||||
await AirtableService.deleteAll({ tableName: 'User' });
|
||||
});
|
||||
|
||||
it('should not create an incident custom field when field name is missing or not specified', function(done) {
|
||||
request
|
||||
.post(`/customField/${projectId}`)
|
||||
.send({ fieldType: 'text' })
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(400);
|
||||
expect(res.body.message).to.be.equal('Field name is required');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not create an incident custom field when field type is missing or not specified', function(done) {
|
||||
request
|
||||
.post(`/customField/${projectId}`)
|
||||
.send({ fieldName: 'missingType' })
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(400);
|
||||
expect(res.body.message).to.be.equal('Field type is required');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup custom fields for all incidents in a project (text)', function(done) {
|
||||
request
|
||||
.post(`/customField/${projectId}`)
|
||||
.send(incidentFieldText)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
customFieldId = res.body._id;
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.fieldName).to.be.equal(
|
||||
incidentFieldText.fieldName
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not create incident custom field with an existing name in a project', function(done) {
|
||||
request
|
||||
.post(`/customField/${projectId}`)
|
||||
.send(incidentFieldText)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(400);
|
||||
expect(res.body.message).to.be.equal(
|
||||
'Custom field with this name already exist'
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should update a particular incident custom field in a project', function(done) {
|
||||
incidentFieldText.fieldName = 'newName';
|
||||
|
||||
request
|
||||
.put(`/customField/${projectId}/${customFieldId}`)
|
||||
.send(incidentFieldText)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.fieldName).to.be.equal(
|
||||
incidentFieldText.fieldName
|
||||
);
|
||||
expect(String(res.body._id)).to.be.equal(String(customFieldId));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should list all the incident custom fields in a project', function(done) {
|
||||
// add one more monitor custom field
|
||||
request
|
||||
.post(`/customField/${projectId}`)
|
||||
.send(incidentFieldNumber)
|
||||
.set('Authorization', authorization)
|
||||
.end(function() {
|
||||
request
|
||||
.get(`/customField/${projectId}?skip=0&limit=10`)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.count).to.be.equal(2);
|
||||
expect(res.body.data).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete a particular monitor custom field in a project', function(done) {
|
||||
request
|
||||
.delete(`/customField/${projectId}/${customFieldId}`)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(String(res.body._id)).to.be.equal(String(customFieldId));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
72
backend/test/data/incomingHttpRequest.js
Normal file
72
backend/test/data/incomingHttpRequest.js
Normal file
@@ -0,0 +1,72 @@
|
||||
module.exports = {
|
||||
incidentRequest: {
|
||||
name: 'pyInt',
|
||||
isDefault: true,
|
||||
createIncident: true,
|
||||
incidentTitle: 'Test Incident',
|
||||
incidentType: 'offline',
|
||||
incidentDescription:
|
||||
'This is a sample incident to test incoming http request',
|
||||
filterMatch: 'any',
|
||||
filters: [
|
||||
{
|
||||
filterCondition: 'equalTo',
|
||||
filterCriteria: 'monitorField',
|
||||
filterText: 'testing',
|
||||
},
|
||||
],
|
||||
monitors: [],
|
||||
},
|
||||
acknowledgeRequest: {
|
||||
name: 'ack',
|
||||
acknowledgeIncident: true,
|
||||
filterMatch: 'any',
|
||||
filters: [
|
||||
{
|
||||
filterCondition: 'greaterThanOrEqualTo',
|
||||
filterCriteria: 'incidentId',
|
||||
filterText: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
resolveRequest: {
|
||||
name: 'resolve',
|
||||
resolveIncident: true,
|
||||
filterMatch: 'any',
|
||||
filters: [
|
||||
{
|
||||
filterCondition: 'greaterThanOrEqualTo',
|
||||
filterCriteria: 'incidentId',
|
||||
filterText: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
incidentNoteRequest: {
|
||||
name: 'incidentNote',
|
||||
updateIncidentNote: true,
|
||||
filterMatch: 'all',
|
||||
filters: [
|
||||
{
|
||||
filterCondition: 'greaterThanOrEqualTo',
|
||||
filterCriteria: 'incidentId',
|
||||
filterText: 1,
|
||||
},
|
||||
],
|
||||
incidentState: 'update',
|
||||
noteContent: 'This is a sample incident note',
|
||||
},
|
||||
internalNoteRequest: {
|
||||
name: 'internalNote',
|
||||
updateInternalNote: true,
|
||||
incidentState: 'investigating',
|
||||
noteContent: 'This is a sample internal note',
|
||||
filterMatch: 'all',
|
||||
filters: [
|
||||
{
|
||||
filterCondition: 'equalTo',
|
||||
filterCriteria: 'incidentId',
|
||||
filterText: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -86,6 +86,7 @@ describe('Enterprise Alert API', function() {
|
||||
monitorId,
|
||||
alertVia: 'email',
|
||||
incidentId,
|
||||
eventType: 'identified',
|
||||
})
|
||||
.end(function(err, res) {
|
||||
alertId = res.body._id;
|
||||
|
||||
336
backend/test/incomingHttpRequest.test.js
Normal file
336
backend/test/incomingHttpRequest.test.js
Normal file
@@ -0,0 +1,336 @@
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
process.env.PORT = 3020;
|
||||
process.env.IS_SAAS_SERVICE = true;
|
||||
const chai = require('chai');
|
||||
const expect = require('chai').expect;
|
||||
const userData = require('./data/user');
|
||||
const app = require('../server');
|
||||
chai.use(require('chai-http'));
|
||||
const request = chai.request.agent(app);
|
||||
const GlobalConfig = require('./utils/globalConfig');
|
||||
const { createUser } = require('./utils/userSignUp');
|
||||
const VerificationTokenModel = require('../backend/models/verificationToken');
|
||||
const AirtableService = require('../backend/services/airtableService');
|
||||
const UserService = require('../backend/services/userService');
|
||||
const ProjectService = require('../backend/services/projectService');
|
||||
const ComponentService = require('../backend/services/componentService');
|
||||
const IncidentPrioritiesService = require('../backend/services/incidentPrioritiesService');
|
||||
const MonitorService = require('../backend/services/monitorService');
|
||||
const IncomingHttpRequestService = require('../backend/services/incomingRequestService');
|
||||
const MonitorCustomFieldService = require('../backend/services/monitorCustomField');
|
||||
const IncidentCustomFieldService = require('../backend/services/customFieldService');
|
||||
const IncidentService = require('../backend/services/incidentService');
|
||||
const axios = require('axios');
|
||||
const {
|
||||
resolveRequest,
|
||||
internalNoteRequest,
|
||||
incidentRequest,
|
||||
incidentNoteRequest,
|
||||
acknowledgeRequest,
|
||||
} = require('./data/incomingHttpRequest');
|
||||
|
||||
describe('Incoming HTTP Request API', function() {
|
||||
const timeout = 30000;
|
||||
let projectId,
|
||||
componentId,
|
||||
userId,
|
||||
token,
|
||||
incidentPriorityId,
|
||||
monitorId,
|
||||
requestId,
|
||||
authorization,
|
||||
createIncidentUrl,
|
||||
acknowledgeIncidentUrl,
|
||||
resolveIncidentUrl,
|
||||
incidentNoteUrl,
|
||||
internalNoteUrl;
|
||||
|
||||
this.timeout(timeout);
|
||||
before(function(done) {
|
||||
GlobalConfig.initTestConfig().then(function() {
|
||||
createUser(request, userData.user, function(err, res) {
|
||||
const project = res.body.project;
|
||||
projectId = project._id;
|
||||
userId = res.body.id;
|
||||
|
||||
VerificationTokenModel.findOne({ userId }, function(
|
||||
err,
|
||||
verificationToken
|
||||
) {
|
||||
request
|
||||
.get(`/user/confirmation/${verificationToken.token}`)
|
||||
.redirects(0)
|
||||
.end(function() {
|
||||
request
|
||||
.post('/user/login')
|
||||
.send({
|
||||
email: userData.user.email,
|
||||
password: userData.user.password,
|
||||
})
|
||||
.end(function(err, res) {
|
||||
token = res.body.tokens.jwtAccessToken;
|
||||
authorization = `Basic ${token}`;
|
||||
|
||||
request
|
||||
.post(`/component/${projectId}`)
|
||||
.set('Authorization', authorization)
|
||||
.send({ name: 'Test Component' })
|
||||
.end(function(err, res) {
|
||||
componentId = res.body._id;
|
||||
|
||||
request
|
||||
.post(`/monitor/${projectId}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
authorization
|
||||
)
|
||||
.send({
|
||||
name: 'testMonitor',
|
||||
criteria: {},
|
||||
componentId,
|
||||
projectId,
|
||||
type: 'manual',
|
||||
data: { description: null },
|
||||
customFields: [
|
||||
{
|
||||
fieldName:
|
||||
'monitorField',
|
||||
fieldValue:
|
||||
'testing',
|
||||
},
|
||||
],
|
||||
})
|
||||
.end(function(err, res) {
|
||||
monitorId = res.body._id;
|
||||
|
||||
MonitorCustomFieldService.create(
|
||||
{
|
||||
projectId,
|
||||
fieldName:
|
||||
'monitorField',
|
||||
fieldType: 'text',
|
||||
}
|
||||
).then(function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
await GlobalConfig.removeTestConfig();
|
||||
await ProjectService.hardDeleteBy({ _id: projectId });
|
||||
await UserService.hardDeleteBy({
|
||||
email: userData.user.email,
|
||||
});
|
||||
await ComponentService.hardDeleteBy({ projectId });
|
||||
await MonitorService.hardDeleteBy({ projectId });
|
||||
await IncomingHttpRequestService.hardDeleteBy({ projectId });
|
||||
await MonitorCustomFieldService.hardDeleteBy({ projectId });
|
||||
await IncidentCustomFieldService.hardDeleteBy({ projectId });
|
||||
await IncidentService.hardDeleteBy({ projectId });
|
||||
await AirtableService.deleteAll({ tableName: 'User' });
|
||||
});
|
||||
|
||||
it('should create an incoming http request (Create Incident)', function(done) {
|
||||
IncidentPrioritiesService.findOne({ projectId }).then(function(
|
||||
priority
|
||||
) {
|
||||
// fetch one of the priorities
|
||||
incidentPriorityId = priority._id;
|
||||
incidentRequest.incidentPriority = incidentPriorityId;
|
||||
|
||||
request
|
||||
.post(`/incoming-request/${projectId}/create-request-url`)
|
||||
.set('Authorization', authorization)
|
||||
.send(incidentRequest)
|
||||
.end(function(err, res) {
|
||||
requestId = res.body._id;
|
||||
createIncidentUrl = res.body.url;
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.name).to.be.equal(incidentRequest.name);
|
||||
expect(res.body.filters).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an incoming http request (Acknowledge Incident)', function(done) {
|
||||
request
|
||||
.post(`/incoming-request/${projectId}/create-request-url`)
|
||||
.set('Authorization', authorization)
|
||||
.send(acknowledgeRequest)
|
||||
.end(function(err, res) {
|
||||
acknowledgeIncidentUrl = res.body.url;
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.name).to.be.equal(acknowledgeRequest.name);
|
||||
expect(res.body.filters).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an incoming http request (Resolve Incident)', function(done) {
|
||||
request
|
||||
.post(`/incoming-request/${projectId}/create-request-url`)
|
||||
.set('Authorization', authorization)
|
||||
.send(resolveRequest)
|
||||
.end(function(err, res) {
|
||||
resolveIncidentUrl = res.body.url;
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.name).to.be.equal(resolveRequest.name);
|
||||
expect(res.body.filters).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an incoming http request (Update incident note)', function(done) {
|
||||
request
|
||||
.post(`/incoming-request/${projectId}/create-request-url`)
|
||||
.send(incidentNoteRequest)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
incidentNoteUrl = res.body.url;
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.name).to.be.equal(incidentNoteRequest.name);
|
||||
expect(res.body.filterMatch).to.be.equal(
|
||||
incidentNoteRequest.filterMatch
|
||||
);
|
||||
expect(res.body.filters).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an incoming http request (Update internal note)', function(done) {
|
||||
request
|
||||
.post(`/incoming-request/${projectId}/create-request-url`)
|
||||
.send(internalNoteRequest)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
internalNoteUrl = res.body.url;
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.name).to.be.equal(internalNoteRequest.name);
|
||||
expect(res.body.filters).to.be.an('array');
|
||||
expect(res.body.filterMatch).to.be.equal(
|
||||
internalNoteRequest.filterMatch
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should update an incoming http request', function(done) {
|
||||
const update = {
|
||||
name: 'updateName',
|
||||
};
|
||||
|
||||
request
|
||||
.put(`/incoming-request/${projectId}/update/${requestId}`)
|
||||
.send(update)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.name).to.be.equal(update.name);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should list all the created incoming http request in a project', function(done) {
|
||||
incidentRequest.name = 'anotherOne';
|
||||
incidentRequest.isDefault = false;
|
||||
incidentRequest.monitors = [monitorId];
|
||||
// add one more incoming http request
|
||||
request
|
||||
.post(`/incoming-request/${projectId}/create-request-url`)
|
||||
.set('Authorization', authorization)
|
||||
.send(incidentRequest)
|
||||
.end(function(err, res) {
|
||||
requestId = res.body._id;
|
||||
|
||||
request
|
||||
.get(`/incoming-request/${projectId}/all-incoming-request`)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.data).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an incident with incoming http request url', function(done) {
|
||||
axios({
|
||||
method: 'post',
|
||||
url: createIncidentUrl,
|
||||
}).then(function(res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.data.status).to.be.equal('success');
|
||||
expect(res.data.created_incidents).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should acknowledge an incident with an incoming http request url', function(done) {
|
||||
axios({
|
||||
method: 'post',
|
||||
url: acknowledgeIncidentUrl,
|
||||
}).then(function(res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.data.status).to.be.equal('success');
|
||||
expect(res.data.acknowledged_incidents).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should resolve an incident with an incoming http request url', function(done) {
|
||||
axios({
|
||||
method: 'post',
|
||||
url: resolveIncidentUrl,
|
||||
}).then(function(res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.data.status).to.be.equal('success');
|
||||
expect(res.data.resolved_incidents).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should add incident note with an incoming http request url', function(done) {
|
||||
// it should also work for a get request
|
||||
axios({
|
||||
method: 'get',
|
||||
url: incidentNoteUrl,
|
||||
}).then(function(res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.data.status).to.be.equal('success');
|
||||
expect(res.data.notes_addedTo).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should add internal note with an incoming http request url', function(done) {
|
||||
axios({
|
||||
method: 'get',
|
||||
url: internalNoteUrl,
|
||||
}).then(function(res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.data.status).to.be.equal('success');
|
||||
expect(res.data.notes_addedTo).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete an incoming http request in project', function(done) {
|
||||
request
|
||||
.delete(`/incoming-request/${projectId}/remove/${requestId}`)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(String(res.body._id)).to.be.equal(String(requestId));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,8 @@
|
||||
try {
|
||||
require('./monitorSla.test');
|
||||
require('./customField.test');
|
||||
require('./incomingHttpRequest.test');
|
||||
require('./monitorCustomField.test');
|
||||
require('./adminCredentials.test');
|
||||
require('./incidentCommunicationSla.test');
|
||||
require('./adminCredentials.test');
|
||||
|
||||
180
backend/test/monitorCustomField.test.js
Normal file
180
backend/test/monitorCustomField.test.js
Normal file
@@ -0,0 +1,180 @@
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
process.env.PORT = 3020;
|
||||
process.env.IS_SAAS_SERVICE = true;
|
||||
const chai = require('chai');
|
||||
const expect = require('chai').expect;
|
||||
const userData = require('./data/user');
|
||||
const app = require('../server');
|
||||
chai.use(require('chai-http'));
|
||||
const request = chai.request.agent(app);
|
||||
const GlobalConfig = require('./utils/globalConfig');
|
||||
const { createUser } = require('./utils/userSignUp');
|
||||
const VerificationTokenModel = require('../backend/models/verificationToken');
|
||||
const AirtableService = require('../backend/services/airtableService');
|
||||
const UserService = require('../backend/services/userService');
|
||||
const ProjectService = require('../backend/services/projectService');
|
||||
const ComponentService = require('../backend/services/componentService');
|
||||
const MonitorCustomFieldService = require('../backend/services/monitorCustomField');
|
||||
|
||||
describe('Monitor Custom Field API', function() {
|
||||
const timeout = 30000;
|
||||
let projectId, userId, token, authorization, monitorCustomFieldId;
|
||||
|
||||
const monitorFieldText = {
|
||||
fieldName: 'textField',
|
||||
fieldType: 'text',
|
||||
},
|
||||
monitorFieldNumber = {
|
||||
fieldName: 'numField',
|
||||
fieldType: 'number',
|
||||
};
|
||||
|
||||
this.timeout(timeout);
|
||||
before(function(done) {
|
||||
GlobalConfig.initTestConfig().then(function() {
|
||||
createUser(request, userData.user, function(err, res) {
|
||||
const project = res.body.project;
|
||||
projectId = project._id;
|
||||
userId = res.body.id;
|
||||
|
||||
VerificationTokenModel.findOne({ userId }, function(
|
||||
err,
|
||||
verificationToken
|
||||
) {
|
||||
request
|
||||
.get(`/user/confirmation/${verificationToken.token}`)
|
||||
.redirects(0)
|
||||
.end(function() {
|
||||
request
|
||||
.post('/user/login')
|
||||
.send({
|
||||
email: userData.user.email,
|
||||
password: userData.user.password,
|
||||
})
|
||||
.end(function(err, res) {
|
||||
token = res.body.tokens.jwtAccessToken;
|
||||
authorization = `Basic ${token}`;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
await GlobalConfig.removeTestConfig();
|
||||
await ProjectService.hardDeleteBy({ _id: projectId });
|
||||
await UserService.hardDeleteBy({
|
||||
email: userData.user.email,
|
||||
});
|
||||
await ComponentService.hardDeleteBy({ projectId });
|
||||
await MonitorCustomFieldService.hardDeleteBy({ projectId });
|
||||
await AirtableService.deleteAll({ tableName: 'User' });
|
||||
});
|
||||
|
||||
it('should not create a monitor custom field when field name is missing or not specified', function(done) {
|
||||
request
|
||||
.post(`/monitorCustomField/${projectId}`)
|
||||
.send({ fieldType: 'text' })
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(400);
|
||||
expect(res.body.message).to.be.equal('Field name is required');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not create a monitor custom field when field type is missing or not specified', function(done) {
|
||||
request
|
||||
.post(`/monitorCustomField/${projectId}`)
|
||||
.send({ fieldName: 'missingType' })
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(400);
|
||||
expect(res.body.message).to.be.equal('Field type is required');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup custom fields for all monitors in a project (text)', function(done) {
|
||||
request
|
||||
.post(`/monitorCustomField/${projectId}`)
|
||||
.send(monitorFieldText)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
monitorCustomFieldId = res.body._id;
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.fieldName).to.be.equal(
|
||||
monitorFieldText.fieldName
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not create monitor custom field with an existing name in a project', function(done) {
|
||||
request
|
||||
.post(`/monitorCustomField/${projectId}`)
|
||||
.send(monitorFieldText)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(400);
|
||||
expect(res.body.message).to.be.equal(
|
||||
'Custom field with this name already exist'
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should update a particular monitor custom field in a project', function(done) {
|
||||
monitorFieldText.fieldName = 'newName';
|
||||
|
||||
request
|
||||
.put(`/monitorCustomField/${projectId}/${monitorCustomFieldId}`)
|
||||
.send(monitorFieldText)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.fieldName).to.be.equal(
|
||||
monitorFieldText.fieldName
|
||||
);
|
||||
expect(String(res.body._id)).to.be.equal(
|
||||
String(monitorCustomFieldId)
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should list all the monitor custom fields in a project', function(done) {
|
||||
// add one more monitor custom field
|
||||
request
|
||||
.post(`/monitorCustomField/${projectId}`)
|
||||
.send(monitorFieldNumber)
|
||||
.set('Authorization', authorization)
|
||||
.end(function() {
|
||||
request
|
||||
.get(`/monitorCustomField/${projectId}?skip=0&limit=10`)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body.count).to.be.equal(2);
|
||||
expect(res.body.data).to.be.an('array');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete a particular monitor custom field in a project', function(done) {
|
||||
request
|
||||
.delete(`/monitorCustomField/${projectId}/${monitorCustomFieldId}`)
|
||||
.set('Authorization', authorization)
|
||||
.end(function(err, res) {
|
||||
expect(res).to.have.status(200);
|
||||
expect(String(res.body._id)).to.be.equal(
|
||||
String(monitorCustomFieldId)
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@ build_n_test_accounts:
|
||||
- if [[ $next_stage == *"skip"* ]]; then exit ${CI_JOB_SKIP_EXIT_CODE:-0}; fi
|
||||
- curl -sSL https://get.docker.com/ | sh #Install docker.
|
||||
- sudo apt-get update
|
||||
- sudo apt install -y gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
|
||||
- sudo apt install -y gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget libgbm-dev
|
||||
- echo "Setup machine for running puppeteer tests"
|
||||
- sudo docker stop $(sudo docker ps -aq) || echo 'No docker containers'
|
||||
- sudo docker rm $(sudo docker ps -aq) || echo 'No docker containers'
|
||||
@@ -19,6 +19,7 @@ build_n_test_accounts:
|
||||
- sudo docker build -t fyipeproject/dashboard:3.0.$CI_PIPELINE_IID ./dashboard
|
||||
- sudo docker run --env-file ./dashboard/.env -e IS_SAAS_SERVICE=true -p 3000:3000 -d fyipeproject/dashboard:3.0.$CI_PIPELINE_IID
|
||||
- sudo docker ps
|
||||
- npm ci
|
||||
- cd accounts
|
||||
- npm ci
|
||||
# try building, if there are warnings then this will fail
|
||||
@@ -42,7 +43,7 @@ build_n_test_enterprise_accounts:
|
||||
- if [[ $next_stage == *"skip"* ]]; then exit ${CI_JOB_SKIP_EXIT_CODE:-0}; fi
|
||||
- curl -sSL https://get.docker.com/ | sh #Install docker.
|
||||
- sudo apt-get update
|
||||
- sudo apt install -y gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
|
||||
- sudo apt install -y gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget libgbm-dev
|
||||
- echo "Setup machine for running puppeteer tests"
|
||||
- sudo docker stop $(sudo docker ps -aq) || echo 'No docker containers'
|
||||
- sudo docker rm $(sudo docker ps -aq) || echo 'No docker containers'
|
||||
@@ -58,6 +59,7 @@ build_n_test_enterprise_accounts:
|
||||
- sudo docker build -t fyipeproject/dashboard:3.0.$CI_PIPELINE_IID ./dashboard
|
||||
- sudo docker run --env-file ./dashboard/.env -p 3000:3000 -d fyipeproject/dashboard:3.0.$CI_PIPELINE_IID
|
||||
- sudo docker ps
|
||||
- npm ci
|
||||
- cd accounts
|
||||
- npm ci
|
||||
# try building, if there are warnings then this will fail
|
||||
|
||||
@@ -20,6 +20,7 @@ mobile_lighthouse_accounts:
|
||||
- sudo docker build -t fyipeproject/dashboard:3.0.$CI_PIPELINE_IID ./dashboard
|
||||
- sudo docker run --env-file ./dashboard/.env -e IS_SAAS_SERVICE=true -p 3000:3000 -d fyipeproject/dashboard:3.0.$CI_PIPELINE_IID
|
||||
- sudo docker ps
|
||||
- npm ci
|
||||
- cd accounts
|
||||
- npm ci
|
||||
- export CHROME_PATH="$(pwd)/node_modules/puppeteer/.local-chromium/linux-722234/chrome-linux/chrome"
|
||||
@@ -57,6 +58,7 @@ desktop_lighthouse_accounts:
|
||||
- sudo docker build -t fyipeproject/dashboard:3.0.$CI_PIPELINE_IID ./dashboard
|
||||
- sudo docker run --env-file ./dashboard/.env -e IS_SAAS_SERVICE=true -p 3000:3000 -d fyipeproject/dashboard:3.0.$CI_PIPELINE_IID
|
||||
- sudo docker ps
|
||||
- npm ci
|
||||
- cd accounts
|
||||
- npm ci
|
||||
- export CHROME_PATH="$(pwd)/node_modules/puppeteer/.local-chromium/linux-722234/chrome-linux/chrome"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user