Compare commits
423 commits
v0.0.1
...
developmen
Author | SHA1 | Date | |
---|---|---|---|
|
94ee509b43 | ||
bab6b9f354 | |||
a0c0c7e032 | |||
90f0a91f50 | |||
067f0543d4 | |||
|
792e431fad | ||
a19ddcb1b7 | |||
|
49e5951747 | ||
|
e6f7ba4b65 | ||
e839ab28b6 | |||
7be68c3fc0 | |||
0dd7ab1e34 | |||
|
53db04dd9c | ||
|
da30036d4e | ||
|
2a5e790e3d | ||
|
e4dcc2e664 | ||
|
a0e28cb440 | ||
|
4312f2ad8b | ||
|
20ec21f8cb | ||
|
ee88c8c31a | ||
|
55eb053b08 | ||
|
fba44b237a | ||
|
20f7e5dc0f | ||
|
d4540e7650 | ||
|
611dbc4bac | ||
|
7c651e1522 | ||
|
3ed88a4d5e | ||
|
e9c81dc428 | ||
|
8a4e7872fb | ||
|
208a85c5a5 | ||
|
839d322327 | ||
|
28b575e819 | ||
|
0f190a70d7 | ||
|
326e4ec322 | ||
|
fe125c812f | ||
|
b91de60175 | ||
|
5abf5d29b3 | ||
|
891bb0aee4 | ||
|
50b9cfdc31 | ||
|
53aae6fc07 | ||
|
5e92829a7a | ||
|
ace6900145 | ||
|
893bdca736 | ||
|
75edb86abe | ||
|
66388593e6 | ||
|
f697b2243d | ||
|
abdbfda56c | ||
|
bad4f7f83c | ||
|
87160f5387 | ||
|
bab670f60b | ||
|
7883dbb169 | ||
|
52bce4dd0a | ||
|
5c85d5aae9 | ||
|
db5f982ed3 | ||
|
1dc46a55e0 | ||
|
420174e953 | ||
|
5e91665e0b | ||
|
301f706365 | ||
|
ade578d78b | ||
|
8859c6977c | ||
|
9fbe36e401 | ||
|
55e62a6b87 | ||
|
03e9e365fc | ||
|
4181a09b96 | ||
|
4b42a08af1 | ||
|
156a2f3764 | ||
|
7c5eedaa87 | ||
|
301a6e619a | ||
|
12a72c00eb | ||
|
e9334cec6d | ||
|
1dd17e7a7a | ||
|
ab72264521 | ||
|
6f801d20de | ||
|
93b21d2547 | ||
|
277c058450 | ||
|
ce64a9140e | ||
|
5ffffbfe08 | ||
|
a5b32936bd | ||
|
5b50803a25 | ||
|
88bd12157b | ||
|
6a338d41a4 | ||
|
a8cd23cf5c | ||
|
a2f8f90513 | ||
|
6e21cf8746 | ||
|
e2960a3fcf | ||
|
b6c0e1b6dd | ||
|
00889b2104 | ||
|
263d3d15ab | ||
|
fe6964fb8d | ||
|
6c807194bc | ||
|
a605d66d37 | ||
|
d550bf5e12 | ||
|
c206a394d2 | ||
|
7ee0b64351 | ||
|
3248caed07 | ||
|
0dcf98fed3 | ||
|
5cd0db3faf | ||
|
b1790a2fe9 | ||
|
75ba91379d | ||
|
8202cdfdb1 | ||
|
40462b7d8f | ||
|
6b1e474916 | ||
|
af04adeb31 | ||
|
df631ad793 | ||
|
7a6f40e300 | ||
|
4b7ffd91b3 | ||
|
c5f6cb2750 | ||
|
bd33f79067 | ||
|
630beeb5d0 | ||
|
71162bcce7 | ||
|
45d52c9cdb | ||
|
1c11f8e936 | ||
|
9f50b3c2ce | ||
|
45e700675a | ||
|
9dcc22bc3c | ||
|
46d2ed7ad8 | ||
|
c98a098ae9 | ||
|
2ded74f63c | ||
|
2998f0b3da | ||
|
d150441aa1 | ||
|
ee636b87fa | ||
|
1b56d6e95c | ||
|
55f3f50576 | ||
|
ec1655a5ca | ||
|
68cf9ee049 | ||
|
6649888876 | ||
|
bc8f024d41 | ||
|
700287fc32 | ||
|
5bb0669204 | ||
|
0c43962f47 | ||
|
e7d2038041 | ||
|
090b3a65cb | ||
|
54f2ad832d | ||
|
f018e72e3a | ||
|
a751cf393e | ||
|
63475b45b4 | ||
|
02be5e78d0 | ||
|
8510c539f7 | ||
|
18340a61f9 | ||
|
df30ab88c4 | ||
|
fb606016ee | ||
|
ee0daa2d57 | ||
|
4d34e5c0f2 | ||
|
478429b3c9 | ||
|
21e163fda2 | ||
|
f7d2faea54 | ||
|
e7e3f90739 | ||
|
10622204b7 | ||
|
c9f786eaee | ||
|
e9b659a4bb | ||
|
adc0a1a7db | ||
|
ca95f8d10d | ||
|
fa2180f603 | ||
|
a388e2629d | ||
|
32b6dc810f | ||
|
6abe0ec6f6 | ||
|
ebdd0354f5 | ||
|
e19102a457 | ||
|
e6658aeccb | ||
|
334ed6b411 | ||
|
a6e5412e40 | ||
|
f05a5816e4 | ||
|
5ef8f9471c | ||
|
6bf111a3be | ||
|
7811806903 | ||
|
b373d4e63f | ||
|
233c523353 | ||
|
7215a31262 | ||
|
2ac7244605 | ||
|
77972adcbf | ||
|
4867a1ecaa | ||
|
cc08f622af | ||
|
a4e4158d7f | ||
|
c244eb8735 | ||
|
1cb3985778 | ||
|
48fd3114b2 | ||
|
4f3e0aa22d | ||
|
735b2de28a | ||
|
daff33657f | ||
|
3d5d20e419 | ||
|
7881ae1103 | ||
|
2709a7893d | ||
|
9d17a331fa | ||
|
52a2b6716c | ||
|
61cd0905d1 | ||
|
093cdef3cf | ||
|
511f680800 | ||
|
351aa5b2c1 | ||
|
6c202fa84a | ||
|
1f24095285 | ||
|
4bd3d67323 | ||
|
085c1b40d5 | ||
|
022f0ab2a1 | ||
|
8b65d8eefb | ||
|
e8217de9b6 | ||
|
db1e3efbf5 | ||
|
40eef44666 | ||
|
463bbe98a9 | ||
|
2188c1272f | ||
|
49bd7d2e0c | ||
|
3e8767e481 | ||
|
f36ef00478 | ||
|
b9578da579 | ||
|
c45790c7e9 | ||
|
e1dfa37f63 | ||
|
f3c6370394 | ||
|
40b0a0088c | ||
|
dc27b286cf | ||
|
144961acb3 | ||
|
2965a8d33c | ||
|
7bb8500315 | ||
|
dbf77ca4ee | ||
|
0b27aa43e4 | ||
|
c8c52d33c4 | ||
|
aaaa7c7a05 | ||
|
c62e74e07a | ||
|
675f319993 | ||
|
eee50b9073 | ||
|
c37b30bea5 | ||
|
db8d27a888 | ||
|
247e38e680 | ||
|
f7697178fd | ||
|
b4f3f754c2 | ||
|
d97c21caf8 | ||
|
f6ef50f686 | ||
|
450b63620f | ||
|
6e9f4133d2 | ||
|
f5c0a76032 | ||
|
45d016c897 | ||
|
db5a712a5d | ||
|
8b7b5e50be | ||
|
5762b35fbe | ||
|
f6dd18bfc8 | ||
|
46e2efeaba | ||
|
9bbf7a5c2e | ||
|
bf965448c5 | ||
|
d11fbcb4e6 | ||
|
5d112ff4a6 | ||
|
ec1aef6627 | ||
|
6c16b7aee0 | ||
|
d674c5b59b | ||
|
ca2cf0fdab | ||
|
9446ef11d4 | ||
|
5f2eee81ba | ||
|
6cca97cc68 | ||
|
fae6b7ce31 | ||
|
2cc087a295 | ||
|
43144da9fc | ||
|
d27d18e729 | ||
|
40973b87ff | ||
|
9e122bf4bc | ||
|
4a388a4c3e | ||
|
22b3e00eaa | ||
|
a20d94d9cb | ||
|
7284452317 | ||
|
d285b04119 | ||
|
004c30cc28 | ||
|
45eb7cb7a1 | ||
|
bd8af1d1bd | ||
|
b18943c268 | ||
|
a2ff339a84 | ||
|
93b5fb52cc | ||
|
511969656e | ||
|
d17a686490 | ||
|
59efe7a650 | ||
|
e64b6057e8 | ||
|
c4b5f8f91f | ||
|
c7020f4283 | ||
|
08a8f464f3 | ||
|
71eee8f06b | ||
|
f9711b6686 | ||
|
ef66009baa | ||
|
057487db4c | ||
|
ee43c80041 | ||
|
11fcf170ba | ||
|
95a918076a | ||
|
4f6edf9470 | ||
|
2784387b12 | ||
|
2ed709476c | ||
|
2d4a64cef4 | ||
|
30919e6eb2 | ||
|
d8fab7c4df | ||
|
f4c9724097 | ||
|
f882ad64dd | ||
|
13d84905b8 | ||
|
b8fcedaf09 | ||
|
b8f5687ac2 | ||
|
99e0a6c5d4 | ||
|
b50c65ba3d | ||
|
46d99d11f9 | ||
|
728f9e3d47 | ||
|
e16cf3e801 | ||
|
7e7bb4ccf3 | ||
|
93f15ae6de | ||
|
9545601d01 | ||
|
32bebd7c24 | ||
|
6eb6425fb8 | ||
|
7e69c111e7 | ||
|
ef84c7fa68 | ||
|
44740d5dd4 | ||
|
d469bc2c7d | ||
|
2092716b95 | ||
|
dd891486ae | ||
|
aa7d17522e | ||
|
e104b2c4d2 | ||
|
0829f8d71b | ||
|
644c097f0b | ||
|
100a3c71a8 | ||
|
0cc3a473cf | ||
|
4428cd6fcd | ||
|
03ec811c6c | ||
|
a44975b879 | ||
|
830b4b3c4f | ||
|
f39c192a2b | ||
|
d9b8ebfc50 | ||
|
0ce452233d | ||
|
f5ee21d067 | ||
|
53fb48e819 | ||
|
614b353686 | ||
|
16ef46972e | ||
|
cc04b9eb11 | ||
|
dd63a4ee4a | ||
|
bf191a7c06 | ||
|
ae2fc74d2e | ||
|
2af4cd8adf | ||
|
7968493fe3 | ||
|
f13546a6fe | ||
|
007ddd294a | ||
|
2b92d02346 | ||
|
4670e10c2f | ||
|
c2ee94f82a | ||
|
961d8eafe3 | ||
|
0af35cd787 | ||
|
0fb3f51748 | ||
|
5ca44cedf3 | ||
|
b7cbbcc23a | ||
|
4a331a3475 | ||
|
7843a87fd6 | ||
|
5ed6bfe7f3 | ||
|
b8775e2e45 | ||
|
d34fdb58ca | ||
|
840d0aabb1 | ||
|
3bde7aed3f | ||
|
b46ab1d8a2 | ||
|
9fa0c16250 | ||
|
694a65f0ee | ||
|
a411a0e7fb | ||
|
4c5074c5fc | ||
|
83bc98ece5 | ||
|
c85602f6e8 | ||
|
5af0e1e35c | ||
|
cc38618b03 | ||
|
3f5a52d4f4 | ||
|
663d78e4bf | ||
|
63006e4be1 | ||
|
c4d1bf5a07 | ||
|
72ceabf43f | ||
|
1c0cd372d5 | ||
|
52834af3fc | ||
|
0286887ead | ||
|
994dcdfb63 | ||
|
0688685676 | ||
|
a5b73c7e59 | ||
|
03299fb5a8 | ||
|
3009365682 | ||
|
8f725b93af | ||
|
356aeae974 | ||
|
c4a9790eb8 | ||
|
faea82d2cc | ||
|
40e20679e0 | ||
|
8a5236ad3a | ||
|
f5239a3822 | ||
|
0b2c9c20f8 | ||
|
0a54cdc091 | ||
|
bca2d62fdc | ||
|
0a9e80fc97 | ||
|
1ce755d2d3 | ||
|
1171f935a7 | ||
|
ed3a14806b | ||
|
aeda2ec027 | ||
|
e82c12d265 | ||
|
0cd7cdc96c | ||
|
cea000b90c | ||
|
ed738b287a | ||
|
8520328582 | ||
|
1eb8d71b4a | ||
|
d8b92af1a3 | ||
|
2ffe32c6f5 | ||
|
a622b0d871 | ||
|
f402d46db8 | ||
|
b59351addf | ||
|
1eb79aa047 | ||
|
64e028f0c5 | ||
|
5d3283171c | ||
|
40bd7459b5 | ||
|
67bb904770 | ||
|
7cf8af3058 | ||
|
089e7f7a97 | ||
|
7b03f6adff | ||
|
f1a6b1886f | ||
|
e32d29b310 | ||
|
f70d3945b6 | ||
|
e62a46c312 | ||
|
8f164ca94a | ||
|
a5b786a749 | ||
|
0907134ff7 | ||
|
662d6be5c9 | ||
|
921b25718d | ||
|
f6c0583481 | ||
|
61ac856cc2 | ||
|
dec53b4630 | ||
|
8807ba6cc9 | ||
|
baf64ff4b7 | ||
|
49be004bbc | ||
|
10664f0da9 | ||
|
78af694c04 | ||
|
d1eb79b394 | ||
|
d2f75af362 | ||
|
7f3e9f4fd0 | ||
|
f34c558d41 | ||
|
70be2cbc2b | ||
|
7b1771b45f | ||
|
bd0c9e4aa5 |
119 changed files with 18846 additions and 7755 deletions
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"project": {
|
||||
"version": "1.0.0-alpha.4",
|
||||
"name": "coreui-angular"
|
||||
},
|
||||
"apps": [
|
||||
{
|
||||
"root": "src",
|
||||
"outDir": "dist",
|
||||
"assets": ["assets"],
|
||||
"index": "index.html",
|
||||
"main": "main.ts",
|
||||
"polyfills": "polyfills.ts",
|
||||
"test": "test.ts",
|
||||
"tsconfig": "tsconfig.app.json",
|
||||
"testTsconfig": "tsconfig.spec.json",
|
||||
"prefix": "app",
|
||||
"scripts": [
|
||||
"../node_modules/moment/min/moment.min.js"
|
||||
],
|
||||
"styles": [
|
||||
"scss/style.scss"
|
||||
],
|
||||
"environmentSource": "environments/environment.ts",
|
||||
"environments": {
|
||||
"dev": "environments/environment.ts",
|
||||
"prod": "environments/environment.prod.ts",
|
||||
"local": "environments/environment.local.ts"
|
||||
}
|
||||
}
|
||||
],
|
||||
"e2e": {
|
||||
"protractor": {
|
||||
"config": "./protractor.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": [
|
||||
{
|
||||
"project": "src/tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"project": "src/tsconfig.spec.json"
|
||||
},
|
||||
{
|
||||
"project": "e2e/tsconfig.e2e.json"
|
||||
}
|
||||
],
|
||||
"test": {
|
||||
"karma": {
|
||||
"config": "./karma.conf.js"
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"styleExt": "scss",
|
||||
"prefixInterfaces": false
|
||||
}
|
||||
}
|
6
.gitattributes
vendored
6
.gitattributes
vendored
|
@ -15,3 +15,9 @@
|
|||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
|
||||
# ensures font files are loaded as binary not text
|
||||
*.ttf binary
|
||||
*.eot binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
|
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -22,7 +22,6 @@ $RECYCLE.BIN/
|
|||
/bower_components
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
*.launch
|
||||
|
@ -41,8 +40,15 @@ testem.log
|
|||
/e2e/*.js
|
||||
/e2e/*.map
|
||||
|
||||
# build
|
||||
/dist
|
||||
|
||||
# local env variable
|
||||
/src/environments/environment.local.ts
|
||||
/src/environments/environment.prod.ts
|
||||
/src/environments/environment.dev.ts
|
||||
/src/environments/environment.ci.ts
|
||||
/src/environments/environments.tar
|
||||
|
||||
# =========================
|
||||
# Operating System Files
|
||||
|
|
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
# Default ignored files
|
||||
/workspace.xml
|
14
.idea/codeStyles/Project.xml
generated
Normal file
14
.idea/codeStyles/Project.xml
generated
Normal file
|
@ -0,0 +1,14 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
<option name="BLANK_LINES_AROUND_FUNCTION" value="0" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="BLANK_LINES_AROUND_METHOD" value="0" />
|
||||
<option name="BLANK_LINES_AROUND_METHOD_IN_INTERFACE" value="0" />
|
||||
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
|
||||
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
6
.idea/misc.xml
generated
Normal file
6
.idea/misc.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/Foodloop-Web.iml" filepath="$PROJECT_DIR$/Foodloop-Web.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
22
.travis.yml
22
.travis.yml
|
@ -2,12 +2,24 @@ addons:
|
|||
chrome: stable
|
||||
language: node_js
|
||||
node_js:
|
||||
- 8
|
||||
- node
|
||||
before_install:
|
||||
- openssl aes-256-cbc -K $encrypted_17157b34afc7_key -iv $encrypted_17157b34afc7_iv -in src/environments/environments.tar.enc -out src/environments/environments.tar -d
|
||||
- tar xf src/environments/environments.tar -C src/environments
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- npm config set spin false
|
||||
install:
|
||||
- npm install
|
||||
- npm install
|
||||
script:
|
||||
- npm run ci
|
||||
- npm run ci
|
||||
before_deploy:
|
||||
- ./bin/build-releases
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: "Ke27Qm16uRgfX+ZVAq92Suu0ZpLOG9TGXDD8ndKNQZ4zl80DOmePoKvVpk/5Ip3H57Me2seqtCwgjeUCfW0dX3KayPoNmGVxWXCsZCH3MZWuaMnGk/Zc1Ef8P3L1sTEG4LD6+59RaOiMwIrMtLPjSlzvV2gc+002O7MHoudx/qZl47L+T01B0Ovh1AueSVN224Q79NrBnbgTtMqaS3x2avLkJmdZpneafqeO5OusOFcvsHvBr7ca0qKv5yIpn4eotK2bo6TFuaC9e9i7gUgPKHb1/GXAK1DcteUDF3AzK/b7T+dqTS+1vowuNKjMZ+ecyB8VDXQlWnBcv/IGn/C3nBmtp2oN97BFQtHguCY42Qk9LZxIu9o0mpOt6aMRiIsfbWstgONKaLzgt4Ce3DFlJ7YR5BFRaoKDdGHoCW+tcucQ/o9vFCbBVZ8sol2aYJOiNHYxlC8A7NLs6YEjyckVAa3q1l6CddnSjrFA5oe9fsLdzUDGhJ57Nv7kF9v6jjsTlZtucrzf8ix9+vNKNfWLQ6K86UIeBT40pPYHLBmWEsiGai+s4IWrYjTscT4zQDHcfQCMuQbbb3NTEfy9Fwv0VQdIR/cKsQgUCZwwlv3RBImryuDFqY2pNOqvnGIcr/OJ/MmY9bbCYEp55dxrZ50dNBbtR4O8IyUDte/ycU4OKbE="
|
||||
file_glob: true
|
||||
file: "../WebApp-Releases/*"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
|
|
135
CHANGELOG.md
135
CHANGELOG.md
|
@ -2,6 +2,141 @@
|
|||
|
||||
# Next Release
|
||||
|
||||
# 0.1.13
|
||||
|
||||
* Added new graphs for organisation view, for better breakdown of spending
|
||||
* Added Suppliers spend search page
|
||||
* Updated and fixes numerous graphs on dashboard
|
||||
|
||||
## Minor
|
||||
|
||||
* *Dev Fixes* Updated Travis config
|
||||
|
||||
|
||||
# 0.1.12
|
||||
|
||||
* Fixed accidentally added app-root
|
||||
|
||||
# 0.1.11
|
||||
|
||||
* Bumped Angular to version 6 and upgraded packages to match
|
||||
* Converted RxJS 5 syntax to RxJS 6
|
||||
* Removed extraneous console logs
|
||||
* Changed dashboard sector list to category all time list & tweaked layout
|
||||
|
||||
# 0.1.10
|
||||
|
||||
* Allowed for creation of yearly recurring transactions
|
||||
* Added google analytics
|
||||
|
||||
# 0.1.9
|
||||
|
||||
* Made layout change to make it neater when chart doesn't show
|
||||
* Made hotfix
|
||||
|
||||
# 0.1.8
|
||||
|
||||
* Amended how category is pulled from server
|
||||
* Added chart on essential purchase numbers as a whole
|
||||
* Added bar chart of category purchases in the month
|
||||
* Added pie chart of purchases by category in the week
|
||||
* Added hint for closing the burger menu while in mobile view
|
||||
|
||||
# v0.1.7
|
||||
|
||||
* Fixed category on upload highlighting
|
||||
|
||||
# v0.1.6
|
||||
|
||||
* Changed layout of category choosing on upload
|
||||
* Added ability to edit and remove recurring transactions
|
||||
* Fixed HttpClient error log viewing
|
||||
* Made transaction validation more lenient
|
||||
|
||||
# v0.1.5
|
||||
|
||||
* Fixed category viewing on purchase
|
||||
* Changed category view from radio buttons to full label buttons
|
||||
* Amended local validation of submit
|
||||
* Changed recurring purchase selection view
|
||||
|
||||
# v0.1.4
|
||||
|
||||
* Amended category list on transaction submission
|
||||
* Added budget view for weekly breakdown of spend by category
|
||||
* Added flag to make purchases essential
|
||||
* Fixed budget view issues and amended it to show essential purchases that week
|
||||
* Added ability to make purchases recurring
|
||||
* Updated Moment dependency due to security issue
|
||||
* Fixed category uploading in upload
|
||||
|
||||
# v0.1.3
|
||||
|
||||
* Made fix to Travis config
|
||||
|
||||
# v0.1.2
|
||||
|
||||
* Removed unused button
|
||||
* Added ability to choose category for transaction
|
||||
* Added version bump on Angular
|
||||
|
||||
# v0.1.1
|
||||
|
||||
* Redid layout on circle customer view
|
||||
* Renamed customer dashboard headers
|
||||
|
||||
# v0.1.0
|
||||
|
||||
* Changed Story Trail choosing to modals
|
||||
* Revamped snippets and graph widgets on customer dashboard
|
||||
* Added local purchase pie chart for customer dashboard
|
||||
* Added week by week purchase list for customer dashboard
|
||||
* Added sector purchase amount list for customer dashboard
|
||||
* Added sector U to available ones
|
||||
* Fixed snippet layout
|
||||
|
||||
# v0.0.7
|
||||
|
||||
* Added ESTA to Story Trail
|
||||
* Reverted Story Trail naming to LIS
|
||||
* Changed Trail map code to make it flexible based on association
|
||||
|
||||
# v0.0.6
|
||||
|
||||
* Fix typo in Map Titles
|
||||
|
||||
# v0.0.5
|
||||
|
||||
* Change page name from Story Trail to Lancaster Independent Story
|
||||
|
||||
# v0.0.4
|
||||
|
||||
* Info window on map replaced with modals
|
||||
* Loading modals added to maps
|
||||
* Lancaster Independent Story markers added
|
||||
* Code dependencies updated
|
||||
* Angular code updated to Angular 5 and made fixes to account for it
|
||||
|
||||
# v0.0.3
|
||||
|
||||
* Customer Leaderboards added
|
||||
* Map made available to Customers
|
||||
* Clustering added to Map
|
||||
* Map renamed from "Supplier Map" to "Purchase Map" to fall in line with
|
||||
previous change
|
||||
* Added placeholder while password reset is not present
|
||||
* Fixed error on store list during a transaction
|
||||
|
||||
|
||||
# v0.0.2
|
||||
|
||||
* Supplier map added to organisation view
|
||||
* Fix various linter errors
|
||||
* Payroll Log Data added
|
||||
* Fixed register box view on login page
|
||||
* Made the form reset properly if adding a new one
|
||||
* Added better error messages on register and account edit
|
||||
|
||||
# v0.0.1
|
||||
|
||||
* Added Travis Testing
|
||||
|
|
12
Foodloop-Web.iml
Normal file
12
Foodloop-Web.iml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
121
README.md
121
README.md
|
@ -1,30 +1,43 @@
|
|||
# LocalLoop Web Interface
|
||||
# LocalSpend - Web Application
|
||||
|
||||
This is the repository for the LocalLoop web interface, for traders and
|
||||
customers to see and submit data to the service.
|
||||
This repository contains the Web application for the LocalSpend system.
|
||||
|
||||
## Current Status
|
||||
|
||||
| Branch | Status |
|
||||
| --- | --- |
|
||||
| *Master:* | [](https://travis-ci.org/Pear-Trading/FoodLoop-Web) |
|
||||
| *Development:* | [](https://travis-ci.org/Pear-Trading/FoodLoop-Web) |
|
||||
| `master` | [](https://travis-ci.org/Pear-Trading/FoodLoop-Web) |
|
||||
| `development` | [](https://travis-ci.org/Pear-Trading/FoodLoop-Web) |
|
||||
|
||||
## Contents
|
||||
|
||||
1. [Getting Started](#getting-started)
|
||||
1. [Environments](#environments)
|
||||
1. [Testing](#testing)
|
||||
1. [Troubleshooting](#troubleshooting)
|
||||
1. [Licences](#licenses)
|
||||
|
||||
## Getting Started
|
||||
|
||||
To get started with development, you will need an up to date version of
|
||||
node.js, git, and access to either the backend dev server, or a local running
|
||||
copy of the backend server from [LocalLoop Server][LocalLoop-Server].
|
||||
To get started with development, you will need an up-to-date version of
|
||||
Node.js, git, and access to either the backend dev. server or a local
|
||||
copy of it from the [LocalSpend Server][LocalLoop-Server] repo.
|
||||
|
||||
For your local node.js, We reccomend using [n][tj/n] on \*nix and Mac, for
|
||||
Windows take a look at [nodist][marcelklehr/nodist] - although other options
|
||||
exist. We reccomend Node.js version 8.0.0+ and npm version 5.3.0+.
|
||||
For your local Node.js, we recommend using:
|
||||
- [n][tj/n] for \*nix and Mac; and
|
||||
- [nodist][marcelklehr/nodist] for Windows.
|
||||
|
||||
To get this repository set up, first clone it and then run the following
|
||||
commands:
|
||||
We reccomend Node.js version 8.0.0+ and npm version 5.3.0+.
|
||||
|
||||
```
|
||||
npm install -g @angular/cli
|
||||
npm install
|
||||
```
|
||||
To get this repository set up:
|
||||
|
||||
1. Clone it
|
||||
2. Install the dependencies:
|
||||
- `npm install -g @angular/cli`
|
||||
- `npm install`
|
||||
3. Start the application:
|
||||
- `npm start`
|
||||
- The app. will automatically reload after source file changes
|
||||
|
||||
[LocalLoop-Server]:https://github.com/Pear-Trading/Foodloop-Server
|
||||
[tj/n]:https://github.com/tj/n
|
||||
|
@ -33,45 +46,51 @@ npm install
|
|||
## Environments
|
||||
|
||||
The app defaults to using the development server. For other options, see
|
||||
`src/environments/environments.ts`
|
||||
`src/environments/environments.ts`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project; the resulting files will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
|
||||
|
||||
## Testing
|
||||
|
||||
There are two types of test in this codebase:
|
||||
- unit tests (using Karma); and
|
||||
- E2E tests (using Protractor).
|
||||
|
||||
### Karma Unit Tests
|
||||
|
||||
To run these, you just need to run `npm run test` - this should work without any further configuration. These run the `*.spec.ts` files next to the normal source files, and are for basic tests for each function on their own.
|
||||
|
||||
These tests are a long-running process, and will automatically run on file changes. Just run the command above, and then check back to the browser window that appears to see any errors as you work!
|
||||
|
||||
### Protractor E2E Tests
|
||||
|
||||
To run these, you will need to run the following command:
|
||||
|
||||
- `webdriver-manager update`
|
||||
|
||||
This will download the correct webdriver for you (we use chromedriver), and
|
||||
any other dependencies it needs.
|
||||
After that, you can run the tests with `npm run e2e`.
|
||||
Note these run once, and will need to be re-run every time you want to run an
|
||||
e2e test.
|
||||
|
||||
These tests are best run regularly, and should show if any issues have emerged
|
||||
in other parts of the application that you are not aware of, or if some part
|
||||
of the flow through the app has changed significantly.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### ‘Error: EACCES: permission denied, access '/usr/local/lib'’ when installing dependencies
|
||||
|
||||
Change npm's default directory by following [these steps](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally#manually-change-npms-default-directory).
|
||||
|
||||
## Licences
|
||||
|
||||
### CoreUI
|
||||
|
||||
The interface itself is based off of [CoreUI][core-ui] which is MIT Licenced.
|
||||
For information, see [MIT Licence](./LICENCE.MIT) included in this repo.
|
||||
For information, see `LICENCE.MIT` included in this repo.
|
||||
|
||||
[core-ui]: http://coreui.io
|
||||
|
||||
# Old README
|
||||
|
||||
This is the old README with some minor tips on getting started, and more reading.
|
||||
|
||||
## Angular2DevelopmentCLI
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0-beta.32.3.
|
||||
|
||||
### Development server
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
### Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
|
||||
|
||||
### Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
|
||||
|
||||
### Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
### Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||
Before running the tests make sure you are serving the app via `ng serve`.
|
||||
|
||||
### Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
|
|
182
angular.json
Normal file
182
angular.json
Normal file
|
@ -0,0 +1,182 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"localloop-web": {
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"projectType": "application",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"assets": [
|
||||
"src/assets",
|
||||
{"input":
|
||||
"./node_modules/@google/markerclustererplus/images",
|
||||
"glob": "*",
|
||||
"output": "/images"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/scss/style.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/moment/min/moment.min.js"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"dev": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.dev.ts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"production": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"local": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.local.ts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ci": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.ci.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "localloop-web:build"
|
||||
},
|
||||
"configurations": {
|
||||
"dev": {
|
||||
"browserTarget": "localloop-web:build:dev"
|
||||
},
|
||||
"production": {
|
||||
"browserTarget": "localloop-web:build:production"
|
||||
},
|
||||
"local": {
|
||||
"browserTarget": "localloop-web:build:local"
|
||||
},
|
||||
"ci": {
|
||||
"browserTarget": "localloop-web:build:ci"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "localloop-web:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"karmaConfig": "./karma.conf.js",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"styles": [
|
||||
"src/scss/style.scss"
|
||||
],
|
||||
"assets": [
|
||||
"src/assets"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"src/tsconfig.app.json",
|
||||
"src/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"localloop-web-e2e": {
|
||||
"root": "",
|
||||
"sourceRoot": "",
|
||||
"projectType": "application",
|
||||
"architect": {
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "./protractor.conf.js",
|
||||
"devServerTarget": "localloop-web:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"local": {
|
||||
"devServerTarget": "localloop-web:serve:local"
|
||||
},
|
||||
"ci": {
|
||||
"devServerTarget": "localloop-web:serve:ci"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"e2e/tsconfig.e2e.json"
|
||||
],
|
||||
"exclude": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "localloop-web",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"prefix": "app",
|
||||
"styleext": "scss"
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"prefix": "app"
|
||||
}
|
||||
}
|
||||
}
|
29
bin/build-releases
Executable file
29
bin/build-releases
Executable file
|
@ -0,0 +1,29 @@
|
|||
#! /bin/bash
|
||||
set -e
|
||||
|
||||
VERSION=`git describe --tags`
|
||||
|
||||
if [ ! -d ../WebApp-Releases ]; then
|
||||
echo "Making new Releases Directory"
|
||||
mkdir ../WebApp-Releases
|
||||
fi
|
||||
|
||||
echo "Building releases for $VERSION"
|
||||
|
||||
echo "Building Prod Release..."
|
||||
|
||||
npm run build:prod
|
||||
|
||||
tar -czf ../WebApp-Releases/LocalLoop-Web-prod-$VERSION.tar.gz dist
|
||||
|
||||
echo "Building Dev Release..."
|
||||
|
||||
npm run build:dev
|
||||
|
||||
tar -czf ../WebApp-Releases/LocalLoop-Web-dev-$VERSION.tar.gz dist
|
||||
|
||||
echo "Cleaning up build artefacts..."
|
||||
|
||||
rm -rf dist
|
||||
|
||||
echo "Done"
|
12
browserslist
Normal file
12
browserslist
Normal file
|
@ -0,0 +1,12 @@
|
|||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
|
@ -1,4 +1,5 @@
|
|||
import { LoginPageObject } from './login.po';
|
||||
import { browser } from "protractor";
|
||||
|
||||
describe('Login Page', () => {
|
||||
let page: LoginPageObject;
|
||||
|
@ -12,13 +13,47 @@ describe('Login Page', () => {
|
|||
expect(page.getLoginHeaderText()).toEqual('Login');
|
||||
});
|
||||
|
||||
it('should have a username box of type text', () => {
|
||||
expect(page.isUsernameFieldPresent()).toBeTruthy();
|
||||
expect(page.getUsernameFieldType()).toBe('text');
|
||||
it('should have a username box of type email', () => {
|
||||
expect(page.isEmailFieldPresent()).toBeTruthy();
|
||||
expect(page.getEmailFieldType()).toEqual('email');
|
||||
});
|
||||
|
||||
it('should have a password box of type password', () => {
|
||||
expect(page.isPasswordFieldPresent()).toBeTruthy();
|
||||
expect(page.getPasswordFieldType()).toBe('password');
|
||||
});
|
||||
|
||||
it('should have a login button of type submit', () => {
|
||||
expect(page.isLoginButtonPresent()).toBeTruthy();
|
||||
expect(page.getLoginButtonType()).toBe('submit');
|
||||
});
|
||||
|
||||
it('should have a disabled login button when empty', () => {
|
||||
expect(page.isLoginButtonEnabled()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should have a disabled login button when only email', () => {
|
||||
page.fillEmailFieldWith('test@example.com');
|
||||
expect(page.isLoginButtonEnabled()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should have a disabled login button when only password', () => {
|
||||
page.fillPasswordFieldWith('abc123');
|
||||
expect(page.isLoginButtonEnabled()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should have an enabled login button when both inputs filled', () => {
|
||||
page.fillEmailFieldWith('test@example.com');
|
||||
page.fillPasswordFieldWith('abc123');
|
||||
expect(page.isLoginButtonEnabled()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should submit the filled data when login pressed', () => {
|
||||
page.fillEmailFieldWith('test@example.com');
|
||||
page.fillPasswordFieldWith('abc123');
|
||||
expect(page.isLoginButtonEnabled()).toBeTruthy();
|
||||
page.getLoginButton().click();
|
||||
browser.waitForAngular();
|
||||
expect(browser.getCurrentUrl()).toContain('dashboard');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,12 +9,23 @@ export class LoginPageObject {
|
|||
return element(by.css('app-root h1')).getText();
|
||||
}
|
||||
|
||||
getUsernameField() { return element(by.id('username')); }
|
||||
getEmailField() { return element(by.id('username')); }
|
||||
getPasswordField() { return element(by.id('password')); }
|
||||
getLoginButton() { return element(by.id('login')); }
|
||||
|
||||
isUsernameFieldPresent() { return this.getUsernameField().isPresent(); }
|
||||
isEmailFieldPresent() { return this.getEmailField().isPresent(); }
|
||||
isPasswordFieldPresent() { return this.getPasswordField().isPresent(); }
|
||||
isLoginButtonPresent() { return this.getLoginButton().isPresent(); }
|
||||
|
||||
getUsernameFieldType() { return this.getUsernameField().getAttribute('type'); }
|
||||
getEmailFieldType() { return this.getEmailField().getAttribute('type'); }
|
||||
getPasswordFieldType() { return this.getPasswordField().getAttribute('type'); }
|
||||
getLoginButtonType() { return this.getLoginButton().getAttribute('type'); }
|
||||
|
||||
isLoginButtonEnabled() { return this.getLoginButton().isEnabled(); }
|
||||
|
||||
clearEmailField() { return this.getEmailField().clear() };
|
||||
clearPasswordField() { return this.getPasswordField().clear() };
|
||||
|
||||
fillEmailFieldWith(text) { return this.getEmailField().sendKeys(text) };
|
||||
fillPasswordFieldWith(text) { return this.getPasswordField().sendKeys(text) };
|
||||
}
|
||||
|
|
|
@ -4,28 +4,28 @@
|
|||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular/cli'],
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular/cli/plugins/karma')
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client:{
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
files: [
|
||||
{ pattern: './src/test.ts', watched: false }
|
||||
|
||||
],
|
||||
preprocessors: {
|
||||
'./src/test.ts': ['@angular/cli']
|
||||
|
||||
},
|
||||
mime: {
|
||||
'text/x-typescript': ['ts','tsx']
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
reports: [ 'html', 'lcovonly' ],
|
||||
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
angularCli: {
|
||||
|
|
21070
package-lock.json
generated
21070
package-lock.json
generated
File diff suppressed because it is too large
Load diff
108
package.json
108
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "localloop-web",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.13",
|
||||
"description": "LocalLoop Web - Web interface for LocalLoop app",
|
||||
"author": "",
|
||||
"url": "http://www.peartrade.org",
|
||||
|
@ -8,57 +8,81 @@
|
|||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"start:dev": "ng serve --optimization=false --configuration=dev",
|
||||
"start:prod": "ng serve --optimization=false --configuration=production",
|
||||
"start:local": "ng serve --optimization=false --configuration=local",
|
||||
"build:dev": "ng build --configuration=dev",
|
||||
"build:prod": "ng build --configuration=production",
|
||||
"test": "ng test",
|
||||
"test:once": "ng test --watch=false",
|
||||
"test:ci": "ng test --watch=false --browsers=ChromeHeadless",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e",
|
||||
"ci": "npm run test:once && npm run e2e"
|
||||
"e2e:ci": "ng e2e --configuration=ci",
|
||||
"ci": "npm run test:ci && npm run e2e:ci"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "4.0.3",
|
||||
"@angular/compiler": "4.0.3",
|
||||
"@angular/core": "4.0.3",
|
||||
"@angular/forms": "4.0.3",
|
||||
"@angular/http": "4.0.3",
|
||||
"@angular/platform-browser": "4.0.3",
|
||||
"@angular/platform-browser-dynamic": "4.0.3",
|
||||
"@angular/router": "4.0.3",
|
||||
"@angular/upgrade": "4.0.3",
|
||||
"@types/moment": "^2.13.0",
|
||||
"angular-in-memory-web-api": "^0.3.1",
|
||||
"chart.js": "2.5.0",
|
||||
"core-js": "2.4.1",
|
||||
"moment": "^2.18.1",
|
||||
"ng2-charts": "1.6.0",
|
||||
"ng2-validation-manager": "^0.3.1",
|
||||
"ngx-bootstrap": "1.6.6",
|
||||
"ngx-pagination": "^3.0.1",
|
||||
"rxjs": "5.4.2",
|
||||
"@agm/core": "^3.0.0-beta.0",
|
||||
"@agm/markerclusterer": "^3.0.0-beta.0",
|
||||
"@angular/common": "10.0.6",
|
||||
"@angular/compiler": "10.0.6",
|
||||
"@angular/core": "10.0.6",
|
||||
"@angular/forms": "10.0.6",
|
||||
"@angular/platform-browser": "10.0.6",
|
||||
"@angular/platform-browser-dynamic": "10.0.6",
|
||||
"@angular/router": "10.0.6",
|
||||
"@angular/upgrade": "10.0.6",
|
||||
"@coreui/coreui-plugin-chartjs-custom-tooltips": "^1.3.1",
|
||||
"@coreui/icons": "0.3.0",
|
||||
"@google/markerclustererplus": "^5.0.4",
|
||||
"ajv": "^6.10.0",
|
||||
"ajv-keywords": "^3.4.0",
|
||||
"angular2-datetimepicker": "^1.1.1",
|
||||
"chart.js": "^2.8.0",
|
||||
"chartjs-adapter-luxon": "^0.2.0",
|
||||
"core-js": "^2.6.9",
|
||||
"devextreme": "^19.1.4",
|
||||
"devextreme-angular": "^19.1.4",
|
||||
"jasmine": "^3.4.0",
|
||||
"jquery": "^3.5.1",
|
||||
"jszip": "^3.2.2",
|
||||
"luxon": "^1.16.1",
|
||||
"moment": "^2.24.0",
|
||||
"ng2-charts": "^2.3.2",
|
||||
"ng2-validation-manager": "0.5.3",
|
||||
"ngx-bootstrap": "^5.0.0",
|
||||
"ngx-filter-pipe": "^2.1.2",
|
||||
"ngx-pagination": "^4.0.0",
|
||||
"popper.js": "^1.15.0",
|
||||
"rxjs": "6.6.0",
|
||||
"stream": "0.0.2",
|
||||
"ts-helpers": "1.1.2",
|
||||
"webpack": "3.5.4",
|
||||
"webpack-dev-server": "2.7.1",
|
||||
"zone.js": "0.8.9"
|
||||
"tslib": "^1.10.0",
|
||||
"web-animations-js": "^2.3.2",
|
||||
"webpack-dev-server": "^3.11.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "1.3.0",
|
||||
"@angular/compiler-cli": "4.0.3",
|
||||
"@types/jasmine": "2.5.54",
|
||||
"@types/jasminewd2": "2.0.2",
|
||||
"@types/node": "8.0.24",
|
||||
"codelyzer": "2.1.1",
|
||||
"jasmine-core": "2.7.0",
|
||||
"@angular-devkit/build-angular": "^0.1000.4",
|
||||
"@angular/cli": "^10.0.4",
|
||||
"@angular/compiler-cli": "10.0.6",
|
||||
"@types/googlemaps": "^3.39.8",
|
||||
"@types/jasmine": "3.3.13",
|
||||
"@types/jasminewd2": "2.0.6",
|
||||
"@types/node": "12.0.10",
|
||||
"codelyzer": "^6.0.0",
|
||||
"jasmine-core": "^3.4.0",
|
||||
"jasmine-spec-reporter": "4.2.1",
|
||||
"karma": "1.7.0",
|
||||
"karma": "^5.1.1",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
"karma-cli": "1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "1.3.0",
|
||||
"karma-jasmine": "1.1.0",
|
||||
"karma-jasmine-html-reporter": "0.2.2",
|
||||
"protractor": "5.1.2",
|
||||
"ts-node": "3.3.0",
|
||||
"tslint": "4.5.1",
|
||||
"typescript": "2.4.2"
|
||||
"karma-cli": "2.0.0",
|
||||
"karma-coverage-istanbul-reporter": "^2.0.5",
|
||||
"karma-jasmine": "^2.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"protractor": "^7.0.0",
|
||||
"readable-stream": "latest",
|
||||
"ts-node": "^8.3.0",
|
||||
"tslint": "^5.18.0",
|
||||
"typescript": "~3.9.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,10 @@ exports.config = {
|
|||
'./e2e/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
'browserName': 'chrome',
|
||||
chromeOptions: {
|
||||
args: [ "--headless" ]
|
||||
}
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
|
|
|
@ -8,13 +8,11 @@ export class AuthGuard implements CanActivate {
|
|||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
if (localStorage.getItem('sessionKey')) {
|
||||
console.log('session key found')
|
||||
// logged in so return true
|
||||
return true;
|
||||
}
|
||||
|
||||
// not logged in so redirect to login page with the return url
|
||||
console.log('no session key found')
|
||||
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -8,11 +8,9 @@ export class CustomerGuard implements CanActivate {
|
|||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
if (localStorage.getItem('usertype') === 'customer') {
|
||||
console.log('Customer logged in');
|
||||
// customer logged in so return true
|
||||
return true;
|
||||
} else if (localStorage.getItem('usertype') === 'organisation') {
|
||||
console.log('not an customer');
|
||||
this.router.navigate(['/dashboard']);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -8,11 +8,9 @@ export class OrgGuard implements CanActivate {
|
|||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
if (localStorage.getItem('usertype') === 'organisation') {
|
||||
console.log('Organisation logged in');
|
||||
// org logged in so return true
|
||||
return true;
|
||||
} else if (localStorage.getItem('usertype') === 'customer') {
|
||||
console.log('not an organisation');
|
||||
this.router.navigate(['/dashboard-customer']);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { environment } from '../environments/environment';
|
||||
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { LocationStrategy, HashLocationStrategy } from '@angular/common';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
|
||||
|
@ -21,7 +24,11 @@ import { CustomerGuard } from './_guards/customer.guard';
|
|||
import { ApiService } from './providers/api-service';
|
||||
|
||||
import { OrgGraphsService } from './providers/org-graphs.service';
|
||||
import { CustGraphsService } from './providers/cust-graphs.service';
|
||||
import { OrgSnippetsService } from './providers/org-snippets.service';
|
||||
import { CustSnippetsService } from './providers/cust-snippets.service';
|
||||
import { CustPiesService } from './providers/cust-pies.service';
|
||||
import { OrgPiesService } from './providers/org-pies.service';
|
||||
|
||||
// Layouts
|
||||
import { FullLayoutComponent } from './layouts/full-layout.component';
|
||||
|
@ -34,21 +41,30 @@ import { P500Component } from './pages/500.component';
|
|||
// Submodules
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { DashboardModule } from './dashboard/dashboard.module';
|
||||
import { ChartsModule } from 'ng2-charts';
|
||||
// import { StackedBarChartComponent } from './panels/stacked-bar.component';
|
||||
import { FilterPipeModule } from 'ngx-filter-pipe';
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
HttpModule,
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
FilterPipeModule,
|
||||
ReactiveFormsModule,
|
||||
NgxPaginationModule,
|
||||
BsDropdownModule.forRoot(),
|
||||
TabsModule.forRoot(),
|
||||
AuthModule,
|
||||
ChartsModule,
|
||||
DashboardModule,
|
||||
// Loaded last to allow for 404 catchall
|
||||
AppRoutingModule,
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
// StackedBarChartComponent,
|
||||
FullLayoutComponent,
|
||||
SimpleLayoutComponent,
|
||||
NAV_DROPDOWN_DIRECTIVES,
|
||||
|
@ -65,6 +81,10 @@ import { DashboardModule } from './dashboard/dashboard.module';
|
|||
ApiService,
|
||||
OrgGraphsService,
|
||||
OrgSnippetsService,
|
||||
CustGraphsService,
|
||||
CustSnippetsService,
|
||||
CustPiesService,
|
||||
OrgPiesService,
|
||||
{
|
||||
provide: LocationStrategy,
|
||||
useClass: HashLocationStrategy
|
||||
|
@ -72,4 +92,10 @@ import { DashboardModule } from './dashboard/dashboard.module';
|
|||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
export class AppModule {
|
||||
constructor () {
|
||||
if (environment.enableAnalytics) {
|
||||
(<any>window).ga('create', environment.analyticsKey, 'auto');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<form [formGroup]="signin" (ngSubmit)="onSubmit()">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-addon">@</span>
|
||||
<input id="username" type="text" class="form-control" formControlName="email" placeholder="Email">
|
||||
<input id="username" type="email" class="form-control" formControlName="email" placeholder="Email">
|
||||
</div>
|
||||
<div class="input-group mb-4">
|
||||
<span class="input-group-addon"><i class="icon-lock"></i></span>
|
||||
|
@ -28,16 +28,17 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<button type="submit" [disabled]="!signin.valid" class="btn btn-primary px-4">Login</button>
|
||||
<button id="login" type="submit" [disabled]="!signin.valid" class="btn btn-primary px-4">Login</button>
|
||||
</div>
|
||||
<div class="col-6 text-right">
|
||||
<button type="button" class="btn btn-link px-0">Forgot password?</button>
|
||||
<button type="button" class="btn btn-link px-0" disabled>Forgot password?</button>
|
||||
</div>
|
||||
<label class="col-12"><strong>Password reset is currently in development and disabled in testing, please contact an administrator to reset your password.</strong></label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-inverse card-primary py-5 d-md-down-none" style="width:44%">
|
||||
<div class="card card-inverse card-primary py-5">
|
||||
<div class="card-block text-center">
|
||||
<div>
|
||||
<h2>Sign up</h2>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Validators, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { Http, Response } from '@angular/http';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'login.component.html',
|
||||
|
@ -15,7 +14,6 @@ export class LoginComponent implements OnInit {
|
|||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private http: Http,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private api: ApiService
|
||||
|
@ -42,15 +40,11 @@ export class LoginComponent implements OnInit {
|
|||
}
|
||||
|
||||
onSubmit() {
|
||||
console.log(this.signin.value);
|
||||
|
||||
this.api
|
||||
.login(this.signin.value)
|
||||
.subscribe(
|
||||
result => {
|
||||
console.log('logged in!');
|
||||
this.loginStatus = 'success';
|
||||
console.log(this.loginStatus);
|
||||
this.router.navigate([this.returnUrl]);
|
||||
},
|
||||
error => {
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
<span class="input-group-addon"><i class="icon-people"></i></span>
|
||||
<select required class="form-control" type="text" formControlName="usertype">
|
||||
<option value=''>Please select</option>
|
||||
<option value='organisation'>Organisation</option>
|
||||
<option value='organisation'>Business/Organisation</option>
|
||||
<option value='customer'>Customer</option>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -106,6 +106,7 @@
|
|||
<option value='R'>Arts, Entertainment & Recreation</option>
|
||||
<option value='S'>Other Service Activities</option>
|
||||
<option value='T'>Household Domestic Business</option>
|
||||
<option value='U'>Extraterritorial Organisations and Bodies</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
@ -137,7 +138,7 @@
|
|||
Form validation failed, please ensure the form is filled correctly.
|
||||
</div>
|
||||
<div *ngSwitchCase="'send_failed'" class="alert alert-danger" role="alert">
|
||||
Failed to send to server, please try again later.
|
||||
{{registerStatusError}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Validators, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { ValidationManager } from "ng2-validation-manager";
|
||||
import { Http, Response } from '@angular/http';
|
||||
import { ValidationManager } from 'ng2-validation-manager';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import {Router } from '@angular/router';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'register.component.html',
|
||||
|
@ -16,117 +15,122 @@ export class RegisterComponent {
|
|||
organisationForm: ValidationManager;
|
||||
years: Object[];
|
||||
registerStatus: any;
|
||||
registerStatusError = 'Error received, please try again.';
|
||||
|
||||
constructor(
|
||||
private http: Http,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private api: ApiService,
|
||||
) {
|
||||
this.years = [];
|
||||
let max = new Date().getFullYear() - 10,
|
||||
min = max - 140;
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private api: ApiService,
|
||||
) {
|
||||
this.years = [];
|
||||
const max = new Date().getFullYear() - 10,
|
||||
min = max - 140;
|
||||
|
||||
for (let i = max; i>=min; i--){
|
||||
this.years.push(i);
|
||||
}
|
||||
this.signupForm = new ValidationManager({
|
||||
token: 'required',
|
||||
usertype: 'required',
|
||||
email: 'required|email',
|
||||
password: 'required',
|
||||
confirmpassword: 'required|equalTo:password'
|
||||
});
|
||||
this.customerForm = new ValidationManager({
|
||||
display_name: 'required',
|
||||
full_name: 'required',
|
||||
postcode: 'required',
|
||||
year_of_birth:'required',
|
||||
});
|
||||
this.organisationForm = new ValidationManager({
|
||||
name: 'required',
|
||||
for (let i = max; i >= min; i--) {
|
||||
this.years.push(i);
|
||||
}
|
||||
this.signupForm = new ValidationManager({
|
||||
token: 'required',
|
||||
usertype: 'required',
|
||||
email: 'required|email',
|
||||
password: 'required',
|
||||
confirmpassword: 'required|equalTo:password'
|
||||
});
|
||||
this.customerForm = new ValidationManager({
|
||||
display_name: 'required',
|
||||
full_name: 'required',
|
||||
postcode: 'required',
|
||||
year_of_birth: 'required',
|
||||
});
|
||||
this.organisationForm = new ValidationManager({
|
||||
name: 'required',
|
||||
sector: 'required',
|
||||
street_name: 'required',
|
||||
town: 'required',
|
||||
postcode: 'required',
|
||||
});
|
||||
street_name: 'required',
|
||||
town: 'required',
|
||||
postcode: 'required',
|
||||
});
|
||||
}
|
||||
|
||||
onSubmitCustomer() {
|
||||
|
||||
console.log(this.signupForm.isValid());
|
||||
if (!this.signupForm.isValid() && !this.customerForm.isValid()) {
|
||||
console.log("Not Valid!");
|
||||
this.registerStatus = "validation_failed";
|
||||
if (!this.signupForm.isValid() && !this.customerForm.isValid()) {
|
||||
console.log('Not Valid!');
|
||||
this.registerStatus = 'validation_failed';
|
||||
console.log(this.registerStatus);
|
||||
return;
|
||||
}
|
||||
let signupForm = this.signupForm.getForm().value;
|
||||
let customerForm = this.customerForm.getForm().value;
|
||||
return;
|
||||
}
|
||||
const signupForm = this.signupForm.getForm().value;
|
||||
const customerForm = this.customerForm.getForm().value;
|
||||
|
||||
let data = {
|
||||
token: signupForm.token,
|
||||
usertype: signupForm.usertype,
|
||||
email: signupForm.email,
|
||||
password: signupForm.password,
|
||||
display_name: customerForm.display_name,
|
||||
full_name: customerForm.full_name,
|
||||
postcode: customerForm.postcode,
|
||||
year_of_birth:customerForm.year_of_birth,
|
||||
};
|
||||
console.log(data);
|
||||
this.api
|
||||
const data = {
|
||||
token: signupForm.token,
|
||||
usertype: signupForm.usertype,
|
||||
email: signupForm.email,
|
||||
password: signupForm.password,
|
||||
display_name: customerForm.display_name,
|
||||
full_name: customerForm.full_name,
|
||||
postcode: customerForm.postcode.toUpperCase(),
|
||||
year_of_birth: customerForm.year_of_birth,
|
||||
};
|
||||
console.log(data);
|
||||
this.api
|
||||
.register(data)
|
||||
.subscribe(
|
||||
result => {
|
||||
console.log('registered!');
|
||||
this.registerStatus = "success";
|
||||
console.log(this.registerStatus);
|
||||
this.router.navigate(['/dashboard']);
|
||||
this.registerStatus = 'success';
|
||||
this.router.navigate(['/dashboard']);
|
||||
},
|
||||
error => {
|
||||
console.log( error._body );
|
||||
this.registerStatus = "send_failed";
|
||||
console.log(this.registerStatus)
|
||||
console.log('Register Error');
|
||||
console.log(error);
|
||||
try {
|
||||
this.registerStatusError = '"' + error.error.error + '" Error, ' + error.error.message;
|
||||
} catch (e) {
|
||||
this.registerStatusError = 'There was a server error, please try again later.';
|
||||
}
|
||||
this.registerStatus = 'send_failed';
|
||||
}
|
||||
);
|
||||
}
|
||||
onSubmitOrganisation() {
|
||||
|
||||
console.log(this.signupForm.isValid());
|
||||
if (!this.signupForm.isValid() || !this.organisationForm.isValid()) {
|
||||
console.log("Not Valid!");
|
||||
this.registerStatus = "validation_failed";
|
||||
if (!this.signupForm.isValid() || !this.organisationForm.isValid()) {
|
||||
console.log('Not Valid!');
|
||||
this.registerStatus = 'validation_failed';
|
||||
console.log(this.registerStatus);
|
||||
return;
|
||||
}
|
||||
let signupForm = this.signupForm.getForm().value;
|
||||
let organisationForm = this.organisationForm.getForm().value;
|
||||
return;
|
||||
}
|
||||
const signupForm = this.signupForm.getForm().value;
|
||||
const organisationForm = this.organisationForm.getForm().value;
|
||||
|
||||
let data = {
|
||||
token: signupForm.token,
|
||||
usertype: signupForm.usertype,
|
||||
email: signupForm.email,
|
||||
password: signupForm.password,
|
||||
name: organisationForm.name,
|
||||
const data = {
|
||||
token: signupForm.token,
|
||||
usertype: signupForm.usertype,
|
||||
email: signupForm.email,
|
||||
password: signupForm.password,
|
||||
name: organisationForm.name,
|
||||
sector: organisationForm.sector,
|
||||
street_name: organisationForm.street_name,
|
||||
town: organisationForm.town,
|
||||
postcode: organisationForm.postcode,
|
||||
};
|
||||
console.log(data);
|
||||
this.api
|
||||
street_name: organisationForm.street_name,
|
||||
town: organisationForm.town,
|
||||
postcode: organisationForm.postcode.toUpperCase(),
|
||||
};
|
||||
this.api
|
||||
.register(data)
|
||||
.subscribe(
|
||||
result => {
|
||||
console.log('registered!');
|
||||
this.registerStatus = "success";
|
||||
console.log(this.registerStatus);
|
||||
this.registerStatus = 'success';
|
||||
this.router.navigate(['/dashboard']);
|
||||
},
|
||||
error => {
|
||||
console.log( error._body );
|
||||
this.registerStatus = "send_failed";
|
||||
console.log(error);
|
||||
try {
|
||||
this.registerStatusError = '"' + error.error.error + '" Error, ' + error.error.message;
|
||||
} catch (e) {
|
||||
this.registerStatusError = 'There was a server error, please try again later.';
|
||||
}
|
||||
this.registerStatus = 'send_failed';
|
||||
console.log(this.registerStatus);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
<option value='R'>Arts, Entertainment & Recreation</option>
|
||||
<option value='S'>Other Service Activities</option>
|
||||
<option value='T'>Household Domestic Business</option>
|
||||
<option value='U'>Extraterritorial Organisations and Bodies</option>
|
||||
</select>
|
||||
<span class="help-block">Alter this if your business sector has changed.</span>
|
||||
</div>
|
||||
|
@ -122,7 +123,7 @@
|
|||
Form validation failed, please ensure the form is filled correctly.
|
||||
</div>
|
||||
<div *ngSwitchCase="'send_failed'" class="alert alert-danger" role="alert">
|
||||
Failed to send to server, please try again later.
|
||||
{{submitStatusError}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -160,7 +161,7 @@
|
|||
Form validation failed, please ensure the form is filled correctly.
|
||||
</div>
|
||||
<div *ngSwitchCase="'send_failed'" class="alert alert-danger" role="alert">
|
||||
Failed to send to server, please try again later.
|
||||
{{submitStatusError}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { Validators, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { Http, Response } from '@angular/http';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'account-edit.component.html',
|
||||
|
@ -14,9 +13,9 @@ export class AccountEditComponent implements OnInit {
|
|||
accountType: any;
|
||||
// @ViewChild('fileInput') fileInput;
|
||||
submitStatus: any;
|
||||
submitStatusError = 'Error received, please try again.';
|
||||
|
||||
constructor(
|
||||
private http: Http,
|
||||
private formBuilder: FormBuilder,
|
||||
private api: ApiService,
|
||||
) {
|
||||
|
@ -71,7 +70,7 @@ export class AccountEditComponent implements OnInit {
|
|||
console.log(this.settingForm.valid);
|
||||
if (!this.settingForm.valid && !this.settingOrganisationForm.valid) {
|
||||
console.log('Not Valid!');
|
||||
this.submitStatus = "validation_failed";
|
||||
this.submitStatus = 'validation_failed';
|
||||
console.log(this.submitStatus);
|
||||
return;
|
||||
}
|
||||
|
@ -90,7 +89,7 @@ export class AccountEditComponent implements OnInit {
|
|||
|
||||
const submitData = {
|
||||
email: settingForm.email,
|
||||
postcode: settingForm.postcode,
|
||||
postcode: settingForm.postcode.toUpperCase(),
|
||||
password: settingForm.password,
|
||||
new_password: settingForm.new_password,
|
||||
name: settingOrganisationForm.name,
|
||||
|
@ -106,14 +105,17 @@ export class AccountEditComponent implements OnInit {
|
|||
.accountEditUpdate(submitData)
|
||||
.subscribe(
|
||||
result => {
|
||||
console.log('data submitted!');
|
||||
this.submitStatus = "success";
|
||||
console.log(this.submitStatus);
|
||||
this.submitStatus = 'success';
|
||||
},
|
||||
error => {
|
||||
console.log( error._body );
|
||||
this.submitStatus = "send_failed";
|
||||
console.log(this.submitStatus);
|
||||
console.log('Edit Error');
|
||||
console.log(error);
|
||||
try {
|
||||
this.submitStatusError = '"' + error.error.error + '" Error, ' + error.error.message;
|
||||
} catch (e) {
|
||||
this.submitStatusError = 'There was a server error, please try again later.';
|
||||
}
|
||||
this.submitStatus = 'send_failed';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -122,8 +124,7 @@ export class AccountEditComponent implements OnInit {
|
|||
console.log(this.settingForm.valid);
|
||||
if (!this.settingForm.valid && !this.settingCustomerForm.valid) {
|
||||
console.log('Not Valid!');
|
||||
this.submitStatus = "validation_failed";
|
||||
console.log(this.submitStatus);
|
||||
this.submitStatus = 'validation_failed';
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -141,7 +142,7 @@ export class AccountEditComponent implements OnInit {
|
|||
|
||||
const submitData = {
|
||||
email: settingForm.email,
|
||||
postcode: settingForm.postcode,
|
||||
postcode: settingForm.postcode.toUpperCase(),
|
||||
password: settingForm.password,
|
||||
new_password: settingForm.new_password,
|
||||
full_name: settingCustomerForm.full_name,
|
||||
|
@ -155,12 +156,21 @@ export class AccountEditComponent implements OnInit {
|
|||
.subscribe(
|
||||
result => {
|
||||
console.log('data submitted!');
|
||||
this.submitStatus = "success";
|
||||
this.submitStatus = 'success';
|
||||
console.log(this.submitStatus);
|
||||
},
|
||||
error => {
|
||||
console.log( error._body );
|
||||
this.submitStatus = "send_failed";
|
||||
console.log('Edit Error');
|
||||
console.log(error);
|
||||
try {
|
||||
console.log(error.error);
|
||||
const jsonError = error.json();
|
||||
console.log('boop');
|
||||
this.submitStatusError = '"' + jsonError.error + '" Error, ' + jsonError.message;
|
||||
} catch (e) {
|
||||
this.submitStatusError = 'There was a server error, please try again later.';
|
||||
}
|
||||
this.submitStatus = 'send_failed';
|
||||
console.log(this.submitStatus);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -24,6 +24,74 @@
|
|||
<span class="help-block">Enter the amount spent, such as 5.35 for £5.35.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input">Essential Purchase</label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
<input type="checkbox" class="mr-auto" [(ngModel)]="essentialPurchase" (ngModelChange)="transactionFormValidate()">
|
||||
<span class="help-block">Tick if the purchase is deemed an essential purchase for budgeting purposes.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input">Recurring Purchase</label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
<input type="checkbox" class="mr-auto" [(ngModel)]="recurringPurchase" (ngModelChange)="transactionFormValidate()">
|
||||
<span class="help-block">Tick if the purchase frequently recurs, such as monthly.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="recurringPurchase" class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input"><strong>Recurring Period</strong></label>
|
||||
<div class="col-md-9">
|
||||
<div class="row">
|
||||
<div class="col-md-6 btn-group-vertical">
|
||||
<label class="btn btn-secondary mb-0" [class.active]="recurringType == 'daily'">
|
||||
<input value="daily" type="radio" name="radios" style="display:none;" [(ngModel)]="recurringType" (ngModelChange)="transactionFormValidate()">Daily
|
||||
</label>
|
||||
<label class="btn btn-secondary mb-0" [class.active]="recurringType == 'weekly'">
|
||||
<input value="weekly" type="radio" name="radios" style="display:none;" [(ngModel)]="recurringType" (ngModelChange)="transactionFormValidate()">Weekly
|
||||
</label>
|
||||
<label class="btn btn-secondary mb-0" [class.active]="recurringType == 'fortnightly'">
|
||||
<input value="fortnightly" type="radio" name="radios" style="display:none;" [(ngModel)]="recurringType" (ngModelChange)="transactionFormValidate()">Fortnightly
|
||||
</label>
|
||||
<label class="btn btn-secondary mb-0" [class.active]="recurringType == 'monthly'">
|
||||
<input value="monthly" type="radio" name="radios" style="display:none;" [(ngModel)]="recurringType" (ngModelChange)="transactionFormValidate()">Monthly
|
||||
</label>
|
||||
<label class="btn btn-secondary mb-0" [class.active]="recurringType == 'quarterly'">
|
||||
<input value="quarterly" type="radio" name="radios" style="display:none;" [(ngModel)]="recurringType" (ngModelChange)="transactionFormValidate()">Quarterly
|
||||
</label>
|
||||
<label class="btn btn-secondary mb-0" [class.active]="recurringType == 'yearly'">
|
||||
<input value="yearly" type="radio" name="radios" style="display:none;" [(ngModel)]="recurringType" (ngModelChange)="transactionFormValidate()">Yearly
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<span class="help-block">Please give the period of time the purchase will recur from "Time of Transaction".</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input">Budget Type</label>
|
||||
<div class="col-md-9">
|
||||
<div class="row">
|
||||
<div class="col-md-6 btn-group-vertical">
|
||||
<label class="btn btn-secondary mb-0" [class.active]="categoryId == null">
|
||||
<input value="" type="radio" name="radios" style="display:none;" [(ngModel)]="categoryId">Uncategorised
|
||||
</label>
|
||||
<label *ngFor="let category of leftCategoryList" class="btn btn-secondary mb-0" [class.active]="categoryId == category">
|
||||
<input [value]="category" type="radio" name="radios" style="display:none;" [(ngModel)]="categoryId">{{ categoryList[category] }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-6 btn-group-vertical">
|
||||
<label *ngFor="let category2 of rightCategoryList" class="btn btn-secondary mb-0" [class.active]=" categoryId == category2">
|
||||
<input [value]="category2" type="radio" name="radios" style="display:none;" [(ngModel)]="categoryId">{{ categoryList[category2] }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<span class="help-block"><strong>Optional:</strong> Choose the Budget Type for the majority of the purchase.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input"><strong>Organisation Name</strong></label>
|
||||
<div class="col-md-9">
|
||||
|
@ -31,24 +99,24 @@
|
|||
<span class="help-block">Enter the name of the organisation money was spent. Choose existing ones from below or if not found, enter the details below.</span>
|
||||
</div>
|
||||
</div>
|
||||
<org-table *ngIf="storeList != null" [orgList]="storeList" (onClick)="addStore($event)"></org-table>
|
||||
<org-table *ngIf="storeList.length > 0" [orgList]="storeList" (onClick)="addStore($event)"></org-table>
|
||||
<div *ngIf="showAddStore">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input"><strong>Organisation Street Name</strong></label>
|
||||
<label class="col-md-3 form-control-label" for="text-input">Organisation Street Name</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control" placeholder="Which Street?" [(ngModel)]="submitOrg.street_name" (ngModelChange)="transactionFormValidate()">
|
||||
<span class="help-block">Enter the street name where the organisation is located at.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input"><strong>Organisation Town</strong></label>
|
||||
<label class="col-md-3 form-control-label" for="text-input">Organisation Town</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control" placeholder="Which Town?" [(ngModel)]="submitOrg.town" (ngModelChange)="transactionFormValidate()">
|
||||
<span class="help-block">Enter the name of the town where the organisation is located at.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input"><strong>Organisation Postcode</strong></label>
|
||||
<label class="col-md-3 form-control-label" for="text-input">Organisation Postcode</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control" placeholder="Postcode if known" [(ngModel)]="submitOrg.postcode" (ngModelChange)="transactionFormValidate()">
|
||||
<span class="help-block">Enter the postcode where the organisation is located at.</span>
|
||||
|
@ -72,7 +140,7 @@
|
|||
<button type="submit" (click)="postTransaction()" [disabled]="transactionFormInvalid" class="btn btn-sm btn-primary"><i class="fa fa-dot-circle-o"></i> Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="false" class="card">
|
||||
<div *ngIf="accountType == 'organisation'" class="card">
|
||||
<div class="card-header">
|
||||
<strong>Profile & Payroll Data</strong>
|
||||
<small>Required Data marked in <strong>bold</strong>.</small>
|
||||
|
@ -89,14 +157,14 @@
|
|||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input"><strong>Total amount of Employees</strong></label>
|
||||
<div class="col-md-9">
|
||||
<input type="number" class="form-control" formControlName="employee_amount" placeholder="0">
|
||||
<input type="number" class="form-control" formControlName="employee_amount" placeholder="0" min="0">
|
||||
<span class="help-block">Enter the amount of employees the organisation has for the entry month.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input"><strong>Total amount of local Employees</strong></label>
|
||||
<div class="col-md-9">
|
||||
<input type="number" class="form-control" formControlName="local_employee_amount" placeholder="0">
|
||||
<input type="number" class="form-control" formControlName="local_employee_amount" placeholder="0" min="0">
|
||||
<span class="help-block">Enter the amount of employees that live locally to the organisation for the entry month.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Validators, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { Http, Response } from '@angular/http';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import { OrgTableComponent } from '../shared/org-table.component';
|
||||
import * as moment from 'moment';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'add-data.component.html',
|
||||
})
|
||||
export class AddDataComponent {
|
||||
export class AddDataComponent implements OnInit {
|
||||
payrollForm: FormGroup;
|
||||
singleSupplierForm: FormGroup;
|
||||
employeeForm: FormGroup;
|
||||
|
@ -18,7 +17,7 @@ export class AddDataComponent {
|
|||
singleSupplierFormStatus: any;
|
||||
employeeFormStatus: any;
|
||||
transactionFormStatus: any;
|
||||
transactionFormStatusError: string = 'Error received, please try again.';
|
||||
transactionFormStatusError = 'Error received, please try again.';
|
||||
accountType: any;
|
||||
|
||||
submitOrg = {
|
||||
|
@ -31,30 +30,37 @@ export class AddDataComponent {
|
|||
organisationTown: string;
|
||||
organisationPostcode: string;
|
||||
amount: number;
|
||||
essentialPurchase = false;
|
||||
recurringPurchase = false;
|
||||
recurringType: string;
|
||||
transactionAdditionType = 1;
|
||||
storeList;
|
||||
storeList = [];
|
||||
showAddStore = false;
|
||||
submitReceipt = false;
|
||||
transactionFormInvalid = true;
|
||||
myDate: any;
|
||||
minDate: any;
|
||||
categoryList: any;
|
||||
categoryIdList: any;
|
||||
leftCategoryList: number[] = [];
|
||||
rightCategoryList: string[] = [];
|
||||
categoryId: number;
|
||||
|
||||
constructor(
|
||||
private http: Http,
|
||||
private formBuilder: FormBuilder,
|
||||
private api: ApiService,
|
||||
) {
|
||||
private formBuilder: FormBuilder,
|
||||
private api: ApiService,
|
||||
) {
|
||||
this.payrollForm = this.formBuilder.group({
|
||||
entry_period: ['', [Validators.required]],
|
||||
employee_amount: ['', [Validators.required]],
|
||||
local_employee_amount: ['', [Validators.required]],
|
||||
gross_payroll: ['', [Validators.required]],
|
||||
entry_period: ['', [Validators.required]],
|
||||
employee_amount: ['', [Validators.required]],
|
||||
local_employee_amount: ['', [Validators.required]],
|
||||
gross_payroll: ['', [Validators.required]],
|
||||
payroll_income_tax: ['', [Validators.required]],
|
||||
payroll_employee_ni: ['', [Validators.required]],
|
||||
payroll_employer_ni: ['', [Validators.required]],
|
||||
payroll_total_pension: ['', [Validators.required]],
|
||||
payroll_other_benefit: ['', [Validators.required]],
|
||||
});
|
||||
});
|
||||
this.employeeForm = this.formBuilder.group({
|
||||
entry_period: ['', [Validators.required]],
|
||||
employee_no: ['', [Validators.required]],
|
||||
|
@ -63,9 +69,20 @@ export class AddDataComponent {
|
|||
employee_ni: ['', [Validators.required]],
|
||||
employee_pension: ['', [Validators.required]],
|
||||
employee_other_benefit: ['', [Validators.required]],
|
||||
});
|
||||
});
|
||||
this.myDate = moment().format('YYYY-MM-DD[T]HH:mm');
|
||||
// this.myDate = new Date().toISOString().slice(0, 16);
|
||||
this.api.categoryList().subscribe(
|
||||
result => {
|
||||
this.categoryList = result.categories;
|
||||
this.categoryIdList = Object.keys(this.categoryList);
|
||||
this.setCategoryList(this.categoryIdList);
|
||||
},
|
||||
error => {
|
||||
console.log('Retrieval Error');
|
||||
console.log( error._body );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -73,13 +90,19 @@ export class AddDataComponent {
|
|||
this.accountType = localStorage.getItem('usertype');
|
||||
}
|
||||
|
||||
getMinDate(){
|
||||
private setCategoryList(data: any) {
|
||||
let halfLength = Math.floor(data.length / 2);
|
||||
this.leftCategoryList = data.splice(0, halfLength);
|
||||
this.rightCategoryList = data;
|
||||
}
|
||||
|
||||
getMinDate() {
|
||||
// gets the April 1st date of the current year
|
||||
let aprilDate = moment().month(3).date(1);
|
||||
let now = moment();
|
||||
const aprilDate = moment().month(3).date(1);
|
||||
const now = moment();
|
||||
// Checks if current time is before April 1st, if so returns true
|
||||
let beforeApril = now.isBefore(aprilDate);
|
||||
if ( beforeApril == true ) {
|
||||
const beforeApril = now.isBefore(aprilDate);
|
||||
if ( beforeApril === true ) {
|
||||
this.minDate = aprilDate.subtract(2, 'years').format('YYYY-MM-DD');
|
||||
} else {
|
||||
this.minDate = aprilDate.subtract(1, 'years').format('YYYY-MM-DD');
|
||||
|
@ -88,16 +111,16 @@ export class AddDataComponent {
|
|||
|
||||
initializeItems() {
|
||||
// Dont bother searching for an empty or undefined string
|
||||
if ( this.submitOrg.name == '' ) {
|
||||
if ( this.submitOrg.name === '' ) {
|
||||
return;
|
||||
}
|
||||
var searchData = {
|
||||
const searchData = {
|
||||
search_name: this.submitOrg.name,
|
||||
};
|
||||
|
||||
this.api.search(searchData).subscribe(
|
||||
data => {
|
||||
if(data.validated.length > 0) {
|
||||
if (data.validated.length > 0) {
|
||||
this.storeList = data.validated;
|
||||
this.showAddStore = false;
|
||||
this.transactionAdditionType = 1;
|
||||
|
@ -107,8 +130,8 @@ export class AddDataComponent {
|
|||
this.transactionAdditionType = 2;
|
||||
}
|
||||
// handle the case when the storelist is empty
|
||||
if(this.storeList.length < 1 ) {
|
||||
this.storeList = null;
|
||||
if (this.storeList.length < 1) {
|
||||
this.storeList = [];
|
||||
this.showAddStore = true;
|
||||
this.transactionAdditionType = 3;
|
||||
}
|
||||
|
@ -120,7 +143,7 @@ export class AddDataComponent {
|
|||
}
|
||||
|
||||
// if user select a item from the list
|
||||
addStore(store){
|
||||
addStore(store) {
|
||||
this.submitOrg = store;
|
||||
this.transactionFormValidate();
|
||||
this.organisationId = store.id;
|
||||
|
@ -132,45 +155,53 @@ export class AddDataComponent {
|
|||
this.initializeItems();
|
||||
|
||||
// set val to the value of the searchbar
|
||||
let val = ev.target.value;
|
||||
const val = ev.target.value;
|
||||
|
||||
// Filter the store list so search seems quicker
|
||||
if (val && val.trim() != '' && this.storeList != null) {
|
||||
if (val && val.trim() !== '' && this.storeList.length > 0) {
|
||||
this.storeList = this.storeList.filter(
|
||||
(item) => {
|
||||
return ( item.name.toLowerCase().indexOf( val.toLowerCase() ) > -1 );
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// if nothing is found
|
||||
if(!this.storeList === null){
|
||||
if (!this.storeList === null) {
|
||||
// display add new store button
|
||||
this.showAddStore = true;
|
||||
}
|
||||
}
|
||||
|
||||
transactionFormValidate() {
|
||||
if( this.submitOrg.name.length == 0 &&
|
||||
this.amount == 0 ) {
|
||||
this.transactionFormInvalid = true;
|
||||
}else{
|
||||
this.transactionFormStatus = null;
|
||||
if (this.submitOrg.name.length &&
|
||||
this.amount &&
|
||||
(this.recurringPurchase &&
|
||||
this.recurringType ||
|
||||
!this.recurringPurchase &&
|
||||
!this.recurringType)) {
|
||||
this.transactionFormInvalid = false;
|
||||
} else {
|
||||
this.transactionFormInvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
public postTransaction() {
|
||||
|
||||
var myParams: any;
|
||||
let myParams: any;
|
||||
let purchaseTime: string;
|
||||
purchaseTime = moment(this.myDate, 'YYYY-MM-DD[T]HH:mm').local().format('YYYY-MM-DD[T]HH:mm:ss.SSSZ');
|
||||
switch(this.transactionAdditionType){
|
||||
switch (this.transactionAdditionType) {
|
||||
case 1:
|
||||
myParams = {
|
||||
transaction_type : this.transactionAdditionType,
|
||||
transaction_value : this.amount,
|
||||
purchase_time : purchaseTime,
|
||||
organisation_id : this.organisationId,
|
||||
category : this.categoryId,
|
||||
essential : this.essentialPurchase,
|
||||
recurring : this.recurringType,
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
|
@ -179,6 +210,9 @@ export class AddDataComponent {
|
|||
transaction_value : this.amount,
|
||||
purchase_time : purchaseTime,
|
||||
organisation_id : this.organisationId,
|
||||
category : this.categoryId,
|
||||
essential : this.essentialPurchase,
|
||||
recurring : this.recurringType,
|
||||
};
|
||||
break;
|
||||
case 3:
|
||||
|
@ -190,6 +224,9 @@ export class AddDataComponent {
|
|||
street_name : this.submitOrg.street_name,
|
||||
town : this.submitOrg.town,
|
||||
postcode : this.submitOrg.postcode,
|
||||
category : this.categoryId,
|
||||
essential : this.essentialPurchase,
|
||||
recurring : this.recurringType,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
@ -199,32 +236,23 @@ export class AddDataComponent {
|
|||
.upload(myParams)
|
||||
.subscribe(
|
||||
result => {
|
||||
if ( result.success == true ) {
|
||||
console.log('Successful Upload');
|
||||
console.log(result);
|
||||
this.transactionFormStatus = "success";
|
||||
console.log(this.transactionFormStatus);
|
||||
if ( result.success === true ) {
|
||||
this.transactionFormStatus = 'success';
|
||||
this.resetForm();
|
||||
} else {
|
||||
console.log('Upload Error');
|
||||
this.transactionFormStatusError = JSON.stringify(result.status) + 'Error, ' + JSON.stringify(result.message);
|
||||
this.transactionFormStatus = "send_failed";
|
||||
console.log(this.transactionFormStatus);
|
||||
this.transactionFormStatus = 'send_failed';
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log('Upload Error');
|
||||
console.log(error);
|
||||
try {
|
||||
console.log(error.error);
|
||||
let jsonError = error.json();
|
||||
console.log("boop");
|
||||
this.transactionFormStatusError = '"' + jsonError.error + '" Error, ' + jsonError.message;
|
||||
} catch(e) {
|
||||
this.transactionFormStatusError = '"' + error.error.error + '" Error, ' + error.error.message;
|
||||
} catch (e) {
|
||||
this.transactionFormStatusError = 'There was a server error, please try again later.';
|
||||
}
|
||||
this.transactionFormStatus = "send_failed";
|
||||
console.log(this.transactionFormStatus);
|
||||
this.transactionFormStatus = 'send_failed';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -236,64 +264,57 @@ export class AddDataComponent {
|
|||
town: '',
|
||||
postcode: '',
|
||||
};
|
||||
this.storeList = null;
|
||||
this.storeList = [];
|
||||
this.amount = null;
|
||||
this.transactionFormInvalid = true;
|
||||
this.showAddStore = false;
|
||||
this.essentialPurchase = false;
|
||||
this.recurringPurchase = false;
|
||||
this.recurringType = null;
|
||||
}
|
||||
|
||||
onSubmitPayroll() {
|
||||
console.log(this.payrollForm.value);
|
||||
console.log(this.payrollForm.value);
|
||||
|
||||
this.api
|
||||
this.api
|
||||
.orgPayroll(this.payrollForm.value)
|
||||
.subscribe(
|
||||
result => {
|
||||
console.log('data submitted!');
|
||||
this.payrollFormStatus = "success";
|
||||
console.log(this.payrollFormStatus);
|
||||
this.payrollFormStatus = 'success';
|
||||
},
|
||||
error => {
|
||||
console.log( error._body );
|
||||
this.payrollFormStatus = "send_failed";
|
||||
console.log(this.payrollFormStatus);
|
||||
this.payrollFormStatus = 'send_failed';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onSubmitSingleSupplier() {
|
||||
console.log(this.singleSupplierForm.value);
|
||||
console.log(this.singleSupplierForm.value);
|
||||
|
||||
this.api
|
||||
this.api
|
||||
.orgSupplier(this.singleSupplierForm.value)
|
||||
.subscribe(
|
||||
result => {
|
||||
console.log('data submitted!');
|
||||
this.singleSupplierFormStatus = "success";
|
||||
console.log(this.singleSupplierFormStatus);
|
||||
this.singleSupplierFormStatus = 'success';
|
||||
},
|
||||
error => {
|
||||
console.log( error._body );
|
||||
this.singleSupplierFormStatus = "send_failed";
|
||||
console.log(this.singleSupplierFormStatus);
|
||||
this.singleSupplierFormStatus = 'send_failed';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onSubmitEmployee() {
|
||||
console.log(this.employeeForm.value);
|
||||
console.log(this.employeeForm.value);
|
||||
|
||||
this.api
|
||||
this.api
|
||||
.orgEmployee(this.employeeForm.value)
|
||||
.subscribe(
|
||||
result => {
|
||||
console.log('data submitted!');
|
||||
this.employeeFormStatus = "success";
|
||||
console.log(this.employeeFormStatus);
|
||||
this.employeeFormStatus = 'success';
|
||||
},
|
||||
error => {
|
||||
console.log( error._body );
|
||||
this.employeeFormStatus = "send_failed";
|
||||
console.log(this.employeeFormStatus);
|
||||
this.employeeFormStatus = 'send_failed';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
172
src/app/dashboard/category-month.component.html
Normal file
172
src/app/dashboard/category-month.component.html
Normal file
|
@ -0,0 +1,172 @@
|
|||
<div class="animated fadeIn">
|
||||
<div class=row>
|
||||
<div *ngIf="weekList1" class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title float-left mb-0">Purchases Last Week</h4>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
<div class="chart-wrapper">
|
||||
<ul *ngIf="weekList1" class="horizontal-bars type-2">
|
||||
<li *ngIf="weekEssential1">
|
||||
<span class="title">Essential Purchases</span>
|
||||
<span class="value">{{ ( weekEssential1 ? weekEssential1.value : 0 ) | currency:'GBP':'symbol':'1.2-2' }} <span class="text-muted small">
|
||||
({{ ( weekEssential1 ? weekEssential1.value : 0 ) / weekListValueSum1 | percent:'1.0-0' }})</span></span>
|
||||
<div class="bars">
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="(weekEssential1.value || 0 ) / weekListValueSum1 | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li *ngFor="let categoryEntry of weekList1 | slice:0:categoryLimit1; let i=index;">
|
||||
<span class="title">{{ categoryEntry.category || 'Uncategorised' }}</span>
|
||||
<span class="value">{{ ( categoryEntry.value || 0 ) | currency:'GBP':'symbol':'1.2-2' }} <span class="text-muted small">
|
||||
({{ (categoryEntry.value || 0 ) / weekListValueSum1 | percent:'1.0-0' }})</span></span>
|
||||
<div class="bars">
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="(categoryEntry.value || 0 ) / weekListValueSum1 | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<div *ngIf="weekList1">
|
||||
<li *ngIf="weekList1.length > categoryLimit1 && disableCategoryButton1 == false" class="divider text-center">
|
||||
<button type="button" class="btn btn-sm btn-link text-muted" (click)="loadMore1()"><i class="icon-options"></i></button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div *ngIf="weekList2" class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title float-left mb-0">Purchases 1 Week Ago</h4>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
<div class="chart-wrapper">
|
||||
<ul *ngIf="weekList2" class="horizontal-bars type-2">
|
||||
<li *ngIf="weekEssential2">
|
||||
<span class="title">Essential Purchases</span>
|
||||
<span class="value">{{ ( weekEssential2 ? weekEssential2.value : 0 ) | currency:'GBP':'symbol':'1.2-2' }} <span class="text-muted small">
|
||||
({{ ( weekEssential2 ? weekEssential2.value : 0 ) / weekListValueSum2 | percent:'1.0-0' }})</span></span>
|
||||
<div class="bars">
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="(weekEssential2.value || 0 ) / weekListValueSum2 | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li *ngFor="let categoryEntry of weekList2 | slice:0:categoryLimit2; let i=index;">
|
||||
<span class="title">{{ categoryEntry.category || 'Uncategorised' }}</span>
|
||||
<span class="value">{{ ( categoryEntry.value || 0 ) | currency:'GBP':'symbol':'1.2-2' }} <span class="text-muted small">
|
||||
({{ (categoryEntry.value || 0 ) / weekListValueSum2 | percent:'1.0-0' }})</span></span>
|
||||
<div class="bars">
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="(categoryEntry.value || 0 ) / weekListValueSum2 | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<div *ngIf="weekList2">
|
||||
<li *ngIf="weekList2.length > categoryLimit2 && disableCategoryButtonFirst == false" class="divider text-center">
|
||||
<button type="button" class="btn btn-sm btn-link text-muted" (click)="loadMore2()"><i class="icon-options"></i></button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div *ngIf="weekList3" class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title float-left mb-0">Purchases 2 Weeks Ago</h4>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
<div class="chart-wrapper">
|
||||
<ul *ngIf="weekList3" class="horizontal-bars type-2">
|
||||
<li *ngIf="weekEssential3">
|
||||
<span class="title">Essential Purchases</span>
|
||||
<span class="value">{{ ( weekEssential3 ? weekEssential3.value : 0 ) | currency:'GBP':'symbol':'1.2-2' }} <span class="text-muted small">
|
||||
({{ ( weekEssential3 ? weekEssential3.value : 0 ) / weekListValueSum3 | percent:'1.0-0' }})</span></span>
|
||||
<div class="bars">
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="(weekEssential3.value || 0 ) / weekListValueSum3 | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li *ngFor="let categoryEntry of weekList3 | slice:0:categoryLimit3; let i=index;">
|
||||
<span class="title">{{ categoryEntry.category || 'Uncategorised' }}</span>
|
||||
<span class="value">{{ ( categoryEntry.value || 0 ) | currency:'GBP':'symbol':'1.2-2' }} <span class="text-muted small">
|
||||
({{ (categoryEntry.value || 0 ) / weekListValueSum3 | percent:'1.0-0' }})</span></span>
|
||||
<div class="bars">
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="(categoryEntry.value || 0 ) / weekListValueSum3 | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<div *ngIf="weekList3">
|
||||
<li *ngIf="weekList3.length > categoryLimit3 && disableCategoryButtonFirst == false" class="divider text-center">
|
||||
<button type="button" class="btn btn-sm btn-link text-muted" (click)="loadMore3()"><i class="icon-options"></i></button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div *ngIf="weekList4" class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title float-left mb-0">Purchases 3 Weeks Ago</h4>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
<div class="chart-wrapper">
|
||||
<ul *ngIf="weekList4" class="horizontal-bars type-2">
|
||||
<li *ngIf="weekEssential4">
|
||||
<span class="title">Essential Purchases</span>
|
||||
<span class="value">{{ ( weekEssential4 ? weekEssential4.value : 0 ) | currency:'GBP':'symbol':'1.2-2' }} <span class="text-muted small">
|
||||
({{ ( weekEssential4 ? weekEssential4.value : 0 ) / weekListValueSum4 | percent:'1.0-0' }})</span></span>
|
||||
<div class="bars">
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="(weekEssential4.value || 0 ) / weekListValueSum4 | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li *ngFor="let categoryEntry of weekList4 | slice:0:categoryLimit4; let i=index;">
|
||||
<span class="title">{{ categoryEntry.category || 'Uncategorised' }}</span>
|
||||
<span class="value">{{ ( categoryEntry.value || 0 ) | currency:'GBP':'symbol':'1.2-2' }} <span class="text-muted small">
|
||||
({{ (categoryEntry.value || 0 ) / weekListValueSum4 | percent:'1.0-0' }})</span></span>
|
||||
<div class="bars">
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
[style.width]="(categoryEntry.value || 0 ) / weekListValueSum4 | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<div *ngIf="weekList4">
|
||||
<li *ngIf="weekList4.length > categoryLimit4 && disableCategoryButtonFirst == false" class="divider text-center">
|
||||
<button type="button" class="btn btn-sm btn-link text-muted" (click)="loadMore4()"><i class="icon-options"></i></button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
</div>
|
119
src/app/dashboard/category-month.component.ts
Normal file
119
src/app/dashboard/category-month.component.ts
Normal file
|
@ -0,0 +1,119 @@
|
|||
import { Directive, Component, OnInit } from '@angular/core';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import { DataType } from '../shared/data-types.enum';
|
||||
import * as moment from 'moment';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'category-month.component.html'
|
||||
})
|
||||
export class CategoryMonthComponent implements OnInit {
|
||||
|
||||
disableCategoryButton1: boolean = false;
|
||||
disableCategoryButton2: boolean = false;
|
||||
disableCategoryButton3: boolean = false;
|
||||
disableCategoryButton4: boolean = false;
|
||||
|
||||
weekPurchaseList = {
|
||||
first: 0,
|
||||
};
|
||||
|
||||
weekList1 = [];
|
||||
weekList2 = [];
|
||||
weekList3 = [];
|
||||
weekList4 = [];
|
||||
weekListValueSum1: number = 0;
|
||||
weekListValueSum2: number = 0;
|
||||
weekListValueSum3: number = 0;
|
||||
weekListValueSum4: number = 0;
|
||||
weekEssential1: number = 0;
|
||||
weekEssential2: number = 0;
|
||||
weekEssential3: number = 0;
|
||||
weekEssential4: number = 0;
|
||||
|
||||
dayList: any[] = [];
|
||||
valueList: number[] = [];
|
||||
myWeek1: any;
|
||||
myWeek2: any;
|
||||
myWeek3: any;
|
||||
myWeek4: any;
|
||||
categoryLimit1: number = 6;
|
||||
categoryLimit2: number = 6;
|
||||
categoryLimit3: number = 6;
|
||||
categoryLimit4: number = 6;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
) {
|
||||
this.setDate();
|
||||
this.api.categoryTransactionList().subscribe(
|
||||
result => {
|
||||
this.setData(result);
|
||||
},
|
||||
error => {
|
||||
console.log('Retrieval Error');
|
||||
console.log( error._body );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
private setDate () {
|
||||
this.myWeek1 = moment().startOf('isoWeek').format('YYYY-MM-DD');
|
||||
this.myWeek2 = moment(this.myWeek1).subtract(1, 'weeks').format('YYYY-MM-DD');
|
||||
this.myWeek3 = moment(this.myWeek2).subtract(1, 'weeks').format('YYYY-MM-DD');
|
||||
this.myWeek4 = moment(this.myWeek3).subtract(1, 'weeks').format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
private setData (data: any) {
|
||||
function prop<T, K extends keyof T>(obj: T, key: K) {
|
||||
return obj[key];
|
||||
}
|
||||
this.weekList1 = prop(data.data.categories, this.myWeek1);
|
||||
this.weekList2 = prop(data.data.categories, this.myWeek2);
|
||||
this.weekList3 = prop(data.data.categories, this.myWeek3);
|
||||
this.weekList4 = prop(data.data.categories, this.myWeek4);
|
||||
this.getMaxValue(this.weekList1, this.weekList2, this.weekList3, this.weekList4);
|
||||
this.weekEssential1 = prop(data.data.essentials, this.myWeek1);
|
||||
this.weekEssential2 = prop(data.data.essentials, this.myWeek2);
|
||||
this.weekEssential3 = prop(data.data.essentials, this.myWeek3);
|
||||
this.weekEssential4 = prop(data.data.essentials, this.myWeek4);
|
||||
}
|
||||
|
||||
private getMaxValue (data1: any,
|
||||
data2: any,
|
||||
data3: any,
|
||||
data4: any) {
|
||||
if (data1) {
|
||||
this.weekListValueSum1 = data1.reduce(function (s, a) {return s + a.value;}, 0);
|
||||
}
|
||||
if (data2) {
|
||||
this.weekListValueSum2 = data2.reduce(function (s, a) {return s + a.value;}, 0);
|
||||
}
|
||||
if (data3) {
|
||||
this.weekListValueSum3 = data3.reduce(function (s, a) {return s + a.value;}, 0);
|
||||
}
|
||||
if (data4) {
|
||||
this.weekListValueSum4 = data4.reduce(function (s, a) {return s + a.value;}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private loadMore1 () {
|
||||
this.disableCategoryButton1 = true;
|
||||
this.categoryLimit1 = 20;
|
||||
}
|
||||
private loadMore2 () {
|
||||
this.disableCategoryButton2 = true;
|
||||
this.categoryLimit2 = 20;
|
||||
}
|
||||
private loadMore3 () {
|
||||
this.disableCategoryButton3 = true;
|
||||
this.categoryLimit3 = 20;
|
||||
}
|
||||
private loadMore4 () {
|
||||
this.disableCategoryButton4 = true;
|
||||
this.categoryLimit4 = 20;
|
||||
}
|
||||
}
|
|
@ -1,131 +1,115 @@
|
|||
<div class="animated fadeIn">
|
||||
<div class="card">
|
||||
<div class="card-footer">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="text-muted">My Points</div>
|
||||
<strong>{{ basicStats.user_sum / 10 | number:'1.0-0' }}</strong>
|
||||
</li>
|
||||
<li>
|
||||
<div class="text-muted">My Rank</div>
|
||||
<div *ngIf="basicStats.user_position == 0" class="statuscontent">
|
||||
<strong>Unranked</strong>
|
||||
</div>
|
||||
<div *ngIf="basicStats.user_position != 0" class="statuscontent">
|
||||
<strong>{{ basicStats.user_position }}</strong>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="text-muted">My Total Spend</div>
|
||||
<strong>{{ basicStats.user_sum | currency:'GBP':true:'1.2-2' }}</strong>
|
||||
</li>
|
||||
<li>
|
||||
<div class="text-muted">Value to Local Economy</div>
|
||||
<strong>{{ basicStats.user_sum * 2.3 | currency:'GBP':true:'1.2-2' }}</strong>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<snippet-bar-cust></snippet-bar-cust>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card card-inverse card-primary">
|
||||
<div class="card-block">
|
||||
<div class="h4 mb-0">{{ basicStats.today_sum | currency:'GBP':true:'1.2-2' }}</div>
|
||||
<div>Total Today</div>
|
||||
<!-- <div class="progress progress-white progress-xs mt-3">
|
||||
<div class="progress-bar" role="progressbar" style="width: 100%" attr.aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
<small class="text-muted">Lorem ipsum dolor sit amet enim.</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card card-inverse card-primary">
|
||||
<div class="card-block">
|
||||
<div class="h4 mb-0">{{ basicStats.today_sum / (basicStats.today_count ? basicStats.today_count : 1) | currency:'GBP':true:'1.2-2' }}</div>
|
||||
<div>Avg. Spend Today</div>
|
||||
<!-- <div class="progress progress-white progress-xs mt-3">
|
||||
<div class="progress-bar" role="progressbar" style="width: 100%" attr.aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
<small class="text-muted">Lorem ipsum dolor sit amet enim.</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card card-inverse card-primary">
|
||||
<div class="card-block">
|
||||
<div class="h4 mb-0">{{ basicStats.week_sum | currency:'GBP':true:'1.2-2' }}</div>
|
||||
<div>Last Week Total</div>
|
||||
<!-- <div class="progress progress-white progress-xs mt-3">
|
||||
<div class="progress-bar" role="progressbar" style="width: 100%" attr.aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
<small class="text-muted">Lorem ipsum dolor sit amet enim.</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card card-inverse card-primary">
|
||||
<div class="card-block">
|
||||
<div class="h4 mb-0">{{ basicStats.week_sum / (basicStats.week_count ? basicStats.week_count : 1) | currency:'GBP':true:'1.2-2' }}</div>
|
||||
<div>Last Week Avg. Spend</div>
|
||||
<!-- <div class="progress progress-white progress-xs mt-3">
|
||||
<div class="progress-bar" role="progressbar" style="width: 100%" attr.aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
<small class="text-muted">Lorem ipsum dolor sit amet enim.</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card card-inverse card-primary">
|
||||
<div class="card-block">
|
||||
<div class="h4 mb-0">{{ basicStats.month_sum | currency:'GBP':true:'1.2-2' }}</div>
|
||||
<div>Last Month Total</div>
|
||||
<!-- <div class="progress progress-white progress-xs mt-3">
|
||||
<div class="progress-bar" role="progressbar" style="width: 100%" attr.aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
<small class="text-muted">Lorem ipsum dolor sit amet enim.</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card card-inverse card-primary">
|
||||
<div class="card-block">
|
||||
<div class="h4 mb-0">{{ basicStats.month_sum / (basicStats.month_count ? basicStats.month_count : 1) | currency:'GBP':true:'1.2-2' }}</div>
|
||||
<div>Last Month Avg. Spend</div>
|
||||
<!-- <div class="progress progress-white progress-xs mt-3">
|
||||
<div class="progress-bar" role="progressbar" style="width: 100%" attr.aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
<small class="text-muted">Lorem ipsum dolor sit amet enim.</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card card-inverse card-primary">
|
||||
<div class="card-block">
|
||||
<div class="h4 mb-0">{{ basicStats.user_sum | currency:'GBP':true:'1.2-2' }}</div>
|
||||
<div>User Total</div>
|
||||
<!-- <div class="progress progress-white progress-xs mt-3">
|
||||
<div class="progress-bar" role="progressbar" style="width: 100%" attr.aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
<small class="text-muted">Lorem ipsum dolor sit amet enim.</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card card-inverse card-primary">
|
||||
<div class="card-block">
|
||||
<div class="h4 mb-0">{{ basicStats.user_sum / (basicStats.user_count ? basicStats.user_count : 1) | currency:'GBP':true:'1.2-2' }}</div>
|
||||
<div>User Avg. Spend</div>
|
||||
<!-- <div class="progress progress-white progress-xs mt-3">
|
||||
<div class="progress-bar" role="progressbar" style="width: 100%" attr.aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
<small class="text-muted">Lorem ipsum dolor sit amet enim.</small> -->
|
||||
</div>
|
||||
</div>
|
||||
<div *ngFor="let widget of widgetList" class="col-sm-6 col-lg-3">
|
||||
<widget-graph *ngIf="widget.type == 'graph'"
|
||||
[graphName]="widget.name"
|
||||
[graphTitle]="widget.title"
|
||||
[graphIcon]="widget.icon"
|
||||
[dataType]="widget.dataType">
|
||||
</widget-graph>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
</div>
|
||||
<div class=row>
|
||||
<div class="col-xl-6">
|
||||
<panel-pie></panel-pie><!--All Purchases -->
|
||||
<!-- <div class="demo-container" ng-app="stacked-bar" ng-controller="stacked-bar">
|
||||
<div id="stacked-bar" dx-chart="chartOptions"></div>
|
||||
</div> -->
|
||||
</div><!--/.col-->
|
||||
<!--<div *ngIf="showCategoryDoughnutChart" class="col-xl-6">
|
||||
<div class="card"> -->
|
||||
<!-- <body style="background-color:rgb(0,0,0);"> -->
|
||||
<!-- <div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title mb-0">Spending by Category</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-wrapper">
|
||||
<canvas baseChart class="chart"
|
||||
[datasets]="doughnutChartDataCategory"
|
||||
[labels]="doughnutChartLabelsCategory"
|
||||
[options]="doughnutChartOptionsCategory"
|
||||
[colors]= "doughnutChartColoursCategory"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="chartType"
|
||||
(chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- </body> -->
|
||||
<!-- </div> --><!--/.col-->
|
||||
<div *ngIf="showEssentialBarChart" class="col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title mb-0">No. of Essential Purchases</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-wrapper">
|
||||
<canvas baseChart class="chart"
|
||||
[datasets]="barChartDataEssential"
|
||||
[labels]="barChartLabelsEssential"
|
||||
[options]="barChartOptionsEssential"
|
||||
[chartType]="barChartTypeEssential"
|
||||
(chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div class="col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title float-left mb-0">Your Purchases by Category</h4>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
<div class="chart-wrapper">
|
||||
<canvas baseChart class="chart"
|
||||
[datasets]="barChartDataCategory"
|
||||
[labels]="barChartLabelsCategory"
|
||||
[options]="barChartOptionsCategory"
|
||||
[colors]="barChartColoursCategory"
|
||||
[legend]="barChartLegendCategory"
|
||||
[chartType]="barChartTypeCategory"
|
||||
(chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div class="col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title float-left mb-0"> Global Puchases by Category</h4>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div *ngIf="showTotalCategoryList" class="chart-wrapper">
|
||||
<ul class="icons-list">
|
||||
<!-- New loop -->
|
||||
<li *ngFor="let category of totalCategoryList | slice:0:totalCategoryLimit; let i=index">
|
||||
<i [ngClass]="['icon-' + category.icon, getBootstrapColour(i)]"></i>
|
||||
<div class="desc">
|
||||
<div class="title">{{ category.category || 'N/A' }}</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="small text-muted">Bought</div>
|
||||
<strong>{{ category.value || 'N/A' }}</strong>
|
||||
</div>
|
||||
</li>
|
||||
<li *ngIf="totalCategoryList.length > totalCategoryLimit && disableCategoryButton == false" class="divider text-center">
|
||||
<button type="button" class="btn btn-sm btn-link text-muted" (click)="categoryLoadMore()"><i class="icon-options"></i></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.row-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,47 +1,205 @@
|
|||
import { Directive, Component, OnInit } from '@angular/core';
|
||||
import { Http, Response } from '@angular/http';
|
||||
import { CurrencyPipe } from '@angular/common';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import { Router } from '@angular/router';
|
||||
import { ChartOptions, ChartType, ChartDataSets } from 'chart.js';
|
||||
import { GraphWidget } from '../widgets/graph-widget.component';
|
||||
import { Color, Label } from 'ng2-charts';
|
||||
import { CustBarSnippetComponent } from '../snippets/cust-snippet-bar.component';
|
||||
import { PiePanel } from '../panels/pie-panel.component';
|
||||
import { DataType } from '../shared/data-types.enum';
|
||||
import * as moment from 'moment';
|
||||
import { MoreStuffComponent } from '../dashboard/more-graphs-and-tables.component';
|
||||
// import { StackedBarChartComponent } from '../panels/stacked-bar.component';
|
||||
|
||||
interface SuppliersComponent {
|
||||
name : string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: 'dashboard-customer.component.html'
|
||||
})
|
||||
export class DashboardCustomerComponent implements OnInit {
|
||||
customersThisMonth: any;
|
||||
moneySpentThisMonth: any;
|
||||
pointsTotal: any;
|
||||
averageTransactionToday: any;
|
||||
|
||||
/* Setting up dashboard's main variables*/
|
||||
name: any;
|
||||
email:any;
|
||||
email: any;
|
||||
myPearPoints: any;
|
||||
trends: any;
|
||||
myRank: any;
|
||||
username: any;
|
||||
maxPurchase: number = 0;
|
||||
|
||||
basicStats = {
|
||||
today_sum: 0,
|
||||
today_count: 0,
|
||||
week_sum: 0,
|
||||
week_count: 0,
|
||||
month_sum: 0,
|
||||
month_count: 0,
|
||||
user_sum: 0,
|
||||
user_count: 0,
|
||||
global_sum: 0,
|
||||
global_count: 0,
|
||||
user_position: 0,
|
||||
disableCategoryButton: boolean = false;
|
||||
|
||||
public bootstrapColours: string[] = ['bg-primary', 'bg-secondary', 'bg-success',
|
||||
'bg-danger', 'bg-warning', 'bg-info'];
|
||||
|
||||
public chartType = 'doughnut';
|
||||
public chartLegend = true;
|
||||
public doughnutChartDataCategory: any[] = [];
|
||||
public doughnutChartLabelsCategory: string[] = [];
|
||||
public doughnutChartColoursCategory: any[] = [
|
||||
{
|
||||
backgroundColor:[
|
||||
'#ffa1b5',
|
||||
'#3cde52',
|
||||
'#52afed',
|
||||
'#c133e3',
|
||||
'#f7fa08',
|
||||
'#75152d',
|
||||
'#ee12ee',
|
||||
'#15eaea',
|
||||
'#eaa015',
|
||||
'#ea1515',
|
||||
'#2d4fcc'
|
||||
]
|
||||
}];
|
||||
|
||||
public doughnutChartOptionsCategory:any = {
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltip, data) => {
|
||||
return this.tooltipLabelCallback(tooltip, data);
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
myWeek1: any;
|
||||
|
||||
weekList1 = [];
|
||||
|
||||
public purchaseNotEssential: number;
|
||||
public purchaseEssential: number;
|
||||
public showEssentialBarChart = false;
|
||||
public showCategoryBarChart = false;
|
||||
public showCategoryDoughnutChart = false;
|
||||
|
||||
public barChartDataEssential:any[]=[
|
||||
{data: 0, label: 'Essential', stack: '1'},
|
||||
{data: 0, label: 'Non-Essential', stack: '1'},
|
||||
];
|
||||
public barChartLabelsEssential:string[] = ['All Purchases'];
|
||||
public barChartOptionsEssential:any = {
|
||||
responsive: true,
|
||||
scales:{
|
||||
xAxes:[{
|
||||
scaleLabel: {
|
||||
display:true,
|
||||
},
|
||||
stacked:true,
|
||||
|
||||
}],
|
||||
}
|
||||
};
|
||||
public barChartTypeEssential:string = 'horizontalBar';
|
||||
|
||||
public barChartOptionsCategory:any = {
|
||||
scaleShowVerticalLines: false,
|
||||
responsive: true,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
callback: label => `£${label}`
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltip, data) => {
|
||||
return this.tooltipLabelCallback(tooltip, data);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
public barChartTypeCategory:string = 'bar';
|
||||
public barChartLegendCategory:boolean = false;
|
||||
public barChartDataCategory:any[]=[];
|
||||
public barChartLabelsCategory:string[] = [];
|
||||
public barChartColoursCategory: any[] = [
|
||||
{
|
||||
backgroundColor:[
|
||||
'#ffa1b5',
|
||||
'#3cde52',
|
||||
'#52afed',
|
||||
'#c133e3',
|
||||
'#f7fa08',
|
||||
'#75152d',
|
||||
'#ee12ee',
|
||||
'#15eaea',
|
||||
'#eaa015',
|
||||
'#ea1515',
|
||||
'#2d4fcc'
|
||||
]
|
||||
}];
|
||||
|
||||
|
||||
weekPurchaseList = {
|
||||
first: 0,
|
||||
second: 0,
|
||||
max: 0,
|
||||
sum: 0,
|
||||
count: 0,
|
||||
};
|
||||
|
||||
showTotalCategoryList: boolean = false;
|
||||
totalCategoryLimit: number = 10;
|
||||
totalCategoryList: any[]=[];
|
||||
|
||||
|
||||
// Graph widgets
|
||||
public widgetList = [
|
||||
{
|
||||
type: 'graph',
|
||||
name: 'total_last_week',
|
||||
icon: 'icon-diamond',
|
||||
title: 'Last Week Total',
|
||||
dataType: DataType.currency,
|
||||
},
|
||||
{
|
||||
type: 'graph',
|
||||
name: 'avg_spend_last_week',
|
||||
icon: 'icon-diamond',
|
||||
title: 'Last Week Avg. Spend',
|
||||
dataType: DataType.currency,
|
||||
},
|
||||
{
|
||||
type: 'graph',
|
||||
name: 'total_last_month',
|
||||
title: 'Last Month Total',
|
||||
dataType: DataType.currency,
|
||||
},
|
||||
{
|
||||
type: 'graph',
|
||||
name: 'avg_spend_last_month',
|
||||
title: 'Last Month Avg. Spend',
|
||||
dataType: DataType.currency,
|
||||
},
|
||||
];
|
||||
|
||||
constructor(
|
||||
private http: Http,
|
||||
private api: ApiService,
|
||||
private currencyPipe: CurrencyPipe,
|
||||
) {
|
||||
this.api.basicStats().subscribe(
|
||||
this.setDate();
|
||||
this.api.customerStats().subscribe(
|
||||
result => {
|
||||
this.basicStats = result;
|
||||
this.setWeekPurchaseList(result.weeks);
|
||||
this.setWeekData(result);
|
||||
this.setChartData(result.data.cat_total);
|
||||
this.totalCategoryList = result.data.cat_list;
|
||||
if (this.totalCategoryList) {
|
||||
this.showTotalCategoryList = true;
|
||||
}
|
||||
this.purchaseEssential = result.data.essentials.purchase_no_essential_total;
|
||||
this.purchaseNotEssential = result.data.essentials.purchase_no_total - this.purchaseEssential;
|
||||
this.barChartDataEssential = [
|
||||
{data: [this.purchaseEssential], label: 'Essential', stack: '1'},
|
||||
{data: [this.purchaseNotEssential], label: 'Non-Essential', stack: '1'},
|
||||
];
|
||||
this.showEssentialBarChart = true;
|
||||
},
|
||||
error => {
|
||||
console.log('Retrieval Error');
|
||||
|
@ -50,6 +208,77 @@ export class DashboardCustomerComponent implements OnInit {
|
|||
);
|
||||
}
|
||||
|
||||
private setChartData(dataCat: any) {
|
||||
this.barChartLabelsCategory = Object.keys(dataCat);
|
||||
let barChartDataCategoryInitial = Object.keys(dataCat).map(key => dataCat[key]);
|
||||
this.barChartDataCategory = [
|
||||
{data: barChartDataCategoryInitial, label: 'Series A'},
|
||||
];
|
||||
this.showCategoryBarChart = true;
|
||||
if (this.weekList1) {
|
||||
let doughnutChartDataCategoryInitial = this.weekList1.map(function(a) {return a.value;});
|
||||
this.doughnutChartDataCategory = [
|
||||
{data: doughnutChartDataCategoryInitial, label: 'Series A'},
|
||||
];
|
||||
// setTimeout is currently a workaround for ng2-charts labels
|
||||
setTimeout(() => this.doughnutChartLabelsCategory = this.weekList1.map(function(a) {return a.category;}), 0);
|
||||
this.showCategoryDoughnutChart = true;
|
||||
}
|
||||
}
|
||||
|
||||
private setDate () {
|
||||
this.myWeek1 = moment().startOf('isoWeek').format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
private setWeekData (data: any) {
|
||||
function prop<T, K extends keyof T>(obj: T, key: K) {
|
||||
return obj[key];
|
||||
}
|
||||
this.weekList1 = prop(data.data.categories, this.myWeek1);
|
||||
}
|
||||
|
||||
public setWeekPurchaseList (data: any) {
|
||||
this.weekPurchaseList = {
|
||||
first: data.first,
|
||||
second: data.second,
|
||||
max: data.max,
|
||||
sum: data.sum,
|
||||
count: data.count,
|
||||
};
|
||||
}
|
||||
|
||||
private categoryLoadMore () {
|
||||
this.disableCategoryButton = true;
|
||||
this.totalCategoryLimit = 30;
|
||||
}
|
||||
|
||||
public getBootstrapColour(index: number) {
|
||||
return this.bootstrapColours[index % this.bootstrapColours.length];
|
||||
}
|
||||
|
||||
public convertHex(hex: string, opacity: number) {
|
||||
hex = hex.replace('#', '');
|
||||
const r = parseInt(hex.substring(0, 2), 16);
|
||||
const g = parseInt(hex.substring(2, 4), 16);
|
||||
const b = parseInt(hex.substring(4, 6), 16);
|
||||
|
||||
const rgba = 'rgba(' + r + ', ' + g + ', ' + b + ', ' + opacity / 100 + ')';
|
||||
return rgba;
|
||||
}
|
||||
|
||||
private tooltipLabelCallback(tooltipItem: any, data: any) {
|
||||
var dataset = data.datasets[tooltipItem.datasetIndex];
|
||||
var value = dataset.data[tooltipItem.index];
|
||||
return this.currencyPipe.transform(value, 'GBP', 'symbol', '1.2-2');
|
||||
}
|
||||
|
||||
// events
|
||||
public chartClicked(e: any): void {
|
||||
}
|
||||
|
||||
public chartHovered(e: any): void {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
<div class="animated fadeIn">
|
||||
<snippet-bar-org></snippet-bar-org>
|
||||
<div class="row">
|
||||
|
@ -6,8 +7,107 @@
|
|||
[graphName]="widget.name"
|
||||
[graphTitle]="widget.title"
|
||||
[graphIcon]="widget.icon"
|
||||
[dataType]="widget.dataType"></widget-graph>
|
||||
[dataType]="widget.dataType">
|
||||
</widget-graph>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
<panel-graph></panel-graph>
|
||||
</div>
|
||||
<div class=row>
|
||||
<div class="col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title mb-0">Number of Essential Purchases</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="showEssentialBarChart" class="chart-wrapper">
|
||||
<canvas baseChart class="chart"
|
||||
[datasets]="barChartDataEssential"
|
||||
[labels]="barChartLabelsEssential"
|
||||
[options]="barChartOptionsEssential"
|
||||
[chartType]="barChartTypeEssential"
|
||||
(chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div class="col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title float-left mb-0">All Organisation Purchases by Category</h4>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
<div class="chart-wrapper">
|
||||
<canvas baseChart class="chart"
|
||||
[datasets]="barChartDataCategory"
|
||||
[labels]="barChartLabelsCategory"
|
||||
[options]="barChartOptionsCategory"
|
||||
[colors]="barChartColoursCategory"
|
||||
[legend]="barChartLegendCategory"
|
||||
[chartType]="barChartTypeCategory"
|
||||
(chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
<div *ngIf="showCategoryDoughnutChart" class="col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title mb-0">This weeks' spending by Category</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-wrapper">
|
||||
<canvas baseChart class="chart"
|
||||
[datasets]="doughnutChartDataCategory"
|
||||
[labels]="doughnutChartLabelsCategory"
|
||||
[options]="doughnutChartOptionsCategory"
|
||||
[colors]="doughnutChartColoursCategory"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="chartType"
|
||||
(chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6">
|
||||
<org-pie-panel></org-pie-panel>
|
||||
</div>
|
||||
<div class="col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title float-left mb-0"> Global Puchases by Category</h4>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div *ngIf="showTotalCategoryList" class="chart-wrapper">
|
||||
<ul class="icons-list">
|
||||
<!-- New loop -->
|
||||
<li *ngFor="let category of totalCategoryList | slice:0:totalCategoryLimit; let i=index">
|
||||
<i [ngClass]="['icon-' + category.icon, getBootstrapColour(i)]"></i>
|
||||
<div class="desc">
|
||||
<div class="title">{{ category.category || 'N/A' }}</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="small text-muted">Bought</div>
|
||||
<strong>{{ category.value || 'N/A' }}</strong>
|
||||
</div>
|
||||
</li>
|
||||
<li *ngIf="totalCategoryList.length > totalCategoryLimit && disableCategoryButton == false" class="divider text-center">
|
||||
<button type="button" class="btn btn-sm btn-link text-muted" (click)="categoryLoadMore()"><i class="icon-options"></i></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.row-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Router, NavigationEnd } from "@angular/router";
|
||||
import { CurrencyPipe } from '@angular/common';
|
||||
import { ChartOptions, ChartType, ChartDataSets } from 'chart.js';
|
||||
import { Color, Label } from 'ng2-charts';
|
||||
import { GraphWidget } from '../widgets/graph-widget.component';
|
||||
import { OrgBarSnippetComponent } from '../snippets/org-snippet-bar.component';
|
||||
import { GraphPanel } from '../panels/graph-panel.component';
|
||||
import { OrgPiePanel } from '../panels/org-pie-panel.component';
|
||||
import { DataType } from '../shared/data-types.enum';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import { environment } from '../../environments/environment';
|
||||
import * as moment from 'moment';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'dashboard.component.html'
|
||||
|
@ -37,6 +44,13 @@ export class DashboardComponent {
|
|||
title: 'Sales Last 30 Days',
|
||||
dataType: DataType.currency,
|
||||
},
|
||||
{
|
||||
type: 'graph',
|
||||
name: 'sales_last_quart',
|
||||
icon: 'icon-diamond',
|
||||
title: 'Sales Last Quart',
|
||||
dataType: DataType.currency,
|
||||
},
|
||||
{
|
||||
type: 'graph',
|
||||
name: 'purchases_last_7_days',
|
||||
|
@ -49,7 +63,307 @@ export class DashboardComponent {
|
|||
title: 'Purchases Last 30 Days',
|
||||
dataType: DataType.currency,
|
||||
},
|
||||
{
|
||||
type: 'graph',
|
||||
name: 'purchases_last_quart;',
|
||||
title: 'Purchases Last Quart',
|
||||
dataType: DataType.currency,
|
||||
},
|
||||
];
|
||||
|
||||
constructor() { }
|
||||
disableCategoryButton: boolean = false;
|
||||
|
||||
public bootstrapColours: string[] = ['bg-primary', 'bg-secondary', 'bg-success',
|
||||
'bg-danger', 'bg-warning', 'bg-info'];
|
||||
|
||||
public chartType = 'doughnut';
|
||||
public chartLegend = true;
|
||||
public doughnutChartDataCategory: any[] = [];
|
||||
public doughnutChartLabelsCategory: string[] = [];
|
||||
public doughnutChartColoursCategory: any[] = [
|
||||
{
|
||||
backgroundColor:[
|
||||
'#ffa1b5',
|
||||
'#3cde52',
|
||||
'#52afed',
|
||||
'#c133e3',
|
||||
'#f7fa08',
|
||||
'#75152d',
|
||||
'#ee12ee',
|
||||
'#15eaea',
|
||||
'#eaa015',
|
||||
'#ea1515',
|
||||
'#2d4fcc'
|
||||
]
|
||||
}];
|
||||
|
||||
|
||||
public doughnutChartOptionsCategory:any = {
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltip, data) => {
|
||||
return this.tooltipLabelCallback(tooltip, data);
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
myWeek1: any;
|
||||
|
||||
weekList1 = [];
|
||||
|
||||
public purchaseNotEssential: number;
|
||||
public purchaseEssential: number;
|
||||
public showEssentialBarChart:boolean = false;
|
||||
public showCategoryBarChart = false;
|
||||
public showCategoryDoughnutChart = false;
|
||||
|
||||
public barChartDataEssential: ChartDataSets[] = [
|
||||
{data: [0], label: 'Essential', stack: '1'},
|
||||
{data: [0], label: 'Non-Essential', stack: '1'},
|
||||
];
|
||||
public barChartLabelsEssential:string[] = ['All Purchases'];
|
||||
public barChartOptionsEssential:any = {
|
||||
responsive: true,
|
||||
scales:{
|
||||
xAxes:[{
|
||||
stacked:true
|
||||
}],
|
||||
yAxes:[{
|
||||
stacked:true
|
||||
}]
|
||||
}
|
||||
};
|
||||
public barChartTypeEssential:string = 'horizontalBar';
|
||||
public barChartColoursCategory: any[] = [
|
||||
{
|
||||
backgroundColor:[
|
||||
'#ffa1b5',
|
||||
'#3cde52',
|
||||
'#52afed',
|
||||
'#c133e3',
|
||||
'#f7fa08',
|
||||
'#75152d',
|
||||
'#ee12ee',
|
||||
'#15eaea',
|
||||
'#eaa015',
|
||||
'#ea1515',
|
||||
'#2d4fcc'
|
||||
]
|
||||
}];
|
||||
|
||||
public barChartOptionsCategory:any = {
|
||||
scaleShowVerticalLines: false,
|
||||
responsive: true,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
callback: label => `£${label}`
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltip, data) => {
|
||||
return this.tooltipLabelCallback(tooltip, data);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
public barChartTypeCategory:string = 'bar';
|
||||
public barChartLegendCategory:boolean = false;
|
||||
public barChartDataCategory:any[]=[];
|
||||
public barChartLabelsCategory:string[] = [];
|
||||
|
||||
public lineChartDataSector: ChartDataSets[] = [
|
||||
{ data: [], label: '' },
|
||||
];
|
||||
public lineChartLabelsSector: Label[] = ['January', 'February', 'March', 'April', 'May', 'June', 'July','August','September','October','November','December'];
|
||||
public lineChartOptionsSector: (ChartOptions & { annotation: any }) = {
|
||||
responsive: true,
|
||||
scales: {
|
||||
// We use this empty structure as a placeholder for dynamic theming.
|
||||
xAxes: [{}],
|
||||
yAxes: [{}]
|
||||
},
|
||||
annotation: {
|
||||
annotations: [
|
||||
{
|
||||
type: 'line',
|
||||
mode: 'vertical',
|
||||
scaleID: 'x-axis-0',
|
||||
value: 'March',
|
||||
borderColor: 'orange',
|
||||
borderWidth: 2,
|
||||
label: {
|
||||
enabled: true,
|
||||
fontColor: 'orange',
|
||||
content: 'LineAnno'
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
public lineChartColorsSector: Color[] = [
|
||||
{ // grey
|
||||
backgroundColor: 'rgba(148,159,177,0.2)',
|
||||
borderColor: 'rgba(148,159,177,1)',
|
||||
pointBackgroundColor: 'rgba(148,159,177,1)',
|
||||
pointBorderColor: '#fff',
|
||||
pointHoverBackgroundColor: '#fff',
|
||||
pointHoverBorderColor: 'rgba(148,159,177,0.8)'
|
||||
},
|
||||
{ // dark grey
|
||||
backgroundColor: 'rgba(77,83,96,0.2)',
|
||||
borderColor: 'rgba(77,83,96,1)',
|
||||
pointBackgroundColor: 'rgba(77,83,96,1)',
|
||||
pointBorderColor: '#fff',
|
||||
pointHoverBackgroundColor: '#fff',
|
||||
pointHoverBorderColor: 'rgba(77,83,96,1)'
|
||||
},
|
||||
{ // red
|
||||
backgroundColor: 'rgba(255,0,0,0.3)',
|
||||
borderColor: 'red',
|
||||
pointBackgroundColor: 'rgba(148,159,177,1)',
|
||||
pointBorderColor: '#fff',
|
||||
pointHoverBackgroundColor: '#fff',
|
||||
pointHoverBorderColor: 'rgba(148,159,177,0.8)'
|
||||
}
|
||||
];
|
||||
public lineChartLegendSector = true;
|
||||
public lineChartTypeSector = 'line';
|
||||
|
||||
weekPurchaseList = {
|
||||
first: 0,
|
||||
second: 0,
|
||||
max: 0,
|
||||
sum: 0,
|
||||
count: 0,
|
||||
};
|
||||
|
||||
showTotalCategoryList: boolean = false;
|
||||
totalCategoryLimit: number = 10;
|
||||
totalCategoryList: any[]=[];
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private api: ApiService,
|
||||
private currencyPipe: CurrencyPipe,
|
||||
) {
|
||||
if (environment.enableAnalytics) {
|
||||
this.router.events.subscribe(event => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
(<any>window).ga('set', 'page', event.urlAfterRedirects);
|
||||
(<any>window).ga('send', 'pageview');
|
||||
}
|
||||
});
|
||||
}
|
||||
this.setDate();
|
||||
this.api.orgStats().subscribe(
|
||||
result => {
|
||||
this.setWeekPurchaseList(result.weeks);
|
||||
this.setWeekData(result);
|
||||
this.setChartDataCat(result.data.cat_total);
|
||||
this.setChartDataEssential(result.data.essentials);
|
||||
this.totalCategoryList = result.data.cat_list;
|
||||
if (this.totalCategoryList) {
|
||||
this.showTotalCategoryList = true;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log('Retrieval Error');
|
||||
console.log( error._body );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private setChartDataEssential (dataEs: any) {
|
||||
this.purchaseEssential = dataEs.purchase_no_essential_total;
|
||||
this.purchaseNotEssential = dataEs.purchase_no_total - this.purchaseEssential;
|
||||
this.barChartDataEssential = [
|
||||
{data: [this.purchaseEssential], label: 'Essential', stack: '1'},
|
||||
{data: [this.purchaseNotEssential], label: 'Non-Essential', stack: '1'},
|
||||
];
|
||||
this.showEssentialBarChart = true;
|
||||
console.log(this.barChartDataEssential);
|
||||
}
|
||||
|
||||
private setChartDataCat(dataCat: any) {
|
||||
this.barChartLabelsCategory = Object.keys(dataCat);
|
||||
let barChartDataCategoryInitial = Object.keys(dataCat).map(key => dataCat[key]);
|
||||
this.barChartDataCategory = [
|
||||
{data: barChartDataCategoryInitial, label: 'Series A'},
|
||||
];
|
||||
this.showCategoryBarChart = true;
|
||||
if (this.weekList1) {
|
||||
let doughnutChartDataCategoryInitial = this.weekList1.map(function(a) {return a.value;});
|
||||
this.doughnutChartDataCategory = [
|
||||
{data: doughnutChartDataCategoryInitial, label: 'Series A'},
|
||||
];
|
||||
// setTimeout is currently a workaround for ng2-charts labels
|
||||
setTimeout(() => this.doughnutChartLabelsCategory = this.weekList1.map(function(a) {return a.category;}), 0);
|
||||
this.showCategoryDoughnutChart = true;
|
||||
}
|
||||
}
|
||||
|
||||
private setChartDataSector(dataSec: any) {
|
||||
|
||||
}
|
||||
|
||||
private setDate () {
|
||||
this.myWeek1 = moment().startOf('isoWeek').format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
private setWeekData (data: any) {
|
||||
function prop<T, K extends keyof T>(obj: T, key: K) {
|
||||
return obj[key];
|
||||
}
|
||||
this.weekList1 = prop(data.data.categories, this.myWeek1);
|
||||
}
|
||||
|
||||
public setWeekPurchaseList (data: any) {
|
||||
this.weekPurchaseList = {
|
||||
first: data.first,
|
||||
second: data.second,
|
||||
max: data.max,
|
||||
sum: data.sum,
|
||||
count: data.count,
|
||||
};
|
||||
}
|
||||
|
||||
private categoryLoadMore () {
|
||||
this.disableCategoryButton = true;
|
||||
this.totalCategoryLimit = 30;
|
||||
}
|
||||
|
||||
public getBootstrapColour(index: number) {
|
||||
return this.bootstrapColours[index % this.bootstrapColours.length];
|
||||
}
|
||||
|
||||
public convertHex(hex: string, opacity: number) {
|
||||
hex = hex.replace('#', '');
|
||||
const r = parseInt(hex.substring(0, 2), 16);
|
||||
const g = parseInt(hex.substring(2, 4), 16);
|
||||
const b = parseInt(hex.substring(4, 6), 16);
|
||||
|
||||
const rgba = 'rgba(' + r + ', ' + g + ', ' + b + ', ' + opacity / 100 + ')';
|
||||
return rgba;
|
||||
}
|
||||
|
||||
private tooltipLabelCallback(tooltipItem: any, data: any) {
|
||||
var dataset = data.datasets[tooltipItem.datasetIndex];
|
||||
var value = dataset.data[tooltipItem.index];
|
||||
return this.currencyPipe.transform(value, 'GBP', 'symbol', '1.2-2');
|
||||
}
|
||||
|
||||
// events
|
||||
public chartClicked(e: any): void {
|
||||
}
|
||||
|
||||
public chartHovered(e: any): void {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { ChartsModule } from 'ng2-charts/ng2-charts';
|
||||
import { ChartsModule } from 'ng2-charts';
|
||||
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
|
||||
import { NgxPaginationModule } from 'ngx-pagination';
|
||||
import { AgmCoreModule, GoogleMapsAPIWrapper } from '@agm/core';
|
||||
import { AgmMarkerClustererModule } from '@agm/markerclusterer';
|
||||
import { ModalModule } from 'ngx-bootstrap/modal';
|
||||
|
||||
import { CurrencyPipe } from '@angular/common';
|
||||
|
||||
|
@ -13,15 +16,36 @@ import { AccountEditComponent } from './account-edit.component';
|
|||
import { AddDataComponent } from './add-data.component';
|
||||
import { FeedbackComponent } from './feedback.component';
|
||||
import { TransactionLogComponent } from './transaction-log.component';
|
||||
import { CategoryMonthComponent } from './category-month.component';
|
||||
import { PayrollLogComponent } from './payroll-log.component';
|
||||
import { SuppliersComponent } from './suppliers.component';
|
||||
import { MoreStuffComponent } from './more-graphs-and-tables.component';
|
||||
import { LeaderboardComponent } from './leaderboard.component';
|
||||
import { MapComponent } from './map.component';
|
||||
import { TrailMapComponent } from './trail-map.component';
|
||||
|
||||
import { GraphWidget } from '../widgets/graph-widget.component';
|
||||
import { OrgBarSnippetComponent } from '../snippets/org-snippet-bar.component';
|
||||
import { CustBarSnippetComponent } from '../snippets/cust-snippet-bar.component';
|
||||
import { GraphPanel } from '../panels/graph-panel.component';
|
||||
import { BubbleChartComponent } from '../panels/bubble-panel.component';
|
||||
import { PiePanel } from '../panels/pie-panel.component';
|
||||
import { OrgPiePanel } from '../panels/org-pie-panel.component';
|
||||
|
||||
import { DashboardRoutingModule } from './dashboard.routing';
|
||||
import { OrgResultComponent } from '../shared/org-result.component';
|
||||
import { OrgTableComponent } from '../shared/org-table.component';
|
||||
import { RecurResultComponent } from '../shared/recur-result.component';
|
||||
import { RecurTableComponent } from '../shared/recur-table.component';
|
||||
import { TransactionResultComponent } from '../shared/transaction-result.component';
|
||||
import { SupplierResultComponent } from '../shared/supplier-result.component';
|
||||
import { WardResultComponent } from '../shared/ward-result.component';
|
||||
import { MetaTypeResultComponent } from '../shared/meta-type-result.component';
|
||||
import { PayrollResultComponent } from '../shared/payroll-result.component';
|
||||
import { LeaderboardResultComponent } from '../shared/leaderboard-result.component';
|
||||
|
||||
// API key env variable import
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -30,9 +54,14 @@ import { TransactionResultComponent } from '../shared/transaction-result.compone
|
|||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
ChartsModule,
|
||||
AgmCoreModule.forRoot({
|
||||
apiKey: environment.mapApiKey
|
||||
}),
|
||||
AgmMarkerClustererModule,
|
||||
BsDropdownModule,
|
||||
NgxPaginationModule,
|
||||
DashboardRoutingModule,
|
||||
ModalModule.forRoot(),
|
||||
],
|
||||
declarations: [
|
||||
DashboardComponent,
|
||||
|
@ -41,15 +70,34 @@ import { TransactionResultComponent } from '../shared/transaction-result.compone
|
|||
AddDataComponent,
|
||||
OrgResultComponent,
|
||||
OrgTableComponent,
|
||||
RecurResultComponent,
|
||||
RecurTableComponent,
|
||||
TransactionLogComponent,
|
||||
CategoryMonthComponent,
|
||||
TransactionResultComponent,
|
||||
SupplierResultComponent,
|
||||
WardResultComponent,
|
||||
MetaTypeResultComponent,
|
||||
PayrollLogComponent,
|
||||
PayrollResultComponent,
|
||||
LeaderboardComponent,
|
||||
LeaderboardResultComponent,
|
||||
MapComponent,
|
||||
TrailMapComponent,
|
||||
FeedbackComponent,
|
||||
GraphWidget,
|
||||
OrgBarSnippetComponent,
|
||||
CustBarSnippetComponent,
|
||||
GraphPanel,
|
||||
PiePanel,
|
||||
OrgPiePanel,
|
||||
BubbleChartComponent,
|
||||
SuppliersComponent,
|
||||
MoreStuffComponent,
|
||||
],
|
||||
providers: [
|
||||
CurrencyPipe
|
||||
CurrencyPipe,
|
||||
GoogleMapsAPIWrapper,
|
||||
],
|
||||
})
|
||||
export class DashboardModule { }
|
||||
|
|
|
@ -12,6 +12,13 @@ import { AccountEditComponent } from './account-edit.component';
|
|||
import { AddDataComponent } from './add-data.component';
|
||||
import { FeedbackComponent } from './feedback.component';
|
||||
import { TransactionLogComponent } from './transaction-log.component';
|
||||
import { CategoryMonthComponent } from './category-month.component';
|
||||
import { PayrollLogComponent } from './payroll-log.component';
|
||||
import { LeaderboardComponent } from './leaderboard.component';
|
||||
import { MapComponent } from './map.component';
|
||||
import { TrailMapComponent } from './trail-map.component';
|
||||
import { MoreStuffComponent } from './more-graphs-and-tables.component';
|
||||
import { SuppliersComponent } from './suppliers.component';
|
||||
|
||||
// Using child path to allow for FullLayout theming
|
||||
const routes: Routes = [
|
||||
|
@ -36,22 +43,59 @@ const routes: Routes = [
|
|||
{
|
||||
path: 'account-edit',
|
||||
component: AccountEditComponent,
|
||||
data: { title: 'Leaderboards' },
|
||||
data: { title: 'Edit Account' },
|
||||
},
|
||||
{
|
||||
path: 'add-data',
|
||||
component: AddDataComponent,
|
||||
data: { title: 'Add Transaction' },
|
||||
},
|
||||
{
|
||||
path: 'leaderboard',
|
||||
component: LeaderboardComponent,
|
||||
data: { title: 'Leaderboards' },
|
||||
canActivate: [CustomerGuard],
|
||||
},
|
||||
{
|
||||
path: 'transaction-log',
|
||||
component: TransactionLogComponent,
|
||||
data: { title: 'Transaction Log' },
|
||||
},
|
||||
{
|
||||
path: 'category-month',
|
||||
component: CategoryMonthComponent,
|
||||
data: { title: 'Budget' },
|
||||
},
|
||||
{
|
||||
path: 'map',
|
||||
component: MapComponent,
|
||||
data: { title: 'Purchase Map' },
|
||||
},
|
||||
{
|
||||
path: 'story-trail',
|
||||
component: TrailMapComponent,
|
||||
data: { title: 'Story Trail' },
|
||||
},
|
||||
{
|
||||
path: 'payroll-log',
|
||||
component: PayrollLogComponent,
|
||||
data: { title: 'Payroll Log' },
|
||||
canActivate: [OrgGuard],
|
||||
},
|
||||
{
|
||||
path: 'feedback',
|
||||
component: FeedbackComponent,
|
||||
data: { title: 'Give Feedback' },
|
||||
},
|
||||
{
|
||||
path: 'suppliers',
|
||||
component: SuppliersComponent,
|
||||
data: { title: 'Suppliers' }
|
||||
},
|
||||
{
|
||||
path: 'more-graphs-and-tables',
|
||||
component: MoreStuffComponent,
|
||||
data: { title: 'Infographics'}
|
||||
}
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Validators, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { Http, Response } from '@angular/http';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'feedback.component.html',
|
||||
})
|
||||
export class FeedbackComponent {
|
||||
export class FeedbackComponent implements OnInit {
|
||||
feedbackForm: FormGroup;
|
||||
loggedInEmail: string;
|
||||
noEmail: boolean = false;
|
||||
noEmail = false;
|
||||
username: any;
|
||||
feedbackFormStatus: any;
|
||||
feedbackFormStatusError: string = 'Error received, please try again.';
|
||||
feedbackFormStatusError = 'Error received, please try again.';
|
||||
|
||||
constructor(
|
||||
private http: Http,
|
||||
private formBuilder: FormBuilder,
|
||||
private api: ApiService,
|
||||
private formBuilder: FormBuilder,
|
||||
private api: ApiService,
|
||||
) {
|
||||
this.feedbackForm = this.formBuilder.group({
|
||||
email: ['', [Validators.required]],
|
||||
|
@ -28,7 +26,7 @@ export class FeedbackComponent {
|
|||
|
||||
ngOnInit(): void {
|
||||
|
||||
if(localStorage.getItem('email')) {
|
||||
if (localStorage.getItem('email')) {
|
||||
this.loggedInEmail = localStorage.getItem('email');
|
||||
}
|
||||
console.log('loggedInEmail: ' + this.loggedInEmail);
|
||||
|
@ -55,34 +53,26 @@ export class FeedbackComponent {
|
|||
.feedback(this.feedbackForm.value)
|
||||
.subscribe(
|
||||
result => {
|
||||
if ( result.success == true ) {
|
||||
if ( result.success === true ) {
|
||||
console.log('Successful Upload');
|
||||
console.log(result);
|
||||
this.feedbackFormStatus = "success";
|
||||
console.log(this.feedbackFormStatus);
|
||||
this.feedbackFormStatus = 'success';
|
||||
this.feedbackForm.patchValue({
|
||||
feedbacktext: '',
|
||||
});
|
||||
} else {
|
||||
console.log('Upload Error');
|
||||
this.feedbackFormStatusError = JSON.stringify(result.status) + 'Error, ' + JSON.stringify(result.message);
|
||||
this.feedbackFormStatus = "send_failed";
|
||||
console.log(this.feedbackFormStatus);
|
||||
this.feedbackFormStatus = 'send_failed';
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log('Upload Error');
|
||||
console.log(error);
|
||||
try {
|
||||
console.log(error.error);
|
||||
let jsonError = error.json();
|
||||
console.log("boop");
|
||||
this.feedbackFormStatusError = '"' + jsonError.error + '" Error, ' + jsonError.message;
|
||||
} catch(e) {
|
||||
this.feedbackFormStatusError = '"' + error.error.error + '" Error, ' + error.error.message;
|
||||
} catch (e) {
|
||||
this.feedbackFormStatusError = 'There was a server error, please try again later.';
|
||||
}
|
||||
this.feedbackFormStatus = "send_failed";
|
||||
console.log(this.feedbackFormStatus);
|
||||
this.feedbackFormStatus = 'send_failed';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
61
src/app/dashboard/leaderboard.component.html
Normal file
61
src/app/dashboard/leaderboard.component.html
Normal file
|
@ -0,0 +1,61 @@
|
|||
<div class="animated fadeIn">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Leaderboard</strong>
|
||||
<small>By default this loads the page with your position.</small>
|
||||
</div>
|
||||
<div *ngIf="!noLeaderboardList" class="card-block">
|
||||
<div class="input-group mb-3">
|
||||
<select type="text" [(ngModel)]="listType" (ngModelChange)="changeLeaderboard($event)">
|
||||
<option value="daily_total">Yesterday Total</option>
|
||||
<option value="daily_count">Yesterday Count</option>
|
||||
<option value="weekly_total" selected>Last Week Total</option>
|
||||
<option value="weekly_count">Last Week Count</option>
|
||||
<option value="monthly_total">Last Month Total</option>
|
||||
<option value="monthly_count">Last Month Count</option>
|
||||
<option value="all_time_total">All Time Total</option>
|
||||
<option value="all_time_count">All Time Count</option>
|
||||
</select>
|
||||
</div>
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Position</th>
|
||||
<th>Name</th>
|
||||
<th class="js-sort-number">Gross amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr leaderboard-result *ngFor="let leaderboard of leaderboardList | paginate: paginateConfig" [leaderboard]="leaderboard" [listType]="listType"></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<pagination-template #p="paginationApi"
|
||||
[id]="paginateConfig.id"
|
||||
(pageChange)="loadLeaderboard($event)">
|
||||
<ul class="pagination">
|
||||
<li class="page-item" [class.disabled]="p.isFirstPage()">
|
||||
<a class="page-link clickable" *ngIf="!p.isFirstPage()" (click)="p.previous()">Prev</a>
|
||||
</li>
|
||||
<li *ngFor="let page of p.pages" class="page-item" [class.active]="p.getCurrent() === page.value">
|
||||
<a class="page-link clickable" (click)="p.setCurrent(page.value)" *ngIf="p.getCurrent() !== page.value">
|
||||
<span>{{ page.label }}</span>
|
||||
</a>
|
||||
<div class="page-link" *ngIf="p.getCurrent() === page.value">
|
||||
<span>{{ page.label }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="page-item" [class.disabled]="p.isLastPage()">
|
||||
<a class="page-link clickable" *ngIf="!p.isLastPage()" (click)="p.next()">Next</a>
|
||||
</li>
|
||||
</ul>
|
||||
</pagination-template>
|
||||
</div>
|
||||
<div *ngIf="noLeaderboardList" class="card-block">
|
||||
No Leaderboard available.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
108
src/app/dashboard/leaderboard.component.ts
Normal file
108
src/app/dashboard/leaderboard.component.ts
Normal file
|
@ -0,0 +1,108 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
// import { PaginatePipe } from 'ngx-pagination';
|
||||
import {PaginationInstance} from 'ngx-pagination';
|
||||
// import { PaginationControlsComponent } from 'ngx-pagination';
|
||||
// import { PaginationControlsDirective } from 'ngx-pagination';
|
||||
// import { TransactionResultComponent } from '../shared/transaction-result.component';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'leaderboard.component.html',
|
||||
})
|
||||
export class LeaderboardComponent implements OnInit {
|
||||
|
||||
leaderboardList;
|
||||
noLeaderboardList = false;
|
||||
public p: any;
|
||||
|
||||
leaderboardData: Array<any>;
|
||||
currentPos: number;
|
||||
listType: any = 'weekly_total';
|
||||
|
||||
public paginateConfig: PaginationInstance = {
|
||||
id: 'leadpaginate',
|
||||
itemsPerPage: 20,
|
||||
currentPage: 1,
|
||||
totalItems: 0
|
||||
};
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadLeaderboard(0);
|
||||
}
|
||||
|
||||
// private fetchLeaderboard() {
|
||||
// this.peopleService.leaderboard(this.listType)
|
||||
// .subscribe(
|
||||
// result => {
|
||||
// this.leaderboardData = result.leaderboard;
|
||||
// this.currentPos = result.user_position;
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
|
||||
public changeLeaderboard(event) {
|
||||
this.loadLeaderboard(0);
|
||||
}
|
||||
|
||||
|
||||
loadLeaderboard(leadPage: number) {
|
||||
console.log(leadPage, this.listType);
|
||||
this.api.leaderboard_fetch(this.listType,leadPage).subscribe(
|
||||
result => {
|
||||
if (result.leaderboard.length > 0) {
|
||||
this.leaderboardList = result.leaderboard;
|
||||
// TODO Rename in server
|
||||
this.paginateConfig.totalItems = result.count;
|
||||
this.paginateConfig.currentPage = result.page;
|
||||
this.noLeaderboardList = false;
|
||||
} else {
|
||||
// handle the case when the leaderboardList is empty
|
||||
this.leaderboardList = null;
|
||||
this.noLeaderboardList = true;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
}
|
||||
);
|
||||
}org
|
||||
|
||||
// // dynamically changes the row style based on player's position
|
||||
// // for instance, top three player and the player him/herself should
|
||||
// // be hightlighted
|
||||
// public getClass(item) {
|
||||
// if( item.position < 4 ) {
|
||||
// return "topThree";
|
||||
// } else if( item.position == this.currentPos ) {
|
||||
// return "user";
|
||||
// }
|
||||
// return "otherUsers";
|
||||
// }
|
||||
//
|
||||
// // show changes by using icon, trending up and trending down or no trend.
|
||||
// public getTrendIcon(item){
|
||||
// if( item.trend < 0 ){
|
||||
// return "md-trending-up";
|
||||
// } else if( item.trend > 0 ){
|
||||
// return "md-trending-down";
|
||||
// }
|
||||
// return "md-remove";
|
||||
// }
|
||||
//
|
||||
// // need to merge this function with getIcon
|
||||
// // this function shows different icon color based on the direction of the position shifted
|
||||
// public getTrendIconColor(item){
|
||||
// if( item.trend < 0 ) {
|
||||
// return "secondary";
|
||||
// } else if( item.trend > 0 ){
|
||||
// return "danger";
|
||||
// }
|
||||
// return "dark";
|
||||
// }
|
||||
|
||||
}
|
71
src/app/dashboard/map.component.html
Normal file
71
src/app/dashboard/map.component.html
Normal file
|
@ -0,0 +1,71 @@
|
|||
<div class="animated fadeIn">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Purchase Map</strong>
|
||||
<small>Click a marker to get location details.</small>
|
||||
</div>
|
||||
<div class="modal fade" bsModal #statusModal="bs-modal" [config]="{backdrop: false, animated: false}"
|
||||
tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm h-100 d-flex flex-column justify-content-center my-0">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title pull-left">Status</h4>
|
||||
<button type="button" class="close pull-right" aria-label="Close" (click)="statusModal.hide()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div [ngSwitch]="dataReceived">
|
||||
<div *ngSwitchCase="'no'">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
No map data received, check your connection.
|
||||
</div>
|
||||
</div>
|
||||
<div *ngSwitchCase="'loading'">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
Map loading, please wait.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- this creates a google map on the page with the given lat/lng from -->
|
||||
<!-- the component as the initial center of the map: -->
|
||||
<agm-map (mapReady)="onMapReady($event)"
|
||||
[latitude]="lat"
|
||||
[longitude]="lng"
|
||||
[zoom]="zoom"
|
||||
(idle)="viewBoundsChanged()">
|
||||
<agm-marker-cluster maxZoom="13">
|
||||
<agm-marker
|
||||
*ngFor="let m of markers"
|
||||
[latitude]="m.latitude"
|
||||
[longitude]="m.longitude"
|
||||
[label]="m.name[0]"
|
||||
[openInfoWindow]="false"
|
||||
(markerClick)="onMarkerClick(m, template)">
|
||||
</agm-marker>
|
||||
</agm-marker-cluster>
|
||||
</agm-map>
|
||||
<ng-template #template>
|
||||
<div class="modal-header d-flex justify-content-between">
|
||||
<img src="assets/img/logo.png" class="w-15" alt="lis logo"><h4 class="modal-title">{{clickedMarker.name}}</h4>
|
||||
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-right">
|
||||
<h5>Located at:</h5>
|
||||
<h6>{{clickedMarker.street_name}}</h6>
|
||||
<h6>{{clickedMarker.town}}</h6>
|
||||
<h6>{{clickedMarker.postcode}}</h6>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.row-->
|
||||
</div>
|
83
src/app/dashboard/map.component.ts
Normal file
83
src/app/dashboard/map.component.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
import { Component, OnInit, AfterViewInit, Input, Output, EventEmitter, ViewChild, TemplateRef } from '@angular/core';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import { AgmCoreModule } from '@agm/core';
|
||||
import { BsModalService, ModalDirective } from 'ngx-bootstrap/modal';
|
||||
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'map.component.html',
|
||||
})
|
||||
export class MapComponent implements OnInit, AfterViewInit {
|
||||
@ViewChild('statusModal', { static: true }) myStatusModal: ModalDirective;
|
||||
lat: number = 54.0466;
|
||||
lng: number = -2.8007;
|
||||
zoom: number = 12;
|
||||
public modalRef: BsModalRef;
|
||||
clickedMarker: any;
|
||||
|
||||
dataReceived: string = 'loading';
|
||||
|
||||
markers: Array<{latitude: number, longitude: number, name: string}>;
|
||||
|
||||
map: any;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private modalService: BsModalService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void { }
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.myStatusModal.show();
|
||||
}
|
||||
|
||||
public onMapReady(map: any) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
openModal(template: TemplateRef<any>) {
|
||||
this.modalRef = this.modalService.show(template);
|
||||
}
|
||||
|
||||
public onMarkerClick(clickedMarker, template: TemplateRef<any>) {
|
||||
console.log(clickedMarker);
|
||||
this.clickedMarker = clickedMarker;
|
||||
this.openModal(template);
|
||||
}
|
||||
|
||||
public viewBoundsChanged() {
|
||||
console.log("finding bounds");
|
||||
const resp = this.map.getBounds();
|
||||
console.log("found bounds");
|
||||
console.log(resp.getNorthEast().lat());
|
||||
console.log(resp.getNorthEast().lng());
|
||||
console.log(resp.getSouthWest().lat());
|
||||
console.log(resp.getSouthWest().lng());
|
||||
const mapData = {
|
||||
north_east: {
|
||||
latitude: resp.getNorthEast().lat(),
|
||||
longitude: resp.getNorthEast().lng()
|
||||
},
|
||||
south_west: {
|
||||
latitude: resp.getSouthWest().lat(),
|
||||
longitude: resp.getSouthWest().lng()
|
||||
},
|
||||
}
|
||||
this.api.getMapData(mapData).subscribe(
|
||||
result => {
|
||||
this.myStatusModal.hide();
|
||||
this.markers = result.suppliers;
|
||||
},
|
||||
error => {
|
||||
this.dataReceived = 'no';
|
||||
console.log('Retrieval Error');
|
||||
console.log( error._body );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
136
src/app/dashboard/more-graphs-and-tables.component.html
Normal file
136
src/app/dashboard/more-graphs-and-tables.component.html
Normal file
|
@ -0,0 +1,136 @@
|
|||
<div class="animated fadeIn">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>Filter</h4>
|
||||
</div>
|
||||
<div class="card-block">
|
||||
<form class="form-inline">
|
||||
<label class="mr-2" for="filter-from">From</label>
|
||||
<input id="filter-from" class="form-control" type="date" [(ngModel)]="filterFrom" name="from">
|
||||
<label class="mx-2" for="filter-to">To</label>
|
||||
<input class="form-control" id="filter-to" type="date" [(ngModel)]="filterTo" name="to">
|
||||
<button type="submit" class="btn btn-primary ml-2" (click)="loadData()">Filter</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Transaction Types</strong>
|
||||
</div>
|
||||
<div *ngIf="metaTypeListAvailable" class="card-block">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Ward</th>
|
||||
<th>Amount of Transactions</th>
|
||||
<th>Sum of Transactions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr meta-type-result *ngFor="let type of metaTypeList" [type]="type"></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div *ngIf="!metaTypeListAvailable" class="card-block">
|
||||
No Data available.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Ward Spending</strong>
|
||||
</div>
|
||||
<div *ngIf="wardListAvailable" class="card-block">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Ward</th>
|
||||
<th>Amount of Transactions</th>
|
||||
<th>Sum of Transactions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ward-result *ngFor="let ward of wardList" [ward]="ward"></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div *ngIf="!wardListAvailable" class="card-block">
|
||||
No Data available.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="animated fadeIn">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<h4 class="card-title mb-0">Supplier spend amount and number of purchases</h4>
|
||||
</div>
|
||||
</div>
|
||||
<small>vertical shows number of purchases, size of bubble shows the total spend amount, horizontal shows date</small>
|
||||
<div class="col-sm-12" *ngIf="!isBubbleChartLoaded">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<div *ngIf="isBubbleChartLoaded">
|
||||
<canvas baseChart
|
||||
[datasets]="supplierBubbleChartData"
|
||||
[options]="supplierBubbleChartOptions"
|
||||
[labels]="supplierBubbleChartLabels"
|
||||
[legend]="showLegend"
|
||||
[chartType]="supplierBubbleChartType">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h4 class="card-title mb-0">Spend & Number of Transactions</h4>
|
||||
<small>Date against Value and Number of Transactions</small>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<canvas baseChart
|
||||
[datasets]="yearSpendChartData"
|
||||
[options]="yearSpendChartOptions"
|
||||
[labels]="yearSpendChartLabels"
|
||||
[legend]="showLegend"
|
||||
[chartType]="yearSpendChartType">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<h4 class="card-title mb-0">Supplier Spend History</h4>
|
||||
</div>
|
||||
<div class="col-sm-6 hidden-sm-down">
|
||||
<button type="button" class="btn btn-danger" (click)="previousSupplierHistoryPage()">Previous Page</button>
|
||||
<button type="button" class="btn btn-info" (click)="nextSupplierHistoryPage()">Next Page</button>
|
||||
<span class="ml-2">Page {{ _supplierHistoryPage }} of {{ _supplierHistoryPages }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="isSupplierChartLoaded">
|
||||
<canvas baseChart #supplierChart
|
||||
[datasets]="supplierMonthChartData"
|
||||
[options]="supplierMonthChartOptions"
|
||||
[labels]="supplierMonthChartLabels"
|
||||
[legend]="showLegend"
|
||||
[chartType]="supplierMonthChartType">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
335
src/app/dashboard/more-graphs-and-tables.component.ts
Normal file
335
src/app/dashboard/more-graphs-and-tables.component.ts
Normal file
|
@ -0,0 +1,335 @@
|
|||
import {Component, OnInit, Input, Output, EventEmitter, ViewChild} from '@angular/core';
|
||||
import {ApiService} from '../providers/api-service';
|
||||
import {BaseChartDirective} from 'ng2-charts';
|
||||
import {CurrencyPipe} from '@angular/common';
|
||||
import {ChartType} from "chart.js";
|
||||
import * as moment from 'moment';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'more-graphs-and-tables.component.html',
|
||||
})
|
||||
export class MoreStuffComponent implements OnInit {
|
||||
@Output() public onClick = new EventEmitter();
|
||||
@Input() public categories: any;
|
||||
|
||||
// Global Filter Setup
|
||||
filterFrom: any;
|
||||
filterTo: any;
|
||||
|
||||
isBubbleChartLoaded: boolean = false;
|
||||
isSupplierChartLoaded: boolean = false;
|
||||
wardList: any;
|
||||
wardListAvailable = false;
|
||||
metaTypeList: any;
|
||||
metaTypeListAvailable = false;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private currencyPipe: CurrencyPipe,
|
||||
) {
|
||||
let now = moment();
|
||||
this.filterTo = now.format('YYYY-MM-DD');
|
||||
now.subtract(1, 'months');
|
||||
this.filterFrom = now.format('YYYY-MM-DD');
|
||||
this.tableSummary();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
public loadData() {
|
||||
this.tableSummary();
|
||||
this.loadYearSpend();
|
||||
this.loadSupplierBubble();
|
||||
this.loadSupplierHistory();
|
||||
}
|
||||
|
||||
public showLegend = true;
|
||||
|
||||
/*
|
||||
* Supplier Bubble Chart Setup
|
||||
*/
|
||||
|
||||
private formatGraphData(data: any): any[] {
|
||||
let graph_data = [];
|
||||
|
||||
data.data.map(item => {
|
||||
graph_data.push({
|
||||
t: item.date,
|
||||
r: item.value > 1000000 ? (item.value / 200000) : (item.value / 100000) + 5,
|
||||
supplier: item.seller,
|
||||
y: item.count,
|
||||
value: item.value,
|
||||
count: item.count,
|
||||
});
|
||||
});
|
||||
|
||||
return graph_data;
|
||||
}
|
||||
|
||||
private loadSupplierBubble() {
|
||||
this.api.loadMiscUrl('organisation/external/supplier_count', {
|
||||
from: this.filterFrom,
|
||||
to: this.filterTo,
|
||||
}).subscribe(
|
||||
result => {
|
||||
this.supplierBubbleChartData[0].data = this.formatGraphData(result);
|
||||
this.isBubbleChartLoaded = true;
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
public supplierBubbleChartType: ChartType = 'bubble';
|
||||
public supplierBubbleChartData: any[] = [
|
||||
{
|
||||
data: [],
|
||||
label: ["Spend"],
|
||||
borderColor: 'blue',
|
||||
hoverBorderColor: 'black',
|
||||
radius: 5,
|
||||
},
|
||||
];
|
||||
public supplierBubbleChartLabels: string[] = [];
|
||||
public supplierBubbleChartOptions: any = {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'month'
|
||||
},
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'Date'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'Number of purchases'
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltip, data) => {
|
||||
return this.bubbleTooltipCallback(tooltip, data);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
private bubbleTooltipCallback(tooltipItem: any, data: any) {
|
||||
let dataset = data.datasets[tooltipItem.datasetIndex];
|
||||
let value = dataset.data[tooltipItem.index];
|
||||
return `${value.supplier}: ${this.currencyPipe.transform(value.value, 'GBP', 'symbol', '1.2-2')} over ${value.count} purchases`;
|
||||
}
|
||||
|
||||
private tableSummary() {
|
||||
this.api.loadMiscUrl('organisation/external/lcc_tables', {
|
||||
from: this.filterFrom,
|
||||
to: this.filterTo,
|
||||
}).subscribe(
|
||||
result => {
|
||||
this.wardList = result.wards;
|
||||
this.metaTypeList = Object.keys(result.types).map(key => result.types[key]);
|
||||
if (this.wardList) {
|
||||
this.wardListAvailable = true;
|
||||
}
|
||||
if (this.metaTypeList) {
|
||||
this.metaTypeListAvailable = true;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log('Retrieval Error');
|
||||
console.log(error._body);
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private loadYearSpend() {
|
||||
this.api.loadMiscUrl('organisation/external/year_spend', {
|
||||
from: this.filterFrom,
|
||||
to: this.filterTo,
|
||||
}).subscribe(
|
||||
result => {
|
||||
let value_data = [];
|
||||
let count_data = [];
|
||||
|
||||
result.data.map(item => {
|
||||
value_data.push({
|
||||
t: item.date,
|
||||
y: item.value,
|
||||
});
|
||||
|
||||
count_data.push({
|
||||
t: item.date,
|
||||
y: item.count,
|
||||
});
|
||||
});
|
||||
|
||||
this.yearSpendChartData[0].data = value_data;
|
||||
this.yearSpendChartData[1].data = count_data;
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
public yearSpendChartData: any[] = [
|
||||
{
|
||||
data: [],
|
||||
label: ["Value £"],
|
||||
fill: false,
|
||||
borderColor: 'red',
|
||||
hoverBackgroundColor: '#ffa1b5',
|
||||
hoverBorderColor: 'red',
|
||||
yAxisID: 'y-value',
|
||||
},
|
||||
{
|
||||
data: [],
|
||||
label: ["Count"],
|
||||
fill: false,
|
||||
borderColor: 'blue',
|
||||
hoverBackgroundColor: '#52afed',
|
||||
hoverBorderColor: 'blue',
|
||||
yAxisID: 'y-count',
|
||||
},
|
||||
];
|
||||
public yearSpendChartOptions: any = {
|
||||
elements: {line: {tension: 0}},
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'month'
|
||||
},
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'Date'
|
||||
}
|
||||
}],
|
||||
yAxes: [
|
||||
{id: 'y-value', position: 'left', beginAtZero: true, type: 'linear'},
|
||||
{id: 'y-count', position: 'right', beginAtZero: true, type: 'linear'},
|
||||
]
|
||||
},
|
||||
};
|
||||
public yearSpendChartLabels: string[] = [];
|
||||
public yearSpendChartType: ChartType = 'line';
|
||||
|
||||
randomData() {
|
||||
return Math.random();
|
||||
}
|
||||
|
||||
lineChartUpdate() {
|
||||
this.loadYearSpend();
|
||||
|
||||
}
|
||||
|
||||
@ViewChild('supplierChart', { read: BaseChartDirective }) supplierChart: BaseChartDirective;
|
||||
|
||||
private loadSupplierHistory() {
|
||||
this.api.loadMiscUrl('organisation/external/supplier_history').subscribe(
|
||||
result => {
|
||||
this._supplierHistoryData = result.data;
|
||||
this._supplierHistoryPage = 1;
|
||||
this._supplierHistoryPages = Math.ceil(this._supplierHistoryData.length / this._supplierHistoryPerPage);
|
||||
this.updateSupplierHistoryData();
|
||||
|
||||
this.isSupplierChartLoaded = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private updateSupplierHistoryData() {
|
||||
|
||||
const lastResult = this._supplierHistoryPerPage * this._supplierHistoryPage;
|
||||
console.log(this._supplierHistoryPage);
|
||||
const firstResult = lastResult - this._supplierHistoryPerPage;
|
||||
|
||||
const pageData = this._supplierHistoryData.slice(firstResult, lastResult);
|
||||
console.log(pageData);
|
||||
|
||||
let labels = [];
|
||||
let year = [];
|
||||
let half = [];
|
||||
let quarter = [];
|
||||
pageData.map(item => {
|
||||
labels.push(item.name);
|
||||
year.push(item.year_total);
|
||||
half.push(item.half_total);
|
||||
quarter.push(item.quarter_total);
|
||||
});
|
||||
|
||||
this.supplierMonthChartData[0].data = quarter;
|
||||
this.supplierMonthChartData[1].data = half;
|
||||
this.supplierMonthChartData[2].data = year;
|
||||
this.supplierMonthChartLabels = labels;
|
||||
}
|
||||
|
||||
public nextSupplierHistoryPage() {
|
||||
if (this._supplierHistoryPage < this._supplierHistoryPages) {
|
||||
this._supplierHistoryPage++;
|
||||
}
|
||||
this.updateSupplierHistoryData();
|
||||
}
|
||||
|
||||
public previousSupplierHistoryPage() {
|
||||
if (this._supplierHistoryPage > 1) {
|
||||
this._supplierHistoryPage--;
|
||||
}
|
||||
this.updateSupplierHistoryData();
|
||||
}
|
||||
|
||||
private _supplierHistoryData: any[];
|
||||
private _supplierHistoryPerPage: number = 15;
|
||||
public _supplierHistoryPage: number = 1;
|
||||
public _supplierHistoryPages: number = 1;
|
||||
public supplierMonthChartData: any[] = [
|
||||
{
|
||||
data: [],
|
||||
label: ["3 Month"],
|
||||
fill: false,
|
||||
borderColor: 'red',
|
||||
hoverBorderColor: 'red',
|
||||
hoverBackgroundColor: 'red',
|
||||
},
|
||||
{
|
||||
data: [],
|
||||
label: ["6 Month"],
|
||||
fill: false,
|
||||
borderColor: 'blue',
|
||||
hoverBorderColor: 'blue',
|
||||
hoverBackgroundColor: 'blue',
|
||||
},
|
||||
{
|
||||
data: [],
|
||||
label: ["12 Month"],
|
||||
fill: false,
|
||||
borderColor: 'orange',
|
||||
hoverBorderColor: 'orange',
|
||||
hoverBackgroundColor: 'orange',
|
||||
},
|
||||
];
|
||||
public supplierMonthChartOptions: any = {
|
||||
//maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'Spend amount £'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'Supplier Names'
|
||||
}
|
||||
}]
|
||||
},
|
||||
};
|
||||
public supplierMonthChartLabels: string[] = [];
|
||||
public supplierMonthChartType: ChartType = 'horizontalBar';
|
||||
}
|
49
src/app/dashboard/payroll-log.component.html
Normal file
49
src/app/dashboard/payroll-log.component.html
Normal file
|
@ -0,0 +1,49 @@
|
|||
<div class="animated fadeIn">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Log of Payroll submissions</strong>
|
||||
<small>Sorted descending from submission date.</small>
|
||||
</div>
|
||||
<div *ngIf="!noPayrollList" class="card-block">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Entry Period</th>
|
||||
<th>Gross Payroll</th>
|
||||
<th>Employees</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr payroll-result *ngFor="let payroll of payrollList | paginate: paginateConfig" [payroll]="payroll"></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<pagination-template #p="paginationApi"
|
||||
[id]="paginateConfig.id"
|
||||
(pageChange)="loadPayrolls($event)">
|
||||
<ul class="pagination">
|
||||
<li class="page-item" [class.disabled]="p.isFirstPage()">
|
||||
<a class="page-link clickable" *ngIf="!p.isFirstPage()" (click)="p.previous()">Prev</a>
|
||||
</li>
|
||||
<li *ngFor="let page of p.pages" class="page-item" [class.active]="p.getCurrent() === page.value">
|
||||
<a class="page-link clickable" (click)="p.setCurrent(page.value)" *ngIf="p.getCurrent() !== page.value">
|
||||
<span>{{ page.label }}</span>
|
||||
</a>
|
||||
<div class="page-link" *ngIf="p.getCurrent() === page.value">
|
||||
<span>{{ page.label }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="page-item" [class.disabled]="p.isLastPage()">
|
||||
<a class="page-link clickable" *ngIf="!p.isLastPage()" (click)="p.next()">Next</a>
|
||||
</li>
|
||||
</ul>
|
||||
</pagination-template>
|
||||
</div>
|
||||
<div *ngIf="noPayrollList" class="card-block">
|
||||
No Payroll data available.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
75
src/app/dashboard/payroll-log.component.ts
Normal file
75
src/app/dashboard/payroll-log.component.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
// import { PaginatePipe } from 'ngx-pagination';
|
||||
import {PaginationInstance} from 'ngx-pagination';
|
||||
// import { PaginationControlsComponent } from 'ngx-pagination';
|
||||
// import { PaginationControlsDirective } from 'ngx-pagination';
|
||||
// import { TransactionResultComponent } from '../shared/transaction-result.component';
|
||||
import * as moment from 'moment';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'payroll-log.component.html',
|
||||
})
|
||||
export class PayrollLogComponent implements OnInit {
|
||||
|
||||
payrollList;
|
||||
noPayrollList = true;
|
||||
myDate: any;
|
||||
minDate: any;
|
||||
|
||||
public paginateConfig: PaginationInstance = {
|
||||
id: 'transpaginate',
|
||||
itemsPerPage: 10,
|
||||
currentPage: 1,
|
||||
totalItems: 0
|
||||
};
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
) {
|
||||
this.myDate = moment().format('YYYY-MM-DD[T]HH:mm');
|
||||
// this.myDate = new Date().toISOString().slice(0, 16);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getMinDate();
|
||||
this.loadPayrolls(1);
|
||||
}
|
||||
|
||||
getMinDate(){
|
||||
// gets the April 1st date of the current year
|
||||
let aprilDate = moment().month(3).date(1);
|
||||
let now = moment();
|
||||
// Checks if current time is before April 1st, if so returns true
|
||||
let beforeApril = now.isBefore(aprilDate);
|
||||
if ( beforeApril == true ) {
|
||||
this.minDate = aprilDate.subtract(2, 'years').format('YYYY-MM-DD');
|
||||
} else {
|
||||
this.minDate = aprilDate.subtract(1, 'years').format('YYYY-MM-DD');
|
||||
}
|
||||
}
|
||||
|
||||
loadPayrolls(logPage: number) {
|
||||
console.log(logPage);
|
||||
this.api.payrollList(logPage).subscribe(
|
||||
result => {
|
||||
if(result.payrolls.length > 0) {
|
||||
this.payrollList = result.payrolls;
|
||||
//TODO Rename in server
|
||||
this.paginateConfig.totalItems = result.page_no;
|
||||
this.paginateConfig.currentPage = logPage;
|
||||
this.noPayrollList = false;
|
||||
} else {
|
||||
// handle the case when the payrollList is empty
|
||||
this.payrollList = null;
|
||||
this.noPayrollList = true;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
79
src/app/dashboard/suppliers.component.html
Normal file
79
src/app/dashboard/suppliers.component.html
Normal file
|
@ -0,0 +1,79 @@
|
|||
<div class="animated fadeIn">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>Search Suppliers</h4>
|
||||
</div>
|
||||
<div *ngIf="supplierListAvailable" class="card-block">
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="text" name="search" [(ngModel)]="searchText" autocomplete="off"
|
||||
placeholder="Search by Name or Postcode" (keydown.enter)="searchSuppliers()">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary" (click)="searchSuppliers()">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="animated fadeIn">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>List of Suppliers</h4>
|
||||
<div class="small">Click on Column Headers to change Sort Order</div>
|
||||
</div>
|
||||
<div *ngIf="supplierListAvailable" class="card-block">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th (click)="sortName()">Name <span class="fa-stack">
|
||||
<i *ngIf="sortBy !== 'name' || sortDir == 'asc'" class="fa fa-sort-up fa-stack-1x"></i>
|
||||
<i *ngIf="sortBy !== 'name' || sortDir == 'desc'" class="fa fa-sort-down fa-stack-1x"></i>
|
||||
</span></th>
|
||||
<th (click)="sortPostcode()">Postcode <span class="fa-stack">
|
||||
<i *ngIf="sortBy !== 'postcode' || sortDir == 'asc'" class="fa fa-sort-up fa-stack-1x"></i>
|
||||
<i *ngIf="sortBy !== 'postcode' || sortDir == 'desc'" class="fa fa-sort-down fa-stack-1x"></i>
|
||||
</span></th>
|
||||
<th (click)="sortSpend()">Spend <span class="fa-stack">
|
||||
<i *ngIf="sortBy !== 'spend' || sortDir == 'asc'" class="fa fa-sort-up fa-stack-1x"></i>
|
||||
<i *ngIf="sortBy !== 'spend' || sortDir == 'desc'" class="fa fa-sort-down fa-stack-1x"></i>
|
||||
</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr supplier-result *ngFor="let supplier of supplierList | paginate: paginateConfig"
|
||||
[supplier]="supplier"></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<pagination-template #p="paginationApi"
|
||||
[id]="paginateConfig.id"
|
||||
(pageChange)="loadSuppliers($event)">
|
||||
<ul class="pagination">
|
||||
<li class="page-item" [class.disabled]="p.isFirstPage()">
|
||||
<a class="page-link clickable" *ngIf="!p.isFirstPage()" (click)="p.previous()">Prev</a>
|
||||
</li>
|
||||
<li *ngFor="let page of p.pages" class="page-item" [class.active]="p.getCurrent() === page.value">
|
||||
<a class="page-link clickable" (click)="p.setCurrent(page.value)" *ngIf="p.getCurrent() !== page.value">
|
||||
<span>{{ page.label }}</span>
|
||||
</a>
|
||||
<div class="page-link" *ngIf="p.getCurrent() === page.value">
|
||||
<span>{{ page.label }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="page-item" [class.disabled]="p.isLastPage()">
|
||||
<a class="page-link clickable" *ngIf="!p.isLastPage()" (click)="p.next()">Next</a>
|
||||
</li>
|
||||
</ul>
|
||||
</pagination-template>
|
||||
</div>
|
||||
<div *ngIf="!supplierListAvailable" class="card-block">
|
||||
No Suppliers available.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
74
src/app/dashboard/suppliers.component.ts
Normal file
74
src/app/dashboard/suppliers.component.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import { Component, OnInit, AfterViewInit, Input, Output, EventEmitter, ViewChild, TemplateRef } from '@angular/core';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import { AgmCoreModule } from '@agm/core';
|
||||
import { BsModalService, ModalDirective } from 'ngx-bootstrap/modal';
|
||||
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
|
||||
import { PaginationInstance } from 'ngx-pagination';
|
||||
import { FilterPipeModule } from 'ngx-filter-pipe';
|
||||
@Component({
|
||||
templateUrl: 'suppliers.component.html',
|
||||
})
|
||||
export class SuppliersComponent implements OnInit, AfterViewInit {
|
||||
@Output() public onClick = new EventEmitter();
|
||||
@Input() public categories: any;
|
||||
public perPage: number = 10;
|
||||
|
||||
searchText: string;
|
||||
|
||||
supplierList: any;
|
||||
supplierListAvailable = false;
|
||||
sortBy = 'name';
|
||||
sortDir = 'asc';
|
||||
|
||||
public paginateConfig: PaginationInstance = {
|
||||
id: 'transpaginate',
|
||||
itemsPerPage: this.perPage,
|
||||
currentPage: 1,
|
||||
totalItems: 0
|
||||
};
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadSuppliers(1);
|
||||
}
|
||||
|
||||
loadSuppliers(logPage: number) {
|
||||
this.api.externalSuppliers(logPage, this.sortBy, this.sortDir, this.perPage, this.searchText).subscribe(
|
||||
result => {
|
||||
this.supplierList = result.suppliers;
|
||||
if (this.supplierList) {
|
||||
this.supplierListAvailable = true;
|
||||
}
|
||||
this.paginateConfig.totalItems = result.page_no;
|
||||
this.paginateConfig.currentPage = logPage;
|
||||
},
|
||||
error => {
|
||||
console.log('Retrieval Error');
|
||||
console.log( error._body );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
sortName() { this.sortByColumn('name'); }
|
||||
sortPostcode() { this.sortByColumn('postcode'); }
|
||||
sortSpend() { this.sortByColumn('spend'); }
|
||||
|
||||
sortByColumn(name) {
|
||||
this.sortBy = name;
|
||||
this.sortDir = this.sortDir === 'asc' ? 'desc' : 'asc';
|
||||
this.loadSuppliers(1);
|
||||
}
|
||||
|
||||
searchSuppliers() {
|
||||
// Go back to page 1 when searching
|
||||
this.loadSuppliers(1);
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
}
|
||||
|
||||
}
|
104
src/app/dashboard/trail-map.component.html
Normal file
104
src/app/dashboard/trail-map.component.html
Normal file
|
@ -0,0 +1,104 @@
|
|||
<div class="animated fadeIn">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Story Trail</strong>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-12 col-sm-6 mb-3">
|
||||
<small>Select a Story Trail to see all the businesses in that story on the map.<br>
|
||||
Click an icon on the map to get more information.</small>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 order-12">
|
||||
<button type="button" class="btn btn-outline-info btn-lg float-right" (click)="openModalAssoc(templateAssoc)">Select Story Trail</button>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #templateAssoc>
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title pull-left">Select View</h4>
|
||||
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef2.hide()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6 text-center">
|
||||
<img src="assets/img/association/lis-logo.png" class="w-50" alt="lis logo"><br>
|
||||
<button type="button" class="btn btn-success mt-3" (click)="modalRef2.hide(); assocMap = 'lis'">Lancaster Independent Story</button>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 text-center">
|
||||
<img src="assets/img/association/esta-logo.png" class="w-50" alt="lis logo"><br>
|
||||
<button type="button" class="btn btn-success mt-3" (click)="modalRef2.hide(); assocMap = 'esta'">ESTA Association</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" (click)="modalRef2.hide()">Close</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="modal fade" bsModal #statusModal="bs-modal" [config]="{backdrop: false, animated: false}"
|
||||
tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm h-100 d-flex flex-column justify-content-center my-0">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title pull-left">Status</h4>
|
||||
<button type="button" class="close pull-right" aria-label="Close" (click)="statusModal.hide()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div [ngSwitch]="dataReceived">
|
||||
<div *ngSwitchCase="'no'">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
No map data received, check your connection.
|
||||
</div>
|
||||
</div>
|
||||
<div *ngSwitchCase="'loading'">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
Map loading, please wait.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- this creates a google map on the page with the given lat/lng from -->
|
||||
<!-- the component as the initial center of the map: -->
|
||||
<agm-map
|
||||
(mapReady)="onMapReady($event)"
|
||||
[latitude]="lat"
|
||||
[longitude]="lng"
|
||||
[zoom]="zoom"
|
||||
(idle)="viewBoundsChanged()">
|
||||
<agm-marker-cluster maxZoom="13">
|
||||
<agm-marker
|
||||
*ngFor="let m of markers"
|
||||
[iconUrl]="'/assets/img/association/' + assocMap + '-map-pin.png'"
|
||||
[latitude]="m.latitude"
|
||||
[longitude]="m.longitude"
|
||||
[openInfoWindow]="false"
|
||||
(markerClick)="onMarkerClick(m, template)">
|
||||
</agm-marker>
|
||||
</agm-marker-cluster>
|
||||
</agm-map>
|
||||
<ng-template #template>
|
||||
<div class="modal-header d-flex justify-content-between">
|
||||
<img src="{{assocLogo}}" class="w-15" alt="lis logo"><h4 class="modal-title">{{clickedMarker.name}}</h4>
|
||||
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-right">
|
||||
<h5>Located at:</h5>
|
||||
<h6>{{clickedMarker.street_name}}</h6>
|
||||
<h6>{{clickedMarker.town}}</h6>
|
||||
<h6>{{clickedMarker.postcode}}</h6>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.row-->
|
||||
</div>
|
93
src/app/dashboard/trail-map.component.ts
Normal file
93
src/app/dashboard/trail-map.component.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
import { Component, OnInit, AfterViewInit, Input, Output, EventEmitter, ViewChild, TemplateRef } from '@angular/core';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import { AgmCoreModule } from '@agm/core';
|
||||
import { BsModalService, ModalDirective } from 'ngx-bootstrap/modal';
|
||||
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'trail-map.component.html',
|
||||
})
|
||||
export class TrailMapComponent implements OnInit, AfterViewInit {
|
||||
@ViewChild('statusModal', { static: true }) myStatusModal: ModalDirective;
|
||||
lat: number = 54.0466;
|
||||
lng: number = -2.8007;
|
||||
zoom: number = 12;
|
||||
public modalRef: BsModalRef;
|
||||
public modalRef2: BsModalRef;
|
||||
clickedMarker: any;
|
||||
assocMap = 'lis';
|
||||
assocLogo: string;
|
||||
|
||||
dataReceived: string = 'loading';
|
||||
|
||||
markers: Array<{latitude: number, longitude: number, name: string}>;
|
||||
|
||||
map: any;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private modalService: BsModalService,
|
||||
) {
|
||||
this.assocLogo = 'assets/img/association/' + this.assocMap + '-logo.png';
|
||||
}
|
||||
|
||||
ngOnInit(): void { }
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.myStatusModal.show();
|
||||
}
|
||||
|
||||
public onMapReady(map: any) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
openModal(template: TemplateRef<any>) {
|
||||
this.modalRef = this.modalService.show(template);
|
||||
}
|
||||
|
||||
openModalAssoc(templateAssoc: TemplateRef<any>) {
|
||||
this.modalRef2 = this.modalService.show(templateAssoc);
|
||||
}
|
||||
|
||||
public onMarkerClick(clickedMarker, template: TemplateRef<any>) {
|
||||
this.clickedMarker = clickedMarker;
|
||||
this.assocLogo = 'assets/img/association/' + this.assocMap + '-logo.png';
|
||||
this.openModal(template);
|
||||
}
|
||||
|
||||
public viewBoundsChanged() {
|
||||
console.log("finding bounds");
|
||||
const resp = this.map.getBounds();
|
||||
console.log("found bounds");
|
||||
console.log(resp.getNorthEast().lat());
|
||||
console.log(resp.getNorthEast().lng());
|
||||
console.log(resp.getSouthWest().lat());
|
||||
console.log(resp.getSouthWest().lng());
|
||||
const mapData = {
|
||||
north_east: {
|
||||
latitude: resp.getNorthEast().lat(),
|
||||
longitude: resp.getNorthEast().lng()
|
||||
},
|
||||
south_west: {
|
||||
latitude: resp.getSouthWest().lat(),
|
||||
longitude: resp.getSouthWest().lng()
|
||||
},
|
||||
association: this.assocMap,
|
||||
}
|
||||
this.api.getAssocData(mapData).subscribe(
|
||||
result => {
|
||||
this.myStatusModal.hide();
|
||||
this.markers = result.locations;
|
||||
},
|
||||
error => {
|
||||
this.dataReceived = 'no';
|
||||
console.log('Retrieval Error');
|
||||
console.log( error._body );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,22 +1,124 @@
|
|||
<div class="animated fadeIn">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Recurring Transactions</strong>
|
||||
<small>Select a Recurring Transaction below to edit it.</small>
|
||||
</div>
|
||||
<div *ngIf="!noRecurringList" class="card-block">
|
||||
<recur-table [recurList]="recurringTransactionList" [categories]="categoryList" (onClick)="recurringTransactionDetails($event, template)"></recur-table>
|
||||
<ng-template #template>
|
||||
<div class="modal-header d-flex justify-content-between">
|
||||
<h4 class="modal-title">Edit Recurring Transaction</h4>
|
||||
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input"><strong>Time of Transaction</strong></label>
|
||||
<div class="col-md-9">
|
||||
<input type="datetime-local" class="form-control" [(ngModel)]="updatedTime">
|
||||
<span class="help-block">Enter the date and time the transaction occurred.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input"><strong>Amount</strong></label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-gbp"></i></span>
|
||||
<input type="number" min="0.00" step="0.01" class="form-control" placeholder="0.00" [(ngModel)]="clickedRecur.value">
|
||||
</div>
|
||||
<span class="help-block">Enter the amount spent, such as 5.35 for £5.35.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input">Essential Purchase</label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
<input type="checkbox" class="mr-auto" [(ngModel)]="clickedRecur.essential">
|
||||
</div>
|
||||
<span class="help-block">Tick if the purchase is deemed an essential purchase for budgeting purposes.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input"><strong>Recurring Period</strong></label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
<select type="text" class="form-control" [(ngModel)]="clickedRecur.recurring_period">
|
||||
<option value="daily">Daily</option>
|
||||
<option value="weekly">Weekly</option>
|
||||
<option value="fortnightly">Fortnightly</option>
|
||||
<option value="monthly">Monthly</option>
|
||||
<option value="quarterly">Quarterly</option>
|
||||
</select>
|
||||
</div>
|
||||
<span class="help-block">Please give the period of time the purchase will recur from "Time of Transaction".</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 form-control-label" for="text-input">Budget Type</label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
<select type="text" class="form-control" [(ngModel)]="clickedRecur.category">
|
||||
<option value="0">Uncategorised</option>
|
||||
<option *ngFor="let category of categoryIdList" [ngValue]="category">
|
||||
{{ categoryList[category] }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<span class="help-block"><strong>Optional:</strong> Choose the Budget Type for the majority of the purchase.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="input-group">
|
||||
<span class="col-12"><strong>WARNING: Clicking "Delete" will completely remove the Recurring Transaction.</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div [ngSwitch]="transactionFormStatus">
|
||||
<div *ngSwitchCase="'success'" class="alert alert-success" role="alert">
|
||||
{{transactionFormStatusSuccess}}
|
||||
</div>
|
||||
<div *ngSwitchCase="'send_failed'" class="alert alert-danger" role="alert">
|
||||
{{transactionFormStatusError}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer d-flex justify-content-between">
|
||||
<button type="submit" (click)="deleteRecurringTransaction()" class="btn btn-sm btn-danger"><i class="fa fa-times"></i> Delete</button>
|
||||
<button type="submit" (click)="editRecurringTransaction()" class="btn btn-sm btn-primary"><i class="fa fa-dot-circle-o"></i> Save</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div *ngIf="noRecurringList" class="card-block">
|
||||
No Recurring Transactions.
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Log of Outgoing Transactions</strong>
|
||||
<small>This lists all purchases that have been submitted.</small>
|
||||
<button *ngIf="accountType == 'organisation'" class="btn pull-right btn-sm" (click)="toggleShowMeta()">
|
||||
<span *ngIf="!showMeta">Show</span><span *ngIf="showMeta">Hide</span> Details
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="!noTransactionList" class="card-block">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Seller</th>
|
||||
<th>Value</th>
|
||||
<th *ngIf="!showMeta">Value</th>
|
||||
<th *ngIf="showMeta">Net Value</th>
|
||||
<th *ngIf="showMeta">Sales Tax Value</th>
|
||||
<th *ngIf="showMeta">Gross Value</th>
|
||||
<th>Purchase Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr transaction-result *ngFor="let transaction of transactionList | paginate: paginateConfig" [transaction]="transaction"></tr>
|
||||
<tr transaction-result *ngFor="let transaction of transactionList | paginate: paginateConfig" [transaction]="transaction" [showMeta]="showMeta"></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<pagination-template #p="paginationApi"
|
||||
|
|
|
@ -1,64 +1,73 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Http, Response } from '@angular/http';
|
||||
import { Component, OnInit, EventEmitter, TemplateRef } from '@angular/core';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
// import { PaginatePipe } from 'ngx-pagination';
|
||||
import {PaginationInstance} from 'ngx-pagination';
|
||||
// import { PaginationControlsComponent } from 'ngx-pagination';
|
||||
// import { PaginationControlsDirective } from 'ngx-pagination';
|
||||
// import { TransactionResultComponent } from '../shared/transaction-result.component';
|
||||
import { BsModalService, ModalDirective } from 'ngx-bootstrap/modal';
|
||||
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
|
||||
import { PaginationInstance } from 'ngx-pagination';
|
||||
import * as moment from 'moment';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'transaction-log.component.html',
|
||||
})
|
||||
export class TransactionLogComponent {
|
||||
export class TransactionLogComponent implements OnInit {
|
||||
|
||||
transactionList;
|
||||
transactionList: any;
|
||||
recurringTransactionList: any;
|
||||
noTransactionList = true;
|
||||
noRecurringList = true;
|
||||
myDate: any;
|
||||
minDate: any;
|
||||
public p: any;
|
||||
public modalRef: BsModalRef;
|
||||
clickedRecur: any;
|
||||
public updatedDate: string;
|
||||
public startTime: string;
|
||||
categoryIdList: any;
|
||||
categoryList: any;
|
||||
categoryNameList: string[] = [];
|
||||
transactionFormStatus: string;
|
||||
transactionFormStatusSuccess: string;
|
||||
transactionFormStatusError = 'Error received, please try again.';
|
||||
updatedTime: string;
|
||||
accountType: any;
|
||||
showMeta = false;
|
||||
|
||||
public paginateConfig: PaginationInstance = {
|
||||
id: 'transpaginate',
|
||||
itemsPerPage: 10,
|
||||
currentPage: 1,
|
||||
totalItems: 0
|
||||
};
|
||||
id: 'transpaginate',
|
||||
itemsPerPage: 10,
|
||||
currentPage: 1,
|
||||
totalItems: 0
|
||||
};
|
||||
|
||||
constructor(
|
||||
private http: Http,
|
||||
private api: ApiService,
|
||||
) {
|
||||
private api: ApiService,
|
||||
private modalService: BsModalService,
|
||||
) {
|
||||
this.myDate = moment().format('YYYY-MM-DD[T]HH:mm');
|
||||
this.api.categoryList().subscribe(
|
||||
result => {
|
||||
this.categoryList = result.categories;
|
||||
this.categoryIdList = Object.keys(this.categoryList);
|
||||
},
|
||||
error => {
|
||||
console.log('Retrieval Error');
|
||||
console.log( error._body );
|
||||
}
|
||||
);
|
||||
// this.myDate = new Date().toISOString().slice(0, 16);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getMinDate();
|
||||
this.loadTransactions(1);
|
||||
}
|
||||
|
||||
getMinDate(){
|
||||
// gets the April 1st date of the current year
|
||||
let aprilDate = moment().month(3).date(1);
|
||||
let now = moment();
|
||||
// Checks if current time is before April 1st, if so returns true
|
||||
let beforeApril = now.isBefore(aprilDate);
|
||||
if ( beforeApril == true ) {
|
||||
this.minDate = aprilDate.subtract(2, 'years').format('YYYY-MM-DD');
|
||||
} else {
|
||||
this.minDate = aprilDate.subtract(1, 'years').format('YYYY-MM-DD');
|
||||
}
|
||||
this.accountType = localStorage.getItem('usertype');
|
||||
}
|
||||
|
||||
loadTransactions(logPage: number) {
|
||||
console.log(logPage);
|
||||
this.api.transList(logPage).subscribe(
|
||||
result => {
|
||||
if(result.transactions.length > 0) {
|
||||
if (result.transactions.length > 0) {
|
||||
this.transactionList = result.transactions;
|
||||
//TODO Rename in server
|
||||
// TODO Rename in server
|
||||
this.paginateConfig.totalItems = result.page_no;
|
||||
this.paginateConfig.currentPage = logPage;
|
||||
this.noTransactionList = false;
|
||||
|
@ -67,6 +76,13 @@ export class TransactionLogComponent {
|
|||
this.transactionList = null;
|
||||
this.noTransactionList = true;
|
||||
}
|
||||
if (result.recurring_transactions) {
|
||||
this.recurringTransactionList = result.recurring_transactions;
|
||||
this.noRecurringList = false;
|
||||
} else {
|
||||
this.recurringTransactionList = null;
|
||||
this.noRecurringList = true;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
|
@ -74,4 +90,82 @@ export class TransactionLogComponent {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
recurringTransactionDetails(clicked, template: TemplateRef<any>) {
|
||||
this.clickedRecur = clicked;
|
||||
this.updatedTime = moment(this.clickedRecur.display_time, 'llll').format('YYYY-MM-DD[T]HH:mm');
|
||||
this.openModal(template);
|
||||
}
|
||||
|
||||
openModal(template: TemplateRef<any>) {
|
||||
this.modalRef = this.modalService.show(template);
|
||||
}
|
||||
|
||||
editRecurringTransaction() {
|
||||
let updatedTimeSubmit = moment(this.updatedTime, 'YYYY-MM-DD[T]HH:mm').local().format('YYYY-MM-DD[T]HH:mm:ss.SSSZ');
|
||||
this.clickedRecur.display_time = moment(this.updatedTime).format('llll');
|
||||
let myParams = {
|
||||
category: (this.clickedRecur.category == 0 ? undefined : this.clickedRecur.category),
|
||||
essential: this.clickedRecur.essential,
|
||||
id: this.clickedRecur.id,
|
||||
apply_time: updatedTimeSubmit,
|
||||
recurring_period: this.clickedRecur.recurring_period,
|
||||
seller: this.clickedRecur.seller,
|
||||
value: this.clickedRecur.value,
|
||||
};
|
||||
this.api
|
||||
.recurUpdate(myParams)
|
||||
.subscribe(
|
||||
result => {
|
||||
if ( result.success === true ) {
|
||||
this.transactionFormStatus = 'success';
|
||||
this.transactionFormStatusSuccess = 'Edit Succeeded.';
|
||||
} else {
|
||||
this.transactionFormStatusError = JSON.stringify(result.status) + 'Error, ' + JSON.stringify(result.message);
|
||||
this.transactionFormStatus = 'send_failed';
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
try {
|
||||
this.transactionFormStatusError = '"' + error.error.error + '" Error, ' + error.error.message;
|
||||
} catch (e) {
|
||||
this.transactionFormStatusError = 'There was a server error, please try again later.';
|
||||
}
|
||||
this.transactionFormStatus = 'send_failed';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
deleteRecurringTransaction() {
|
||||
let myParams = {
|
||||
id: this.clickedRecur.id,
|
||||
};
|
||||
this.api
|
||||
.recurDelete(myParams)
|
||||
.subscribe(
|
||||
result => {
|
||||
if ( result.success === true ) {
|
||||
this.transactionFormStatus = 'success';
|
||||
this.transactionFormStatusSuccess = 'Delete Succeeded.';
|
||||
} else {
|
||||
this.transactionFormStatusError = JSON.stringify(result.status) + 'Error, ' + JSON.stringify(result.message);
|
||||
this.transactionFormStatus = 'send_failed';
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
try {
|
||||
this.transactionFormStatusError = '"' + error.error.error + '" Error, ' + error.error.message;
|
||||
} catch (e) {
|
||||
this.transactionFormStatusError = 'There was a server error, please try again later.';
|
||||
}
|
||||
this.transactionFormStatus = 'send_failed';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
toggleShowMeta() {
|
||||
this.showMeta = !this.showMeta;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,26 +25,99 @@
|
|||
<div class="sidebar">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav">
|
||||
<li class="nav-item">
|
||||
<div class="row no-gutters mt-1 d-lg-none small text-center text-muted">
|
||||
<div class="col-12">Click ☰ to Close Menu</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/dashboard']">
|
||||
<i class="icon-speedometer"></i> Dashboard
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-speedometer"></i></div>
|
||||
<div class="col-10">Dashboard</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li *ngIf="accountType == 'organisation'" class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/more-graphs-and-tables']">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-map"></i></div>
|
||||
<div class="col-10">Infographics</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/add-data']">
|
||||
<i class="icon-basket"></i> Add Transaction
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-basket"></i></div>
|
||||
<div class="col-10">Add Transaction</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/feedback']">
|
||||
<i class="icon-envelope-letter"></i> Enter Feedback
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-envelope-letter"></i></div>
|
||||
<div class="col-10">Enter Feedback</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/map']">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-map"></i></div>
|
||||
<div class="col-10">Purchase Map</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/story-trail']">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-map"></i></div>
|
||||
<div class="col-10">Story Trail</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li *ngIf="accountType == 'customer'" class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/leaderboard']">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-basket"></i></div>
|
||||
<div class="col-10">Leaderboard</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/transaction-log']">
|
||||
<i class="icon-basket"></i> Transaction Log
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-basket"></i></div>
|
||||
<div class="col-10">Transaction Log</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li *ngIf="accountType == 'customer'" class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/category-month']">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-basket"></i></div>
|
||||
<div class="col-10">Budget</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li *ngIf="accountType == 'organisation'" class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/suppliers']">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-speedometer"></i></div>
|
||||
<div class="col-10">Suppliers</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li *ngIf="accountType == 'organisation'" class="nav-item">
|
||||
<a class="nav-link" routerLinkActive="active" [routerLink]="['/payroll-log']">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-2"><i class="icon-basket"></i></div>
|
||||
<div class="col-10">Payroll Log</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
@ -7,16 +7,16 @@ import { Router } from '@angular/router';
|
|||
templateUrl: './full-layout.component.html',
|
||||
})
|
||||
export class FullLayoutComponent implements OnInit {
|
||||
displayName: any;
|
||||
public displayName: string;
|
||||
public accountType: any;
|
||||
public disabled = false;
|
||||
public status: {isopen: boolean} = {isopen: false};
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private router: Router,
|
||||
) {}
|
||||
|
||||
public disabled = false;
|
||||
public status: {isopen: boolean} = {isopen: false};
|
||||
|
||||
public toggled(open: boolean): void {
|
||||
console.log('Dropdown is now: ', open);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ export class FullLayoutComponent implements OnInit {
|
|||
// getDisplayName function from api didnt work
|
||||
ngOnInit(): void {
|
||||
this.displayName = localStorage.getItem('displayname') || 'User';
|
||||
this.accountType = localStorage.getItem('usertype');
|
||||
}
|
||||
|
||||
userLogout() {
|
||||
|
@ -38,7 +39,6 @@ export class FullLayoutComponent implements OnInit {
|
|||
.logout()
|
||||
.subscribe(
|
||||
result => {
|
||||
console.log('Logged out!');
|
||||
localStorage.clear();
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
<h4 class="pt-3">Oops! You're lost.</h4>
|
||||
<p class="text-muted">The page you are looking for was not found.</p>
|
||||
</div>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary btn-lg" type="button"(click)="goBack()">Go Back</button>
|
||||
</span>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary btn-lg" type="button"(click)="goBack()">Go Back</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,11 +7,10 @@ import { Location } from '@angular/common';
|
|||
export class P404Component {
|
||||
|
||||
constructor(
|
||||
private location: Location
|
||||
) {
|
||||
}
|
||||
private location: Location,
|
||||
) { }
|
||||
|
||||
goBack(): void {
|
||||
this.location.back();
|
||||
}
|
||||
goBack(): void {
|
||||
this.location.back();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
<p class="text-muted">The page you are looking for is temporarily unavailable.</p>
|
||||
</div>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary btn-lg" type="button"(click)="goBack()">Go Back</button>
|
||||
</span>
|
||||
<button class="btn btn-primary btn-lg" type="button"(click)="goBack()">Go Back</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,11 +7,10 @@ import { Location } from '@angular/common';
|
|||
export class P500Component {
|
||||
|
||||
constructor(
|
||||
private location: Location
|
||||
) {
|
||||
}
|
||||
private location: Location,
|
||||
) { }
|
||||
|
||||
goBack(): void {
|
||||
this.location.back();
|
||||
}
|
||||
goBack(): void {
|
||||
this.location.back();
|
||||
}
|
||||
}
|
13
src/app/panels/bubble-panel.component.html
Normal file
13
src/app/panels/bubble-panel.component.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<div>
|
||||
<div>
|
||||
<div style="display: block">
|
||||
<canvas baseChart
|
||||
[datasets]="bubbleChartData"
|
||||
[options]="bubbleChartOptions"
|
||||
[colors]="bubbleChartColors"
|
||||
[legend]="bubbleChartLegend"
|
||||
[chartType]="bubbleChartType">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
97
src/app/panels/bubble-panel.component.ts
Normal file
97
src/app/panels/bubble-panel.component.ts
Normal file
|
@ -0,0 +1,97 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChartOptions, ChartType, ChartDataSets } from 'chart.js';
|
||||
import { Color } from 'ng2-charts';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bubble-chart',
|
||||
templateUrl: './bubble-panel.component.html',
|
||||
})
|
||||
export class BubbleChartComponent implements OnInit {
|
||||
public bubbleChartOptions: ChartOptions = {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
ticks: {
|
||||
min: 0,
|
||||
max: 30,
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
ticks: {
|
||||
min: 0,
|
||||
max: 30,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
public bubbleChartType: ChartType = 'bubble';
|
||||
public bubbleChartLegend = true;
|
||||
|
||||
public bubbleChartData: ChartDataSets[] = [
|
||||
{
|
||||
data: [
|
||||
{ x: 10, y: 10, r: 10 },
|
||||
{ x: 15, y: 5, r: 15 },
|
||||
{ x: 26, y: 12, r: 23 },
|
||||
{ x: 7, y: 8, r: 8 },
|
||||
],
|
||||
label: 'Series A',
|
||||
backgroundColor: 'green',
|
||||
borderColor: 'blue',
|
||||
hoverBackgroundColor: 'purple',
|
||||
hoverBorderColor: 'red',
|
||||
},
|
||||
];
|
||||
|
||||
public bubbleChartColors: Color[] = [
|
||||
{
|
||||
backgroundColor: [
|
||||
'red',
|
||||
'green',
|
||||
'blue',
|
||||
'purple',
|
||||
'yellow',
|
||||
'brown',
|
||||
'magenta',
|
||||
'cyan',
|
||||
'orange',
|
||||
'pink'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
// events
|
||||
public chartClicked({ event, active }: { event: MouseEvent, active: {}[] }): void {
|
||||
console.log(event, active);
|
||||
}
|
||||
|
||||
public chartHovered({ event, active }: { event: MouseEvent, active: {}[] }): void {
|
||||
console.log(event, active);
|
||||
}
|
||||
|
||||
private rand(max: number) {
|
||||
return Math.trunc(Math.random() * max);
|
||||
}
|
||||
|
||||
private randomPoint(maxCoordinate: number) {
|
||||
const x = this.rand(maxCoordinate);
|
||||
const y = this.rand(maxCoordinate);
|
||||
const r = this.rand(30) + 5;
|
||||
return { x, y, r };
|
||||
}
|
||||
|
||||
public randomize(): void {
|
||||
const numberOfPoints = this.rand(5) + 5;
|
||||
const data = Array.apply(null, { length: numberOfPoints }).map(r => this.randomPoint(30));
|
||||
this.bubbleChartData[0].data = data;
|
||||
}
|
||||
}
|
|
@ -1,18 +1,9 @@
|
|||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
<div class="col-sm-12">
|
||||
<h4 class="card-title mb-0">Customers</h4>
|
||||
</div><!--/.col-->
|
||||
<div class="col-sm-7 hidden-sm-down">
|
||||
<div class="btn-toolbar float-right" role="toolbar" aria-label="Toolbar with button groups">
|
||||
<div class="btn-group mr-3" data-toggle="buttons" aria-label="First group">
|
||||
<label class="btn btn-outline-secondary active">
|
||||
<input type="radio" name="options" id="option2" checked> Week
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--/.col-->
|
||||
</div><!--/.row-->
|
||||
<div class="chart-wrapper" style="height:300px;margin-top:40px;">
|
||||
<canvas baseChart class="chart"
|
||||
|
|
|
@ -82,12 +82,12 @@ export class GraphPanel implements OnInit {
|
|||
pointHoverBackgroundColor: '#fff'
|
||||
},
|
||||
{ // brandSuccess
|
||||
backgroundColor: 'transparent',
|
||||
backgroundColor: this.convertHex(this.brandInfo, 10),
|
||||
borderColor: this.brandSuccess,
|
||||
pointHoverBackgroundColor: '#fff'
|
||||
},
|
||||
{ // brandDanger
|
||||
backgroundColor: 'transparent',
|
||||
backgroundColor: this.convertHex(this.brandDanger, 10),
|
||||
borderColor: this.brandDanger,
|
||||
pointHoverBackgroundColor: '#fff',
|
||||
borderWidth: 1,
|
||||
|
|
19
src/app/panels/org-pie-panel.component.html
Normal file
19
src/app/panels/org-pie-panel.component.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title mb-0">Global Purchases by Type</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-wrapper">
|
||||
<canvas baseChart class="chart"
|
||||
[data]="doughnutChartDataLocal"
|
||||
[labels]="doughnutChartLabelsLocal"
|
||||
[colors]="doughnutChartColors"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="chartType"
|
||||
(chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
86
src/app/panels/org-pie-panel.component.ts
Normal file
86
src/app/panels/org-pie-panel.component.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import { OrgPiesService } from '../providers/org-pies.service';
|
||||
import { DataType } from '../shared/data-types.enum';
|
||||
import { ChartData } from '../_interfaces/chart-data';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'org-pie-panel',
|
||||
templateUrl: 'org-pie-panel.component.html',
|
||||
})
|
||||
|
||||
export class OrgPiePanel implements OnInit {
|
||||
|
||||
public chartType = 'pie';
|
||||
public chartLegend = true;
|
||||
public doughnutChartDataLocal: number[] = [];
|
||||
public doughnutChartColors: any[] = [
|
||||
{
|
||||
backgroundColor:[
|
||||
'#ffa1b5',
|
||||
'#3cde52',
|
||||
'#52afed',
|
||||
'#c133e3',
|
||||
'#f7fa08',
|
||||
'#75152d',
|
||||
'#ee12ee',
|
||||
'#15eaea',
|
||||
'#eaa015',
|
||||
'#ea1515',
|
||||
'#2d4fcc'
|
||||
]
|
||||
},
|
||||
{
|
||||
borderColor: [
|
||||
'red',
|
||||
'green',
|
||||
'blue',
|
||||
'purple',
|
||||
'yellow',
|
||||
'brown',
|
||||
'magenta',
|
||||
'cyan',
|
||||
'orange',
|
||||
'pink'
|
||||
]
|
||||
},
|
||||
{ borderWidth: [100]
|
||||
},
|
||||
];
|
||||
|
||||
public doughnutChartLabelsLocal: string[] = [];
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private pieService: OrgPiesService,
|
||||
) {
|
||||
this.pieService.getOrgPie().subscribe(
|
||||
result => {
|
||||
this.setChartData(result.local_all);
|
||||
},
|
||||
error => {
|
||||
console.log('Retrieval Error');
|
||||
console.log( error._body );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
|
||||
}
|
||||
|
||||
private setChartData(dataLocal: any) {
|
||||
this.doughnutChartDataLocal = Object.keys(dataLocal).map(key => dataLocal[key]);
|
||||
// setTimeout is currently a workaround for ng2-charts labels
|
||||
setTimeout(() => this.doughnutChartLabelsLocal = Object.keys(dataLocal), 0);
|
||||
}
|
||||
|
||||
// events
|
||||
public chartClicked(e: any): void {
|
||||
}
|
||||
|
||||
public chartHovered(e: any): void {
|
||||
}
|
||||
|
||||
}
|
19
src/app/panels/pie-panel.component.html
Normal file
19
src/app/panels/pie-panel.component.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<div class="card">
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4 class="card-title mb-0">All Purchases by Category</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-wrapper">
|
||||
<canvas baseChart class="chart"
|
||||
[data]="doughnutChartDataLocal"
|
||||
[labels]="doughnutChartLabelsLocal"
|
||||
[colors]="doughnutChartColors"
|
||||
[legend]="chartLegend"
|
||||
[chartType]="chartType"
|
||||
(chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
94
src/app/panels/pie-panel.component.ts
Normal file
94
src/app/panels/pie-panel.component.ts
Normal file
|
@ -0,0 +1,94 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { ApiService } from '../providers/api-service';
|
||||
import { CustPiesService } from '../providers/cust-pies.service';
|
||||
import { DataType } from '../shared/data-types.enum';
|
||||
import { ChartData } from '../_interfaces/chart-data';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'panel-pie',
|
||||
templateUrl: 'pie-panel.component.html',
|
||||
})
|
||||
|
||||
export class PiePanel implements OnInit {
|
||||
|
||||
public chartType = 'pie';
|
||||
public chartLegend = true;
|
||||
public doughnutChartDataLocal: number[] = [];
|
||||
public doughnutChartLabelsLocal: string[] = [];
|
||||
public doughnutChartColors: any[] = [
|
||||
{ backgroundColor: [
|
||||
'#ffa1b5',
|
||||
'#3cde52',
|
||||
'#52afed',
|
||||
'#c133e3',
|
||||
'#f7fa08',
|
||||
'#75152d',
|
||||
'#ee12ee',
|
||||
'#15eaea',
|
||||
'#eaa015',
|
||||
'#ea1515',
|
||||
'#2d4fcc'
|
||||
]
|
||||
},
|
||||
{ borderColor:[
|
||||
'red',
|
||||
'green',
|
||||
'blue',
|
||||
'purple',
|
||||
'yellow',
|
||||
'brown',
|
||||
'magenta',
|
||||
'cyan',
|
||||
'orange',
|
||||
'pink'
|
||||
]
|
||||
},
|
||||
{ borderWidth: [10]
|
||||
}
|
||||
];
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private pieService: CustPiesService,
|
||||
) {
|
||||
this.pieService.getPie().subscribe(
|
||||
result => {
|
||||
this.setChartData(result.local_all);
|
||||
},
|
||||
error => {
|
||||
console.log('Retrieval Error');
|
||||
console.log( error._body );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
|
||||
}
|
||||
|
||||
private setChartData(dataLocal: any) {
|
||||
this.doughnutChartDataLocal = Object.keys(dataLocal).map(key => dataLocal[key]);
|
||||
// setTimeout is currently a workaround for ng2-charts labels
|
||||
setTimeout(() => this.doughnutChartLabelsLocal = Object.keys(dataLocal), 0);
|
||||
}
|
||||
|
||||
// convert Hex to RGBA
|
||||
public convertHex(hex: string, opacity: number) {
|
||||
hex = hex.replace('#', '');
|
||||
const r = parseInt(hex.substring(0, 2), 16);
|
||||
const g = parseInt(hex.substring(2, 4), 16);
|
||||
const b = parseInt(hex.substring(4, 6), 16);
|
||||
|
||||
const rgba = 'rgba(' + r + ', ' + g + ', ' + b + ', ' + opacity / 100 + ')';
|
||||
return rgba;
|
||||
}
|
||||
|
||||
// events
|
||||
public chartClicked(e: any): void {
|
||||
}
|
||||
|
||||
public chartHovered(e: any): void {
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import { map } from 'rxjs/operators';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { environment } from '../../environments/environment';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
|
||||
/* this provider handles the interaction between server and client */
|
||||
|
||||
|
@ -11,7 +11,7 @@ export class ApiService {
|
|||
private apiUrl = environment.apiUrl;
|
||||
private sessionKey: string = null;
|
||||
constructor(
|
||||
private http: Http,
|
||||
private http: HttpClient,
|
||||
) {
|
||||
if (localStorage.getItem('sessionKey') ) {
|
||||
this.sessionKey = localStorage.getItem('sessionKey');
|
||||
|
@ -20,47 +20,44 @@ export class ApiService {
|
|||
|
||||
public post(url: string, data: any = {}) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post(
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + url,
|
||||
data
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
// Login API
|
||||
|
||||
public getSessionKey() {
|
||||
console.log('get key');
|
||||
return this.sessionKey;
|
||||
}
|
||||
|
||||
public setSessionKey(key) {
|
||||
console.log('set key');
|
||||
this.sessionKey = key;
|
||||
localStorage.setItem('sessionKey', this.sessionKey);
|
||||
}
|
||||
|
||||
public removeSessionKey() {
|
||||
console.log('remove key');
|
||||
this.sessionKey = null;
|
||||
localStorage.removeItem('sessionKey');
|
||||
}
|
||||
|
||||
public register(data) {
|
||||
return this.http.post(
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/register',
|
||||
data
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
public login(data) {
|
||||
return this.http
|
||||
.post(
|
||||
.post<any>(
|
||||
this.apiUrl + '/login',
|
||||
data
|
||||
)
|
||||
.map(
|
||||
).pipe(
|
||||
map(
|
||||
result => {
|
||||
const json = result.json();
|
||||
const json = result;
|
||||
this.setSessionKey(json.session_key);
|
||||
this.setUserInfo(
|
||||
json.email,
|
||||
|
@ -69,24 +66,23 @@ export class ApiService {
|
|||
this.setUserType(json.user_type);
|
||||
return json;
|
||||
}
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
public logout() {
|
||||
console.log(this.sessionKey);
|
||||
const key = this.sessionKey;
|
||||
return this.http
|
||||
.post(
|
||||
.post<any>(
|
||||
this.apiUrl + '/logout',
|
||||
{ session_key : key },
|
||||
)
|
||||
.map(
|
||||
).pipe(
|
||||
map(
|
||||
response => {
|
||||
localStorage.clear();
|
||||
this.sessionKey = null;
|
||||
return response.json();
|
||||
return response;
|
||||
}
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
// Submits feedback
|
||||
|
@ -96,70 +92,161 @@ export class ApiService {
|
|||
data.package_name = 'Foodloop Web';
|
||||
data.version_code = 'dev';
|
||||
data.version_number = 'dev';
|
||||
console.log(data);
|
||||
return this.http.post(
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/feedback',
|
||||
data
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
// gets transaction list for log
|
||||
|
||||
public transList(data) {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post(
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/outgoing-transactions',
|
||||
{
|
||||
session_key : key,
|
||||
page : data
|
||||
}
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
// Basic Customer User stats API
|
||||
public categoryList() {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/search/category',
|
||||
{
|
||||
session_key : key,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Basic Customer User stats API
|
||||
public categoryTransactionList() {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/stats/category',
|
||||
{
|
||||
session_key : key,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// LCC data
|
||||
public externalTransactions() {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/v1/organisation/external/transactions',
|
||||
{
|
||||
session_key : key,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public loadMiscUrl(extra_url, extraArgs = {}) {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/v1/' + extra_url,
|
||||
{
|
||||
session_key : key,
|
||||
...extraArgs,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public externalSuppliers(page, sortBy, sortDir, perPage, search) {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/v1/organisation/external/suppliers',
|
||||
{
|
||||
session_key : key,
|
||||
page : page,
|
||||
sort_by : sortBy,
|
||||
sort_dir : sortDir,
|
||||
per_page : perPage,
|
||||
search : search,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Searches organisations used for transaction submission
|
||||
|
||||
public search(data) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post(
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/search',
|
||||
data
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
// Uploads a transaction
|
||||
|
||||
public upload(data) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post(
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/upload',
|
||||
data
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
// Edits a recurring transaction
|
||||
|
||||
public recurUpdate(data) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/recurring-transactions',
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
// Edits a recurring transaction
|
||||
|
||||
public recurDelete(data) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/recurring-transactions/delete',
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
// gets payroll list for log
|
||||
|
||||
public payrollList(data) {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/v1/organisation/payroll',
|
||||
{
|
||||
session_key : key,
|
||||
page : data
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// handles Org data added
|
||||
|
||||
public orgPayroll(data) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post(
|
||||
this.apiUrl + '/org/payroll',
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/v1/organisation/payroll/add',
|
||||
data
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
public orgSupplier(data) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post(
|
||||
this.apiUrl + '/org/supplier',
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/v1/organisation/supplier/add',
|
||||
data
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
public orgEmployee(data) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post(
|
||||
this.apiUrl + '/org/employee',
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/v1/organisation/employee/add',
|
||||
data
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
// Handles user data interaction
|
||||
|
@ -175,7 +262,6 @@ export class ApiService {
|
|||
public setUserInfo(
|
||||
email: string,
|
||||
display_name: string) {
|
||||
console.log('set UserInfo');
|
||||
localStorage.setItem('email', email);
|
||||
localStorage.setItem('displayname', display_name);
|
||||
}
|
||||
|
@ -183,7 +269,6 @@ export class ApiService {
|
|||
// Sets usertype
|
||||
|
||||
public setUserType(user_type: string) {
|
||||
console.log('set UserType');
|
||||
localStorage.setItem('usertype', user_type);
|
||||
}
|
||||
|
||||
|
@ -191,74 +276,100 @@ export class ApiService {
|
|||
|
||||
public accountFullLoad() {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post(
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/user',
|
||||
{ session_key : key },
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
public accountEditUpdate(data) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post(
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/user/account',
|
||||
data
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
// Deletes account details on logout
|
||||
|
||||
public removeUserInfo() {
|
||||
console.log('remove UserInfo');
|
||||
localStorage.removeItem('email');
|
||||
localStorage.removeItem('displayname');
|
||||
}
|
||||
|
||||
public getFullName() {
|
||||
console.log('get Full Name');
|
||||
localStorage.getItem('fullname');
|
||||
}
|
||||
|
||||
public getDisplayName() {
|
||||
console.log('get Display Name');
|
||||
localStorage.getItem('displayname');
|
||||
}
|
||||
|
||||
public getPostcode() {
|
||||
console.log('get Postcode');
|
||||
localStorage.getItem('postcode');
|
||||
}
|
||||
|
||||
public getYearOfBirth() {
|
||||
console.log('get Year of Birth');
|
||||
localStorage.getItem('yearofbirth');
|
||||
}
|
||||
|
||||
public getEmail() {
|
||||
console.log('get email');
|
||||
localStorage.getItem('email');
|
||||
}
|
||||
|
||||
// Leaderboard Api
|
||||
|
||||
public leaderboard_fetch(data) {
|
||||
public leaderboard_fetch(
|
||||
type: string,
|
||||
page: number) {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post(
|
||||
this.apiUrl + '/stats/leaderboard',
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/stats/leaderboard/paged',
|
||||
{
|
||||
session_key : key,
|
||||
type : data
|
||||
type : type,
|
||||
page: page,
|
||||
}
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
// Initial Map Data
|
||||
public getMapData(data) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/v1/supplier/location',
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
// Load Association Data
|
||||
public getAssocData(data) {
|
||||
data.session_key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/v1/supplier/location/trail',
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
// Basic Customer User stats API
|
||||
public basicStats() {
|
||||
public customerStats() {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post(
|
||||
this.apiUrl + '/stats',
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/stats/customer',
|
||||
{
|
||||
session_key : key,
|
||||
}
|
||||
).map( response => response.json() );
|
||||
);
|
||||
}
|
||||
|
||||
// Basic Customer User stats API
|
||||
public orgStats() {
|
||||
const key = this.sessionKey;
|
||||
return this.http.post<any>(
|
||||
this.apiUrl + '/stats/organisation',
|
||||
{
|
||||
session_key : key,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
14
src/app/providers/cust-graphs.service.ts
Normal file
14
src/app/providers/cust-graphs.service.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ApiService } from './api-service';
|
||||
|
||||
@Injectable()
|
||||
export class CustGraphsService {
|
||||
private custGraphUrl = '/v1/customer/graphs';
|
||||
|
||||
constructor(private api: ApiService) { }
|
||||
|
||||
public getGraph(name: string, data: any = {}) {
|
||||
data.graph = name;
|
||||
return this.api.post(this.custGraphUrl, data);
|
||||
}
|
||||
}
|
14
src/app/providers/cust-pies.service.ts
Normal file
14
src/app/providers/cust-pies.service.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ApiService } from './api-service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class CustPiesService {
|
||||
private custPieUrl = '/v1/customer/pies';
|
||||
|
||||
constructor(private api: ApiService) { }
|
||||
|
||||
public getPie(): Observable<any> {
|
||||
return this.api.post(this.custPieUrl);
|
||||
}
|
||||
}
|
15
src/app/providers/cust-snippets.service.ts
Normal file
15
src/app/providers/cust-snippets.service.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ApiService } from './api-service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class CustSnippetsService {
|
||||
private custSnippetsUrl = '/v1/customer/snippets';
|
||||
|
||||
constructor(private api: ApiService) { }
|
||||
|
||||
// This endpoint should mimic basicStats
|
||||
public getData(): Observable<any> {
|
||||
return this.api.post(this.custSnippetsUrl);
|
||||
}
|
||||
}
|
14
src/app/providers/org-pies.service.ts
Normal file
14
src/app/providers/org-pies.service.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ApiService } from './api-service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class OrgPiesService {
|
||||
private orgPieUrl = '/v1/organisation/pies';
|
||||
|
||||
constructor(private api: ApiService) { }
|
||||
|
||||
public getOrgPie(): Observable<any> {
|
||||
return this.api.post(this.orgPieUrl);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ApiService } from './api-service';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class OrgSnippetsService {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
import {filter} from 'rxjs/operators';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
|
||||
import 'rxjs/add/operator/filter';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-breadcrumbs',
|
||||
|
@ -18,7 +20,7 @@ export class BreadcrumbsComponent implements OnInit {
|
|||
breadcrumbs: Array<Object>;
|
||||
constructor(private router: Router, private route: ActivatedRoute) {}
|
||||
ngOnInit(): void {
|
||||
this.router.events.filter(event => event instanceof NavigationEnd).subscribe(event => {
|
||||
this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(event => {
|
||||
this.breadcrumbs = [];
|
||||
let currentRoute = this.route.root,
|
||||
url = '';
|
||||
|
|
3
src/app/shared/leaderboard-result.component.html
Normal file
3
src/app/shared/leaderboard-result.component.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<td>{{ leaderboard.position }}</td>
|
||||
<td class="text-truncate">{{ leaderboard.display_name }}</td>
|
||||
<td>{{ listType.includes('total') ? (leaderboard.value | currency:'GBP':'symbol':'1.2-2') : (leaderboard.value | number:'1.0-0') }}</td>
|
20
src/app/shared/leaderboard-result.component.ts
Normal file
20
src/app/shared/leaderboard-result.component.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
|
||||
interface LeaderboardData {
|
||||
position: number;
|
||||
display_name: number,
|
||||
value: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line
|
||||
selector: '[leaderboard-result]',
|
||||
templateUrl: 'leaderboard-result.component.html',
|
||||
})
|
||||
export class LeaderboardResultComponent implements OnInit {
|
||||
@Input() public leaderboard: LeaderboardData;
|
||||
@Input() public listType: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
}
|
3
src/app/shared/meta-type-result.component.html
Normal file
3
src/app/shared/meta-type-result.component.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<td>{{type.type}}</td>
|
||||
<td>{{type.count}}</td>
|
||||
<td>{{type.sum | currency:'GBP':'symbol':'1.2-2' }}</td>
|
19
src/app/shared/meta-type-result.component.ts
Normal file
19
src/app/shared/meta-type-result.component.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
|
||||
interface MetaTypeData {
|
||||
type: string;
|
||||
sum: number;
|
||||
count: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line
|
||||
selector: '[meta-type-result]',
|
||||
templateUrl: 'meta-type-result.component.html',
|
||||
})
|
||||
export class MetaTypeResultComponent implements OnInit {
|
||||
@Input() public type: MetaTypeData;
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ interface OrgData {
|
|||
}
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line
|
||||
selector: '[org-result]',
|
||||
templateUrl: 'org-result.component.html',
|
||||
})
|
||||
|
@ -19,6 +20,6 @@ export class OrgResultComponent {
|
|||
public orgClick(): void {
|
||||
this.onClick.emit(
|
||||
this.org
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ interface OrgData {
|
|||
}
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line
|
||||
selector: 'org-table',
|
||||
templateUrl: 'org-table.component.html',
|
||||
})
|
||||
|
|
3
src/app/shared/payroll-result.component.html
Normal file
3
src/app/shared/payroll-result.component.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<td>{{payrollDate}}</td>
|
||||
<td>{{payroll.gross_payroll | currency:'GBP':'symbol':'1.2-2' }}</td>
|
||||
<td>{{payroll.employee_amount}}</td>
|
22
src/app/shared/payroll-result.component.ts
Normal file
22
src/app/shared/payroll-result.component.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import * as moment from 'moment';
|
||||
|
||||
interface PayrollData {
|
||||
entry_period: string;
|
||||
gross_payroll: number;
|
||||
employee_amount: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line
|
||||
selector: '[payroll-result]',
|
||||
templateUrl: 'payroll-result.component.html',
|
||||
})
|
||||
export class PayrollResultComponent implements OnInit {
|
||||
@Input() public payroll: PayrollData;
|
||||
public payrollDate: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.payrollDate = moment(this.payroll.entry_period).format('MMMM YYYY');
|
||||
}
|
||||
}
|
6
src/app/shared/recur-result.component.html
Normal file
6
src/app/shared/recur-result.component.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
<td (click)="recurClick()">{{recur.seller}}</td>
|
||||
<td (click)="recurClick()">{{categories[recur.category] || 'Uncategorised'}}</td>
|
||||
<td (click)="recurClick()">{{recur.essential == 1 ? 'true' : 'false'}}</td>
|
||||
<td (click)="recurClick()">{{recur.display_time}}</td>
|
||||
<td (click)="recurClick()">{{recur.recurring_period}}</td>
|
||||
<td (click)="recurClick()">{{recur.value}}</td>
|
40
src/app/shared/recur-result.component.ts
Normal file
40
src/app/shared/recur-result.component.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import * as moment from 'moment';
|
||||
|
||||
interface RecurData {
|
||||
category: number;
|
||||
essential: number;
|
||||
id: number;
|
||||
last_updated: string;
|
||||
recurring_period: string;
|
||||
seller: string;
|
||||
start_time: string;
|
||||
value: number;
|
||||
display_time: any;
|
||||
}
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line
|
||||
selector: '[recur-result]',
|
||||
templateUrl: 'recur-result.component.html',
|
||||
})
|
||||
export class RecurResultComponent {
|
||||
@Input() public recur: RecurData;
|
||||
@Output() public onClick = new EventEmitter();
|
||||
@Input() public categories: any;
|
||||
public updatedDate: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.recur.last_updated) {
|
||||
this.recur.display_time = moment(this.recur.last_updated).format('llll');
|
||||
} else {
|
||||
this.recur.display_time = moment(this.recur.start_time).format('llll');
|
||||
}
|
||||
}
|
||||
|
||||
public recurClick(): void {
|
||||
this.onClick.emit(
|
||||
this.recur
|
||||
);
|
||||
}
|
||||
}
|
17
src/app/shared/recur-table.component.html
Normal file
17
src/app/shared/recur-table.component.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<div class="form-group row">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Seller</th>
|
||||
<th>Category</th>
|
||||
<th>Essential</th>
|
||||
<th>Last Applied</th>
|
||||
<th>Recurring Period</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr recur-result *ngFor="let recur of recurList" [categories]="categories" [recur]="recur" (onClick)="recurClick($event, template)"></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
30
src/app/shared/recur-table.component.ts
Normal file
30
src/app/shared/recur-table.component.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { RecurResultComponent } from '../shared/recur-result.component';
|
||||
|
||||
interface RecurData {
|
||||
category: string;
|
||||
essential: number;
|
||||
id: number;
|
||||
last_updated: string;
|
||||
recurring_period: string;
|
||||
seller: string;
|
||||
start_time: string;
|
||||
value: number;
|
||||
display_time: any;
|
||||
}
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line
|
||||
selector: 'recur-table',
|
||||
templateUrl: 'recur-table.component.html',
|
||||
})
|
||||
export class RecurTableComponent {
|
||||
@Input() public recurList: Array<RecurData>;
|
||||
@Output() public onClick = new EventEmitter();
|
||||
@Input() public categories: any;
|
||||
|
||||
|
||||
public recurClick(event: any): void {
|
||||
this.onClick.emit( event );
|
||||
}
|
||||
}
|
3
src/app/shared/supplier-result.component.html
Normal file
3
src/app/shared/supplier-result.component.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<td>{{supplier.name}}</td>
|
||||
<td>{{supplier.postcode}}</td>
|
||||
<td>{{supplier.spend | currency:'GBP':'symbol':'1.2-2' }}</td>
|
19
src/app/shared/supplier-result.component.ts
Normal file
19
src/app/shared/supplier-result.component.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
|
||||
interface SupplierData {
|
||||
name: string;
|
||||
postcode: string;
|
||||
spend: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line
|
||||
selector: '[supplier-result]',
|
||||
templateUrl: 'supplier-result.component.html',
|
||||
})
|
||||
export class SupplierResultComponent implements OnInit {
|
||||
@Input() public supplier: SupplierData;
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
<td>{{transaction.seller}}</td>
|
||||
<td>{{transaction.value | currency:'GBP':true:'1.2-2' }}</td>
|
||||
<td *ngIf="!showMeta">{{transaction.value | currency:'GBP':'symbol':'1.2-2' }}</td>
|
||||
<td *ngIf="showMeta">{{transaction.net_value | currency:'GBP':'symbol':'1.2-2' }}</td>
|
||||
<td *ngIf="showMeta">{{transaction.sales_tax_value | currency:'GBP':'symbol':'1.2-2' }}</td>
|
||||
<td *ngIf="showMeta">{{transaction.gross_value | currency:'GBP':'symbol':'1.2-2' }}</td>
|
||||
<td>{{transactionDate}}</td>
|
||||
|
|
|
@ -3,16 +3,18 @@ import * as moment from 'moment';
|
|||
|
||||
interface TransactionData {
|
||||
seller: number;
|
||||
value: string;
|
||||
value: number;
|
||||
purchase_time: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line
|
||||
selector: '[transaction-result]',
|
||||
templateUrl: 'transaction-result.component.html',
|
||||
})
|
||||
export class TransactionResultComponent implements OnInit {
|
||||
@Input() public transaction: TransactionData;
|
||||
@Input() public showMeta: boolean;
|
||||
public transactionDate: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
3
src/app/shared/ward-result.component.html
Normal file
3
src/app/shared/ward-result.component.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<td>{{ward.ward}}</td>
|
||||
<td>{{ward.count}}</td>
|
||||
<td>{{ward.sum | currency:'GBP':'symbol':'1.2-2' }}</td>
|
19
src/app/shared/ward-result.component.ts
Normal file
19
src/app/shared/ward-result.component.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
|
||||
interface WardData {
|
||||
ward: string;
|
||||
sum: number;
|
||||
count: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line
|
||||
selector: '[ward-result]',
|
||||
templateUrl: 'ward-result.component.html',
|
||||
})
|
||||
export class WardResultComponent implements OnInit {
|
||||
@Input() public ward: WardData;
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
}
|
55
src/app/snippets/cust-snippet-bar.component.html
Normal file
55
src/app/snippets/cust-snippet-bar.component.html
Normal file
|
@ -0,0 +1,55 @@
|
|||
<div class="row">
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card text-center" style="background-color: transparent; border: none">
|
||||
<div class="card-block">
|
||||
<ul style="list-style: none; padding-left: 0;">
|
||||
<li class="hidden-sm-down">
|
||||
<div><h5 class="text-dark-green">My Points</h5></div>
|
||||
<div class="number-circle mx-auto"><strong>{{ userSum / 10 | number:'1.0-0' }}</strong></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card text-center" style="background-color: transparent; border: none">
|
||||
<div class="card-block">
|
||||
<ul style="list-style: none; padding-left: 0;">
|
||||
<li class="hidden-sm-down">
|
||||
<div><h5 class="text-dark-green">My Rank</h5></div>
|
||||
<div *ngIf="userPosition == 0" class="statuscontent">
|
||||
<div class="number-circle mx-auto"><strong>Unranked</strong></div>
|
||||
</div>
|
||||
<div *ngIf="userPosition != 0" class="statuscontent">
|
||||
<div class="number-circle mx-auto"><strong>{{ userPosition }}</strong></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card text-center" style="background-color: transparent; border: none">
|
||||
<div class="card-block">
|
||||
<ul style="list-style: none; padding-left: 0;">
|
||||
<li class="hidden-sm-down">
|
||||
<div><h5 class="text-dark-green">My Total Spend</h5></div>
|
||||
<div class="number-circle mx-auto"><strong>{{ userSum | currency:'GBP':'symbol':'1.2-2' }}</strong></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card text-center" style="background-color: transparent; border: none">
|
||||
<div class="card-block">
|
||||
<ul style="list-style: none; padding-left: 0;">
|
||||
<li class="hidden-sm-down">
|
||||
<div><h5 class="text-dark-green">Value to Local Economy</h5></div>
|
||||
<div class="number-circle mx-auto"><strong>{{ userSum * 2.3 | currency:'GBP':'symbol':'1.2-2' }}</strong></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
26
src/app/snippets/cust-snippet-bar.component.ts
Normal file
26
src/app/snippets/cust-snippet-bar.component.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { CustSnippetsService } from '../providers/cust-snippets.service';
|
||||
|
||||
@Component({
|
||||
selector: 'snippet-bar-cust',
|
||||
templateUrl: 'cust-snippet-bar.component.html',
|
||||
})
|
||||
export class CustBarSnippetComponent implements OnInit {
|
||||
|
||||
public userSum = 0;
|
||||
public userPosition = 0;
|
||||
|
||||
constructor(
|
||||
private snippetsService: CustSnippetsService,
|
||||
) { }
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.snippetsService.getData()
|
||||
.subscribe(
|
||||
result => {
|
||||
this.userSum = result.snippets.user_sum;
|
||||
this.userPosition = result.snippets.user_position;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,20 +2,28 @@
|
|||
<div class="card-footer">
|
||||
<ul>
|
||||
<li class="hidden-sm-down">
|
||||
<div class="text-muted">Customers This Month</div>
|
||||
<div class="text-muted">Sales Total</div>
|
||||
<strong>{{ allSalesCount }}</strong>
|
||||
</li>
|
||||
<li class="hidden-sm-down">
|
||||
<div class="text-muted">Money Spent Total</div>
|
||||
<strong>{{ allPurchasesTotal | currency:'GBP':'symbol':'1.2-2'}}</strong>
|
||||
</li>
|
||||
<li class="hidden-sm-down">
|
||||
<div class="text-muted">Sales This Month</div>
|
||||
<strong>{{ thisMonthSalesCount }}</strong>
|
||||
</li>
|
||||
<li class="hidden-sm-down">
|
||||
<div class="text-muted">Money Spent This Month</div>
|
||||
<strong>{{ thisMonthPurchasesTotal | currency:'GBP':true:'1.2-2'}}</strong>
|
||||
<strong>{{ thisMonthPurchasesTotal | currency:'GBP':'symbol':'1.2-2'}}</strong>
|
||||
</li>
|
||||
<li class="hidden-sm-down">
|
||||
<div class="text-muted">Customers Today</div>
|
||||
<div class="text-muted">Sales Today</div>
|
||||
<strong>{{ todaySalesCount }}</strong>
|
||||
</li>
|
||||
<li class="hidden-sm-down">
|
||||
<div class="text-muted">Average Transaction Today</div>
|
||||
<strong>{{ ( todaySalesTotal / todaySalesCount ) || 0 | currency:'GBP':true:'1.2-2'}}</strong>
|
||||
<strong>{{ ( todaySalesTotal / todaySalesCount ) || 0 | currency:'GBP':'symbol':'1.2-2'}}</strong>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,8 @@ import { OrgSnippetsService } from '../providers/org-snippets.service';
|
|||
})
|
||||
export class OrgBarSnippetComponent implements OnInit {
|
||||
|
||||
public allSalesCount = 0;
|
||||
public allSalesTotal = 0;
|
||||
public thisMonthSalesCount = 0;
|
||||
public thisMonthSalesTotal = 0;
|
||||
public thisWeekSalesCount = 0;
|
||||
|
@ -14,6 +16,8 @@ export class OrgBarSnippetComponent implements OnInit {
|
|||
public todaySalesCount = 0;
|
||||
public todaySalesTotal = 0;
|
||||
|
||||
public allPurchasesCount = 0;
|
||||
public allPurchasesTotal = 0;
|
||||
public thisMonthPurchasesCount = 0;
|
||||
public thisMonthPurchasesTotal = 0;
|
||||
public thisWeekPurchasesCount = 0;
|
||||
|
@ -29,6 +33,8 @@ export class OrgBarSnippetComponent implements OnInit {
|
|||
this.snippetsService.getData()
|
||||
.subscribe(
|
||||
result => {
|
||||
this.allSalesCount = result.snippets.all_sales_count;
|
||||
this.allSalesTotal = result.snippets.all_sales_total;
|
||||
this.thisMonthSalesCount = result.snippets.this_month_sales_count;
|
||||
this.thisMonthSalesTotal = result.snippets.this_month_sales_total;
|
||||
this.thisWeekSalesCount = result.snippets.this_week_sales_count;
|
||||
|
@ -36,6 +42,8 @@ export class OrgBarSnippetComponent implements OnInit {
|
|||
this.todaySalesCount = result.snippets.today_sales_count;
|
||||
this.todaySalesTotal = result.snippets.today_sales_total;
|
||||
|
||||
this.allPurchasesCount = result.snippets.all_purchases_count;
|
||||
this.allPurchasesTotal = result.snippets.all_purchases_total;
|
||||
this.thisMonthPurchasesCount = result.snippets.this_week_purchases_count;
|
||||
this.thisMonthPurchasesTotal = result.snippets.this_week_purchases_total;
|
||||
this.thisWeekPurchasesCount = result.snippets.this_month_purchases_count;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue