mirror of
https://github.com/valitydev/dashboard.git
synced 2024-11-06 02:25:23 +00:00
Shops table update (#316)
* implemented basic logic of new shop tables * fixed karma * refactored implementation, removed unused code * added tests * changed tests configs * fixed linter * fixed review issues * fixed ci * test ci * fixed startWith generics, added todos * fixed review issues * fixed review issues * fixed review issues * fixed review issues * fixed reload bug * added tslint rules, fixed review issues, added some tests * fixed build errors Co-authored-by: egrigorieva <e.grigoreva@rbkmoney.com>
This commit is contained in:
parent
06904e97b2
commit
5861fe2c39
@ -26,7 +26,7 @@ module.exports = function (config) {
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
browsers: ['ChromeHeadless'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
|
281
package-lock.json
generated
281
package-lock.json
generated
@ -3246,6 +3246,16 @@
|
||||
"source-map": "^0.6.1"
|
||||
}
|
||||
},
|
||||
"@types/yauzl": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
|
||||
"integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
|
||||
@ -4051,6 +4061,40 @@
|
||||
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
|
||||
"dev": true
|
||||
},
|
||||
"bl": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
|
||||
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
|
||||
"dev": true,
|
||||
"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==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"blob": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||
@ -5756,6 +5800,12 @@
|
||||
"integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
|
||||
"dev": true
|
||||
},
|
||||
"devtools-protocol": {
|
||||
"version": "0.0.818844",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.818844.tgz",
|
||||
"integrity": "sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg==",
|
||||
"dev": true
|
||||
},
|
||||
"dezalgo": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
|
||||
@ -6873,6 +6923,12 @@
|
||||
"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==",
|
||||
"dev": true
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
|
||||
@ -9197,6 +9253,15 @@
|
||||
"integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==",
|
||||
"dev": true
|
||||
},
|
||||
"jasmine-marbles": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-marbles/-/jasmine-marbles-0.6.0.tgz",
|
||||
"integrity": "sha512-1uzgjEesEeCb+r+v46qn5x326TiGqk5SUZa+A3O+XnMCjG/pGcUOhL9Xsg5L7gLC6RFHyWGTkB5fei4rcvIOiQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "^4.5.0"
|
||||
}
|
||||
},
|
||||
"jasmine-spec-reporter": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz",
|
||||
@ -10414,6 +10479,12 @@
|
||||
"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==",
|
||||
"dev": true
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.24.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||
@ -10593,6 +10664,12 @@
|
||||
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
|
||||
"dev": true
|
||||
},
|
||||
"node-fetch-npm": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz",
|
||||
@ -12574,6 +12651,12 @@
|
||||
"ipaddr.js": "1.9.0"
|
||||
}
|
||||
},
|
||||
"proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"dev": true
|
||||
},
|
||||
"prr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
||||
@ -12645,6 +12728,144 @@
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"dev": true
|
||||
},
|
||||
"puppeteer": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.5.0.tgz",
|
||||
"integrity": "sha512-OM8ZvTXAhfgFA7wBIIGlPQzvyEETzDjeRa4mZRCRHxYL+GNH5WAuYUQdja3rpWZvkX/JKqmuVgbsxDNsDFjMEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.0",
|
||||
"devtools-protocol": "0.0.818844",
|
||||
"extract-zip": "^2.0.0",
|
||||
"https-proxy-agent": "^4.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"pkg-dir": "^4.2.0",
|
||||
"progress": "^2.0.1",
|
||||
"proxy-from-env": "^1.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"tar-fs": "^2.0.0",
|
||||
"unbzip2-stream": "^1.3.3",
|
||||
"ws": "^7.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"agent-base": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
|
||||
"integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==",
|
||||
"dev": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
||||
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"extract-zip": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/yauzl": "^2.9.1",
|
||||
"debug": "^4.1.1",
|
||||
"get-stream": "^5.1.0",
|
||||
"yauzl": "^2.10.0"
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"https-proxy-agent": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz",
|
||||
"integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"agent-base": "5",
|
||||
"debug": "4"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true
|
||||
},
|
||||
"pkg-dir": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-up": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz",
|
||||
"integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"q": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
|
||||
@ -14778,6 +14999,44 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.1.4"
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz",
|
||||
"integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==",
|
||||
"dev": true,
|
||||
"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==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "4.6.10",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.6.10.tgz",
|
||||
@ -15193,6 +15452,28 @@
|
||||
"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==",
|
||||
"dev": true,
|
||||
"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==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"unicode-canonical-property-names-ecmascript": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
||||
|
@ -102,9 +102,10 @@
|
||||
"codelyzer": "^5.1.2",
|
||||
"del": "^5.1.0",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"jasmine-marbles": "^0.6.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~4.4.1",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.5",
|
||||
"karma-jasmine": "~3.1.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
@ -113,6 +114,7 @@
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.0.5",
|
||||
"protractor": "~5.4.2",
|
||||
"puppeteer": "^5.5.0",
|
||||
"ts-node": "~8.8.1",
|
||||
"tslint": "~6.1.0",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { AnalyticsService as APIAnalyticsService, SplitUnit } from '../../api-codegen/anapi';
|
||||
import { AnalyticsService as APIAnalyticsService, InlineResponse200, SplitUnit } from '../../api-codegen/anapi';
|
||||
import { PaymentInstitutionRealm } from '../model';
|
||||
import { genXRequestID, toDateLike } from '../utils';
|
||||
|
||||
@ -215,4 +216,13 @@ export class AnalyticsService {
|
||||
params.paymentInstitutionRealm
|
||||
);
|
||||
}
|
||||
|
||||
getGroupBalances(params: { shopIDs?: string[]; excludeShopIDs?: string[] }): Observable<InlineResponse200> {
|
||||
return this.analyticsService.getCurrentBalancesGroupByShop(
|
||||
genXRequestID(),
|
||||
undefined,
|
||||
params.shopIDs,
|
||||
params.excludeShopIDs
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,11 @@ import { SHARE_REPLAY_CONF } from '../../custom-operators';
|
||||
import { genXRequestID } from '../utils';
|
||||
|
||||
@Injectable()
|
||||
export class ShopService {
|
||||
export class ApiShopsService {
|
||||
private reloadShops$ = new Subject<void>();
|
||||
|
||||
shops$: Observable<Shop[]> = this.reloadShops$.pipe(
|
||||
startWith(undefined as Shop[]),
|
||||
startWith<void, null>(null),
|
||||
switchMapTo(this.shopsService.getShops(genXRequestID())),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
@ -1,3 +1,3 @@
|
||||
export * from './shop.service';
|
||||
export * from './api-shops.service';
|
||||
export * from './shop.module';
|
||||
export * from './operators';
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { CAPIModule } from '../capi';
|
||||
import { ShopService } from './shop.service';
|
||||
import { ApiShopsService } from './api-shops.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [CAPIModule],
|
||||
providers: [ShopService],
|
||||
providers: [ApiShopsService],
|
||||
})
|
||||
export class ShopModule {}
|
||||
|
@ -5,7 +5,7 @@ import { TestShopService } from './test-shop.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-root',
|
||||
templateUrl: './app.component.html',
|
||||
templateUrl: 'app.component.html',
|
||||
providers: [TestShopService],
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
|
@ -6,7 +6,7 @@ import { ThemeManager, ThemeName } from '../../theme-manager';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-actionbar',
|
||||
templateUrl: './actionbar.component.html',
|
||||
templateUrl: 'actionbar.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ActionbarComponent {
|
||||
|
@ -4,7 +4,7 @@ import { BrandType } from './brand-type';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-brand',
|
||||
templateUrl: './brand.component.html',
|
||||
templateUrl: 'brand.component.html',
|
||||
styleUrls: ['./brand.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Event, Router } from '@angular/router';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { distinctUntilChanged, map, shareReplay, startWith } from 'rxjs/operators';
|
||||
|
||||
@ -22,7 +22,7 @@ export enum LinkId {
|
||||
@Injectable()
|
||||
export class ToolbarLinksService {
|
||||
private url$ = this.router.events.pipe(
|
||||
startWith(null),
|
||||
startWith<Event, null>(null),
|
||||
map(() => this.router.url),
|
||||
distinctUntilChanged(),
|
||||
shareReplay(1)
|
||||
|
@ -6,7 +6,7 @@ import { LinkId, ToolbarLinksService } from './toolbar-links.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-toolbar',
|
||||
templateUrl: './toolbar.component.html',
|
||||
templateUrl: 'toolbar.component.html',
|
||||
styleUrls: ['./toolbar.component.scss'],
|
||||
providers: [ToolbarLinksService],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
@ -47,7 +47,8 @@ export class CreateInvoiceTemplateService {
|
||||
form = this.createForm();
|
||||
|
||||
summary$ = this.cartForm.valueChanges.pipe(
|
||||
startWith(this.cartForm.value),
|
||||
// TODO: add form types
|
||||
startWith<any, any>(this.cartForm.value),
|
||||
map((v) => v.reduce((sum, c) => sum + c.price * c.quantity, 0)),
|
||||
shareReplay(1)
|
||||
);
|
||||
@ -123,7 +124,7 @@ export class CreateInvoiceTemplateService {
|
||||
|
||||
private subscribeFormChanges() {
|
||||
const templateType$ = this.form.controls.templateType.valueChanges.pipe(
|
||||
startWith(this.form.value.templateType),
|
||||
startWith<TemplateType, TemplateType>(this.form.value.templateType),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
const costType$ = this.form.controls.costType.valueChanges.pipe(startWith(this.form.value.costType));
|
||||
|
@ -19,7 +19,8 @@ export class CreateInvoiceService {
|
||||
form = this.createForm();
|
||||
|
||||
totalAmount$ = this.form.controls.cart.valueChanges.pipe(
|
||||
startWith(this.form.controls.cart.value),
|
||||
// TODO: add form types
|
||||
startWith<any, any>(this.form.controls.cart.value),
|
||||
map((v) => v.map(({ price, quantity }) => price * quantity).reduce((sum, s) => (sum += s), 0)),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ import { TranslocoService } from '@ngneat/transloco';
|
||||
import { of, ReplaySubject } from 'rxjs';
|
||||
import { map, shareReplay, startWith, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { ShopService } from '../../../api';
|
||||
import { ApiShopsService } from '../../../api';
|
||||
import { BankContent } from '../../../api-codegen/aggr-proxy';
|
||||
import { BankAccount } from '../../../api-codegen/capi';
|
||||
import { filterShopsByRealm } from '../../payment-section/operations/operators';
|
||||
@ -58,7 +58,7 @@ export class CreateShopRussianLegalEntityComponent {
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private createShopRussianLegalEntityService: CreateShopRussianLegalEntityService,
|
||||
private shopService: ShopService,
|
||||
private shopService: ApiShopsService,
|
||||
private transloco: TranslocoService,
|
||||
private snackBar: MatSnackBar,
|
||||
private router: Router
|
||||
|
@ -4,11 +4,11 @@ import { map, pluck, switchMap, switchMapTo } from 'rxjs/operators';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import {
|
||||
ApiShopsService,
|
||||
ClaimsService,
|
||||
createDocumentModificationUnit,
|
||||
PayoutsService,
|
||||
QuestionaryService,
|
||||
ShopService,
|
||||
} from '../../../api';
|
||||
import {
|
||||
BankAccount,
|
||||
@ -22,7 +22,7 @@ import {
|
||||
export class CreateShopRussianLegalEntityService {
|
||||
constructor(
|
||||
private claimsService: ClaimsService,
|
||||
private shopService: ShopService,
|
||||
private shopService: ApiShopsService,
|
||||
private payoutsService: PayoutsService,
|
||||
private questionaryService: QuestionaryService
|
||||
) {}
|
||||
|
@ -9,7 +9,7 @@ export interface StatusViewInfo {
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-details-status-item',
|
||||
templateUrl: './status-details-item.component.html',
|
||||
templateUrl: 'status-details-item.component.html',
|
||||
})
|
||||
export class StatusDetailsItemComponent {
|
||||
@Input() color: Color;
|
||||
|
@ -4,7 +4,7 @@ import { TranslocoService } from '@ngneat/transloco';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { pluck } from 'rxjs/operators';
|
||||
|
||||
import { ClaimsService, ShopService } from '../../../../api';
|
||||
import { ApiShopsService, ClaimsService } from '../../../../api';
|
||||
import { ClaimStatus } from '../../../../api/claims';
|
||||
import { booleanDelay, takeError } from '../../../../custom-operators';
|
||||
import { ActionBtnContent, TestEnvBtnContent } from './content-config';
|
||||
@ -18,7 +18,7 @@ export class PaymentsService {
|
||||
isLoading$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
private shopService: ShopService,
|
||||
private shopService: ApiShopsService,
|
||||
private claimService: ClaimsService,
|
||||
private snackBar: MatSnackBar,
|
||||
private transloco: TranslocoService
|
||||
|
@ -26,7 +26,8 @@ export class AuthorityConfirmingDocumentComponent {
|
||||
switchMap((form) =>
|
||||
form
|
||||
? form.valueChanges.pipe(
|
||||
startWith(form.value),
|
||||
// TODO: add form types
|
||||
startWith<any, any>(form.value),
|
||||
map((v) => v.type === this.customType)
|
||||
)
|
||||
: of(false)
|
||||
|
@ -3,7 +3,7 @@ import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-page-not-found',
|
||||
templateUrl: './page-not-found.component.html',
|
||||
templateUrl: 'page-not-found.component.html',
|
||||
})
|
||||
export class PageNotFoundComponent {
|
||||
constructor(private router: Router) {}
|
||||
|
@ -7,7 +7,7 @@ import { getPaymentStatusInfo } from '../../get-payment-status-info';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-details',
|
||||
templateUrl: './details.component.html',
|
||||
templateUrl: 'details.component.html',
|
||||
})
|
||||
export class DetailsComponent implements OnChanges {
|
||||
@Input() payment: PaymentSearchResult;
|
||||
|
@ -15,7 +15,7 @@ export interface CancelHoldData {
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-cancel-hold',
|
||||
templateUrl: './cancel-hold.component.html',
|
||||
templateUrl: 'cancel-hold.component.html',
|
||||
providers: [PaymentService],
|
||||
})
|
||||
export class CancelHoldComponent {
|
||||
|
@ -21,7 +21,7 @@ export interface ConfirmHoldData {
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-confirm-hold',
|
||||
templateUrl: './confirm-hold.component.html',
|
||||
templateUrl: 'confirm-hold.component.html',
|
||||
providers: [PaymentService],
|
||||
})
|
||||
export class ConfirmHoldComponent {
|
||||
|
@ -11,7 +11,7 @@ const onHoldExpirationEnum = PaymentFlowHold.OnHoldExpirationEnum;
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-hold-details',
|
||||
templateUrl: './hold-details.component.html',
|
||||
templateUrl: 'hold-details.component.html',
|
||||
})
|
||||
export class HoldDetailsComponent {
|
||||
@Input() payment: PaymentSearchResult;
|
||||
|
@ -9,7 +9,7 @@ import { InvoiceDetailsService } from './invoice-details.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-invoice-details',
|
||||
templateUrl: './invoice-details.component.html',
|
||||
templateUrl: 'invoice-details.component.html',
|
||||
styleUrls: ['./invoice-details.component.scss'],
|
||||
providers: [InvoiceDetailsService],
|
||||
})
|
||||
|
@ -2,6 +2,6 @@ import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-make-recurrent',
|
||||
templateUrl: './make-recurrent.component.html',
|
||||
templateUrl: 'make-recurrent.component.html',
|
||||
})
|
||||
export class MakeRecurrentComponent {}
|
||||
|
@ -4,7 +4,7 @@ import { CustomerPayer } from '../../../../api-codegen/capi/swagger-codegen';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-customer-payer',
|
||||
templateUrl: './customer-payer.component.html',
|
||||
templateUrl: 'customer-payer.component.html',
|
||||
})
|
||||
export class CustomerPayerComponent {
|
||||
@Input() customerPayer: CustomerPayer;
|
||||
|
@ -11,7 +11,7 @@ export enum PayerType {
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-payer-details',
|
||||
templateUrl: './payer-details.component.html',
|
||||
templateUrl: 'payer-details.component.html',
|
||||
})
|
||||
export class PayerDetailsComponent {
|
||||
@Input() payer: Payer;
|
||||
|
@ -5,7 +5,7 @@ import { LAYOUT_GAP } from '../../../constants';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-payment-resource-payer',
|
||||
templateUrl: './payment-resource-payer.component.html',
|
||||
templateUrl: 'payment-resource-payer.component.html',
|
||||
})
|
||||
export class PaymentResourcePayerComponent {
|
||||
@Input() paymentResourcePayer: PaymentResourcePayer;
|
||||
|
@ -7,7 +7,7 @@ import { PayerType } from './payer-details';
|
||||
import { ReceivePaymentService } from './receive-payment.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: './payment-details.component.html',
|
||||
templateUrl: 'payment-details.component.html',
|
||||
styleUrls: ['./payment-details.component.scss'],
|
||||
providers: [ReceivePaymentService],
|
||||
})
|
||||
|
@ -4,7 +4,7 @@ import { BankCardDetails, PaymentToolDetailsBankCard } from '../../../../api-cod
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-bank-card',
|
||||
templateUrl: './bank-card.component.html',
|
||||
templateUrl: 'bank-card.component.html',
|
||||
})
|
||||
export class BankCardComponent {
|
||||
@Input() bankCard: BankCardDetails;
|
||||
|
@ -9,7 +9,7 @@ import DigitalWalletDetailsTypeEnum = PaymentToolDetailsDigitalWallet.DigitalWal
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-digital-wallet',
|
||||
templateUrl: './digital-wallet.component.html',
|
||||
templateUrl: 'digital-wallet.component.html',
|
||||
})
|
||||
export class DigitalWalletComponent implements OnChanges {
|
||||
@Input() digitalWallet: DigitalWalletDetails;
|
||||
|
@ -4,7 +4,7 @@ import { PaymentTerminalDetails } from '../../../../api-codegen/capi/swagger-cod
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-payment-terminal',
|
||||
templateUrl: './payment-terminal.component.html',
|
||||
templateUrl: 'payment-terminal.component.html',
|
||||
})
|
||||
export class PaymentTerminalComponent {
|
||||
@Input() paymentTerminal: PaymentTerminalDetails;
|
||||
|
@ -5,7 +5,7 @@ import { PaymentToolDetails } from '../../../api-codegen/capi';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-payment-tool',
|
||||
templateUrl: './payment-tool.component.html',
|
||||
templateUrl: 'payment-tool.component.html',
|
||||
})
|
||||
export class PaymentToolComponent {
|
||||
@Input() paymentToolDetails: PaymentToolDetails;
|
||||
|
@ -5,7 +5,7 @@ import { LAYOUT_GAP } from '../../constants';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-recurrent-details',
|
||||
templateUrl: './recurrent-details.component.html',
|
||||
templateUrl: 'recurrent-details.component.html',
|
||||
})
|
||||
export class RecurrentDetailsComponent {
|
||||
@Input() recurrentPayer: RecurrentPayer;
|
||||
|
@ -23,7 +23,7 @@ export interface CreateRefundData {
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-create-refund',
|
||||
templateUrl: './create-refund.component.html',
|
||||
templateUrl: 'create-refund.component.html',
|
||||
providers: [CreateRefundService],
|
||||
})
|
||||
export class CreateRefundComponent implements OnInit {
|
||||
|
@ -5,13 +5,13 @@ import { switchMap } from 'rxjs/operators';
|
||||
import { Account, Refund, RefundParams, Shop } from '../../../../api-codegen/capi/swagger-codegen';
|
||||
import { AccountService } from '../../../../api/account';
|
||||
import { RefundService } from '../../../../api/refund';
|
||||
import { ShopService } from '../../../../api/shop';
|
||||
import { ApiShopsService } from '../../../../api/shop';
|
||||
|
||||
@Injectable()
|
||||
export class CreateRefundService {
|
||||
constructor(
|
||||
private refundService: RefundService,
|
||||
private shopService: ShopService,
|
||||
private shopService: ApiShopsService,
|
||||
private accountService: AccountService
|
||||
) {}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import { LAYOUT_GAP } from '../../../constants';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-refund-item',
|
||||
templateUrl: './refund-item.component.html',
|
||||
templateUrl: 'refund-item.component.html',
|
||||
})
|
||||
export class RefundItemComponent implements OnChanges {
|
||||
@Input() refund: RefundSearchResult;
|
||||
|
@ -11,7 +11,7 @@ const PaymentStatuses = PaymentSearchResult.StatusEnum;
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-refunds',
|
||||
templateUrl: './refunds.component.html',
|
||||
templateUrl: 'refunds.component.html',
|
||||
styleUrls: ['./refunds.component.scss'],
|
||||
providers: [RefundsService],
|
||||
})
|
||||
|
@ -15,7 +15,7 @@ import { DetailsItemModule, LayoutModule } from '@dsh/components/layout';
|
||||
import { AccountService } from '../../../api/account';
|
||||
import { RefundService } from '../../../api/refund';
|
||||
import { RefundSearchService } from '../../../api/search';
|
||||
import { ShopService } from '../../../api/shop';
|
||||
import { ApiShopsService } from '../../../api/shop';
|
||||
import { ToMajorModule } from '../../../to-major';
|
||||
import { StatusDetailsItemModule } from '../status-details-item';
|
||||
import { CreateRefundComponent } from './create-refund';
|
||||
@ -41,7 +41,7 @@ import { RefundsComponent } from './refunds.component';
|
||||
],
|
||||
declarations: [CreateRefundComponent, RefundsComponent, RefundItemComponent],
|
||||
exports: [RefundsComponent],
|
||||
providers: [RefundSearchService, RefundService, ShopService, AccountService],
|
||||
providers: [RefundSearchService, RefundService, ApiShopsService, AccountService],
|
||||
entryComponents: [CreateRefundComponent],
|
||||
})
|
||||
export class RefundsModule {}
|
||||
|
@ -9,7 +9,7 @@ export interface StatusViewInfo {
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-details-status-item',
|
||||
templateUrl: './status-details-item.component.html',
|
||||
templateUrl: 'status-details-item.component.html',
|
||||
})
|
||||
export class StatusDetailsItemComponent {
|
||||
@Input() color: Color;
|
||||
|
@ -14,7 +14,7 @@ import { distinctUntilChanged, map, scan, shareReplay, switchMap, take } from 'r
|
||||
import { Daterange } from '@dsh/pipes/daterange';
|
||||
|
||||
import { Shop } from '../../../../api-codegen/capi';
|
||||
import { ShopService } from '../../../../api/shop';
|
||||
import { ApiShopsService } from '../../../../api/shop';
|
||||
import { filterShopsByRealm, removeEmptyProperties } from '../../operations/operators';
|
||||
import { searchFilterParamsToDaterange } from '../../reports/reports-search-filters/search-filter-params-to-daterange';
|
||||
import { SearchParams } from '../search-params';
|
||||
@ -66,7 +66,7 @@ export class AnalyticsSearchFiltersComponent implements OnChanges {
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(private shopService: ShopService) {
|
||||
constructor(private shopService: ApiShopsService) {
|
||||
this.selectedCurrency$.subscribe((currency) => {
|
||||
this.searchParams$.next({ currency });
|
||||
this.selectedShopIDs$.next([]);
|
||||
|
@ -2,10 +2,10 @@ import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexModule } from '@angular/flex-layout';
|
||||
|
||||
import { FilterShopsModule } from '@dsh/app/shared/*';
|
||||
import { FilterShopsModule } from '@dsh/app/shared/components';
|
||||
import { CurrencyFilterModule } from '@dsh/app/shared/components/filters/currency-filter';
|
||||
import { DaterangeFilterModule } from '@dsh/components/filters/daterange-filter';
|
||||
|
||||
import { CurrencyFilterModule } from '../../../../shared/components/filters/currency-filter';
|
||||
import { AnalyticsSearchFiltersComponent } from './analytics-search-filters.component';
|
||||
|
||||
@NgModule({
|
||||
|
@ -7,7 +7,7 @@ import { AveragePaymentService } from './average-payment.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-average-payment',
|
||||
templateUrl: './average-payment.component.html',
|
||||
templateUrl: 'average-payment.component.html',
|
||||
providers: [AveragePaymentService],
|
||||
})
|
||||
export class AveragePaymentComponent implements OnChanges {
|
||||
|
@ -6,7 +6,7 @@ import { ChartData } from '../utils';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-bar-chart-item',
|
||||
templateUrl: './bar-chart-item.component.html',
|
||||
templateUrl: 'bar-chart-item.component.html',
|
||||
})
|
||||
export class BarChartItemComponent implements OnChanges {
|
||||
@Input() spinnerType: SpinnerType;
|
||||
|
@ -6,7 +6,7 @@ import { DistributionChartData } from '../utils';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-donut-chart-item',
|
||||
templateUrl: './donut-chart-item.component.html',
|
||||
templateUrl: 'donut-chart-item.component.html',
|
||||
styleUrls: ['donut-chart-item.component.scss'],
|
||||
})
|
||||
export class DonutChartItemComponent implements OnChanges {
|
||||
|
@ -8,7 +8,7 @@ import { PaymentSplitAmountService } from './payment-split-amount.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-payment-split-amount',
|
||||
templateUrl: './payment-split-amount.component.html',
|
||||
templateUrl: 'payment-split-amount.component.html',
|
||||
providers: [PaymentSplitAmountService],
|
||||
})
|
||||
export class PaymentSplitAmountComponent implements OnChanges {
|
||||
|
@ -8,7 +8,7 @@ import { PaymentSplitCountService } from './payment-split-count.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-payment-split-count',
|
||||
templateUrl: './payment-split-count.component.html',
|
||||
templateUrl: 'payment-split-count.component.html',
|
||||
providers: [PaymentSplitCountService],
|
||||
})
|
||||
export class PaymentSplitCountComponent implements OnChanges {
|
||||
|
@ -7,7 +7,7 @@ import { PaymentsAmountService } from './payments-amount.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-payments-amount',
|
||||
templateUrl: './payments-amount.component.html',
|
||||
templateUrl: 'payments-amount.component.html',
|
||||
providers: [PaymentsAmountService],
|
||||
})
|
||||
export class PaymentsAmountComponent implements OnChanges {
|
||||
|
@ -7,7 +7,7 @@ import { PaymentsCountService } from './payments-count.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-payments-count',
|
||||
templateUrl: './payments-count.component.html',
|
||||
templateUrl: 'payments-count.component.html',
|
||||
providers: [PaymentsCountService],
|
||||
})
|
||||
export class PaymentsCountComponent implements OnChanges {
|
||||
|
@ -8,7 +8,7 @@ import { PaymentsErrorDistributionService } from './payments-error-distribution.
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-payments-error-distribution',
|
||||
templateUrl: './payments-error-distribution.component.html',
|
||||
templateUrl: 'payments-error-distribution.component.html',
|
||||
providers: [PaymentsErrorDistributionService],
|
||||
encapsulation: ViewEncapsulation.Emulated,
|
||||
})
|
||||
|
@ -8,7 +8,7 @@ import { PaymentsToolDistributionService } from './payments-tool-distribution.se
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-payments-tool-distribution',
|
||||
templateUrl: './payments-tool-distribution.component.html',
|
||||
templateUrl: 'payments-tool-distribution.component.html',
|
||||
providers: [PaymentsToolDistributionService],
|
||||
})
|
||||
export class PaymentsToolDistributionComponent implements OnChanges {
|
||||
|
@ -2,7 +2,7 @@ import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-percent-difference',
|
||||
templateUrl: './percent-difference.component.html',
|
||||
templateUrl: 'percent-difference.component.html',
|
||||
})
|
||||
export class PercentDifferenceComponent implements OnChanges {
|
||||
@Input() current: number;
|
||||
|
@ -7,7 +7,7 @@ import { RefundsAmountService } from './refunds-amount.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-refunds-amount',
|
||||
templateUrl: './refunds-amount.component.html',
|
||||
templateUrl: 'refunds-amount.component.html',
|
||||
providers: [RefundsAmountService],
|
||||
})
|
||||
export class RefundsAmountComponent implements OnChanges {
|
||||
|
@ -7,7 +7,7 @@ import { StatData } from '../utils';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-stat-item',
|
||||
templateUrl: './stat-item.component.html',
|
||||
templateUrl: 'stat-item.component.html',
|
||||
styleUrls: ['./stat-item.component.scss'],
|
||||
})
|
||||
export class StatItemComponent implements OnChanges {
|
||||
|
@ -3,7 +3,7 @@ import { FormBuilder } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { pluck, shareReplay } from 'rxjs/operators';
|
||||
|
||||
import { ShopService } from '../../../../../api';
|
||||
import { ApiShopsService } from '../../../../../api';
|
||||
import { SHARE_REPLAY_CONF } from '../../../../../custom-operators';
|
||||
import { filterShopsByRealm } from '../../../operations/operators';
|
||||
|
||||
@ -17,5 +17,5 @@ export class CreateInvoiceOrInvoiceTemplateService {
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
|
||||
constructor(private fb: FormBuilder, private route: ActivatedRoute, private shopService: ShopService) {}
|
||||
constructor(private fb: FormBuilder, private route: ActivatedRoute, private shopService: ApiShopsService) {}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
// TODO: add unit tests
|
@ -1 +0,0 @@
|
||||
export * from './create-shop-dialog.component';
|
@ -0,0 +1,21 @@
|
||||
import { isNil } from '@ngneat/transloco';
|
||||
|
||||
import { Dict } from '../../../../../../../type-utils';
|
||||
import { Shop as ApiShop } from '../../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { ShopBalance } from '../../types/shop-balance';
|
||||
import { ShopItem } from '../../types/shop-item';
|
||||
|
||||
export function combineShopItem(shops: ApiShop[], balances: ShopBalance[]): ShopItem[] {
|
||||
const balancesMap = balances.reduce((acc: Dict<ShopBalance>, el: ShopBalance) => {
|
||||
acc[el.id] = el;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return shops.map((shop: ApiShop) => {
|
||||
const balance = balancesMap[shop.id];
|
||||
return {
|
||||
...shop,
|
||||
balance: isNil(balance) ? null : balance.data,
|
||||
} as ShopItem;
|
||||
});
|
||||
}
|
@ -0,0 +1,485 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { Observable, of, ReplaySubject } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { InlineResponse200, ShopLocation } from '../../../../../../api-codegen/anapi/swagger-codegen';
|
||||
import { Shop } from '../../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { AnalyticsService } from '../../../../../../api/analytics';
|
||||
import { PaymentInstitutionRealm } from '../../../../../../api/model';
|
||||
import { ApiShopsService } from '../../../../../../api/shop';
|
||||
import { ShopBalanceModule } from '../../shops-list/shop-balance';
|
||||
import { ShopsBalanceService } from '../shops-balance/shops-balance.service';
|
||||
import { FetchShopsService } from './fetch-shops.service';
|
||||
|
||||
class MockApiShopsService {
|
||||
shops$: Observable<Shop[]>;
|
||||
|
||||
private innerShops$ = new ReplaySubject<Shop[]>(1);
|
||||
private mockShops: Shop[];
|
||||
|
||||
constructor() {
|
||||
this.shops$ = this.innerShops$.asObservable();
|
||||
}
|
||||
|
||||
reloadShops(): void {
|
||||
this.innerShops$.next(this.mockShops);
|
||||
}
|
||||
|
||||
setMockShops(shops: Shop[]): void {
|
||||
this.mockShops = shops;
|
||||
}
|
||||
}
|
||||
|
||||
class MockAnalyticsService {
|
||||
private innerResponse: InlineResponse200 = {
|
||||
result: [],
|
||||
};
|
||||
|
||||
getGroupBalances(): Observable<InlineResponse200> {
|
||||
console.log('getGroupBalances');
|
||||
return of(this.innerResponse);
|
||||
}
|
||||
|
||||
setMockBalancesResponse(response: InlineResponse200): void {
|
||||
this.innerResponse = response;
|
||||
}
|
||||
}
|
||||
|
||||
describe('FetchShopsService', () => {
|
||||
let service: FetchShopsService;
|
||||
let apiShopsService: MockApiShopsService;
|
||||
let analyticsService: MockAnalyticsService;
|
||||
let balancesService: ShopBalanceModule;
|
||||
|
||||
beforeEach(() => {
|
||||
apiShopsService = new MockApiShopsService();
|
||||
analyticsService = new MockAnalyticsService();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
FetchShopsService,
|
||||
ShopsBalanceService,
|
||||
{
|
||||
provide: ApiShopsService,
|
||||
useValue: apiShopsService,
|
||||
},
|
||||
{
|
||||
provide: AnalyticsService,
|
||||
useValue: analyticsService,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = TestBed.inject(FetchShopsService);
|
||||
balancesService = TestBed.inject(ShopsBalanceService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('initRealm', () => {
|
||||
it('should init realm and init allShops$ work', () => {
|
||||
apiShopsService.setMockShops([
|
||||
{
|
||||
id: 'mock1',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mock2',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mock3',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 3,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const expectedShops$ = cold('a', {
|
||||
a: ['mock1', 'mock2'],
|
||||
});
|
||||
|
||||
apiShopsService.reloadShops();
|
||||
service.initRealm(PaymentInstitutionRealm.test);
|
||||
|
||||
expect(
|
||||
service.allShops$.pipe(
|
||||
map((list) => {
|
||||
return list.map(({ id }) => id);
|
||||
})
|
||||
)
|
||||
).toBeObservable(expectedShops$);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initOffsetIndex', () => {
|
||||
it('should init shop$ working', () => {
|
||||
apiShopsService.setMockShops([
|
||||
{
|
||||
id: 'mock1',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mock2',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mock3',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mock4',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mock5',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mock6',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mock7',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const expectedShops$ = cold('a', {
|
||||
a: ['mock1', 'mock2', 'mock3', 'mock4', 'mock5'],
|
||||
});
|
||||
|
||||
apiShopsService.reloadShops();
|
||||
service.initRealm(PaymentInstitutionRealm.test);
|
||||
service.initOffsetIndex(3);
|
||||
|
||||
expect(
|
||||
service.loadedShops$.pipe(
|
||||
map((list) => {
|
||||
return list.map(({ id }) => id);
|
||||
})
|
||||
)
|
||||
).toBeObservable(expectedShops$);
|
||||
});
|
||||
});
|
||||
|
||||
describe('refreshData', () => {
|
||||
it('should call apiShopService reload method', () => {
|
||||
const spyOnApiShopService = spyOn(apiShopsService, 'reloadShops').and.callThrough();
|
||||
|
||||
service.refreshData();
|
||||
|
||||
expect(spyOnApiShopService).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should update allShops list', () => {
|
||||
apiShopsService.setMockShops([
|
||||
{
|
||||
id: 'mock6',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
service.initRealm(PaymentInstitutionRealm.test);
|
||||
apiShopsService.reloadShops();
|
||||
|
||||
apiShopsService.setMockShops([
|
||||
{
|
||||
id: 'mock6',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mock16',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
service.refreshData();
|
||||
|
||||
const expectedShops$ = cold('a', {
|
||||
a: ['mock6', 'mock16'],
|
||||
});
|
||||
|
||||
expect(
|
||||
service.allShops$.pipe(
|
||||
map((list) => {
|
||||
return list.map(({ id }) => id);
|
||||
})
|
||||
)
|
||||
).toBeObservable(expectedShops$);
|
||||
});
|
||||
|
||||
it('should update loading value', () => {
|
||||
apiShopsService.setMockShops([]);
|
||||
|
||||
apiShopsService.reloadShops();
|
||||
service.initRealm(PaymentInstitutionRealm.test);
|
||||
service.initOffsetIndex(-1);
|
||||
|
||||
service.refreshData();
|
||||
|
||||
expect(service.isLoading$).toBeObservable(
|
||||
cold('a', {
|
||||
a: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('showMore', () => {
|
||||
it('should update loading value', () => {
|
||||
apiShopsService.setMockShops([]);
|
||||
|
||||
apiShopsService.reloadShops();
|
||||
service.initRealm(PaymentInstitutionRealm.test);
|
||||
service.initOffsetIndex(-1);
|
||||
|
||||
service.showMore();
|
||||
|
||||
expect(service.isLoading$).toBeObservable(
|
||||
cold('a', {
|
||||
a: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,124 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, combineLatest, Observable, ReplaySubject } from 'rxjs';
|
||||
import {
|
||||
distinctUntilChanged,
|
||||
map,
|
||||
mapTo,
|
||||
pluck,
|
||||
scan,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
tap,
|
||||
withLatestFrom,
|
||||
} from 'rxjs/operators';
|
||||
|
||||
import { Shop as ApiShop } from '../../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { PaymentInstitutionRealm } from '../../../../../../api/model';
|
||||
import { ApiShopsService } from '../../../../../../api/shop';
|
||||
import { SHARE_REPLAY_CONF } from '../../../../../../custom-operators';
|
||||
import { filterShopsByRealm, mapToTimestamp } from '../../../../operations/operators';
|
||||
import { ShopBalance } from '../../types/shop-balance';
|
||||
import { ShopItem } from '../../types/shop-item';
|
||||
import { ShopsBalanceService } from '../shops-balance/shops-balance.service';
|
||||
import { combineShopItem } from './combine-shop-item';
|
||||
|
||||
const LIST_OFFSET = 5;
|
||||
|
||||
@Injectable()
|
||||
export class FetchShopsService {
|
||||
allShops$: Observable<ApiShop[]>;
|
||||
loadedShops$: Observable<ShopItem[]>;
|
||||
lastUpdated$: Observable<string>;
|
||||
isLoading$: Observable<boolean>;
|
||||
hasMore$: Observable<boolean>;
|
||||
|
||||
private selectedIndex$ = new ReplaySubject<number>(1);
|
||||
private listOffset$: Observable<number>;
|
||||
|
||||
private realmData$ = new ReplaySubject<PaymentInstitutionRealm>(1);
|
||||
|
||||
private showMore$ = new ReplaySubject<void>(1);
|
||||
private loader$ = new BehaviorSubject<boolean>(true);
|
||||
|
||||
constructor(private apiShopsService: ApiShopsService, private shopsBalance: ShopsBalanceService) {
|
||||
this.initAllShopsFetching();
|
||||
this.initOffsetObservable();
|
||||
this.initShownShopsObservable();
|
||||
this.initIndicators();
|
||||
}
|
||||
|
||||
initRealm(realm: PaymentInstitutionRealm): void {
|
||||
this.realmData$.next(realm);
|
||||
}
|
||||
|
||||
initOffsetIndex(offsetIndex: number): void {
|
||||
this.selectedIndex$.next(offsetIndex);
|
||||
this.showMore$.next();
|
||||
}
|
||||
|
||||
refreshData(): void {
|
||||
this.startLoading();
|
||||
this.apiShopsService.reloadShops();
|
||||
}
|
||||
|
||||
showMore(): void {
|
||||
this.startLoading();
|
||||
this.showMore$.next();
|
||||
}
|
||||
|
||||
protected startLoading(): void {
|
||||
this.loader$.next(true);
|
||||
}
|
||||
|
||||
protected stopLoading(): void {
|
||||
this.loader$.next(false);
|
||||
}
|
||||
|
||||
protected updateShopsBalances(shops: ApiShop[]): void {
|
||||
const shopIds: string[] = shops.map(({ id }: ApiShop) => id);
|
||||
this.shopsBalance.setShopIds(shopIds);
|
||||
}
|
||||
|
||||
private initAllShopsFetching(): void {
|
||||
this.allShops$ = this.realmData$.pipe(
|
||||
filterShopsByRealm(this.apiShopsService.shops$),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
}
|
||||
|
||||
private initOffsetObservable(): void {
|
||||
this.listOffset$ = this.showMore$.pipe(
|
||||
mapTo(LIST_OFFSET),
|
||||
withLatestFrom(this.selectedIndex$),
|
||||
map(([curOffset]: [number, number]) => curOffset),
|
||||
scan((offset: number, limit: number) => offset + limit, 0),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
}
|
||||
|
||||
private initShownShopsObservable(): void {
|
||||
this.loadedShops$ = combineLatest([this.allShops$, this.listOffset$]).pipe(
|
||||
map(([shops, showedCount]: [ShopItem[], number]) => shops.slice(0, showedCount)),
|
||||
tap((shops: ApiShop[]) => {
|
||||
this.updateShopsBalances(shops);
|
||||
}),
|
||||
switchMap((shops: ApiShop[]) => {
|
||||
return this.shopsBalance.balances$.pipe(
|
||||
distinctUntilChanged(),
|
||||
map((balances: ShopBalance[]) => combineShopItem(shops, balances))
|
||||
);
|
||||
}),
|
||||
tap(() => this.stopLoading()),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
}
|
||||
|
||||
private initIndicators(): void {
|
||||
this.lastUpdated$ = this.allShops$.pipe(mapToTimestamp, shareReplay(1));
|
||||
this.isLoading$ = this.loader$.asObservable();
|
||||
this.hasMore$ = combineLatest([this.allShops$.pipe(pluck('length')), this.listOffset$]).pipe(
|
||||
map(([count, showedCount]: [number, number]) => count > showedCount),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { AnalyticsService as APIAnalyticsService } from '../../../../../../api-codegen/anapi';
|
||||
import { InlineResponse200 } from '../../../../../../api-codegen/anapi/swagger-codegen';
|
||||
import { AnalyticsService } from '../../../../../../api/analytics';
|
||||
import { ShopsBalanceService } from './shops-balance.service';
|
||||
|
||||
@Injectable()
|
||||
class MockAnalyticsService extends AnalyticsService {
|
||||
set mockGroupBalances(value: InlineResponse200) {
|
||||
this._mockGroupBalances = value;
|
||||
}
|
||||
private _mockGroupBalances: InlineResponse200;
|
||||
|
||||
getGroupBalances(): Observable<InlineResponse200> {
|
||||
return of(this._mockGroupBalances);
|
||||
}
|
||||
}
|
||||
|
||||
describe('ShopsBalanceService', () => {
|
||||
let service: ShopsBalanceService;
|
||||
let analyticsService: MockAnalyticsService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ShopsBalanceService,
|
||||
{
|
||||
provide: AnalyticsService,
|
||||
useClass: MockAnalyticsService,
|
||||
},
|
||||
{
|
||||
provide: APIAnalyticsService,
|
||||
useValue: null, // not used
|
||||
},
|
||||
],
|
||||
});
|
||||
service = TestBed.inject(ShopsBalanceService);
|
||||
analyticsService = TestBed.inject(AnalyticsService) as MockAnalyticsService;
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('balances$', () => {
|
||||
it('should return balances list from AnalyticsService', () => {
|
||||
const expected$ = cold('a', {
|
||||
a: [
|
||||
{
|
||||
id: 'first-shop',
|
||||
data: {
|
||||
amount: 20,
|
||||
currency: 'USD',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'second-shop',
|
||||
data: {
|
||||
amount: 4,
|
||||
currency: 'USD',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'third-shop',
|
||||
data: {
|
||||
amount: 2,
|
||||
currency: 'USD',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
analyticsService.mockGroupBalances = {
|
||||
result: [
|
||||
{
|
||||
groupBySHopResults: [
|
||||
{
|
||||
id: 'first-shop',
|
||||
amountResults: [
|
||||
{
|
||||
amount: 20,
|
||||
currency: 'USD',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'second-shop',
|
||||
amountResults: [
|
||||
{
|
||||
amount: 4,
|
||||
currency: 'USD',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'third-shop',
|
||||
amountResults: [
|
||||
{
|
||||
amount: 2,
|
||||
currency: 'USD',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(service.balances$).toBeObservable(expected$);
|
||||
});
|
||||
|
||||
it('should return list with nullable data if api has no data for some shops', () => {
|
||||
const expected$ = cold('a', {
|
||||
a: [
|
||||
{
|
||||
id: 'first-shop',
|
||||
data: null,
|
||||
},
|
||||
{
|
||||
id: 'second-shop',
|
||||
data: null,
|
||||
},
|
||||
{
|
||||
id: 'third-shop',
|
||||
data: {
|
||||
amount: 2,
|
||||
currency: 'USD',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
analyticsService.mockGroupBalances = {
|
||||
result: [
|
||||
{
|
||||
groupBySHopResults: [
|
||||
{
|
||||
id: 'first-shop',
|
||||
amountResults: [],
|
||||
},
|
||||
{
|
||||
id: 'second-shop',
|
||||
},
|
||||
{
|
||||
id: 'third-shop',
|
||||
amountResults: [
|
||||
{
|
||||
amount: 2,
|
||||
currency: 'USD',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(service.balances$).toBeObservable(expected$);
|
||||
});
|
||||
|
||||
it('should return empty balances list if api has no data', () => {
|
||||
const expected$ = cold('a', {
|
||||
a: [],
|
||||
});
|
||||
|
||||
analyticsService.mockGroupBalances = {
|
||||
result: [
|
||||
{
|
||||
groupBySHopResults: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(service.balances$).toBeObservable(expected$);
|
||||
});
|
||||
|
||||
it('should return empty balances list if there was an error', () => {
|
||||
const expected$ = cold('a', {
|
||||
a: [],
|
||||
});
|
||||
|
||||
analyticsService.getGroupBalances = () => {
|
||||
return of({
|
||||
result: [],
|
||||
}).pipe(
|
||||
map(() => {
|
||||
throw new Error('[TEST ERROR] Mock Error');
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
expect(service.balances$).toBeObservable(expected$);
|
||||
});
|
||||
});
|
||||
// setShopIds
|
||||
});
|
@ -0,0 +1,44 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { isNil } from '@ngneat/transloco';
|
||||
import { Observable, of, ReplaySubject } from 'rxjs';
|
||||
import { catchError, distinctUntilChanged, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { AnalyticsService } from '../../../../../../api/analytics';
|
||||
import { SHARE_REPLAY_CONF } from '../../../../../../custom-operators';
|
||||
import { ShopBalance } from '../../types/shop-balance';
|
||||
|
||||
@Injectable()
|
||||
export class ShopsBalanceService {
|
||||
balances$: Observable<ShopBalance[]>;
|
||||
|
||||
private shopIDsChange$ = new ReplaySubject<string[]>(1);
|
||||
|
||||
constructor(private analyticsService: AnalyticsService) {
|
||||
this.balances$ = this.shopIDsChange$.pipe(
|
||||
distinctUntilChanged(),
|
||||
switchMap((shopIDs: string[]) => {
|
||||
return this.analyticsService.getGroupBalances({ shopIDs }).pipe(
|
||||
map(({ result }) => {
|
||||
return result
|
||||
.flatMap(({ groupBySHopResults }) => groupBySHopResults)
|
||||
.map(({ id, amountResults = [] }) => {
|
||||
return {
|
||||
id,
|
||||
data: isNil(amountResults) || isNil(amountResults[0]) ? null : amountResults[0],
|
||||
};
|
||||
});
|
||||
}),
|
||||
catchError((err) => {
|
||||
console.error(err);
|
||||
return of([]);
|
||||
})
|
||||
);
|
||||
}),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
}
|
||||
|
||||
setShopIds(shopIDs: string[]): void {
|
||||
this.shopIDsChange$.next(shopIDs);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<dsh-row
|
||||
*transloco="let s; scope: 'shops'; read: 'shops'"
|
||||
fxLayout="row"
|
||||
fxLayoutAlign="space-between center"
|
||||
fxLayoutGap="24px"
|
||||
color="primary"
|
||||
>
|
||||
<dsh-row-header-label fxFlex>{{ s('panel.name') }}</dsh-row-header-label>
|
||||
<dsh-row-header-label fxFlex fxHide.lt-md> {{ s('panel.balance') }} </dsh-row-header-label>
|
||||
</dsh-row>
|
@ -0,0 +1 @@
|
||||
// TODO: add unit tests
|
@ -0,0 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-row-header',
|
||||
templateUrl: 'shop-row-header.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShopRowHeaderComponent {}
|
@ -0,0 +1,14 @@
|
||||
<dsh-row fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="24px">
|
||||
<ng-container *ngTemplateOutlet="shop ? tableGrid : loading; context: { $implicit: shop }"></ng-container>
|
||||
</dsh-row>
|
||||
|
||||
<ng-template #loading>
|
||||
<dsh-row-label>Loading ...</dsh-row-label>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #tableGrid let-item>
|
||||
<dsh-row-label fxFlex>{{ item.details.name }}</dsh-row-label>
|
||||
<dsh-row-label fxFlex fxHide.lt-md>
|
||||
<dsh-shop-balance [shop]="shop"></dsh-shop-balance>
|
||||
</dsh-row-label>
|
||||
</ng-template>
|
@ -0,0 +1 @@
|
||||
// TODO: add unit tests
|
@ -0,0 +1,12 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
|
||||
import { ShopItem } from '../../../types/shop-item';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-row',
|
||||
templateUrl: 'shop-row.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShopRowComponent {
|
||||
@Input() shop: ShopItem;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ShopsExpandedIdManagerService } from './shops-expanded-id-manager.service';
|
||||
|
||||
describe('ShopsExpandedIdManagerService', () => {
|
||||
let service: ShopsExpandedIdManagerService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ShopsExpandedIdManagerService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,19 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { ExpandedIdManager } from '@dsh/app/shared/services';
|
||||
|
||||
import { Shop } from '../../../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { FetchShopsService } from '../../../services/fetch-shops/fetch-shops.service';
|
||||
|
||||
@Injectable()
|
||||
export class ShopsExpandedIdManagerService extends ExpandedIdManager<Shop> {
|
||||
constructor(protected route: ActivatedRoute, protected router: Router, private shopsService: FetchShopsService) {
|
||||
super(route, router);
|
||||
}
|
||||
|
||||
protected get dataSet$(): Observable<Shop[]> {
|
||||
return this.shopsService.allShops$;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export * from './shop-balance.module';
|
@ -0,0 +1,8 @@
|
||||
<ng-container
|
||||
*ngTemplateOutlet="shop.balance ? balanceRow : emptyBalance; context: { $implicit: shop.balance }"
|
||||
></ng-container>
|
||||
|
||||
<ng-template #balanceRow let-balance>
|
||||
{{ balance.amount | toMajor | currency: balance.currency:'symbol' }}
|
||||
</ng-template>
|
||||
<ng-template #emptyBalance>--/--</ng-template>
|
@ -0,0 +1,58 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ShopLocation } from '../../../../../../api-codegen/anapi/swagger-codegen';
|
||||
import { ToMajorModule } from '../../../../../../to-major';
|
||||
import { ShopItem } from '../../types/shop-item';
|
||||
import { ShopBalanceComponent } from './shop-balance.component';
|
||||
|
||||
const mockShop: ShopItem = {
|
||||
id: 'mock',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
url: 'example.com',
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
balance: {
|
||||
amount: 20,
|
||||
currency: 'USD',
|
||||
},
|
||||
};
|
||||
|
||||
describe('ShopBalanceComponent', () => {
|
||||
let component: ShopBalanceComponent;
|
||||
let fixture: ComponentFixture<ShopBalanceComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ToMajorModule],
|
||||
declarations: [ShopBalanceComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShopBalanceComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.shop = mockShop;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,12 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
|
||||
import { ShopItem } from '../../types/shop-item';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-balance',
|
||||
templateUrl: 'shop-balance.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShopBalanceComponent {
|
||||
@Input() shop: ShopItem;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { ToMajorModule } from '../../../../../../to-major';
|
||||
import { ShopBalanceComponent } from './shop-balance.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, ToMajorModule],
|
||||
declarations: [ShopBalanceComponent],
|
||||
exports: [ShopBalanceComponent],
|
||||
})
|
||||
export class ShopBalanceModule {}
|
@ -0,0 +1,22 @@
|
||||
<ng-container *transloco="let p; scope: 'shops'; read: 'shops.panel'">
|
||||
<div class="mat-title">{{ p('actions') }}</div>
|
||||
<div>
|
||||
<ng-container *ngTemplateOutlet="shop.isSuspended ? activateButton : suspendButton"></ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #activateButton>
|
||||
<ng-container *transloco="let p; scope: 'shops'; read: 'shops.panel'">
|
||||
<button (click)="activate(shop.id)" color="accent" dsh-stroked-button>
|
||||
{{ p('activate') }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #suspendButton>
|
||||
<ng-container *transloco="let p; scope: 'shops'; read: 'shops.panel'">
|
||||
<button (click)="suspend(shop.id)" color="warn" dsh-stroked-button>
|
||||
{{ p('suspend') }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</ng-template>
|
@ -0,0 +1,226 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { TranslocoTestingModule } from '@ngneat/transloco';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
import { ShopLocation } from '../../../../../../../../api-codegen/anapi/swagger-codegen';
|
||||
import { ShopsService } from '../../../../../../../../api-codegen/capi/shops.service';
|
||||
import { Shop } from '../../../../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { ApiShopsService } from '../../../../../../../../api/shop';
|
||||
import { ShopItem } from '../../../../types/shop-item';
|
||||
import { ShopActionsService } from '../../services/shop-actions/shop-actions.service';
|
||||
import { ShopActionResult } from '../../types/shop-action-result';
|
||||
import { ShopActionsComponent } from './shop-actions.component';
|
||||
|
||||
const mockShop: ShopItem = {
|
||||
id: 'mock',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
url: 'example.com',
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
balance: {
|
||||
amount: 20,
|
||||
currency: 'USD',
|
||||
},
|
||||
};
|
||||
|
||||
class MockShopsService {
|
||||
getShops(): Observable<Shop[]> {
|
||||
return of([]);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class MockApiShopsService extends ApiShopsService {
|
||||
set mockShops(shops: Shop[]) {
|
||||
this._shops = shops;
|
||||
}
|
||||
private _shops: Shop[] = [];
|
||||
|
||||
set mockActionResponse(response: any) {
|
||||
this._actionResponse = response;
|
||||
}
|
||||
private _actionResponse: any;
|
||||
|
||||
getShopByID(shopID: string): Observable<Shop> {
|
||||
return of(this._shops.find(({ id }) => id === shopID));
|
||||
}
|
||||
|
||||
getShops(): Observable<Shop[]> {
|
||||
return of(this._shops);
|
||||
}
|
||||
|
||||
reloadShops() {
|
||||
this._shops = cloneDeep(this._shops);
|
||||
}
|
||||
|
||||
suspendShop() {
|
||||
return of(this._actionResponse);
|
||||
}
|
||||
|
||||
activateShop() {
|
||||
return of(this._actionResponse);
|
||||
}
|
||||
}
|
||||
|
||||
class MockMatDialogRef<T = any, R = any> extends MatDialogRef<T, R> {}
|
||||
|
||||
class MockMatDialog {
|
||||
private _dialogRef: MockMatDialogRef;
|
||||
|
||||
get dialogRef(): MockMatDialogRef {
|
||||
return this._dialogRef;
|
||||
}
|
||||
|
||||
open<T, R = any>(): MockMatDialogRef<T, R> {
|
||||
this._dialogRef = new MockMatDialogRef(null, null);
|
||||
return this._dialogRef;
|
||||
}
|
||||
}
|
||||
|
||||
describe('ShopActionsComponent', () => {
|
||||
let component: ShopActionsComponent;
|
||||
let fixture: ComponentFixture<ShopActionsComponent>;
|
||||
let mockDialog: MockMatDialog;
|
||||
let actionsService: ShopActionsService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
mockDialog = new MockMatDialog();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslocoTestingModule.withLangs({
|
||||
en: {
|
||||
shops: {
|
||||
panel: {
|
||||
activate: 'activate',
|
||||
suspend: 'suspend',
|
||||
},
|
||||
suspend: {
|
||||
success: 'success suspend',
|
||||
error: 'error suspend',
|
||||
},
|
||||
activate: {
|
||||
success: 'success activate',
|
||||
error: 'error activate',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
MatSnackBarModule,
|
||||
],
|
||||
declarations: [ShopActionsComponent],
|
||||
providers: [
|
||||
ShopActionsService,
|
||||
{
|
||||
provide: ApiShopsService,
|
||||
useClass: MockApiShopsService,
|
||||
},
|
||||
{
|
||||
provide: ShopsService,
|
||||
useClass: MockShopsService,
|
||||
},
|
||||
{
|
||||
provide: MatDialog,
|
||||
useValue: mockDialog,
|
||||
},
|
||||
],
|
||||
})
|
||||
.overrideComponent(ShopActionsComponent, { set: { providers: [] } })
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShopActionsComponent);
|
||||
component = fixture.componentInstance;
|
||||
actionsService = TestBed.inject(ShopActionsService);
|
||||
|
||||
component.shop = mockShop;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('suspend', () => {
|
||||
it('should call service suspend method', () => {
|
||||
const spyOnSuspend = spyOn(actionsService, 'suspend').and.returnValue(of(ShopActionResult.SUCCESS));
|
||||
|
||||
component.suspend('id');
|
||||
|
||||
expect(spyOnSuspend).toHaveBeenCalledTimes(1);
|
||||
expect(spyOnSuspend).toHaveBeenCalledWith('id');
|
||||
});
|
||||
|
||||
it('should emit update data if suspend was successful', () => {
|
||||
const spyOnSuspend = spyOn(actionsService, 'suspend').and.returnValue(of(ShopActionResult.SUCCESS));
|
||||
const spyOnUpdateData = spyOn(component.updateData, 'emit');
|
||||
|
||||
component.suspend('id');
|
||||
|
||||
expect(spyOnSuspend).toHaveBeenCalledTimes(1);
|
||||
expect(spyOnUpdateData).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should emit update data if suspend was not successful', () => {
|
||||
const spyOnSuspend = spyOn(actionsService, 'suspend').and.returnValue(of(ShopActionResult.ERROR));
|
||||
const spyOnUpdateData = spyOn(component.updateData, 'emit');
|
||||
|
||||
component.suspend('id');
|
||||
|
||||
expect(spyOnSuspend).toHaveBeenCalledTimes(1);
|
||||
expect(spyOnUpdateData).not.toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('activate', () => {
|
||||
it('should call service activate method', () => {
|
||||
const spyOnActivate = spyOn(actionsService, 'activate').and.returnValue(of(ShopActionResult.SUCCESS));
|
||||
|
||||
component.activate('id');
|
||||
|
||||
expect(spyOnActivate).toHaveBeenCalledTimes(1);
|
||||
expect(spyOnActivate).toHaveBeenCalledWith('id');
|
||||
});
|
||||
|
||||
it('should emit update data if activate was successful', () => {
|
||||
const spyOnActivate = spyOn(actionsService, 'activate').and.returnValue(of(ShopActionResult.SUCCESS));
|
||||
const spyOnUpdateData = spyOn(component.updateData, 'emit');
|
||||
|
||||
component.activate('id');
|
||||
|
||||
expect(spyOnActivate).toHaveBeenCalledTimes(1);
|
||||
expect(spyOnUpdateData).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should emit update data if activate was not successful', () => {
|
||||
const spyOnActivate = spyOn(actionsService, 'activate').and.returnValue(of(ShopActionResult.ERROR));
|
||||
const spyOnUpdateData = spyOn(component.updateData, 'emit');
|
||||
|
||||
component.activate('id');
|
||||
|
||||
expect(spyOnActivate).toHaveBeenCalledTimes(1);
|
||||
expect(spyOnUpdateData).not.toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,38 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { filter, take } from 'rxjs/operators';
|
||||
|
||||
import { ShopItem } from '../../../../types/shop-item';
|
||||
import { isSuccessfulShopAction } from '../../services/shop-actions/is-successful-shop-action';
|
||||
import { ShopActionsService } from '../../services/shop-actions/shop-actions.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-actions',
|
||||
templateUrl: 'shop-actions.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [ShopActionsService],
|
||||
})
|
||||
export class ShopActionsComponent {
|
||||
@Input() shop: ShopItem;
|
||||
|
||||
@Output() updateData = new EventEmitter<void>();
|
||||
|
||||
constructor(private shopActions: ShopActionsService) {}
|
||||
|
||||
suspend(id: string): void {
|
||||
this.shopActions
|
||||
.suspend(id)
|
||||
.pipe(take(1), filter(isSuccessfulShopAction))
|
||||
.subscribe(() => {
|
||||
this.updateData.emit();
|
||||
});
|
||||
}
|
||||
|
||||
activate(id: string): void {
|
||||
this.shopActions
|
||||
.activate(id)
|
||||
.pipe(take(1), filter(isSuccessfulShopAction))
|
||||
.subscribe(() => {
|
||||
this.updateData.emit();
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
// TODO: add unit tests
|
@ -1,6 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
|
||||
import { ShopContractDetailsService } from './shop-contract-details.service';
|
||||
import { ShopContractDetailsService } from '../../services/shop-contract-details/shop-contract-details.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-contract-details',
|
@ -0,0 +1,9 @@
|
||||
<ng-container *transloco="let p; scope: 'shops'; read: 'shops.panel'">
|
||||
<div class="mat-title">{{ p('id') }}</div>
|
||||
<div class="mat-subheading-2">{{ id }}</div>
|
||||
<div>
|
||||
<button dsh-button [cdkCopyToClipboard]="id" (cdkCopyToClipboardCopied)="copied($event)">
|
||||
{{ p('copy') }}
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
@ -0,0 +1,60 @@
|
||||
import { ClipboardModule } from '@angular/cdk/clipboard';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { TranslocoTestingModule } from '@ngneat/transloco';
|
||||
|
||||
import { ShopIdComponent } from './shop-id.component';
|
||||
|
||||
describe('ShopIdComponent', () => {
|
||||
let component: ShopIdComponent;
|
||||
let fixture: ComponentFixture<ShopIdComponent>;
|
||||
let snackbar: MatSnackBar;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslocoTestingModule.withLangs({
|
||||
en: {
|
||||
copied: 'Copied!',
|
||||
copyFailed: 'CopyFailed!',
|
||||
},
|
||||
}),
|
||||
MatSnackBarModule,
|
||||
ClipboardModule,
|
||||
],
|
||||
declarations: [ShopIdComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShopIdComponent);
|
||||
component = fixture.componentInstance;
|
||||
snackbar = TestBed.inject(MatSnackBar);
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('copied', () => {
|
||||
it('should open snackbar with successful copy text', () => {
|
||||
const spyOnSnackBar = spyOn(snackbar, 'open').and.returnValue(null);
|
||||
|
||||
component.copied(true);
|
||||
|
||||
expect(spyOnSnackBar).toHaveBeenCalledTimes(1);
|
||||
expect(spyOnSnackBar).toHaveBeenCalledWith('en.copied', 'OK', { duration: 1000 });
|
||||
});
|
||||
|
||||
it('should open snackbar with error copy text', () => {
|
||||
const spyOnSnackBar = spyOn(snackbar, 'open').and.returnValue(null);
|
||||
|
||||
component.copied(false);
|
||||
|
||||
expect(spyOnSnackBar).toHaveBeenCalledTimes(1);
|
||||
expect(spyOnSnackBar).toHaveBeenCalledWith('en.copyFailed', 'OK', { duration: 1000 });
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,18 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-id',
|
||||
templateUrl: 'shop-id.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShopIdComponent {
|
||||
@Input() id: string;
|
||||
|
||||
constructor(private snackBar: MatSnackBar, private transloco: TranslocoService) {}
|
||||
|
||||
copied(isCopied: boolean) {
|
||||
this.snackBar.open(this.transloco.translate(isCopied ? 'copied' : 'copyFailed'), 'OK', { duration: 1000 });
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<div *transloco="let p; scope: 'shops'; read: 'shops.panel'" fxLayout="column" fxLayoutGap="24px">
|
||||
<dsh-details-item [title]="p('name')" fxFlex fxFlex.lt-md="100">{{ shop.details.name }}</dsh-details-item>
|
||||
<dsh-details-item [title]="p('url')" fxFlex fxFlex.lt-md="100">{{ shop.location.url }}</dsh-details-item>
|
||||
<div fxLayout="row" fxLayout.lt-md="column" fxLayoutAlign="space-between" fxLayoutGap="24px">
|
||||
<dsh-details-item [title]="p('balance')" fxFlex fxFlex.lt-md="100">
|
||||
<dsh-shop-balance [shop]="shop"></dsh-shop-balance>
|
||||
</dsh-details-item>
|
||||
<dsh-details-item [title]="p('createdAt')" fxFlex fxFlex.lt-md="100">{{
|
||||
shop.createdAt | date: 'dd MMMM yyyy, HH:mm:ss'
|
||||
}}</dsh-details-item>
|
||||
<dsh-details-item [title]="p('category')" fxFlex fxFlex.lt-md="100">{{
|
||||
(category$ | async)?.name
|
||||
}}</dsh-details-item>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,102 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { TranslocoTestingModule } from '@ngneat/transloco';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { DetailsItemModule } from '@dsh/components/layout';
|
||||
|
||||
import { ShopLocation } from '../../../../../../../../api-codegen/anapi/swagger-codegen';
|
||||
import { Category } from '../../../../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { ShopItem } from '../../../../types/shop-item';
|
||||
import { ShopBalanceModule } from '../../../shop-balance';
|
||||
import { CategoryService } from '../../services/category/category.service';
|
||||
import { ShopInfoComponent } from './shop-info.component';
|
||||
|
||||
const mockShop: ShopItem = {
|
||||
id: 'mock',
|
||||
createdAt: new Date(),
|
||||
isBlocked: false,
|
||||
isSuspended: false,
|
||||
categoryID: 1,
|
||||
location: {
|
||||
locationType: ShopLocation.LocationTypeEnum.ShopLocationUrl,
|
||||
url: 'example.com',
|
||||
},
|
||||
details: {
|
||||
name: 'my name',
|
||||
description: 'some description',
|
||||
},
|
||||
contractID: 'contractID',
|
||||
payoutToolID: 'payoutToolID',
|
||||
scheduleID: 1,
|
||||
account: {
|
||||
currency: 'USD',
|
||||
guaranteeID: 2,
|
||||
settlementID: 2,
|
||||
},
|
||||
balance: {
|
||||
amount: 20,
|
||||
currency: 'USD',
|
||||
},
|
||||
};
|
||||
|
||||
class MockCategoryService {
|
||||
category$ = new Subject<Category>();
|
||||
|
||||
updateID(categoryID: number) {
|
||||
this.category$.next({
|
||||
categoryID,
|
||||
name: 'Mock Category',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('ShopInfoComponent', () => {
|
||||
let component: ShopInfoComponent;
|
||||
let fixture: ComponentFixture<ShopInfoComponent>;
|
||||
let mockCategoryService: MockCategoryService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
mockCategoryService = new MockCategoryService();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslocoTestingModule.withLangs({
|
||||
en: {
|
||||
shops: {
|
||||
panel: {
|
||||
name: 'PanelName',
|
||||
url: 'PanelUrl',
|
||||
balance: 'PanelBalance',
|
||||
createdAt: 'PanelCreatedAt',
|
||||
category: 'PanelCategory',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
FlexLayoutModule,
|
||||
DetailsItemModule,
|
||||
ShopBalanceModule,
|
||||
],
|
||||
declarations: [ShopInfoComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: CategoryService,
|
||||
useValue: mockCategoryService,
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShopInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.shop = mockShop;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,32 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import isNil from 'lodash.isnil';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { Category } from '../../../../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { ShopItem } from '../../../../types/shop-item';
|
||||
import { CategoryService } from '../../services/category/category.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-info',
|
||||
templateUrl: 'shop-info.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShopInfoComponent {
|
||||
@Input()
|
||||
get shop(): ShopItem {
|
||||
return this._shop;
|
||||
}
|
||||
set shop(shopItem: ShopItem) {
|
||||
this._shop = shopItem;
|
||||
if (isNil(shopItem)) {
|
||||
return;
|
||||
}
|
||||
this.categoryService.updateID(shopItem.categoryID);
|
||||
}
|
||||
|
||||
category$: Observable<Category> = this.categoryService.category$;
|
||||
|
||||
private _shop: ShopItem;
|
||||
|
||||
constructor(private categoryService: CategoryService) {}
|
||||
}
|
@ -0,0 +1 @@
|
||||
// TODO: add unit tests
|
@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
|
||||
import { PayoutToolParams } from './payout-tool-params';
|
||||
import { ShopPayoutToolDetailsService } from './shop-payout-tool-details.service';
|
||||
import { ShopPayoutToolDetailsService } from '../../services/shop-payout-tool-details/shop-payout-tool-details.service';
|
||||
import { PayoutToolParams } from '../../types/payout-tool-params';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-payout-tool-details',
|
@ -0,0 +1,2 @@
|
||||
export * from './shop-details.component';
|
||||
export * from './shop-details.module';
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CategoryService } from './category.service';
|
||||
|
||||
describe('CategoryService', () => {
|
||||
let service: CategoryService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(CategoryService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,27 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { combineLatest, Observable, Subject } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
|
||||
import { Category } from '../../../../../../../../api-codegen/capi/swagger-codegen';
|
||||
import { CategoriesService } from '../../../../../../../../api/categories';
|
||||
import { SHARE_REPLAY_CONF } from '../../../../../../../../custom-operators';
|
||||
|
||||
@Injectable()
|
||||
export class CategoryService {
|
||||
category$: Observable<Category>;
|
||||
|
||||
private categoryID$ = new Subject<number>();
|
||||
|
||||
constructor(private categoriesService: CategoriesService) {
|
||||
this.category$ = combineLatest([this.categoryID$, this.categoriesService.categories$]).pipe(
|
||||
map(([categoryID, categories]: [number, Category[]]) =>
|
||||
categories.find((c) => c.categoryID === categoryID)
|
||||
),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
}
|
||||
|
||||
updateID(categoryID: number): void {
|
||||
this.categoryID$.next(categoryID);
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
import { ShopActionResult } from '../../types/shop-action-result';
|
||||
|
||||
export function isSuccessfulShopAction(action: ShopActionResult): boolean {
|
||||
return action === ShopActionResult.SUCCESS;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ShopActionsService } from './shop-actions.service';
|
||||
|
||||
describe('ShopActionsService', () => {
|
||||
let service: ShopActionsService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ShopActionsService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,61 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, filter, map, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { ConfirmActionDialogComponent } from '@dsh/components/popups';
|
||||
|
||||
import { ApiShopsService } from '../../../../../../../../api/shop';
|
||||
import { ShopActionResult } from '../../types/shop-action-result';
|
||||
|
||||
@Injectable()
|
||||
export class ShopActionsService {
|
||||
constructor(
|
||||
private shopService: ApiShopsService,
|
||||
private dialog: MatDialog,
|
||||
private snackBar: MatSnackBar,
|
||||
private transloco: TranslocoService
|
||||
) {}
|
||||
|
||||
suspend(shopID: string): Observable<ShopActionResult> {
|
||||
return this.dialog
|
||||
.open(ConfirmActionDialogComponent)
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
filter((r) => r === 'confirm'),
|
||||
switchMap(() => this.shopService.suspendShop(shopID)),
|
||||
map(() => {
|
||||
this.snackBar.open(this.transloco.translate('suspend.success', null, 'shops'), 'OK', {
|
||||
duration: 3000,
|
||||
});
|
||||
return ShopActionResult.SUCCESS;
|
||||
}),
|
||||
catchError(() => {
|
||||
this.snackBar.open(this.transloco.translate('suspend.error', null, 'shops'), 'OK');
|
||||
return of(ShopActionResult.ERROR);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
activate(shopID: string): Observable<ShopActionResult> {
|
||||
return this.dialog
|
||||
.open(ConfirmActionDialogComponent)
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
filter((r) => r === 'confirm'),
|
||||
switchMap(() => this.shopService.activateShop(shopID)),
|
||||
map(() => {
|
||||
this.snackBar.open(this.transloco.translate('activate.success', null, 'shops'), 'OK', {
|
||||
duration: 3000,
|
||||
});
|
||||
return ShopActionResult.SUCCESS;
|
||||
}),
|
||||
catchError(() => {
|
||||
this.snackBar.open(this.transloco.translate('activate.error', null, 'shops'), 'OK');
|
||||
return of(ShopActionResult.ERROR);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user