Compare commits

...
This repository has been archived on 2023-08-16. You can view files and clone it, but cannot push or open issues or pull requests.

104 commits

Author SHA1 Message Date
Rumperuu
94ee509b43
Merge pull request #114 from Pear-Trading/ben/update-readme
Update and clean up README
2020-10-19 20:39:21 +01:00
bab6b9f354 Merge branch 'development' into ben/update-readme 2020-10-19 20:27:50 +01:00
a0c0c7e032 Remove old readme section 2020-10-18 14:48:43 +01:00
90f0a91f50 Reformat build status table 2020-10-18 14:46:21 +01:00
067f0543d4 Add table of contents 2020-10-18 14:45:03 +01:00
Rumperuu
792e431fad
Merge pull request #110 from Pear-Trading/ben/fix/sidebar-links
Correct which links are shown for different users
2020-10-18 14:40:40 +01:00
a19ddcb1b7 Add property to component 2020-10-18 14:03:23 +01:00
Rumperuu
49e5951747
Merge branch 'development' into ben/fix/sidebar-links 2020-10-18 13:33:54 +01:00
Rumperuu
e6f7ba4b65
Merge pull request #111 from Pear-Trading/ben/fix/postcode-validation
Capitalise entered postcodes
2020-10-18 13:32:21 +01:00
e839ab28b6 Capitalise entered postcodes 2020-10-17 16:41:10 +01:00
7be68c3fc0 Correct which links are shown for different users 2020-10-17 16:21:05 +01:00
0dd7ab1e34 Clean up README 2020-10-17 14:39:57 +01:00
Tom Bloor
53db04dd9c
Merge pull request #103 from Pear-Trading/finn/fixes
Bring dependencies up to date
2020-08-13 16:52:20 +01:00
Tom Bloor
da30036d4e
Merge branch 'development' into finn/fixes 2020-08-13 16:31:57 +01:00
Tom Bloor
2a5e790e3d
Fix e2e test and actually check login works 2020-08-13 16:24:49 +01:00
Finn Kempers
e4dcc2e664 fixing agm 2020-08-12 13:27:34 +01:00
Finn Kempers
a0e28cb440 Fixes to declaration name of agm markers 2020-08-12 13:12:11 +01:00
Finn Kempers
4312f2ad8b fixed a variable 2020-08-10 10:11:22 +01:00
Finn Kempers
20ec21f8cb npm audit fix run 2020-08-10 09:49:27 +01:00
Finn Kempers
ee88c8c31a package-lock automatic update 2020-08-10 09:48:41 +01:00
Finn Kempers
55eb053b08 package fixes due to missing peer & npm warnings 2020-07-29 20:13:08 +01:00
Finn Kempers
fba44b237a Additional fix needed for clusters 2020-07-29 14:55:20 +01:00
Finn Kempers
20f7e5dc0f angular json fix 2020-07-29 14:50:43 +01:00
Finn Kempers
d4540e7650 Upgrade to Angular 10 2020-07-29 14:40:01 +01:00
Finn Kempers
611dbc4bac changed agm cluster and updated agm dependencies
the changelog didnt mention this properly to do but as seen in change 7982bfbcf0 the module was renamed and needed changing.
2020-07-29 14:32:51 +01:00
Finn Kempers
7c651e1522 package updates to match angular v 2020-07-29 14:08:12 +01:00
Finn Kempers
3ed88a4d5e ng update to 9 2020-07-29 13:31:46 +01:00
Finn Kempers
e9c81dc428 ng update angular 8 done 2020-07-29 13:03:09 +01:00
Finn Kempers
8a4e7872fb reverting updates 2020-07-29 13:01:24 +01:00
Finn Kempers
208a85c5a5 fix to get ng update to work 2020-07-29 12:56:49 +01:00
Finn Kempers
839d322327 rxjs update 2020-07-29 12:54:28 +01:00
Finn Kempers
28b575e819 npm audit fix fixes 2020-07-29 12:44:33 +01:00
Finn
0f190a70d7 package fixes 2020-01-08 20:00:57 +00:00
Finn
326e4ec322 testing stuff 2020-01-08 18:27:54 +00:00
Thomas Bloor
fe125c812f
Merge branch 'master' into development 2019-09-16 13:05:26 +01:00
Thomas Bloor
b91de60175
Merge branch 'Release-v0.1.13' 2019-09-16 13:02:07 +01:00
Thomas Bloor
5abf5d29b3
Updated changelog and package version 2019-09-16 13:01:04 +01:00
Finn
891bb0aee4 fixes quill audit issue 2019-09-11 14:10:53 +01:00
Thomas Bloor
50b9cfdc31
update webdriver-manager 2019-09-11 13:54:22 +01:00
Thomas Bloor
53aae6fc07
Updated api key for deploy build 2019-09-11 13:11:52 +01:00
Thomas Bloor
5e92829a7a
use package config in script 2019-09-09 21:39:58 +01:00
Thomas Bloor
ace6900145
Fix prod configuration name 2019-09-09 21:33:28 +01:00
Tom Bloor
893bdca736
Merge pull request #85 from Pear-Trading/TBSliver/CIFixes
CI Update
2019-09-09 21:26:20 +01:00
Thomas Bloor
75edb86abe
update secret files 2019-09-09 21:19:51 +01:00
Thomas Bloor
66388593e6
note even sure why that was there 2019-09-09 21:09:47 +01:00
Thomas Bloor
f697b2243d
more CI fixes 2019-09-09 21:08:49 +01:00
Thomas Bloor
abdbfda56c
change to use latest node for compilation 2019-09-09 21:00:30 +01:00
Thomas Bloor
bad4f7f83c
fix CI config 2019-09-09 20:55:21 +01:00
Thomas Bloor
87160f5387
remove uneccessary stuff and work on CI 2019-09-09 20:45:24 +01:00
Thomas Bloor
bab670f60b
Merge branch 'finn/wards' into development 2019-09-09 19:56:26 +01:00
Thomas Bloor
7883dbb169
Fix a large amount of things in the ui 2019-09-09 19:52:38 +01:00
Finn
52bce4dd0a Added initial working table for transaction types 2019-09-09 16:13:34 +01:00
Finn
5c85d5aae9 ward import fixed 2019-09-05 17:18:52 +01:00
Finn
db5f982ed3 BROKEN: Added wards table 2019-09-04 18:21:21 +01:00
Tom Slater
1dc46a55e0 Misc chart work
Search feature on both suppliers and the pagination feature on the spend history still not functional
2019-08-30 16:50:19 +01:00
Tom Slater
420174e953 chart work 2019-08-28 10:47:44 +01:00
Tom Slater
5e91665e0b Merge remote-tracking branch 'origin/tyu-tyu/charts' into tyu-tyu/charts 2019-08-23 11:42:39 +01:00
Tom Slater
301f706365 Chart improvements
Minor fixes  + refresh on supplier spend chart
2019-08-22 16:59:51 +01:00
Tom Slater
ade578d78b Merge remote-tracking branch 'origin/tyu-tyu/char' into tyu-tyu/charts 2019-08-21 11:53:53 +01:00
Tom Slater
8859c6977c Colour fixes 2019-08-21 11:51:10 +01:00
Tom Slater
9fbe36e401 Misc chart work 2019-08-20 16:47:20 +01:00
Finn
55e62a6b87
properly displaying essential purchases dataset 2019-08-20 16:43:53 +01:00
Finn
03e9e365fc
removed console logs that show password 2019-08-20 14:33:44 +01:00
Tom Slater
4181a09b96 Merge remote-tracking branch 'origin/felix/customranges' into tyu-tyu/char 2019-08-19 13:48:47 +01:00
Tom Slater
4b42a08af1 Chart work update
working on essential purchase graph
2019-08-19 12:30:36 +01:00
Tom Slater
156a2f3764 Misc Chart work 2019-08-16 16:58:35 +01:00
Felix
7c5eedaa87 removed test graph 2019-08-16 15:55:22 +01:00
Felix
301a6e619a commented out console.logs 2019-08-16 15:24:49 +01:00
Felix
12a72c00eb added loading spinner for when graph is loading 2019-08-16 15:23:18 +01:00
Tom Slater
e9334cec6d Merge branch 'tyu-tyu/chart' into felix/customranges 2019-08-16 13:58:13 +01:00
Tom Slater
1dd17e7a7a Chart work
Misc Chart work
2019-08-16 12:27:05 +01:00
Felix
ab72264521 IT WORKS! (date range picker) 2019-08-16 12:13:43 +01:00
Felix
6f801d20de more debugging 2019-08-16 11:32:45 +01:00
Felix
93b21d2547 debugging caching 2019-08-16 10:51:47 +01:00
Felix
277c058450 caching data from server to increase speed 2019-08-16 10:27:49 +01:00
Felix
ce64a9140e turns out the brackets were the wrong way around 2019-08-15 16:51:06 +01:00
Felix
5ffffbfe08 date pickers work 2019-08-15 16:34:13 +01:00
Felix
a5b32936bd forgot to grab important change from dev server 2019-08-15 15:10:48 +01:00
Felix
5b50803a25 more debugging 2019-08-15 14:51:10 +01:00
Felix
88bd12157b fixed incorrect initial value 2019-08-15 14:33:56 +01:00
Felix
6a338d41a4 removed unnecessary console.log() 2019-08-15 14:23:28 +01:00
Felix
a8cd23cf5c bubble chart updates on new dates entered to field
but is slowwwwwwww
2019-08-15 14:22:33 +01:00
Felix
a2f8f90513 custom dateRange code works 2019-08-15 12:37:49 +01:00
Felix
6e21cf8746 debugging custom range 2019-08-15 12:09:46 +01:00
Felix
e2960a3fcf fetching the right data this time at least 2019-08-14 16:36:02 +01:00
Felix
b6c0e1b6dd debugging options 2019-08-14 16:27:28 +01:00
Felix
00889b2104 fetches correct date ranges 2019-08-14 16:06:09 +01:00
Felix
263d3d15ab yeet 2019-08-14 15:43:41 +01:00
Tom Slater
fe6964fb8d WIP event timepicker 2019-08-14 15:34:08 +01:00
Finn
6c807194bc
fixed errors with ngModel 2019-08-14 14:18:53 +01:00
Tom Slater
a605d66d37 work on date picker bubble chart 2019-08-14 12:07:05 +01:00
Tom Slater
d550bf5e12 Work on date picker function on bubble chart 2019-08-14 09:49:18 +01:00
Tom Slater
c206a394d2 Added time filter options + Chart Labels
Cleaned up graphs that aren't relevant to this particular page, added chart labels and a WIP time filter option
2019-08-09 10:21:40 +01:00
Tom Slater
7ee0b64351 Chart Labels and clean up
Added axis labels and cleaned up charts a little.
2019-08-07 16:21:40 +01:00
Tom Slater
3248caed07 Misc Chart Improvements
Fixed doughnut chart colour loading issues, added WIP dashboard charts, few other misc changes
2019-08-06 16:51:44 +01:00
Tom Bloor
0dcf98fed3
Various updates 2019-07-15 12:38:06 +01:00
Tom Bloor
5cd0db3faf
More graphs with testing data 2019-07-15 01:17:01 +01:00
Finn
b1790a2fe9
Added meta data to transaction list 2019-07-12 20:47:01 +01:00
Finn
75ba91379d
made fixes to imports and supplier list 2019-07-12 20:18:28 +01:00
Finn
8202cdfdb1
Amended supplier list 2019-07-12 20:05:06 +01:00
Finn
40462b7d8f
Added proper supplier table 2019-07-12 18:50:59 +01:00
Finn
6b1e474916
Fixed suppliers 2019-07-12 17:45:43 +01:00
Finn
af04adeb31
Fixed errors when there is no data available 2019-07-12 17:38:19 +01:00
Finn
df631ad793
various fixes to dashboard stuff 2019-07-12 17:12:01 +01:00
71 changed files with 9457 additions and 4227 deletions

1
.gitignore vendored
View file

@ -22,7 +22,6 @@ $RECYCLE.BIN/
/bower_components
# IDEs and editors
/.idea
.project
.classpath
*.launch

3
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/workspace.xml

View 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>

View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

6
.idea/misc.xml Normal file
View 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 Normal file
View 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 Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -1,16 +1,12 @@
# Force sudo due to issue in travis
sudo: required
addons:
chrome: stable
language: node_js
node_js:
- 8
- node
before_install:
- openssl aes-256-cbc -K $encrypted_9d2af3734b6c_key -iv $encrypted_9d2af3734b6c_iv -in src/environments/environments.tar.enc -out src/environments/environments.tar -d
- 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
@ -21,7 +17,7 @@ before_deploy:
deploy:
provider: releases
api_key:
secure: IZKEl1/LnzCN4/uMPKqUktURoNFjDmrybGKwrKbTPS27iXLrC9x4NwrQnOgipLF5h6Jeee4Qf1LhFCEpL/VTBTGMljQU2uOUFqn7TTGV5Ok7wxn2Z4CYeLDHMqDeGEECidAs8pB7I8lQvk5iUqmQT0NbuB0tDgavM9XCAecGUrFHh2tQaqPRGgZ1Q+1QB5d68EItSncMFKgRKja3Jq3u6ArF3uR3qY0dx9UKgcSUkKMnYbkBoEmiuCthdtwsH6WiIp4+K5GnVkGWxFa6KwdUJseHvUCuwS0Jp6lz47fP75xr4pHP45BxL7s549P60Iyr5AMRUge2+LPQV5QdbRH4guQV6qWI2L6Vw64fdC3lK00MhNFNO4M9p1T9eeldXhvUJwBDjzjoHlsrZysUMBy5G9CpQIVJtaPSVMWm/9yfPbu/B3k85cCHLb3fTos3altCWREIUScWIdDAHczJmmITUE9d5KljT8t2gLzCiCqB3BQ6ZzN8Ur68EIJ2ePuCO4644S+1shg5AXYDzXuUd30J57UGnuELQSMQkuZvMUQy8PmNO/iInIqpnuAwOwT4YD0MHgXrjD38a9+KszDjY+9HrIWcQE8P7AuN0iVf2voB5uLTOckkd4mXF9sskwoREVYJM+jz49HViggYyn1MHLsnlY5//vhMo5HNaf2DtT8fhk4=
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

View file

@ -2,6 +2,17 @@
# 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

12
Foodloop-Web.iml Normal file
View 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>

123
README.md
View file

@ -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:* | [![Build Status](https://travis-ci.org/Pear-Trading/FoodLoop-Web.svg?branch=master)](https://travis-ci.org/Pear-Trading/FoodLoop-Web) |
| *Development:* | [![Build Status](https://travis-ci.org/Pear-Trading/FoodLoop-Web.svg?branch=development)](https://travis-ci.org/Pear-Trading/FoodLoop-Web) |
| `master` | [![Build Status](https://travis-ci.org/Pear-Trading/FoodLoop-Web.svg?branch=master)](https://travis-ci.org/Pear-Trading/FoodLoop-Web) |
| `development` | [![Build Status](https://travis-ci.org/Pear-Trading/FoodLoop-Web.svg?branch=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
@ -32,46 +45,52 @@ npm install
## Environments
The app defaults to using the development server. For other options, see
`src/environments/environments.ts`
The app defaults to using the development server. For other options, see
`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).

View file

@ -17,7 +17,12 @@
"tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts",
"assets": [
"src/assets"
"src/assets",
{"input":
"./node_modules/@google/markerclustererplus/images",
"glob": "*",
"output": "/images"
}
],
"styles": [
"src/scss/style.scss"
@ -112,9 +117,6 @@
"karmaConfig": "./karma.conf.js",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"scripts": [
"node_modules/moment/min/moment.min.js"
],
"styles": [
"src/scss/style.scss"
],

View file

@ -12,13 +12,13 @@ echo "Building releases for $VERSION"
echo "Building Prod Release..."
ng build --prod
npm run build:prod
tar -czf ../WebApp-Releases/LocalLoop-Web-prod-$VERSION.tar.gz dist
echo "Building Dev Release..."
ng build --dev
npm run build:dev
tar -czf ../WebApp-Releases/LocalLoop-Web-dev-$VERSION.tar.gz dist

View file

@ -1,4 +1,5 @@
import { LoginPageObject } from './login.po';
import { browser } from "protractor";
describe('Login Page', () => {
let page: LoginPageObject;
@ -13,12 +14,46 @@ describe('Login Page', () => {
});
it('should have a username box of type email', () => {
expect(page.isUsernameFieldPresent()).toBeTruthy();
expect(page.getUsernameFieldType()).toEqual('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');
});
});

View file

@ -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) };
}

View file

@ -7,7 +7,6 @@ module.exports = function (config) {
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require("readable-stream");
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),

11444
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "localloop-web",
"version": "0.1.11",
"version": "0.1.13",
"description": "LocalLoop Web - Web interface for LocalLoop app",
"author": "",
"url": "http://www.peartrade.org",
@ -8,9 +8,13 @@
"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:ci": "ng test --watch=false",
"test:ci": "ng test --watch=false --browsers=ChromeHeadless",
"lint": "ng lint",
"e2e": "ng e2e",
"e2e:ci": "ng e2e --configuration=ci",
@ -18,63 +22,67 @@
},
"private": true,
"dependencies": {
"@agm/core": "1.0.0-beta.6",
"@agm/js-marker-clusterer": "1.0.0-beta.6",
"@angular/common": "8.1.0",
"@angular/compiler": "8.1.0",
"@angular/core": "8.1.0",
"@angular/forms": "8.1.0",
"@angular/platform-browser": "8.1.0",
"@angular/platform-browser-dynamic": "8.1.0",
"@angular/router": "8.1.0",
"@angular/upgrade": "8.1.0",
"@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.3.1",
"js-marker-clusterer": "1.0.0",
"jquery": "^3.5.1",
"jszip": "^3.2.2",
"luxon": "^1.16.1",
"moment": "^2.24.0",
"ng2-charts": "^2.3.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.5.2",
"rxjs": "6.6.0",
"stream": "0.0.2",
"ts-helpers": "1.1.2",
"tslib": "^1.10.0",
"web-animations-js": "^2.3.2",
"webpack-dev-server": "^3.7.2",
"zone.js": "~0.9.1"
"webpack-dev-server": "^3.11.0",
"zone.js": "~0.10.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.801.0",
"@angular/cli": "^8.1.0",
"@angular/compiler-cli": "8.1.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": "^5.1.0",
"codelyzer": "^6.0.0",
"jasmine-core": "^3.4.0",
"jasmine-spec-reporter": "4.2.1",
"karma": "^4.1.0",
"karma": "^5.1.1",
"karma-chrome-launcher": "2.2.0",
"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": "^5.4.2",
"protractor": "^7.0.0",
"readable-stream": "latest",
"ts-node": "^8.3.0",
"tslint": "^5.18.0",
"typescript": "~3.4.5"
"typescript": "~3.9.7"
}
}

View file

@ -9,7 +9,10 @@ exports.config = {
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
'browserName': 'chrome',
chromeOptions: {
args: [ "--headless" ]
}
},
directConnect: true,
baseUrl: 'http://localhost:4200/',

View file

@ -4,6 +4,7 @@ import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { LocationStrategy, HashLocationStrategy } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
@ -40,17 +41,18 @@ 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 { SuppliersComponent } from './dashboard/suppliers.component';
import { MoreStuffComponent } from './dashboard/more-graphs-and-tables.component';
import { FilterPipeModule } from 'ngx-filter-pipe';
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
FilterPipeModule,
ReactiveFormsModule,
NgxPaginationModule,
BsDropdownModule.forRoot(),
TabsModule.forRoot(),
@ -69,11 +71,8 @@ import { MoreStuffComponent } from './dashboard/more-graphs-and-tables.component
BreadcrumbsComponent,
SIDEBAR_TOGGLE_DIRECTIVES,
AsideToggleDirective,
SuppliersComponent,
MoreStuffComponent,
P404Component,
P500Component,
],
providers: [
AuthGuard,

View file

@ -28,7 +28,7 @@
</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" disabled>Forgot password?</button>

View file

@ -40,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 => {

View file

@ -70,7 +70,7 @@ export class RegisterComponent {
password: signupForm.password,
display_name: customerForm.display_name,
full_name: customerForm.full_name,
postcode: customerForm.postcode,
postcode: customerForm.postcode.toUpperCase(),
year_of_birth: customerForm.year_of_birth,
};
console.log(data);
@ -114,9 +114,8 @@ export class RegisterComponent {
sector: organisationForm.sector,
street_name: organisationForm.street_name,
town: organisationForm.town,
postcode: organisationForm.postcode,
postcode: organisationForm.postcode.toUpperCase(),
};
console.log(data);
this.api
.register(data)
.subscribe(

View file

@ -89,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,
@ -142,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,

View file

@ -24,6 +24,7 @@
<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">
@ -156,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>

View file

@ -1,6 +1,6 @@
<div class="animated fadeIn">
<div class=row>
<div class="col-md-6">
<div *ngIf="weekList1" class="col-md-6">
<div class="card">
<div class="card-block">
<div class="row">
@ -42,7 +42,7 @@
</div>
</div>
</div><!--/.col-->
<div class="col-md-6">
<div *ngIf="weekList2" class="col-md-6">
<div class="card">
<div class="card-block">
<div class="row">
@ -84,7 +84,7 @@
</div>
</div>
</div><!--/.col-->
<div class="col-md-6">
<div *ngIf="weekList3" class="col-md-6">
<div class="card">
<div class="card-block">
<div class="row">
@ -126,7 +126,7 @@
</div>
</div>
</div><!--/.col-->
<div class="col-md-6">
<div *ngIf="weekList4" class="col-md-6">
<div class="card">
<div class="card-block">
<div class="row">

View file

@ -17,13 +17,13 @@
<div id="stacked-bar" dx-chart="chartOptions"></div>
</div> -->
</div><!--/.col-->
<div *ngIf="showCategoryDoughnutChart" class="col-xl-6">
<div class="card">
<!--<div *ngIf="showCategoryDoughnutChart" class="col-xl-6">
<div class="card"> -->
<!-- <body style="background-color:rgb(0,0,0);"> -->
<div class="card-block">
<!-- <div class="card-block">
<div class="row">
<div class="col-12">
<h4 class="card-title mb-0">Weekly Spending by Category</h4>
<h4 class="card-title mb-0">Spending by Category</h4>
</div>
</div>
<div class="chart-wrapper">
@ -31,15 +31,15 @@
[datasets]="doughnutChartDataCategory"
[labels]="doughnutChartLabelsCategory"
[options]="doughnutChartOptionsCategory"
[colors]= "doughnutChartColoursCategory"
[legend]="chartLegend"
[chartType]="chartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
</div> -->
<!-- </body> -->
</div>
</div><!--/.col-->
<!-- </div> --><!--/.col-->
<div *ngIf="showEssentialBarChart" class="col-xl-6">
<div class="card">
<div class="card-block">
@ -60,163 +60,56 @@
</div>
</div>
</div><!--/.col-->
<div *ngIf="showCategoryBarChart" class="col-xl-6">
<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">Monthly Spending by Category</h4>
<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 class="chart-wrapper">
<canvas baseChart
[datasets]="barChartDataCategory"
[labels]="barChartLabelsCategory"
[options]="barChartOptionsCategory"
[legend]="barChartLegendCategory"
[chartType]="barChartTypeCategory"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
</div><!--/.row-->
</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">Weekly Purchase No.</h4>
</div><!--/.col-->
</div><!--/.row-->
<div class="chart-wrapper">
<ul class="horizontal-bars type-2">
<li>
<span class="title">This Week</span>
<span class="value">{{ (weekPurchaseList.first || 0 ) }} <span class="text-muted small">
({{ (weekPurchaseList.first || 0 ) / weekPurchaseList.max | 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]="(weekPurchaseList.first || 0 ) / weekPurchaseList.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</li>
<li>
<span class="title">Last Week</span>
<span class="value">{{ weekPurchaseList.second || 0 }} <span class="text-muted small">
({{ (weekPurchaseList.second || 0 ) / weekPurchaseList.max | 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]="(weekPurchaseList.second || 0 ) / weekPurchaseList.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</li>
<li>
<span class="title">Week Maximum</span>
<span class="value">{{ weekPurchaseList.max || 0 }} <span class="text-muted small">
(100%)</span></span>
<div class="bars">
<div class="progress" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: 100%" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</li>
<li>
<span class="title">Weekly Average</span>
<span class="value">{{ (weekPurchaseList.sum / weekPurchaseList.count) || 0 | number:'1.0-0'}} <span class="text-muted small">
({{ ((weekPurchaseList.sum / weekPurchaseList.count) || 0) / weekPurchaseList.max | 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]="((weekPurchaseList.sum / weekPurchaseList.count) || 0) / weekPurchaseList.max | percent:'1.0-0'" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</li>
</ul>
</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 Purchases by Category</h4>
</div><!--/.col-->
</div><!--/.row-->
<div class="chart-wrapper">
<canvas baseChart class="chart"
[datasets]="barChartDataCategory"
[labels]="barChartLabelsCategory"
[options]="barChartOptionsCategory"
[legend]="barChartLegendCategory"
[chartType]="barChartTypeCategory"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
<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>
</div><!--/.col-->
<div class="col-12">
<div class="card">
<div class="card-block">
<div class="row">
<div class="col-12">
<h4 class="card-title float-left mb-0">Spend by company and Industrial sector</h4>
</div><!--/.col-->
</div><!--/.row-->
<div class="chart-wrapper">
<canvas baseChart class="chart"
[datasets]="lineChartData"
[labels]="lineChartLabels"
[options]="lineChartOptions"
[legend]="lineChartLegend"
[chartType]="lineChartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
<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>
</div><!--/.col-->
</div><!--/.row-->
</div>
</div>
</div>

View file

@ -2,16 +2,17 @@ import { Directive, Component, OnInit } from '@angular/core';
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 { SuppliersComponent } from '../dashboard/suppliers.component';
import { MoreStuffComponent } from '../dashboard/more-graphs-and-tables.component';
// import { StackedBarChartComponent } from '../panels/stacked-bar.component';
interface RecurSupplierData {
interface SuppliersComponent {
name : string;
}
@ -38,6 +39,22 @@ export class DashboardCustomerComponent implements OnInit {
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: {
@ -68,11 +85,12 @@ export class DashboardCustomerComponent implements OnInit {
responsive: true,
scales:{
xAxes:[{
stacked:true
scaleLabel: {
display:true,
},
stacked:true,
}],
yAxes:[{
stacked:true
}]
}
};
public barChartTypeEssential:string = 'horizontalBar';
@ -100,7 +118,22 @@ export class DashboardCustomerComponent implements OnInit {
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 = {
@ -115,81 +148,6 @@ export class DashboardCustomerComponent implements OnInit {
totalCategoryLimit: number = 10;
totalCategoryList: any[]=[];
public lineChartData: ChartDataSets[] = [
{ data: [65, 59, 80, 81, 56, 55, 40], label: 'Series A' },
{ data: [28, 48, 40, 19, 86, 27, 90], label: 'Series B' },
{ data: [180, 480, 770, 90, 1000, 270, 400], label: 'Series C', yAxisID: 'y-axis-1' }
];
public lineChartLabels: Label[] = ['January', 'February', 'March', 'April', 'May', 'June', 'July','August','September','October','November','December'];
public lineChartOptions: (ChartOptions & { annotation: any }) = {
responsive: true,
scales: {
// We use this empty structure as a placeholder for dynamic theming.
xAxes: [{}],
yAxes: [
{
id: 'y-axis-0',
position: 'left',
},
{
id: 'y-axis-1',
position: 'right',
gridLines: {
color: 'rgba(255,0,0,0.3)',
},
ticks: {
fontColor: 'red',
}
}
]
},
annotation: {
annotations: [
{
type: 'line',
mode: 'vertical',
scaleID: 'x-axis-0',
value: 'March',
borderColor: 'orange',
borderWidth: 2,
label: {
enabled: true,
fontColor: 'orange',
content: 'LineAnno'
}
},
],
},
};
public lineChartColors: 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 lineChartLegend = true;
public lineChartType = 'line';
// Graph widgets
public widgetList = [

View file

@ -1,3 +1,4 @@
<div class="animated fadeIn">
<snippet-bar-org></snippet-bar-org>
<div class="row">
@ -11,68 +12,102 @@
</div><!--/.col-->
</div><!--/.row-->
<panel-graph></panel-graph>
<div class="card">
<div class="card-block">
<div class="row">
<div class="col-12">
<h4 class="card-title float-left mb-0">Spend by company and Industrial sector</h4>
</div><!--/.col-->
</div><!--/.row-->
<div class="chart-wrapper">
<canvas baseChart class="chart"
[datasets]="lineChartDataSector"
[labels]="lineChartLabelsSector"
[options]="lineChartOptionsSector"
[legend]="lineChartLegendSector"
[chartType]="lineChartTypeSector"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
</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">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=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">Weekly Spending by Category</h4>
<h4 class="card-title mb-0">Number of Essential Purchases</h4>
</div>
</div>
<div class="chart-wrapper">
<div *ngIf="showEssentialBarChart" class="chart-wrapper">
<canvas baseChart class="chart"
[datasets]="doughnutChartDataCategory"
[labels]="doughnutChartLabelsCategory"
[options]="doughnutChartOptionsCategory"
[legend]="chartLegend"
[chartType]="chartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
[datasets]="barChartDataEssential"
[labels]="barChartLabelsEssential"
[options]="barChartOptionsEssential"
[chartType]="barChartTypeEssential"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</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>
<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>

View file

@ -44,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',
@ -56,6 +63,12 @@ export class DashboardComponent {
title: 'Purchases Last 30 Days',
dataType: DataType.currency,
},
{
type: 'graph',
name: 'purchases_last_quart;',
title: 'Purchases Last Quart',
dataType: DataType.currency,
},
];
disableCategoryButton: boolean = false;
@ -67,6 +80,23 @@ export class DashboardComponent {
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: {
@ -84,13 +114,13 @@ export class DashboardComponent {
public purchaseNotEssential: number;
public purchaseEssential: number;
public showEssentialBarChart = false;
public showEssentialBarChart:boolean = false;
public showCategoryBarChart = false;
public showCategoryDoughnutChart = false;
public barChartDataEssential:any[]=[
{data: 0, label: 'Essential', stack: '1'},
{data: 0, label: 'Non-Essential', stack: '1'},
public barChartDataEssential: ChartDataSets[] = [
{data: [0], label: 'Essential', stack: '1'},
{data: [0], label: 'Non-Essential', stack: '1'},
];
public barChartLabelsEssential:string[] = ['All Purchases'];
public barChartOptionsEssential:any = {
@ -105,6 +135,22 @@ export class DashboardComponent {
}
};
public barChartTypeEssential:string = 'horizontalBar';
public barChartColoursCategory: any[] = [
{
backgroundColor:[
'#ffa1b5',
'#3cde52',
'#52afed',
'#c133e3',
'#f7fa08',
'#75152d',
'#ee12ee',
'#15eaea',
'#eaa015',
'#ea1515',
'#2d4fcc'
]
}];
public barChartOptionsCategory:any = {
scaleShowVerticalLines: false,
@ -219,17 +265,11 @@ export class DashboardComponent {
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;
}
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');
@ -238,6 +278,17 @@ export class DashboardComponent {
);
}
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]);
@ -257,21 +308,7 @@ export class DashboardComponent {
}
private setChartDataSector(dataSec: any) {
this.barChartLabelsCategory = Object.keys(dataSec);
let lineChartDataSectorInitial = Object.keys(dataSec).map(key => dataSec[key]);
this.lineChartDataSector = [
{data: lineChartDataSectorInitial, 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 () {

View file

@ -5,7 +5,7 @@ import { ChartsModule } from 'ng2-charts';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { NgxPaginationModule } from 'ngx-pagination';
import { AgmCoreModule, GoogleMapsAPIWrapper } from '@agm/core';
import { AgmJsMarkerClustererModule } from '@agm/js-marker-clusterer';
import { AgmMarkerClustererModule } from '@agm/markerclusterer';
import { ModalModule } from 'ngx-bootstrap/modal';
import { CurrencyPipe } from '@angular/common';
@ -18,6 +18,8 @@ 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';
@ -26,6 +28,7 @@ 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';
@ -35,6 +38,9 @@ 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';
@ -51,7 +57,7 @@ import { environment } from '../../environments/environment';
AgmCoreModule.forRoot({
apiKey: environment.mapApiKey
}),
AgmJsMarkerClustererModule,
AgmMarkerClustererModule,
BsDropdownModule,
NgxPaginationModule,
DashboardRoutingModule,
@ -69,6 +75,9 @@ import { environment } from '../../environments/environment';
TransactionLogComponent,
CategoryMonthComponent,
TransactionResultComponent,
SupplierResultComponent,
WardResultComponent,
MetaTypeResultComponent,
PayrollLogComponent,
PayrollResultComponent,
LeaderboardComponent,
@ -82,6 +91,9 @@ import { environment } from '../../environments/environment';
GraphPanel,
PiePanel,
OrgPiePanel,
BubbleChartComponent,
SuppliersComponent,
MoreStuffComponent,
],
providers: [
CurrencyPipe,

View file

@ -95,7 +95,7 @@ const routes: Routes = [
{
path: 'more-graphs-and-tables',
component: MoreStuffComponent,
data: { title: 'More Stuff'}
data: { title: 'Infographics'}
}
],
}

View file

@ -22,7 +22,7 @@ export class LeaderboardComponent implements OnInit {
public paginateConfig: PaginationInstance = {
id: 'leadpaginate',
itemsPerPage: 10,
itemsPerPage: 20,
currentPage: 1,
totalItems: 0
};
@ -70,7 +70,7 @@ export class LeaderboardComponent implements OnInit {
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

View file

@ -39,9 +39,8 @@
[latitude]="lat"
[longitude]="lng"
[zoom]="zoom"
[scaleControl]="true"
(idle)="viewBoundsChanged()">
<agm-marker-cluster maxZoom="13" imagePath="https://raw.githubusercontent.com/googlemaps/v3-utility-library/master/markerclustererplus/images/m">
<agm-marker-cluster maxZoom="13">
<agm-marker
*ngFor="let m of markers"
[latitude]="m.latitude"

View file

@ -1,28 +1,136 @@
<script src="node_modules/chart.js/src/chart.js"></script>
<div class="animated fadeIn">
<!-- <div class="form-group row"> -->
<div class="card">
<div class="card-block">
<div class="row">
<div class="col-12">
<h4 class="card-title mb-0">Spend by Company Type</h4>
</div>
</div>
<div style="display: block">
<canvas baseChart
[datasets]="bubbleChartDataCategory"
[options]="bubbleChartOptionsCategory"
[colors]="sampleBubbleChartColors"
[labels]="bubbleChartLabelsCategory"
[legend]="chartLegend"
[chartType]="chartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)">
<!--bootstrapColours-->
</canvas>
</div>
<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>
<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>

View file

@ -1,140 +1,335 @@
import { Component, OnInit, AfterViewInit, Input, Output, EventEmitter, ViewChild, TemplateRef } from '@angular/core';
import { ApiService } from '../providers/api-service';
import { ChartOptions, ChartType, ChartDataSets } from 'chart.js';
import { Color } from 'ng2-charts';
import { CurrencyPipe } from '@angular/common';
import { DataType } from '../shared/data-types.enum';
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';
import { BubbleChartComponent } from '../panels/bubble-panel';
import { AgmCoreModule } from '@agm/core';
import { BsModalService, ModalDirective } from 'ngx-bootstrap/modal';
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
// interface RecurSupplierData {
// name : string;
// }
@Component({
templateUrl: 'more-graphs-and-tables.component.html',
})
export class MoreStuffComponent implements OnInit {
// @Input() public recurList: Array<RecurSupplierData>;
@Output() public onClick = new EventEmitter();
@Input() public categories: any;
public recurClick(event: any): void {
this.onClick.emit( event );
}
// 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,
) { }
ngOnInit(): void {
) {
let now = moment();
this.filterTo = now.format('YYYY-MM-DD');
now.subtract(1, 'months');
this.filterFrom = now.format('YYYY-MM-DD');
this.tableSummary();
}
// main vars
ngOnInit(): void {
this.loadData();
}
public bootstrapColours: string[] = ['bg-primary', 'bg-secondary', 'bg-success',
'bg-danger', 'bg-warning', 'bg-info'];
public loadData() {
this.tableSummary();
this.loadYearSpend();
this.loadSupplierBubble();
this.loadSupplierHistory();
}
// REAL chart data
public showLegend = true;
public chartType = 'bubble';
public chartLegend = true;
public bubbleChartDataCategory: any[] = [
/*
* 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: [
{ 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',
},
{
data: [
{ x: 10, y: 2, r: 10 },
{ x: 15, y: 1, r: 15 },
{ x: 26, y: 7, r: 23 },
{ x: 5, y: 8, r: 8 },
],
label: ["Series B"],
backgroundColor: 'green',
borderColor: 'blue',
hoverBackgroundColor: 'purple',
hoverBorderColor: 'red',
},
data: [],
label: ["Spend"],
borderColor: 'blue',
hoverBorderColor: 'black',
radius: 5,
},
];
public bubbleChartLabelsCategory: string[] = [];
public bubbleChartOptionsCategory:any = {
public supplierBubbleChartLabels: string[] = [];
public supplierBubbleChartOptions: any = {
responsive: true,
scales: {
xAxes: [
{
ticks: {
min: 0,
}
}
],
yAxes: [
{
ticks: {
min: 0,
}
}
]
},
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.tooltipLabelCallback(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 setChartData(dataCat: any) {
// now we just need some data and it will display!
}
public chartClicked({ event, active }: { event: MouseEvent, active: {}[] }): void {
console.log(event, active);
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);
}
)
}
public chartHovered({ event, active }: { event: MouseEvent, active: {}[] }): void {
console.log(event, active);
}
// functions
private loadYearSpend() {
this.api.loadMiscUrl('organisation/external/year_spend', {
from: this.filterFrom,
to: this.filterTo,
}).subscribe(
result => {
let value_data = [];
let count_data = [];
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');
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;
}
)
}
// SAMPLE chart data
public sampleBubbleChartColors: Color[] = [
public yearSpendChartData: any[] = [
{
backgroundColor: [
'red',
'green',
'blue',
'purple',
'yellow',
'brown',
'magenta',
'cyan',
'orange',
'pink'
]
}
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';
}

View file

@ -1,33 +1,79 @@
<script type="text/javascript" charset="utf8" src=""></script>
<div class="animated fadeIn">
<div class="form-group row">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Name</th>
<th>Postcode</th>
<th>Spend</th>
</tr>
</thead>
<tbody>
<!-- table body - name, postcode and spend data should be presented here in cost descending order -->
<tr recur-result *ngFor="let recur of recurList" tr.names="name" tr.recur="recur" (onClick)="recurClick($event, template)"></tr>
<tr>
<td>Tom's Tippity Top Toenails Ltd.</td>
<td>LA11LY</td>
<td>£250,000.00</td>
</tr>
<tr>
<td>Selena's Scratching Sticks Inc.</td>
<td>WS15PQ</td>
<td>£5.00</td>
</tr>
<tr>
<td>Big Barry and Son's Balloon Store and Clown Outfits Corp.</td>
<td>PF43RD</td>
<td>£22.00</td>
</tr>
</tbody>
</table>
<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><!--/.col-->
</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>

View file

@ -3,30 +3,69 @@ 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';
interface RecurSupplierData {
name : string;
}
import { PaginationInstance } from 'ngx-pagination';
import { FilterPipeModule } from 'ngx-filter-pipe';
@Component({
templateUrl: 'suppliers.component.html',
})
export class SuppliersComponent implements OnInit, AfterViewInit {
@Input() public recurList: Array<RecurSupplierData>;
@Output() public onClick = new EventEmitter();
@Input() public categories: any;
public perPage: number = 10;
searchText: string;
public recurClick(event: any): void {
this.onClick.emit( event );
}
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() {

View file

@ -72,9 +72,8 @@
[latitude]="lat"
[longitude]="lng"
[zoom]="zoom"
[scaleControl]="true"
(idle)="viewBoundsChanged()">
<agm-marker-cluster maxZoom="13" imagePath="https://raw.githubusercontent.com/googlemaps/v3-utility-library/master/markerclustererplus/images/m">
<agm-marker-cluster maxZoom="13">
<agm-marker
*ngFor="let m of markers"
[iconUrl]="'/assets/img/association/' + assocMap + '-map-pin.png'"

View file

@ -89,7 +89,7 @@
</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> Edit</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>
@ -101,18 +101,24 @@
<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"

View file

@ -29,13 +29,15 @@ export class TransactionLogComponent implements OnInit {
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 api: ApiService,
@ -57,6 +59,7 @@ export class TransactionLogComponent implements OnInit {
ngOnInit(): void {
this.loadTransactions(1);
this.accountType = localStorage.getItem('usertype');
}
loadTransactions(logPage: number) {
@ -162,4 +165,7 @@ export class TransactionLogComponent implements OnInit {
);
}
toggleShowMeta() {
this.showMeta = !this.showMeta;
}
}

View file

@ -38,6 +38,14 @@
</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']">
<div class="row no-gutters align-items-center">
@ -86,7 +94,7 @@
</div>
</a>
</li>
<li class="nav-item">
<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>
@ -94,7 +102,7 @@
</div>
</a>
</li>
<li class="nav-item">
<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>
@ -102,14 +110,6 @@
</div>
</a>
</li>
<li 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">Bubble Charts</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">

View file

@ -4,8 +4,7 @@ import { Color } from 'ng2-charts';
@Component({
selector: 'app-bubble-chart',
templateUrl: './bubble-chart.component.html',
styleUrls: ['./bubble-chart.component.scss']
templateUrl: './bubble-panel.component.html',
})
export class BubbleChartComponent implements OnInit {
public bubbleChartOptions: ChartOptions = {
@ -95,4 +94,4 @@ export class BubbleChartComponent implements OnInit {
const data = Array.apply(null, { length: numberOfPoints }).map(r => this.randomPoint(30));
this.bubbleChartData[0].data = data;
}
}
}

View file

@ -1,19 +0,0 @@
<div class="card">
<div class="card-block">
<div class="row">
<div class="col-12">
<h4 class="card-title mb-0">All Purchases</h4>
</div>
</div>
<div class="chart-wrapper">
<canvas baseChart class="chart"
[data]="doughnutChartDataLocal"
[labels]="doughnutChartLabelsLocal"
[legend]="chartLegend"
[chartType]="chartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
</div>

View file

@ -1,84 +0,0 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import 'dist/chartjs-chart-financial/chartjs-chart-financial';
import * as luxon from 'luxon';
import 'chartjs-adapter-luxon';
import { ChartOptions } from 'chart.js';
import { Color, BaseChartDirective } from 'ng2-charts';
@Component({
selector: 'app-financial-chart',
templateUrl: './financial-chart.component.html',
styleUrls: ['./financial-chart.component.css']
})
export class FinancialChartComponent implements OnInit {
barCount = 60;
initialDateStr = '01 Apr 2017 00:00 Z';
public financialChartData = [
{
label: 'CHRT - Chart.js Corporation',
data: this.getRandomData(this.initialDateStr, this.barCount)
},
];
public financialChartOptions: ChartOptions = {
responsive: true,
maintainAspectRatio: false,
};
public financialChartColors: Color[] = [
{
borderColor: 'black',
backgroundColor: 'rgba(255,0,0,0.3)',
},
];
public financialChartLegend : Boolean;
public chartLegend : Boolean;
public financialChartType = 'candlestick';
public chartType : string;
public financialChartPlugins = [];
@ViewChild(BaseChartDirective, { static: true }) chart: BaseChartDirective;
constructor() { }
ngOnInit() {
this.financialChartType = 'candlestick';
this.chartType=this.financialChartType;
this.financialChartLegend = this.chartLegend;
}
randomNumber(min: number, max: number) {
return Math.random() * (max - min) + min;
}
randomBar(date: luxon.DateTime, lastClose: number) {
const open = this.randomNumber(lastClose * 0.95, lastClose * 1.05);
const close = this.randomNumber(open * 0.95, open * 1.05);
const high = this.randomNumber(Math.max(open, close), Math.max(open, close) * 1.1);
const low = this.randomNumber(Math.min(open, close) * 0.9, Math.min(open, close));
return {
t: date.valueOf(),
o: open,
h: high,
l: low,
c: close
};
}
getRandomData(dateStr: string, count: number) {
let date = luxon.DateTime.fromRFC2822(dateStr);
const data = [this.randomBar(date, 30)];
while (data.length < count) {
date = date.plus({ days: 1 });
if (date.weekday <= 5) {
data.push(this.randomBar(date, data[data.length - 1].c));
}
}
return data;
}
update() {
// candlestick vs ohlc
this.financialChartType = this.financialChartType === 'candlestick' ? 'ohlc' : 'candlestick';
}
}

View file

@ -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"

View file

@ -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,

View file

@ -2,13 +2,14 @@
<div class="card-block">
<div class="row">
<div class="col-12">
<h4 class="card-title mb-0">All Purchases</h4>
<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)"

View file

@ -12,9 +12,43 @@ import { ChartData } from '../_interfaces/chart-data';
export class OrgPiePanel implements OnInit {
public chartType = 'doughnut';
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(

View file

@ -2,13 +2,14 @@
<div class="card-block">
<div class="row">
<div class="col-12">
<h4 class="card-title mb-0">All Purchases</h4>
<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)"

View file

@ -12,10 +12,41 @@ import { ChartData } from '../_interfaces/chart-data';
export class PiePanel implements OnInit {
public chartType = 'doughnut';
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,

View file

@ -1,19 +0,0 @@
<div class="card">
<div class="card-block">
<div class="row">
<div class="col-12">
<h4 class="card-title mb-0">All Purchases</h4>
</div>
</div>
<div class="chart-wrapper">
<canvas baseChart class="chart"
[data]="doughnutChartDataLocal"
[labels]="doughnutChartLabelsLocal"
[legend]="chartLegend"
[chartType]="chartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
</div>

View file

@ -1,72 +0,0 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { SingleDataSet, Label } from 'ng2-charts';
import { ChartType } from 'chart.js';
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: 'polar-area',
templateUrl: 'polar-panel.component.html',
})
export class PolarAreaChartComponent implements OnInit {
// PolarArea
public chartType : 'polar-area';
public polarAreaChartLabels: Label[];
public polarAreaChartData: SingleDataSet;
public chartLegend : Boolean;
public polarAreaLegend : Boolean;
public polarChartLabelsLocal: string[] = [];
public polarChartDataLocal: number[] = [];
public polarAreaChartType: ChartType = 'polarArea';
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 );
}
);
}
ngOnInit() {
this.polarAreaLegend = this.chartLegend;
this.polarAreaLegend = true;
}
private setChartData(dataLocal: any) {
this.polarChartDataLocal = Object.keys(dataLocal).map(key => dataLocal[key]);
// setTimeout is currently a workaround for ng2-charts labels
setTimeout(() => this.polarChartLabelsLocal = Object.keys(dataLocal), 0);
}
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({ event, active }: { event: MouseEvent, active: {}[] }): void {
console.log(event, active);
}
public chartHovered({ event, active }: { event: MouseEvent, active: {}[] }): void {
console.log(event, active);
}
}

View file

@ -1,19 +0,0 @@
<div class="card">
<div class="card-block">
<div class="row">
<div class="col-12">
<h4 class="card-title mb-0">All Purchases</h4>
</div>
</div>
<div class="chart-wrapper">
<canvas baseChart class="chart"
attr.[data]="doughnutChartDataLocal"
attr.[labels]="doughnutChartLabelsLocal"
attr.[legend]="chartLegend"
attr.[chartType]="chartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
</div>

View file

@ -1,68 +0,0 @@
import { NgModule, enableProdMode } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
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';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { DxChartModule } from 'devextreme-angular';
if(!/localhost/.test(document.location.host)) {
enableProdMode();
}
@Component({
selector: 'stacked-bar',
templateUrl: 'stacked-bar.component.html',
})
@NgModule({
imports: [
BrowserModule,
DxChartModule
],
declarations: [StackedBarChartComponent],
bootstrap: [StackedBarChartComponent]
})
export class StackedBarChartComponent {
public chartType: 'stacked-bar';
public chartLegend = true;
public stackedBarChartDataLocal : number[] = [];
public stackedBarChartLabelsLocal : string[] = [];
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 {
console.log("stacked bar graph tried to initialise");
}
private setChartData(dataLocal: any) {
this.stackedBarChartDataLocal = Object.keys(dataLocal).map(key => dataLocal[key]);
// setTimeout is currently a workaround for ng2-charts labels
setTimeout(() => this.stackedBarChartLabelsLocal = Object.keys(dataLocal), 0);
}
customizeTooltip(arg: any) {
return {
text: arg.percentText + ' - ' + arg.valueText
};
}
}
platformBrowserDynamic().bootstrapModule(StackedBarChartComponent);

View file

@ -144,12 +144,28 @@ export class ApiService {
);
}
public externalSuppliers() {
public loadMiscUrl(extra_url, extraArgs = {}) {
const key = this.sessionKey;
return this.http.post<any>(
this.apiUrl + '/organisation/external/suppliers',
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,
}
);
}

View file

@ -0,0 +1,3 @@
<td>{{type.type}}</td>
<td>{{type.count}}</td>
<td>{{type.sum | currency:'GBP':'symbol':'1.2-2' }}</td>

View 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 {
}
}

View file

@ -0,0 +1,3 @@
<td>{{supplier.name}}</td>
<td>{{supplier.postcode}}</td>
<td>{{supplier.spend | currency:'GBP':'symbol':'1.2-2' }}</td>

View 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 {
}
}

View file

@ -1,3 +1,6 @@
<td>{{transaction.seller}}</td>
<td>{{transaction.value | currency:'GBP':'symbol':'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>

View file

@ -14,6 +14,7 @@ interface TransactionData {
})
export class TransactionResultComponent implements OnInit {
@Input() public transaction: TransactionData;
@Input() public showMeta: boolean;
public transactionDate: string;
ngOnInit(): void {

View file

@ -0,0 +1,3 @@
<td>{{ward.ward}}</td>
<td>{{ward.count}}</td>
<td>{{ward.sum | currency:'GBP':'symbol':'1.2-2' }}</td>

View 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 {
}
}

View file

@ -5,7 +5,7 @@
export const environment = {
production: false,
apiUrl: 'https://dev.peartrade.org/api',
apiUrl: 'https://dev.localspend.co.uk/api',
mapApiKey: 'CHANGEME',
enableAnalytics: false,
analyticsKey: 'CHANGEME',

View file

@ -48,7 +48,7 @@ $navbar-border: (
);
$navbar-brand-width: 155px;
$navbar-brand-bg: #fff;
$navbar-brand-logo: url('../assets/img/logo.png');
$navbar-brand-logo: url('../../assets/img/logo.png');
$navbar-brand-logo-size: 70px auto;
$navbar-brand-border: (
bottom: (

View file

@ -74,7 +74,6 @@ agm-map {
}
}
.small {
padding-left: 20px;
}
&.legend {
text-align: center;

View file

@ -0,0 +1,28 @@
.spinner {
width: 40px;
height: 40px;
background-color: #333;
margin: 100px auto;
-webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;
animation: sk-rotateplane 1.2s infinite ease-in-out;
}
@-webkit-keyframes sk-rotateplane {
0% { -webkit-transform: perspective(120px) }
50% { -webkit-transform: perspective(120px) rotateY(180deg) }
100% { -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg) }
}
@keyframes sk-rotateplane {
0% {
transform: perspective(120px) rotateX(0deg) rotateY(0deg);
-webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)
} 50% {
transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
-webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)
} 100% {
transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
-webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
}
}

View file

@ -66,7 +66,6 @@ small,
.small {
font-size: $small-font-size;
font-weight: $font-weight-normal;
padding-left: 20px;
}
mark,

View file

@ -76,3 +76,4 @@
// Custom styles
@import "custom";
@import "bootstrap/spinner";