浏览代码

Database, eventbus, pluginloader fixes

master
peter 6 年前
父节点
当前提交
ee45a15d5f
共有 100 个文件被更改,包括 17753 次插入6323 次删除
  1. 1
    1
      package.json
  2. 7
    10
      src/backend/Admin.ts
  3. 47
    7
      src/backend/Eventbus.ts
  4. 5
    5
      src/backend/PluginLoader.ts
  5. 0
    60
      src/frontend/.drone.yml
  6. 1
    1
      src/frontend/.editorconfig
  7. 5
    12
      src/frontend/.gitignore
  8. 0
    6
      src/frontend/.gitmodules
  9. 63
    14
      src/frontend/README.md
  10. 110
    77
      src/frontend/angular.json
  11. 2
    2
      src/frontend/browserslist
  12. 0
    3
      src/frontend/custom-webpack.config.js
  13. 0
    23
      src/frontend/e2e/src/app.e2e-spec.ts
  14. 0
    11
      src/frontend/e2e/src/app.po.ts
  15. 1
    0
      src/frontend/e2e/tsconfig.e2e.json
  16. 20
    7
      src/frontend/karma.conf.js
  17. 14657
    6009
      src/frontend/package-lock.json
  18. 100
    57
      src/frontend/package.json
  19. 9
    9
      src/frontend/protractor.conf.js
  20. 0
    9
      src/frontend/proxy.conf.json
  21. 168
    0
      src/frontend/src/app/@core/core.module.ts
  22. 0
    0
      src/frontend/src/app/@core/data/.gitkeep
  23. 1
    0
      src/frontend/src/app/@core/data/README.md
  24. 6
    0
      src/frontend/src/app/@core/data/country-order.ts
  25. 21
    0
      src/frontend/src/app/@core/data/earning.ts
  26. 25
    0
      src/frontend/src/app/@core/data/electricity.ts
  27. 8
    0
      src/frontend/src/app/@core/data/orders-chart.ts
  28. 14
    0
      src/frontend/src/app/@core/data/orders-profit-chart.ts
  29. 5
    0
      src/frontend/src/app/@core/data/profit-bar-animation-chart.ts
  30. 8
    0
      src/frontend/src/app/@core/data/profit-chart.ts
  31. 10
    0
      src/frontend/src/app/@core/data/security-cameras.ts
  32. 4
    0
      src/frontend/src/app/@core/data/smart-table.ts
  33. 5
    0
      src/frontend/src/app/@core/data/solar.ts
  34. 5
    0
      src/frontend/src/app/@core/data/stats-bar.ts
  35. 12
    0
      src/frontend/src/app/@core/data/stats-progress-bar.ts
  36. 12
    0
      src/frontend/src/app/@core/data/temperature-humidity.ts
  37. 11
    0
      src/frontend/src/app/@core/data/traffic-bar.ts
  38. 5
    0
      src/frontend/src/app/@core/data/traffic-chart.ts
  39. 20
    0
      src/frontend/src/app/@core/data/traffic-list.ts
  40. 12
    0
      src/frontend/src/app/@core/data/user-activity.ts
  41. 21
    0
      src/frontend/src/app/@core/data/users.ts
  42. 12
    0
      src/frontend/src/app/@core/data/visitors-analytics.ts
  43. 1
    0
      src/frontend/src/app/@core/mock/README.md
  44. 29
    0
      src/frontend/src/app/@core/mock/country-order.service.ts
  45. 103
    0
      src/frontend/src/app/@core/mock/earning.service.ts
  46. 95
    0
      src/frontend/src/app/@core/mock/electricity.service.ts
  47. 65
    0
      src/frontend/src/app/@core/mock/mock-data.module.ts
  48. 155
    0
      src/frontend/src/app/@core/mock/orders-chart.service.ts
  49. 45
    0
      src/frontend/src/app/@core/mock/orders-profit-chart.service.ts
  50. 33
    0
      src/frontend/src/app/@core/mock/periods.service.ts
  51. 43
    0
      src/frontend/src/app/@core/mock/profit-bar-animation-chart.service.ts
  52. 77
    0
      src/frontend/src/app/@core/mock/profit-chart.service.ts
  53. 30
    0
      src/frontend/src/app/@core/mock/security-cameras.service.ts
  54. 432
    0
      src/frontend/src/app/@core/mock/smart-table.service.ts
  55. 12
    0
      src/frontend/src/app/@core/mock/solar.service.ts
  56. 16
    0
      src/frontend/src/app/@core/mock/stats-bar.service.ts
  57. 31
    0
      src/frontend/src/app/@core/mock/stats-progress-bar.service.ts
  58. 27
    0
      src/frontend/src/app/@core/mock/temperature-humidity.service.ts
  59. 47
    0
      src/frontend/src/app/@core/mock/traffic-bar.service.ts
  60. 16
    0
      src/frontend/src/app/@core/mock/traffic-chart.service.ts
  61. 85
    0
      src/frontend/src/app/@core/mock/traffic-list.service.ts
  62. 57
    0
      src/frontend/src/app/@core/mock/user-activity.service.ts
  63. 53
    0
      src/frontend/src/app/@core/mock/users.service.ts
  64. 57
    0
      src/frontend/src/app/@core/mock/visitors-analytics.service.ts
  65. 5
    0
      src/frontend/src/app/@core/module-import-guard.ts
  66. 0
    0
      src/frontend/src/app/@core/utils/.gitkeep
  67. 32
    0
      src/frontend/src/app/@core/utils/analytics.service.ts
  68. 11
    0
      src/frontend/src/app/@core/utils/index.ts
  69. 20
    0
      src/frontend/src/app/@core/utils/layout.service.ts
  70. 66
    0
      src/frontend/src/app/@core/utils/player.service.ts
  71. 92
    0
      src/frontend/src/app/@core/utils/state.service.ts
  72. 30
    0
      src/frontend/src/app/@theme/components/footer/footer.component.scss
  73. 17
    0
      src/frontend/src/app/@theme/components/footer/footer.component.ts
  74. 29
    0
      src/frontend/src/app/@theme/components/header/header.component.html
  75. 70
    0
      src/frontend/src/app/@theme/components/header/header.component.scss
  76. 94
    0
      src/frontend/src/app/@theme/components/header/header.component.ts
  77. 4
    0
      src/frontend/src/app/@theme/components/index.ts
  78. 33
    0
      src/frontend/src/app/@theme/components/search-input/search-input.component.scss
  79. 35
    0
      src/frontend/src/app/@theme/components/search-input/search-input.component.ts
  80. 37
    0
      src/frontend/src/app/@theme/components/tiny-mce/tiny-mce.component.ts
  81. 0
    0
      src/frontend/src/app/@theme/directives/.gitkeep
  82. 3
    0
      src/frontend/src/app/@theme/layouts/index.ts
  83. 9
    0
      src/frontend/src/app/@theme/layouts/one-column/one-column.layout.scss
  84. 26
    0
      src/frontend/src/app/@theme/layouts/one-column/one-column.layout.ts
  85. 9
    0
      src/frontend/src/app/@theme/layouts/three-columns/three-columns.layout.scss
  86. 32
    0
      src/frontend/src/app/@theme/layouts/three-columns/three-columns.layout.ts
  87. 9
    0
      src/frontend/src/app/@theme/layouts/two-columns/two-columns.layout.scss
  88. 30
    0
      src/frontend/src/app/@theme/layouts/two-columns/two-columns.layout.ts
  89. 0
    0
      src/frontend/src/app/@theme/pipes/.gitkeep
  90. 11
    0
      src/frontend/src/app/@theme/pipes/capitalize.pipe.ts
  91. 5
    0
      src/frontend/src/app/@theme/pipes/index.ts
  92. 9
    0
      src/frontend/src/app/@theme/pipes/number-with-commas.pipe.ts
  93. 14
    0
      src/frontend/src/app/@theme/pipes/plural.pipe.ts
  94. 9
    0
      src/frontend/src/app/@theme/pipes/round.pipe.ts
  95. 18
    0
      src/frontend/src/app/@theme/pipes/timing.pipe.ts
  96. 8
    0
      src/frontend/src/app/@theme/styles/_layout.scss
  97. 11
    0
      src/frontend/src/app/@theme/styles/_overrides.scss
  98. 20
    0
      src/frontend/src/app/@theme/styles/pace.theme.scss
  99. 33
    0
      src/frontend/src/app/@theme/styles/styles.scss
  100. 0
    0
      src/frontend/src/app/@theme/styles/theme.corporate.ts

+ 1
- 1
package.json 查看文件

@@ -43,7 +43,7 @@
43 43
     "sqlite3": "^4.1.0",
44 44
     "trash": "^6.0.0",
45 45
     "upgiter": "^1.0.4",
46
-    "uuid": "^3.3.2"
46
+    "uuid": "^3.3.3"
47 47
   },
48 48
   "devDependencies": {
49 49
     "@types/express": "^4.17.0",

+ 7
- 10
src/backend/Admin.ts 查看文件

@@ -5,15 +5,13 @@ import { promises as fs, mkdirSync } from "fs"
5 5
 import { RPCServer } from 'rpclibrary/js/src/Backend'
6 6
 import { AdminConf, TableDefiniton } from './Types';
7 7
 import { RPCConfigLoader } from './RPCConfigLoader';
8
-
8
+import { RPCPluginLoader } from './PluginLoader';
9 9
 import * as Path from 'path'
10
-
11 10
 import Knex = require('knex');
12 11
 import http = require('http');
13 12
 import express = require('express');
14 13
 import { TableDefinitionExporter } from './Interfaces';
15 14
 import { FrontworkEventBus } from './Eventbus';
16
-import { Plugin } from './Plugin';
17 15
 
18 16
 const logger = getLogger("admin", 'debug') 
19 17
 
@@ -22,14 +20,12 @@ implements TableDefinitionExporter {
22 20
 
23 21
     private express
24 22
     private httpServer
23
+    private pluginLoader:RPCPluginLoader = new RPCPluginLoader(this)
25 24
     private eventBus:FrontworkEventBus = new FrontworkEventBus(this)
26
-    private plugins: Plugin[] = []
27 25
     config: RPCConfigLoader<AdminConf>
28 26
     knex:Knex
29 27
 
30
-    constructor(){
31
-        this.eventBus = new FrontworkEventBus(this)
32
-    }
28
+    constructor(){}
33 29
 
34 30
     async start(){
35 31
         this.initConfig()
@@ -74,7 +70,8 @@ implements TableDefinitionExporter {
74 70
     private startWebsocket(){
75 71
         new RPCServer(20000, [
76 72
             this.config,
77
-            ...this.plugins
73
+            this.pluginLoader,
74
+            this.eventBus
78 75
         ])
79 76
     }
80 77
 
@@ -134,7 +131,7 @@ implements TableDefinitionExporter {
134 131
         logger.info("Webserver stopped")
135 132
     }
136 133
 
137
-    public async makeKnex():Promise<Knex>{
134
+    async makeKnex():Promise<Knex>{
138 135
         const conf:Knex.Config = this.config.getConfigKey("dbConf")
139 136
 
140 137
         logger.debug("Making new knex:", conf)        
@@ -157,7 +154,7 @@ implements TableDefinitionExporter {
157 154
     getTableDefinitions(): TableDefiniton[]{
158 155
         return [
159 156
             this.eventBus,
160
-            ...this.plugins
157
+            ...this.pluginLoader.getPlugins()
161 158
         ].flatMap(exporter => exporter.getTableDefinitions())
162 159
     }
163 160
 }

+ 47
- 7
src/backend/Eventbus.ts 查看文件

@@ -1,8 +1,10 @@
1 1
 import { RPCExporter } from "rpclibrary/js/src/Interfaces";
2
-import { SubscriptionResponse, ErrorResponse } from "rpclibrary/js/src/Types";
3
-import { makeSubResponse } from "rpclibrary/js/src/Utils";
2
+import { SubscriptionResponse, ErrorResponse, SuccessResponse } from "rpclibrary/js/src/Types";
4 3
 import { FrontworkAdmin } from "./Admin";
5 4
 import { TableDefinitionExporter } from "./Interfaces";
5
+import { getLogger } from 'frontblock-generic/Types';
6
+
7
+import * as uuid from 'uuid/v4'
6 8
 
7 9
 export type NotificationSeverity = 'Info' | 'Important' | 'Error'
8 10
 
@@ -22,25 +24,63 @@ export type EventbusIfc = {
22 24
     }
23 25
 } 
24 26
 
27
+const logger = getLogger("Eventbus", 'debug') 
28
+
25 29
 export class FrontworkEventBus 
26 30
 implements RPCExporter<EventbusIfc, "Eventbus">, TableDefinitionExporter {
27 31
     name = "Eventbus" as "Eventbus" 
28
-    
32
+    private subscriptions : { [uid in string]:Function } = {}
33
+
29 34
     constructor(private admin: FrontworkAdmin){
30
-        this.admin
35
+    }
36
+
37
+    async subscribeNotifications(callback) : Promise<SubscriptionResponse>{
38
+        const uid = uuid()
39
+        this.subscriptions[uid] = callback
40
+        return { result: 'Success', uuid: uid }
41
+    }
42
+
43
+    async getNotificationLog() : Promise<Notification[]>{
44
+        try{
45
+            return await this.admin.knex.select('*').from('notifications')
46
+        }catch(e){
47
+            logger.error(e)
48
+            throw e
49
+        }
50
+    }
51
+
52
+    async unsubscribeNotifications(uid:string) : Promise<SuccessResponse | ErrorResponse>{
53
+        if(!this.subscriptions[uid]) return { result: 'Error', message: "Unknown subscription" }
54
+        delete this.subscriptions[uid]
55
+        return { result: 'Success' }
56
+    }
57
+
58
+    async pushNotification(notification: Notification){
59
+        if(!notification.time) notification.time = Date.now()
60
+        logger.debug("inserting into notifications", notification)
61
+        try{
62
+            await this.admin.knex('notifications').insert(notification)
63
+        }catch(e){
64
+            logger.error(e)
65
+            throw e
66
+        }
67
+ 
68
+        Object.values(this.subscriptions).forEach(callback => {
69
+            callback(notification)
70
+        })       
31 71
     }
32 72
 
33 73
     exportRPCs(){
34 74
         return [{
35 75
             name: 'getNotificationLog' as 'getNotificationLog',
36
-            call: async () => []
76
+            call: async () => await this.getNotificationLog()
37 77
         },{
38 78
             name: 'pushNotification' as 'pushNotification',
39
-            call: async () => {}
79
+            call: async (notification: Notification) => { return await this.pushNotification(notification) }
40 80
         },{
41 81
             name: 'subscribeNotificaitons' as 'subscribeNotifications',
42 82
             hook: async (callback: Function) => {
43
-                return makeSubResponse({})
83
+                return await this.subscribeNotifications(callback)
44 84
             }
45 85
         }]
46 86
     }

+ 5
- 5
src/backend/PluginLoader.ts 查看文件

@@ -5,7 +5,7 @@ import { Plugin } from "./Plugin";
5 5
 import { FrontworkAdmin } from "./Admin";
6 6
 
7 7
 class PluginLoader {
8
-    private runningPlugins: Plugin[]
8
+    private runningPlugins: Plugin[] = []
9 9
     private pluginUpdaters:{[name in string]:Git.Updater} = {}
10 10
 
11 11
     constructor(private admin:FrontworkAdmin){}
@@ -33,9 +33,9 @@ class PluginLoader {
33 33
         //logger.warn("Cloning fb-dist/"+name+".git into ./plugins/"+name+" ..."+(force?" USING FORCE!":""))
34 34
         this.pluginUpdaters[name] = new Git.Updater({
35 35
             schema: 'https',
36
-            localPath: './dist',
36
+            localPath: './plugins/'+name,
37 37
             remoteHost: 'www.versioncontrol.me',
38
-            remotePath: 'frontwork-distribution',
38
+            remotePath: 'frontblock-distribution',
39 39
             repoName: name
40 40
         })
41 41
         const status = await this.pluginUpdaters[name].cloneRepo(force)
@@ -63,7 +63,7 @@ class PluginLoader {
63 63
 
64 64
         let evalstr = "../plugins/"+name+"/Plugin"
65 65
         const pluginClass = await eval('require')(evalstr)
66
-        const pluginObj = new pluginClass.default(this)
66
+        const pluginObj = new pluginClass.default(this.admin)
67 67
         try{
68 68
             if(pluginObj.start)
69 69
                 await pluginObj.start()
@@ -137,7 +137,7 @@ export type PluginLoaderIfc = {
137 137
     }
138 138
 }
139 139
 
140
-class RPCPluginLoader 
140
+export class RPCPluginLoader 
141 141
 extends PluginLoader
142 142
 implements RPCExporter<PluginLoaderIfc, "PluginLoader">{
143 143
 

+ 0
- 60
src/frontend/.drone.yml 查看文件

@@ -1,60 +0,0 @@
1
-kind: pipeline
2
-name: default
3
-
4
-steps:
5
-- name: restore cache
6
-  image: drillster/drone-volume-cache
7
-  settings:
8
-    restore: true
9
-    mount:
10
-      - ./node_modules
11
-  volumes:
12
-  - name: cache
13
-    path: /cache
14
-    
15
-- name: npm install
16
-  image: node:12
17
-  commands:
18
-  - npm install
19
-
20
-- name: npm run build
21
-  image: node:12
22
-  commands:
23
-  - npm run build
24
-
25
-- name: rebuild cache
26
-  image: drillster/drone-volume-cache
27
-  settings:
28
-    rebuild: true
29
-    mount:
30
-      - ./node_modules
31
-  volumes:
32
-  - name: cache
33
-    path: /cache
34
-
35
-- name: deploy static files
36
-  image: node:12
37
-  commands:
38
-  - git config --global user.email "${DRONE_COMMIT_AUTHOR_EMAIL}"
39
-  - git config --global user.name "${DRONE_COMMIT_AUTHOR}"
40
-  - git clone https://gitea.frontblock.me/fb-dist/${DRONE_REPO_NAME}.git
41
-  - cp -r ./dist/* ./${DRONE_REPO_NAME}
42
-  - cd ./${DRONE_REPO_NAME}
43
-  - git add -A
44
-  - git commit --allow-empty -m "drone taged as version ${DRONE_TAG}"
45
-  - git tag ${DRONE_TAG}
46
-  - git push https://$GIT_USER:$GIT_PASSWORD@gitea.frontblock.me/fb-dist/${DRONE_REPO_NAME}.git master ${DRONE_TAG}
47
-  environment:
48
-    GIT_USER:
49
-      from_secret: git_user
50
-    GIT_PASSWORD:
51
-      from_secret: git_password
52
-  when:
53
-    event:
54
-    - tag
55
-
56
-
57
-volumes:
58
-- name: cache 
59
-  host:
60
-    path: /tmp

+ 1
- 1
src/frontend/.editorconfig 查看文件

@@ -1,4 +1,4 @@
1
-# Editor configuration, see https://editorconfig.org
1
+# Editor configuration, see http://editorconfig.org
2 2
 root = true
3 3
 
4 4
 [*]

+ 5
- 12
src/frontend/.gitignore 查看文件

@@ -1,19 +1,11 @@
1
-# See http://help.github.com/ignore-files/ for more about ignoring files.
2
-
3 1
 # compiled output
4 2
 /dist
5 3
 /tmp
6 4
 /out-tsc
7
-# Only exists if Bazel was run
8
-/bazel-out 
9 5
 
10 6
 # dependencies
11 7
 /node_modules
12 8
 
13
-# profiling files
14
-chrome-profiler-events.json
15
-speed-measure-plugin.json
16
-
17 9
 # IDEs and editors
18 10
 /.idea
19 11
 .project
@@ -29,7 +21,6 @@ speed-measure-plugin.json
29 21
 !.vscode/tasks.json
30 22
 !.vscode/launch.json
31 23
 !.vscode/extensions.json
32
-.history/*
33 24
 
34 25
 # misc
35 26
 /.sass-cache
@@ -37,12 +28,14 @@ speed-measure-plugin.json
37 28
 /coverage
38 29
 /libpeerconnection.log
39 30
 npm-debug.log
40
-yarn-error.log
41 31
 testem.log
42 32
 /typings
33
+/docs
34
+
35
+# e2e
36
+/e2e/*.js
37
+/e2e/*.map
43 38
 
44 39
 # System Files
45 40
 .DS_Store
46 41
 Thumbs.db
47
-
48
-src/assets/*.js

+ 0
- 6
src/frontend/.gitmodules 查看文件

@@ -1,6 +0,0 @@
1
-[submodule "src/app/paymentmanager"]
2
-	path = src/app/paymentmanager
3
-	url = ssh://git@gitea.frontblock.me:2222/fb-plugin/paymentmanager.git
4
-[submodule "src/app/wallet"]
5
-	path = src/app/wallet
6
-	url = ssh://git@gitea.frontblock.me:2222/fb-plugin/wallet.git

+ 63
- 14
src/frontend/README.md 查看文件

@@ -1,27 +1,76 @@
1
-# Dashboard
1
+# ngx-admin [<img src="https://i.imgur.com/oMcxwZ0.png" alt="Eva Design System" height="20px" />](https://eva.design) [![Build Status](https://travis-ci.org/akveo/ngx-admin.svg?branch=master)](https://travis-ci.org/akveo/ngx-admin) [![Dependency Status](https://david-dm.org/akveo/ngx-admin/status.svg)](https://david-dm.org/akveo/ng2-admin)
2 2
 
3
-This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.0.
3
+[Who uses ngx-admin?](https://github.com/akveo/ngx-admin/issues/1645)| [Documentation](https://akveo.github.io/ngx-admin/?utm_source=github&utm_medium=ngx_admin_readme&utm_campaign=themes) | [Installation Guidelines](https://akveo.github.io/ngx-admin/docs/getting-started/what-is-ngxadmin?utm_source=github&utm_medium=ngx_admin_readme&utm_campaign=themes)
4 4
 
5
-## Development server
5
+# Admin template based on Angular 8+ and <a href="https://github.com/akveo/nebular">Nebular</a>
6
+<a target="_blank" href="http://akveo.com/ngx-admin/pages/dashboard?theme=corporate&utm_source=github&utm_medium=ngx_admin_readme&utm_campaign=main_pic"><img src="https://i.imgur.com/mFdqvgG.png"/></a>
6 7
 
7
-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.
8
+### Backend Integration Bundles
9
+Easy way to integrate ngx-admin with backend (.NET, Node.js, Java etc.).
8 10
 
9
-## Code scaffolding
11
+<a target="_blank" href="https://store.akveo.com/collections/all/?utm_source=github&utm_medium=ngx_admin_readme">
12
+  <img src="https://i.imgur.com/oiQHhop.png"/>
13
+</a>
10 14
 
11
-Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
15
+[Checkout our Store](https://store.akveo.com/collections/all/?utm_source=github&utm_medium=ngx_admin_readme) for ready to use Backend Bundles.
12 16
 
13
-## Build
17
+### With 4 stunning visual themes
14 18
 
15
-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.
16 19
 
17
-## Running unit tests
18 20
 
19
-Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
21
+#### Default
22
+<a target="_blank" href="http://akveo.com/ngx-admin/pages/dashboard?theme=default&utm_source=github&utm_medium=ngx_admin_readme&utm_campaign=themes"><img src="https://i.imgur.com/Kn3xDKQ.png"/></a>
20 23
 
21
-## Running end-to-end tests
24
+#### Dark
25
+<a target="_blank" href="http://akveo.com/ngx-admin/pages/dashboard?theme=dark&utm_source=github&utm_medium=ngx_admin_readme&utm_campaign=themes"><img src="https://i.imgur.com/FAn5iXY.png"/></a>
22 26
 
23
-Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
27
+#### Cosmic
28
+<a target="_blank" href="http://akveo.com/ngx-admin/pages/dashboard?theme=cosmic&utm_source=github&utm_medium=ngx_admin_readme&utm_campaign=themes"><img src="https://i.imgur.com/iJu2YDF.png"/></a>
24 29
 
25
-## Further help
30
+#### Corporate
31
+<a target="_blank" href="http://akveo.com/ngx-admin/pages/dashboard?theme=corporate&utm_source=github&utm_medium=ngx_admin_readme&utm_campaign=themes"><img src="https://i.imgur.com/GpUt6NW.png"/></a>
26 32
 
27
-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).
33
+### What's included:
34
+
35
+- Angular 8+ & Typescript
36
+- Bootstrap 4+ & SCSS
37
+- Responsive layout
38
+- RTL support
39
+- High resolution
40
+- Flexibly configurable themes with **hot-reload** (3 themes included)
41
+- Authentication module with multiple providers
42
+- 40+ Angular Components
43
+- 60+ Usage Examples
44
+
45
+### Demo
46
+
47
+<a target="_blank" href="http://akveo.com/ngx-admin/">Live Demo</a>
48
+
49
+## Documentation
50
+This template is using [Nebular](https://github.com/akveo/nebular) modules set, [here you can find documentation and other useful articles](https://akveo.github.io/nebular/docs/guides/install-based-on-starter-kit).
51
+
52
+### Empty starter kit
53
+Don't need all the pages and modules and just looking for an empty starter kit for your next project? Check out our [starter-kit branch](https://github.com/akveo/ngx-admin/tree/starter-kit).
54
+
55
+## BrowserStack
56
+This project runs its tests on multiple desktop and mobile browsers using [BrowserStack](http://www.browserstack.com).
57
+
58
+<img src="https://cloud.githubusercontent.com/assets/131406/22254249/534d889e-e254-11e6-8427-a759fb23b7bd.png" height="40" />
59
+
60
+## More from Akveo
61
+
62
+- [Eva Icons](https://github.com/akveo/eva-icons) - 480+ beautiful Open Source icons
63
+- [Nebular](https://github.com/akveo/nebular) - Angular Components, Auth and Security
64
+
65
+### How can I support developers?
66
+- Star our GitHub repo :star:
67
+- Create pull requests, submit bugs, suggest new features or documentation updates :wrench:
68
+- Follow us on [Twitter](https://twitter.com/akveo_inc) :feet:
69
+- Like our page on [Facebook](https://www.facebook.com/akveo/) :thumbsup:
70
+
71
+### Looking for engineering services? 
72
+Visit [our homepage](http://akveo.com/) or simply leave us a message to [contact@akveo.com](mailto:contact@akveo.com). We will be happy to work with you!
73
+
74
+### From Developers
75
+Made with :heart: by [Akveo team](http://akveo.com/). Follow us on [Twitter](https://twitter.com/akveo_inc) to get the latest news first!
76
+We're always happy to receive your feedback!

+ 110
- 77
src/frontend/angular.json 查看文件

@@ -3,147 +3,180 @@
3 3
   "version": 1,
4 4
   "newProjectRoot": "projects",
5 5
   "projects": {
6
-    "dashboard": {
7
-      "projectType": "application",
8
-      "schematics": {
9
-        "@schematics/angular:component": {
10
-          "style": "scss"
11
-        }
12
-      },
6
+    "ngx-admin-demo": {
13 7
       "root": "",
14 8
       "sourceRoot": "src",
15
-      "prefix": "app",
9
+      "projectType": "application",
16 10
       "architect": {
17 11
         "build": {
18
-          "builder": "@angular-builders/custom-webpack:browser",
12
+          "builder": "@angular-devkit/build-angular:browser",
19 13
           "options": {
20
-            "customWebpackConfig": {"path": "./custom-webpack.config.js"},
21
-
14
+            "preserveSymlinks": true,
15
+            "rebaseRootRelativeCssUrls": true,
22 16
             "outputPath": "dist",
23 17
             "index": "src/index.html",
24 18
             "main": "src/main.ts",
19
+            "tsConfig": "src/tsconfig.app.json",
25 20
             "polyfills": "src/polyfills.ts",
26
-            "tsConfig": "tsconfig.app.json",
27
-            
28
-            "aot": false,
29 21
             "assets": [
22
+              "src/assets",
30 23
               "src/favicon.ico",
31
-              "src/assets"
24
+              "src/favicon.png",
25
+              {
26
+                "glob": "**/*",
27
+                "input": "node_modules/leaflet/dist/images",
28
+                "output": "/assets/img/markers"
29
+              }
32 30
             ],
33 31
             "styles": [
34
-              "src/styles.scss",
35
-              "node_modules/@clr/icons/clr-icons.min.css",
36
-              "node_modules/@clr/ui/clr-ui-dark.min.css"
32
+              "node_modules/bootstrap/dist/css/bootstrap.css",
33
+              "node_modules/typeface-exo/index.css",
34
+              "node_modules/roboto-fontface/css/roboto/roboto-fontface.css",
35
+              "node_modules/ionicons/scss/ionicons.scss",
36
+              "node_modules/@fortawesome/fontawesome-free/css/all.css",
37
+              "node_modules/socicon/css/socicon.css",
38
+              "node_modules/nebular-icons/scss/nebular-icons.scss",
39
+              "node_modules/angular-tree-component/dist/angular-tree-component.css",
40
+              "node_modules/pace-js/templates/pace-theme-flash.tmpl.css",
41
+              "node_modules/leaflet/dist/leaflet.css",
42
+              "src/app/@theme/styles/styles.scss"
37 43
             ],
38 44
             "scripts": [
39
-              "node_modules/systemjs/dist/system.js",
40
-              "node_modules/@webcomponents/custom-elements/custom-elements.min.js",
41
-              "node_modules/@clr/icons/clr-icons.min.js"
45
+              "node_modules/pace-js/pace.min.js",
46
+              "node_modules/tinymce/tinymce.min.js",
47
+              "node_modules/tinymce/themes/modern/theme.min.js",
48
+              "node_modules/tinymce/plugins/link/plugin.min.js",
49
+              "node_modules/tinymce/plugins/paste/plugin.min.js",
50
+              "node_modules/tinymce/plugins/table/plugin.min.js",
51
+              "node_modules/echarts/dist/echarts.min.js",
52
+              "node_modules/echarts/dist/extension/bmap.min.js",
53
+              "node_modules/chart.js/dist/Chart.min.js",
54
+              "node_modules/rpclibrary/js/browser/rpclibrary.browser.js"
42 55
             ]
43 56
           },
44 57
           "configurations": {
45 58
             "production": {
46
-              "tsConfig": "tsconfig.prod.json",
47
-              "fileReplacements": [
48
-                {
49
-                  "replace": "src/environments/environment.ts",
50
-                  "with": "src/environments/environment.prod.ts"
51
-                }
52
-              ],
53 59
               "optimization": true,
54 60
               "outputHashing": "all",
55 61
               "sourceMap": false,
56 62
               "extractCss": true,
57 63
               "namedChunks": false,
58
-              "aot": false,
64
+              "aot": true,
59 65
               "extractLicenses": true,
60 66
               "vendorChunk": false,
61 67
               "buildOptimizer": true,
62
-              "budgets": [
63
-                {
64
-                  "type": "initial",
65
-                  "maximumWarning": "2mb",
66
-                  "maximumError": "5mb"
67
-                }
68
-              ]
69
-            },
70
-
71
-            "prodlike": {
72
-              "tsConfig": "tsconfig.prod.json",
73 68
               "fileReplacements": [
74 69
                 {
75 70
                   "replace": "src/environments/environment.ts",
76
-                  "with": "src/environments/environment.prodlike.ts"
71
+                  "with": "src/environments/environment.prod.ts"
77 72
                 }
78 73
               ]
79 74
             }
80 75
           }
81 76
         },
82 77
         "serve": {
83
-          "builder": "@angular-builders/custom-webpack:dev-server",
84
-          "tsConfig": "tsconfig.app.json",
78
+          "builder": "@angular-devkit/build-angular:dev-server",
85 79
           "options": {
86
-            "browserTarget": "dashboard:build"
80
+            "browserTarget": "ngx-admin-demo:build"
87 81
           },
88 82
           "configurations": {
89 83
             "production": {
90
-              "browserTarget": "dashboard:build:production"
91
-            },
92
-            "prodlike": {
93
-              "browserTarget": "dashboard:build:prodlike"
84
+              "browserTarget": "ngx-admin-demo:build:production"
94 85
             }
95 86
           }
96 87
         },
97 88
         "extract-i18n": {
98 89
           "builder": "@angular-devkit/build-angular:extract-i18n",
99 90
           "options": {
100
-            "browserTarget": "dashboard:build"
91
+            "browserTarget": "ngx-admin-demo:build"
101 92
           }
102 93
         },
103 94
         "test": {
104
-          "builder": "@angular-builders/custom-webpack:karma",
95
+          "builder": "@angular-devkit/build-angular:karma",
105 96
           "options": {
106 97
             "main": "src/test.ts",
98
+            "karmaConfig": "./karma.conf.js",
107 99
             "polyfills": "src/polyfills.ts",
108
-            "tsConfig": "tsconfig.spec.json",
109
-            "karmaConfig": "karma.conf.js",
110
-            "assets": [
111
-              "src/favicon.ico",
112
-              "src/assets"
100
+            "tsConfig": "src/tsconfig.spec.json",
101
+            "scripts": [
102
+              "node_modules/pace-js/pace.min.js",
103
+              "node_modules/tinymce/tinymce.min.js",
104
+              "node_modules/tinymce/themes/modern/theme.min.js",
105
+              "node_modules/tinymce/plugins/link/plugin.min.js",
106
+              "node_modules/tinymce/plugins/paste/plugin.min.js",
107
+              "node_modules/tinymce/plugins/table/plugin.min.js",
108
+              "node_modules/echarts/dist/echarts.min.js",
109
+              "node_modules/echarts/dist/extension/bmap.min.js",
110
+              "node_modules/chart.js/dist/Chart.min.js"
113 111
             ],
114 112
             "styles": [
115
-              "src/styles.scss"
113
+              "node_modules/bootstrap/dist/css/bootstrap.css",
114
+              "node_modules/typeface-exo/index.css",
115
+              "node_modules/roboto-fontface/css/roboto/roboto-fontface.css",
116
+              "node_modules/ionicons/scss/ionicons.scss",
117
+              "node_modules/font-awesome/scss/font-awesome.scss",
118
+              "node_modules/socicon/css/socicon.css",
119
+              "node_modules/nebular-icons/scss/nebular-icons.scss",
120
+              "node_modules/pace-js/templates/pace-theme-flash.tmpl.css",
121
+              "src/app/@theme/styles/styles.scss"
116 122
             ],
117
-            "scripts": []
123
+            "assets": [
124
+              "src/assets",
125
+              "src/favicon.ico",
126
+              "src/favicon.png",
127
+              {
128
+                "glob": "**/*",
129
+                "input": "node_modules/leaflet/dist/images",
130
+                "output": "/assets/img/markers"
131
+              }
132
+            ]
118 133
           }
119 134
         },
120 135
         "lint": {
121
-          "builder": "@angular-builders/custom-webpack:tslint",
136
+          "builder": "@angular-devkit/build-angular:tslint",
122 137
           "options": {
123 138
             "tsConfig": [
124
-              "tsconfig.app.json",
125
-              "tsconfig.spec.json",
126
-              "e2e/tsconfig.json"
139
+              "src/tsconfig.app.json",
140
+              "src/tsconfig.spec.json"
127 141
             ],
128
-            "exclude": [
129
-              "**/node_modules/**",
130
-              "**/backend/**"
131
-            ]
142
+            "typeCheck": true,
143
+            "exclude": []
132 144
           }
133
-        },
145
+        }
146
+      }
147
+    },
148
+    "ngx-admin-demo-e2e": {
149
+      "root": "",
150
+      "sourceRoot": "",
151
+      "projectType": "application",
152
+      "architect": {
134 153
         "e2e": {
135
-          "builder": "@angular-builders/custom-webpack:protractor",
154
+          "builder": "@angular-devkit/build-angular:protractor",
136 155
           "options": {
137
-            "protractorConfig": "e2e/protractor.conf.js",
138
-            "devServerTarget": "dashboard:serve"
139
-          },
140
-          "configurations": {
141
-            "production": {
142
-              "devServerTarget": "dashboard:serve:production"
143
-            }
156
+            "protractorConfig": "./protractor.conf.js",
157
+            "devServerTarget": "ngx-admin-demo:serve"
158
+          }
159
+        },
160
+        "lint": {
161
+          "builder": "@angular-devkit/build-angular:tslint",
162
+          "options": {
163
+            "tsConfig": [
164
+              "e2e/tsconfig.e2e.json"
165
+            ],
166
+            "exclude": []
144 167
           }
145 168
         }
146 169
       }
147
-    }},
148
-  "defaultProject": "dashboard"
149
-}
170
+    }
171
+  },
172
+  "defaultProject": "ngx-admin-demo",
173
+  "schematics": {
174
+    "@schematics/angular:component": {
175
+      "prefix": "ngx",
176
+      "styleext": "scss"
177
+    },
178
+    "@schematics/angular:directive": {
179
+      "prefix": "ngx"
180
+    }
181
+  }
182
+}

+ 2
- 2
src/frontend/browserslist 查看文件

@@ -3,10 +3,10 @@
3 3
 # https://github.com/browserslist/browserslist#queries
4 4
 
5 5
 # You can see what browsers were selected by your queries by running:
6
-#   npx browserslist  
6
+#   npx browserslist
7 7
 
8 8
 > 0.5%
9 9
 last 2 versions
10 10
 Firefox ESR
11 11
 not dead
12
-not IE 9-11 # For IE 9-11 support, remove 'not'.
12
+IE 11

+ 0
- 3
src/frontend/custom-webpack.config.js 查看文件

@@ -1,3 +0,0 @@
1
-module.exports = {
2
-    externals: ['log4js']
3
-}

+ 0
- 23
src/frontend/e2e/src/app.e2e-spec.ts 查看文件

@@ -1,23 +0,0 @@
1
-import { AppPage } from './app.po';
2
-import { browser, logging } from 'protractor';
3
-
4
-describe('workspace-project App', () => {
5
-  let page: AppPage;
6
-
7
-  beforeEach(() => {
8
-    page = new AppPage();
9
-  });
10
-
11
-  it('should display welcome message', () => {
12
-    page.navigateTo();
13
-    expect(page.getTitleText()).toEqual('Welcome to dashboard!');
14
-  });
15
-
16
-  afterEach(async () => {
17
-    // Assert that there are no errors emitted from the browser
18
-    const logs = await browser.manage().logs().get(logging.Type.BROWSER);
19
-    expect(logs).not.toContain(jasmine.objectContaining({
20
-      level: logging.Level.SEVERE,
21
-    } as logging.Entry));
22
-  });
23
-});

+ 0
- 11
src/frontend/e2e/src/app.po.ts 查看文件

@@ -1,11 +0,0 @@
1
-import { browser, by, element } from 'protractor';
2
-
3
-export class AppPage {
4
-  navigateTo() {
5
-    return browser.get(browser.baseUrl) as Promise<any>;
6
-  }
7
-
8
-  getTitleText() {
9
-    return element(by.css('app-root h1')).getText() as Promise<string>;
10
-  }
11
-}

src/frontend/e2e/tsconfig.json → src/frontend/e2e/tsconfig.e2e.json 查看文件

@@ -2,6 +2,7 @@
2 2
   "extends": "../tsconfig.json",
3 3
   "compilerOptions": {
4 4
     "outDir": "../out-tsc/e2e",
5
+    "baseUrl": "./",
5 6
     "module": "commonjs",
6 7
     "target": "es5",
7 8
     "types": [

+ 20
- 7
src/frontend/karma.conf.js 查看文件

@@ -2,7 +2,7 @@
2 2
 // https://karma-runner.github.io/1.0/config/configuration-file.html
3 3
 
4 4
 module.exports = function (config) {
5
-  config.set({
5
+  const configuration = {
6 6
     basePath: '',
7 7
     frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 8
     plugins: [
@@ -12,21 +12,34 @@ module.exports = function (config) {
12 12
       require('karma-coverage-istanbul-reporter'),
13 13
       require('@angular-devkit/build-angular/plugins/karma')
14 14
     ],
15
-    client: {
15
+    client:{
16 16
       clearContext: false // leave Jasmine Spec Runner output visible in browser
17 17
     },
18 18
     coverageIstanbulReporter: {
19
-      dir: require('path').join(__dirname, './coverage/dashboard'),
20
-      reports: ['html', 'lcovonly', 'text-summary'],
19
+      dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
21 20
       fixWebpackSourcePaths: true
22 21
     },
22
+    angularCli: {
23
+      environment: 'dev'
24
+    },
23 25
     reporters: ['progress', 'kjhtml'],
24 26
     port: 9876,
25 27
     colors: true,
26 28
     logLevel: config.LOG_INFO,
27 29
     autoWatch: true,
28 30
     browsers: ['Chrome'],
29
-    singleRun: false,
30
-    restartOnFileChange: true
31
-  });
31
+    customLaunchers: {
32
+      Chrome_travis_ci: {
33
+        base: 'Chrome',
34
+        flags: ['--no-sandbox']
35
+      }
36
+    },
37
+    singleRun: false
38
+  };
39
+
40
+  if (process.env.TRAVIS) {
41
+    configuration.browsers = ['Chrome_travis_ci'];
42
+  }
43
+
44
+  config.set(configuration);
32 45
 };

+ 14657
- 6009
src/frontend/package-lock.json
文件差异内容过多而无法显示
查看文件


+ 100
- 57
src/frontend/package.json 查看文件

@@ -1,70 +1,113 @@
1 1
 {
2
-  "name": "dashboard",
3
-  "version": "0.0.0",
2
+  "name": "ngx-admin",
3
+  "version": "4.0.1",
4
+  "license": "MIT",
5
+  "repository": {
6
+    "type": "git",
7
+    "url": "git+https://github.com/akveo/ngx-admin.git"
8
+  },
9
+  "bugs": {
10
+    "url": "https://github.com/akveo/ngx-admin/issues"
11
+  },
4 12
   "scripts": {
5 13
     "ng": "ng",
6
-    "start": "ng serve --aot=false --optimization=false --proxy-config proxy.conf.json ",
7
-    "start-prodlike": "npm run build-assets && ng serve --aot=false --optimization=false --proxy-config proxy.conf.json --configuration=prodlike",
8
-    "build": "rm -f src/assets/*.js; ng build --prod --aot=false --optimization=false --build-optimizer=false",
9
-    "build-assets": "npm run copy-frontends; npm run deep-clean-plugins",
10
-    "get-submodules": "git submodule update --init && git submodule foreach git checkout master",
11
-    "copy-frontends": "for module in $(git config --file .gitmodules --get-regexp path | awk '{ print $2 }'); do npm i --prefix $module && npm run --prefix $module build && cp $module/dist/FrontendPlugin.js ./src/assets/$(basename $module).js; done",
12
-    "deep-clean-plugins": "for module in $(git config --file .gitmodules --get-regexp path | awk '{ print $2 }'); do rm -rf $module/node_modules; done",
14
+    "conventional-changelog": "conventional-changelog",
15
+    "start": "ng serve",
16
+    "build": "ng build",
17
+    "build:prod": "npm run build -- --prod --aot",
13 18
     "test": "ng test",
19
+    "test:coverage": "rimraf coverage && npm run test -- --code-coverage",
14 20
     "lint": "ng lint",
21
+    "lint:fix": "ng lint ngx-admin-demo --fix",
22
+    "lint:styles": "stylelint ./src/**/*.scss",
23
+    "lint:ci": "npm run lint && npm run lint:styles",
24
+    "pree2e": "webdriver-manager update --standalone false --gecko false",
15 25
     "e2e": "ng e2e",
16
-    "update-frontblock": "npm remove frontblock frontblock-generic; npm install frontblock-generic@latest frontblock@latest"
26
+    "docs": "compodoc -p src/tsconfig.app.json -d docs",
27
+    "docs:serve": "compodoc -p src/tsconfig.app.json -d docs -s",
28
+    "prepush": "npm run lint:ci",
29
+    "release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s"
17 30
   },
18
-  "private": true,
19 31
   "dependencies": {
20
-    "@angular/animations": "~8.2.1",
21
-    "@angular/common": "~8.2.1",
22
-    "@angular/compiler": "~8.2.1",
23
-    "@angular/core": "~8.2.1",
24
-    "@angular/forms": "~8.2.1",
25
-    "@angular/platform-browser": "~8.2.1",
26
-    "@angular/platform-browser-dynamic": "~8.2.1",
27
-    "@angular/router": "~8.2.1",
28
-    "@clr/angular": "^2.1.1",
29
-    "@clr/icons": "^2.1.1",
30
-    "@clr/ui": "^2.1.1",
31
-    "@types/knex": "^0.16.1",
32
-    "@webcomponents/custom-elements": "^1.0.0",
33
-    "btc-hdkey": "0.0.17",
34
-    "coinselect": "^3.1.11",
35
-    "frontblock": "^0.15.1",
36
-    "frontblock-generic": "^0.34.1",
37
-    "key-file-storage": "^2.2.4",
38
-    "node-fetch": "^2.6.0",
39
-    "rxjs": "~6.5.2",
40
-    "stream": "0.0.2",
41
-    "systemjs": "^0.21.3",
32
+    "@agm/core": "^1.0.0-beta.5",
33
+    "@angular/animations": "^8.0.0",
34
+    "@angular/cdk": "^8.0.0",
35
+    "@angular/common": "^8.0.0",
36
+    "@angular/compiler": "^8.0.0",
37
+    "@angular/core": "^8.0.0",
38
+    "@angular/forms": "^8.0.0",
39
+    "@angular/platform-browser": "^8.0.0",
40
+    "@angular/platform-browser-dynamic": "^8.0.0",
41
+    "@angular/router": "^8.0.0",
42
+    "@asymmetrik/ngx-leaflet": "3.0.1",
43
+    "@nebular/auth": "4.4.0",
44
+    "@nebular/eva-icons": "4.4.0",
45
+    "@nebular/security": "4.4.0",
46
+    "@nebular/theme": "4.4.0",
47
+    "@swimlane/ngx-charts": "^10.0.0",
48
+    "angular-tree-component": "7.2.0",
49
+    "angular2-chartjs": "0.4.1",
50
+    "angular2-toaster": "^7.0.0",
51
+    "bootstrap": "4.3.1",
52
+    "chart.js": "2.7.1",
53
+    "ckeditor": "4.7.3",
54
+    "classlist.js": "1.1.20150312",
55
+    "core-js": "2.5.1",
56
+    "echarts": "^4.0.2",
57
+    "eva-icons": "^1.1.0",
58
+    "intl": "1.2.5",
59
+    "ionicons": "2.0.1",
60
+    "leaflet": "1.2.0",
61
+    "nebular-icons": "1.1.0",
62
+    "ng2-ckeditor": "^1.2.2",
63
+    "ng2-completer": "2.0.8",
64
+    "ng2-smart-table": "1.3.5",
65
+    "ngx-echarts": "^4.0.1",
66
+    "node-sass": "^4.12.0",
67
+    "normalize.css": "6.0.0",
68
+    "pace-js": "1.0.2",
69
+    "roboto-fontface": "0.8.0",
70
+    "rpclibrary": "^1.3.17",
71
+    "rxjs": "6.5.2",
72
+    "rxjs-compat": "6.3.0",
73
+    "socicon": "3.0.5",
74
+    "tinymce": "4.5.7",
42 75
     "tslib": "^1.9.0",
43
-    "uuid": "^3.3.2",
44
-    "zone.js": "~0.10.1"
76
+    "typeface-exo": "0.0.22",
77
+    "web-animations-js": "github:angular/web-animations-js#release_pr208",
78
+    "zone.js": "~0.9.1"
45 79
   },
46 80
   "devDependencies": {
47
-    "@angular-builders/custom-webpack": "^8.2.0",
48
-    "@angular-builders/dev-server": "^7.3.1",
49
-    "@angular-devkit/build-angular": "^0.802.2",
50
-    "@angular/cli": "^8.2.2",
51
-    "@angular/compiler-cli": "~8.2.1",
52
-    "@angular/language-service": "~8.2.1",
53
-    "@types/jasmine": "~3.4.0",
54
-    "@types/jasminewd2": "~2.0.3",
55
-    "@types/node": "^12.7.1",
56
-    "@types/systemjs": "^0.20.6",
57
-    "codelyzer": "^5.0.0",
58
-    "jasmine-core": "~3.4.0",
59
-    "jasmine-spec-reporter": "~4.2.1",
60
-    "karma": "~4.2.0",
61
-    "karma-chrome-launcher": "~3.0.0",
62
-    "karma-coverage-istanbul-reporter": "~2.1.0",
63
-    "karma-jasmine": "~2.0.1",
64
-    "karma-jasmine-html-reporter": "^1.4.0",
65
-    "protractor": "~5.4.0",
66
-    "ts-node": "~8.3.0",
67
-    "tslint": "~5.18.0",
68
-    "typescript": "~3.5.3"
81
+    "@angular-devkit/build-angular": "~0.800.2",
82
+    "@angular/cli": "^8.0.2",
83
+    "@angular/compiler-cli": "^8.0.0",
84
+    "@angular/language-service": "8.0.0",
85
+    "@compodoc/compodoc": "1.0.1",
86
+    "@fortawesome/fontawesome-free": "^5.2.0",
87
+    "@types/d3-color": "1.0.5",
88
+    "@types/googlemaps": "^3.30.4",
89
+    "@types/jasmine": "2.5.54",
90
+    "@types/jasminewd2": "2.0.3",
91
+    "@types/leaflet": "1.2.3",
92
+    "@types/node": "6.0.90",
93
+    "codelyzer": "^5.0.1",
94
+    "conventional-changelog-cli": "1.3.4",
95
+    "husky": "0.13.3",
96
+    "jasmine-core": "2.6.4",
97
+    "jasmine-spec-reporter": "4.1.1",
98
+    "karma": "1.7.1",
99
+    "karma-chrome-launcher": "2.1.1",
100
+    "karma-cli": "1.0.1",
101
+    "karma-coverage-istanbul-reporter": "1.3.0",
102
+    "karma-jasmine": "1.1.0",
103
+    "karma-jasmine-html-reporter": "0.2.2",
104
+    "npm-run-all": "4.0.2",
105
+    "protractor": "5.1.2",
106
+    "rimraf": "2.6.1",
107
+    "stylelint": "7.13.0",
108
+    "ts-node": "3.2.2",
109
+    "tslint": "^5.7.0",
110
+    "tslint-language-service": "^0.9.9",
111
+    "typescript": "3.4.5"
69 112
   }
70 113
 }

src/frontend/e2e/protractor.conf.js → src/frontend/protractor.conf.js 查看文件

@@ -1,19 +1,18 @@
1
-// @ts-check
2 1
 // Protractor configuration file, see link for more information
3 2
 // https://github.com/angular/protractor/blob/master/lib/config.ts
4 3
 
5 4
 const { SpecReporter } = require('jasmine-spec-reporter');
6 5
 
7
-/**
8
- * @type { import("protractor").Config }
9
- */
10 6
 exports.config = {
11 7
   allScriptsTimeout: 11000,
12 8
   specs: [
13
-    './src/**/*.e2e-spec.ts'
9
+    './e2e/**/*.e2e-spec.ts'
14 10
   ],
15 11
   capabilities: {
16
-    'browserName': 'chrome'
12
+    'browserName': 'chrome',
13
+    'chromeOptions': {
14
+      'args': ['show-fps-counter=true', '--no-sandbox']
15
+    }
17 16
   },
18 17
   directConnect: true,
19 18
   baseUrl: 'http://localhost:4200/',
@@ -25,8 +24,9 @@ exports.config = {
25 24
   },
26 25
   onPrepare() {
27 26
     require('ts-node').register({
28
-      project: require('path').join(__dirname, './tsconfig.json')
27
+      project: 'e2e/tsconfig.e2e.json'
29 28
     });
30
-    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
29
+
30
+    jasmine.getEnv().addReporter(new SpecReporter({ acspec: { displayStacktrace: true } }));
31 31
   }
32
-};
32
+};

+ 0
- 9
src/frontend/proxy.conf.json 查看文件

@@ -1,9 +0,0 @@
1
-{
2
-  "/widgets-repo/*": {
3
-    "target": "http://localhost:4201",
4
-    "secure": false,
5
-    "pathRewrite": {
6
-      "^/widgets-repo": ""
7
-    }
8
-  }
9
-}

+ 168
- 0
src/frontend/src/app/@core/core.module.ts 查看文件

@@ -0,0 +1,168 @@
1
+import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
2
+import { CommonModule } from '@angular/common';
3
+import { NbAuthModule, NbDummyAuthStrategy } from '@nebular/auth';
4
+import { NbSecurityModule, NbRoleProvider } from '@nebular/security';
5
+import { of as observableOf } from 'rxjs';
6
+
7
+import { throwIfAlreadyLoaded } from './module-import-guard';
8
+import {
9
+  AnalyticsService,
10
+  LayoutService,
11
+  PlayerService,
12
+  StateService,
13
+} from './utils';
14
+import { UserData } from './data/users';
15
+import { ElectricityData } from './data/electricity';
16
+import { SmartTableData } from './data/smart-table';
17
+import { UserActivityData } from './data/user-activity';
18
+import { OrdersChartData } from './data/orders-chart';
19
+import { ProfitChartData } from './data/profit-chart';
20
+import { TrafficListData } from './data/traffic-list';
21
+import { EarningData } from './data/earning';
22
+import { OrdersProfitChartData } from './data/orders-profit-chart';
23
+import { TrafficBarData } from './data/traffic-bar';
24
+import { ProfitBarAnimationChartData } from './data/profit-bar-animation-chart';
25
+import { TemperatureHumidityData } from './data/temperature-humidity';
26
+import { SolarData } from './data/solar';
27
+import { TrafficChartData } from './data/traffic-chart';
28
+import { StatsBarData } from './data/stats-bar';
29
+import { CountryOrderData } from './data/country-order';
30
+import { StatsProgressBarData } from './data/stats-progress-bar';
31
+import { VisitorsAnalyticsData } from './data/visitors-analytics';
32
+import { SecurityCamerasData } from './data/security-cameras';
33
+
34
+import { UserService } from './mock/users.service';
35
+import { ElectricityService } from './mock/electricity.service';
36
+import { SmartTableService } from './mock/smart-table.service';
37
+import { UserActivityService } from './mock/user-activity.service';
38
+import { OrdersChartService } from './mock/orders-chart.service';
39
+import { ProfitChartService } from './mock/profit-chart.service';
40
+import { TrafficListService } from './mock/traffic-list.service';
41
+import { EarningService } from './mock/earning.service';
42
+import { OrdersProfitChartService } from './mock/orders-profit-chart.service';
43
+import { TrafficBarService } from './mock/traffic-bar.service';
44
+import { ProfitBarAnimationChartService } from './mock/profit-bar-animation-chart.service';
45
+import { TemperatureHumidityService } from './mock/temperature-humidity.service';
46
+import { SolarService } from './mock/solar.service';
47
+import { TrafficChartService } from './mock/traffic-chart.service';
48
+import { StatsBarService } from './mock/stats-bar.service';
49
+import { CountryOrderService } from './mock/country-order.service';
50
+import { StatsProgressBarService } from './mock/stats-progress-bar.service';
51
+import { VisitorsAnalyticsService } from './mock/visitors-analytics.service';
52
+import { SecurityCamerasService } from './mock/security-cameras.service';
53
+import { MockDataModule } from './mock/mock-data.module';
54
+
55
+const socialLinks = [
56
+  {
57
+    url: 'https://github.com/akveo/nebular',
58
+    target: '_blank',
59
+    icon: 'github',
60
+  },
61
+  {
62
+    url: 'https://www.facebook.com/akveo/',
63
+    target: '_blank',
64
+    icon: 'facebook',
65
+  },
66
+  {
67
+    url: 'https://twitter.com/akveo_inc',
68
+    target: '_blank',
69
+    icon: 'twitter',
70
+  },
71
+];
72
+
73
+const DATA_SERVICES = [
74
+  { provide: UserData, useClass: UserService },
75
+  { provide: ElectricityData, useClass: ElectricityService },
76
+  { provide: SmartTableData, useClass: SmartTableService },
77
+  { provide: UserActivityData, useClass: UserActivityService },
78
+  { provide: OrdersChartData, useClass: OrdersChartService },
79
+  { provide: ProfitChartData, useClass: ProfitChartService },
80
+  { provide: TrafficListData, useClass: TrafficListService },
81
+  { provide: EarningData, useClass: EarningService },
82
+  { provide: OrdersProfitChartData, useClass: OrdersProfitChartService },
83
+  { provide: TrafficBarData, useClass: TrafficBarService },
84
+  { provide: ProfitBarAnimationChartData, useClass: ProfitBarAnimationChartService },
85
+  { provide: TemperatureHumidityData, useClass: TemperatureHumidityService },
86
+  { provide: SolarData, useClass: SolarService },
87
+  { provide: TrafficChartData, useClass: TrafficChartService },
88
+  { provide: StatsBarData, useClass: StatsBarService },
89
+  { provide: CountryOrderData, useClass: CountryOrderService },
90
+  { provide: StatsProgressBarData, useClass: StatsProgressBarService },
91
+  { provide: VisitorsAnalyticsData, useClass: VisitorsAnalyticsService },
92
+  { provide: SecurityCamerasData, useClass: SecurityCamerasService },
93
+];
94
+
95
+export class NbSimpleRoleProvider extends NbRoleProvider {
96
+  getRole() {
97
+    // here you could provide any role based on any auth flow
98
+    return observableOf('guest');
99
+  }
100
+}
101
+
102
+export const NB_CORE_PROVIDERS = [
103
+  ...MockDataModule.forRoot().providers,
104
+  ...DATA_SERVICES,
105
+  ...NbAuthModule.forRoot({
106
+
107
+    strategies: [
108
+      NbDummyAuthStrategy.setup({
109
+        name: 'email',
110
+        delay: 3000,
111
+      }),
112
+    ],
113
+    forms: {
114
+      login: {
115
+        socialLinks: socialLinks,
116
+      },
117
+      register: {
118
+        socialLinks: socialLinks,
119
+      },
120
+    },
121
+  }).providers,
122
+
123
+  NbSecurityModule.forRoot({
124
+    accessControl: {
125
+      guest: {
126
+        view: '*',
127
+      },
128
+      user: {
129
+        parent: 'guest',
130
+        create: '*',
131
+        edit: '*',
132
+        remove: '*',
133
+      },
134
+    },
135
+  }).providers,
136
+
137
+  {
138
+    provide: NbRoleProvider, useClass: NbSimpleRoleProvider,
139
+  },
140
+  AnalyticsService,
141
+  LayoutService,
142
+  PlayerService,
143
+  StateService,
144
+];
145
+
146
+@NgModule({
147
+  imports: [
148
+    CommonModule,
149
+  ],
150
+  exports: [
151
+    NbAuthModule,
152
+  ],
153
+  declarations: [],
154
+})
155
+export class CoreModule {
156
+  constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
157
+    throwIfAlreadyLoaded(parentModule, 'CoreModule');
158
+  }
159
+
160
+  static forRoot(): ModuleWithProviders {
161
+    return <ModuleWithProviders>{
162
+      ngModule: CoreModule,
163
+      providers: [
164
+        ...NB_CORE_PROVIDERS,
165
+      ],
166
+    };
167
+  }
168
+}

src/frontend/src/app/app.component.scss → src/frontend/src/app/@core/data/.gitkeep 查看文件


+ 1
- 0
src/frontend/src/app/@core/data/README.md 查看文件

@@ -0,0 +1 @@
1
+Application-wise data providers.

+ 6
- 0
src/frontend/src/app/@core/data/country-order.ts 查看文件

@@ -0,0 +1,6 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export abstract class CountryOrderData {
4
+  abstract getCountriesCategories(): Observable<string[]>;
5
+  abstract getCountriesCategoriesData(country: string): Observable<number[]>;
6
+}

+ 21
- 0
src/frontend/src/app/@core/data/earning.ts 查看文件

@@ -0,0 +1,21 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export interface LiveUpdateChart {
4
+  liveChart: { value: [string, number] }[];
5
+  delta: {
6
+    up: boolean;
7
+    value: number;
8
+  };
9
+  dailyIncome: number;
10
+}
11
+
12
+export interface PieChart {
13
+  value: number;
14
+  name: string;
15
+}
16
+
17
+export abstract class EarningData {
18
+  abstract getEarningLiveUpdateCardData(currency: string): Observable<any[]>;
19
+  abstract getEarningCardData(currency: string): Observable<LiveUpdateChart>;
20
+  abstract getEarningPieChartData(): Observable<PieChart[]>;
21
+}

+ 25
- 0
src/frontend/src/app/@core/data/electricity.ts 查看文件

@@ -0,0 +1,25 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export interface Month {
4
+  month: string;
5
+  delta: string;
6
+  down: boolean;
7
+  kWatts: string;
8
+  cost: string;
9
+}
10
+
11
+export interface Electricity {
12
+  title: string;
13
+  active?: boolean;
14
+  months: Month[];
15
+}
16
+
17
+export interface ElectricityChart {
18
+  label: string;
19
+  value: number;
20
+}
21
+
22
+export abstract class ElectricityData {
23
+  abstract getListData(): Observable<Electricity[]>;
24
+  abstract getChartData(): Observable<ElectricityChart[]>;
25
+}

+ 8
- 0
src/frontend/src/app/@core/data/orders-chart.ts 查看文件

@@ -0,0 +1,8 @@
1
+export interface OrdersChart {
2
+  chartLabel: string[];
3
+  linesData: number[][];
4
+}
5
+
6
+export abstract class OrdersChartData {
7
+  abstract getOrdersChartData(period: string): OrdersChart;
8
+}

+ 14
- 0
src/frontend/src/app/@core/data/orders-profit-chart.ts 查看文件

@@ -0,0 +1,14 @@
1
+import { Observable } from 'rxjs';
2
+import { OrdersChart } from './orders-chart';
3
+import { ProfitChart  } from './profit-chart';
4
+
5
+export interface OrderProfitChartSummary {
6
+  title: string;
7
+  value: number;
8
+}
9
+
10
+export abstract class OrdersProfitChartData {
11
+  abstract getOrderProfitChartSummary(): Observable<OrderProfitChartSummary[]>;
12
+  abstract getOrdersChartData(period: string): Observable<OrdersChart>;
13
+  abstract getProfitChartData(period: string): Observable<ProfitChart>;
14
+}

+ 5
- 0
src/frontend/src/app/@core/data/profit-bar-animation-chart.ts 查看文件

@@ -0,0 +1,5 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export abstract class ProfitBarAnimationChartData {
4
+  abstract getChartData(): Observable<{ firstLine: number[]; secondLine: number[]; }>;
5
+}

+ 8
- 0
src/frontend/src/app/@core/data/profit-chart.ts 查看文件

@@ -0,0 +1,8 @@
1
+export interface ProfitChart {
2
+  chartLabel: string[];
3
+  data: number[][];
4
+}
5
+
6
+export abstract class ProfitChartData {
7
+  abstract getProfitChartData(period: string): ProfitChart;
8
+}

+ 10
- 0
src/frontend/src/app/@core/data/security-cameras.ts 查看文件

@@ -0,0 +1,10 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export interface Camera {
4
+  title: string;
5
+  source: string;
6
+}
7
+
8
+export abstract class SecurityCamerasData {
9
+  abstract getCamerasData(): Observable<Camera[]>;
10
+}

+ 4
- 0
src/frontend/src/app/@core/data/smart-table.ts 查看文件

@@ -0,0 +1,4 @@
1
+
2
+export abstract class SmartTableData {
3
+  abstract getData(): any[];
4
+}

+ 5
- 0
src/frontend/src/app/@core/data/solar.ts 查看文件

@@ -0,0 +1,5 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export abstract class SolarData {
4
+  abstract getSolarData(): Observable<number>;
5
+}

+ 5
- 0
src/frontend/src/app/@core/data/stats-bar.ts 查看文件

@@ -0,0 +1,5 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export abstract class StatsBarData {
4
+  abstract getStatsBarData(): Observable<number[]>;
5
+}

+ 12
- 0
src/frontend/src/app/@core/data/stats-progress-bar.ts 查看文件

@@ -0,0 +1,12 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export interface ProgressInfo {
4
+  title: string;
5
+  value: number;
6
+  activeProgress: number;
7
+  description: string;
8
+}
9
+
10
+export abstract class StatsProgressBarData {
11
+  abstract getProgressInfoData(): Observable<ProgressInfo[]>;
12
+}

+ 12
- 0
src/frontend/src/app/@core/data/temperature-humidity.ts 查看文件

@@ -0,0 +1,12 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export interface Temperature {
4
+  value: number;
5
+  min: number;
6
+  max: number;
7
+}
8
+
9
+export abstract class TemperatureHumidityData {
10
+  abstract getTemperatureData(): Observable<Temperature>;
11
+  abstract getHumidityData(): Observable<Temperature>;
12
+}

+ 11
- 0
src/frontend/src/app/@core/data/traffic-bar.ts 查看文件

@@ -0,0 +1,11 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export interface TrafficBar {
4
+  data: number[];
5
+  labels: string[];
6
+  formatter: string;
7
+}
8
+
9
+export abstract class TrafficBarData {
10
+  abstract getTrafficBarData(period: string): Observable<TrafficBar>;
11
+}

+ 5
- 0
src/frontend/src/app/@core/data/traffic-chart.ts 查看文件

@@ -0,0 +1,5 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export abstract class TrafficChartData {
4
+  abstract getTrafficChartData(): Observable<number[]>;
5
+}

+ 20
- 0
src/frontend/src/app/@core/data/traffic-list.ts 查看文件

@@ -0,0 +1,20 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export interface TrafficList {
4
+  date: string;
5
+  value: number;
6
+  delta: {
7
+    up: boolean;
8
+    value: number;
9
+  };
10
+  comparison: {
11
+    prevDate: string;
12
+    prevValue: number;
13
+    nextDate: string;
14
+    nextValue: number;
15
+  };
16
+}
17
+
18
+export abstract class TrafficListData {
19
+  abstract getTrafficListData(period: string): Observable<TrafficList>;
20
+}

+ 12
- 0
src/frontend/src/app/@core/data/user-activity.ts 查看文件

@@ -0,0 +1,12 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export interface UserActive {
4
+  date: string;
5
+  pagesVisitCount: number;
6
+  deltaUp: boolean;
7
+  newVisits: number;
8
+}
9
+
10
+export abstract class UserActivityData {
11
+  abstract getUserActivityData(period: string): Observable<UserActive[]>;
12
+}

+ 21
- 0
src/frontend/src/app/@core/data/users.ts 查看文件

@@ -0,0 +1,21 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export interface User {
4
+  name: string;
5
+  picture: string;
6
+}
7
+
8
+export interface Contacts {
9
+  user: User;
10
+  type: string;
11
+}
12
+
13
+export interface RecentUsers extends Contacts {
14
+  time: number;
15
+}
16
+
17
+export abstract class UserData {
18
+  abstract getUsers(): Observable<User[]>;
19
+  abstract getContacts(): Observable<Contacts[]>;
20
+  abstract getRecentUsers(): Observable<RecentUsers[]>;
21
+}

+ 12
- 0
src/frontend/src/app/@core/data/visitors-analytics.ts 查看文件

@@ -0,0 +1,12 @@
1
+import { Observable } from 'rxjs';
2
+
3
+export interface OutlineData {
4
+  label: string;
5
+  value: number;
6
+}
7
+
8
+export abstract class VisitorsAnalyticsData {
9
+  abstract getInnerLineChartData(): Observable<number[]>;
10
+  abstract getOutlineLineChartData(): Observable<OutlineData[]>;
11
+  abstract getPieChartData(): Observable<number>;
12
+}

+ 1
- 0
src/frontend/src/app/@core/mock/README.md 查看文件

@@ -0,0 +1 @@
1
+Application-wise data providers.

+ 29
- 0
src/frontend/src/app/@core/mock/country-order.service.ts 查看文件

@@ -0,0 +1,29 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf, Observable } from 'rxjs';
3
+import { CountryOrderData } from '../data/country-order';
4
+
5
+@Injectable()
6
+export class CountryOrderService extends CountryOrderData {
7
+
8
+  private countriesCategories = [
9
+    'Sofas',
10
+    'Furniture',
11
+    'Lighting',
12
+    'Tables',
13
+    'Textiles',
14
+  ];
15
+  private countriesCategoriesLength = this.countriesCategories.length;
16
+  private generateRandomData(nPoints: number): number[] {
17
+    return Array.from(Array(nPoints)).map(() => {
18
+      return Math.round(Math.random() * 20);
19
+    });
20
+  }
21
+
22
+  getCountriesCategories(): Observable<string[]> {
23
+    return observableOf(this.countriesCategories);
24
+  }
25
+
26
+  getCountriesCategoriesData(country: string): Observable<number[]> {
27
+    return observableOf(this.generateRandomData(this.countriesCategoriesLength));
28
+  }
29
+}

+ 103
- 0
src/frontend/src/app/@core/mock/earning.service.ts 查看文件

@@ -0,0 +1,103 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf, Observable } from 'rxjs';
3
+import { LiveUpdateChart, PieChart, EarningData } from '../data/earning';
4
+
5
+@Injectable()
6
+export class EarningService extends EarningData {
7
+
8
+  private currentDate: Date = new Date();
9
+  private currentValue = Math.random() * 1000;
10
+  private ONE_DAY = 24 * 3600 * 1000;
11
+
12
+  private pieChartData = [
13
+    {
14
+      value: 50,
15
+      name: 'Bitcoin',
16
+    },
17
+    {
18
+      value: 25,
19
+      name: 'Tether',
20
+    },
21
+    {
22
+      value: 25,
23
+      name: 'Ethereum',
24
+    },
25
+  ];
26
+
27
+  private liveUpdateChartData = {
28
+    bitcoin: {
29
+      liveChart: [],
30
+      delta: {
31
+        up: true,
32
+        value: 4,
33
+      },
34
+      dailyIncome: 45895,
35
+    },
36
+    tether: {
37
+      liveChart: [],
38
+      delta: {
39
+        up: false,
40
+        value: 9,
41
+      },
42
+      dailyIncome: 5862,
43
+    },
44
+    ethereum: {
45
+      liveChart: [],
46
+      delta: {
47
+        up: false,
48
+        value: 21,
49
+      },
50
+      dailyIncome: 584,
51
+    },
52
+  };
53
+
54
+  getDefaultLiveChartData(elementsNumber: number) {
55
+    this.currentDate = new Date();
56
+    this.currentValue = Math.random() * 1000;
57
+
58
+    return Array.from(Array(elementsNumber))
59
+      .map(item => this.generateRandomLiveChartData());
60
+  }
61
+
62
+  generateRandomLiveChartData() {
63
+    this.currentDate = new Date(+this.currentDate + this.ONE_DAY);
64
+    this.currentValue = this.currentValue + Math.random() * 20 - 11;
65
+
66
+    if (this.currentValue < 0) {
67
+      this.currentValue = Math.random() * 100;
68
+    }
69
+
70
+    return {
71
+      value: [
72
+        [
73
+          this.currentDate.getFullYear(),
74
+          this.currentDate.getMonth(),
75
+          this.currentDate.getDate(),
76
+        ].join('/'),
77
+        Math.round(this.currentValue),
78
+      ],
79
+    };
80
+  }
81
+
82
+  getEarningLiveUpdateCardData(currency): Observable<any[]> {
83
+    const data = this.liveUpdateChartData[currency.toLowerCase()];
84
+    const newValue = this.generateRandomLiveChartData();
85
+
86
+    data.liveChart.shift();
87
+    data.liveChart.push(newValue);
88
+
89
+    return observableOf(data.liveChart);
90
+  }
91
+
92
+  getEarningCardData(currency: string): Observable<LiveUpdateChart> {
93
+    const data = this.liveUpdateChartData[currency.toLowerCase()];
94
+
95
+    data.liveChart = this.getDefaultLiveChartData(150);
96
+
97
+    return observableOf(data);
98
+  }
99
+
100
+  getEarningPieChartData(): Observable<PieChart[]> {
101
+    return observableOf(this.pieChartData);
102
+  }
103
+}

+ 95
- 0
src/frontend/src/app/@core/mock/electricity.service.ts 查看文件

@@ -0,0 +1,95 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf, Observable } from 'rxjs';
3
+import { Electricity, ElectricityChart, ElectricityData } from '../data/electricity';
4
+
5
+@Injectable()
6
+export class ElectricityService extends ElectricityData {
7
+
8
+  private listData: Electricity[] = [
9
+    {
10
+      title: '2015',
11
+      months: [
12
+        { month: 'Jan', delta: '0.97', down: true, kWatts: '816', cost: '97' },
13
+        { month: 'Feb', delta: '1.83', down: true, kWatts: '806', cost: '95' },
14
+        { month: 'Mar', delta: '0.64', down: true, kWatts: '803', cost: '94' },
15
+        { month: 'Apr', delta: '2.17', down: false, kWatts: '818', cost: '98' },
16
+        { month: 'May', delta: '1.32', down: true, kWatts: '809', cost: '96' },
17
+        { month: 'Jun', delta: '0.05', down: true, kWatts: '808', cost: '96' },
18
+        { month: 'Jul', delta: '1.39', down: false, kWatts: '815', cost: '97' },
19
+        { month: 'Aug', delta: '0.73', down: true, kWatts: '807', cost: '95' },
20
+        { month: 'Sept', delta: '2.61', down: true, kWatts: '792', cost: '92' },
21
+        { month: 'Oct', delta: '0.16', down: true, kWatts: '791', cost: '92' },
22
+        { month: 'Nov', delta: '1.71', down: true, kWatts: '786', cost: '89' },
23
+        { month: 'Dec', delta: '0.37', down: false, kWatts: '789', cost: '91' },
24
+      ],
25
+    },
26
+    {
27
+      title: '2016',
28
+      active: true,
29
+      months: [
30
+        { month: 'Jan', delta: '1.56', down: true, kWatts: '789', cost: '91' },
31
+        { month: 'Feb', delta: '0.33', down: false, kWatts: '791', cost: '92' },
32
+        { month: 'Mar', delta: '0.62', down: true, kWatts: '790', cost: '92' },
33
+        { month: 'Apr', delta: '1.93', down: true, kWatts: '783', cost: '87' },
34
+        { month: 'May', delta: '2.52', down: true, kWatts: '771', cost: '83' },
35
+        { month: 'Jun', delta: '0.39', down: false, kWatts: '774', cost: '85' },
36
+        { month: 'Jul', delta: '1.61', down: true, kWatts: '767', cost: '81' },
37
+        { month: 'Aug', delta: '1.41', down: true, kWatts: '759', cost: '76' },
38
+        { month: 'Sept', delta: '1.03', down: true, kWatts: '752', cost: '74' },
39
+        { month: 'Oct', delta: '2.94', down: false, kWatts: '769', cost: '82' },
40
+        { month: 'Nov', delta: '0.26', down: true, kWatts: '767', cost: '81' },
41
+        { month: 'Dec', delta: '1.62', down: true, kWatts: '760', cost: '76' },
42
+      ],
43
+    },
44
+    {
45
+      title: '2017',
46
+      months: [
47
+        { month: 'Jan', delta: '1.34', down: false, kWatts: '789', cost: '91' },
48
+        { month: 'Feb', delta: '0.95', down: false, kWatts: '793', cost: '93' },
49
+        { month: 'Mar', delta: '0.25', down: true, kWatts: '791', cost: '92' },
50
+        { month: 'Apr', delta: '1.72', down: false, kWatts: '797', cost: '95' },
51
+        { month: 'May', delta: '2.62', down: true, kWatts: '786', cost: '90' },
52
+        { month: 'Jun', delta: '0.72', down: false, kWatts: '789', cost: '91' },
53
+        { month: 'Jul', delta: '0.78', down: true, kWatts: '784', cost: '89' },
54
+        { month: 'Aug', delta: '0.36', down: true, kWatts: '782', cost: '88' },
55
+        { month: 'Sept', delta: '0.55', down: false, kWatts: '787', cost: '90' },
56
+        { month: 'Oct', delta: '1.81', down: true, kWatts: '779', cost: '86' },
57
+        { month: 'Nov', delta: '1.12', down: true, kWatts: '774', cost: '84' },
58
+        { month: 'Dec', delta: '0.52', down: false, kWatts: '776', cost: '95' },
59
+      ],
60
+    },
61
+  ];
62
+
63
+  private chartPoints = [
64
+    490, 490, 495, 500,
65
+    505, 510, 520, 530,
66
+    550, 580, 630, 720,
67
+    800, 840, 860, 870,
68
+    870, 860, 840, 800,
69
+    720, 200, 145, 130,
70
+    130, 145, 200, 570,
71
+    635, 660, 670, 670,
72
+    660, 630, 580, 460,
73
+    380, 350, 340, 340,
74
+    340, 340, 340, 340,
75
+    340, 340, 340,
76
+  ];
77
+
78
+  chartData: ElectricityChart[];
79
+
80
+  constructor() {
81
+    super();
82
+    this.chartData = this.chartPoints.map((p, index) => ({
83
+      label: (index % 5 === 3) ? `${Math.round(index / 5)}` : '',
84
+      value: p,
85
+    }));
86
+  }
87
+
88
+  getListData(): Observable<Electricity[]> {
89
+    return observableOf(this.listData);
90
+  }
91
+
92
+  getChartData(): Observable<ElectricityChart[]> {
93
+    return observableOf(this.chartData);
94
+  }
95
+}

+ 65
- 0
src/frontend/src/app/@core/mock/mock-data.module.ts 查看文件

@@ -0,0 +1,65 @@
1
+import { NgModule, ModuleWithProviders } from '@angular/core';
2
+import { CommonModule } from '@angular/common';
3
+
4
+import { UserService } from './users.service';
5
+import { ElectricityService } from './electricity.service';
6
+import { SmartTableService } from './smart-table.service';
7
+import { UserActivityService } from './user-activity.service';
8
+import { OrdersChartService } from './orders-chart.service';
9
+import { ProfitChartService } from './profit-chart.service';
10
+import { TrafficListService } from './traffic-list.service';
11
+import { PeriodsService } from './periods.service';
12
+import { EarningService } from './earning.service';
13
+import { OrdersProfitChartService } from './orders-profit-chart.service';
14
+import { TrafficBarService } from './traffic-bar.service';
15
+import { ProfitBarAnimationChartService } from './profit-bar-animation-chart.service';
16
+import { TemperatureHumidityService } from './temperature-humidity.service';
17
+import { SolarService } from './solar.service';
18
+import { TrafficChartService } from './traffic-chart.service';
19
+import { StatsBarService } from './stats-bar.service';
20
+import { CountryOrderService } from './country-order.service';
21
+import { StatsProgressBarService } from './stats-progress-bar.service';
22
+import { VisitorsAnalyticsService } from './visitors-analytics.service';
23
+import { SecurityCamerasService } from './security-cameras.service';
24
+
25
+const SERVICES = [
26
+  UserService,
27
+  ElectricityService,
28
+  SmartTableService,
29
+  UserActivityService,
30
+  OrdersChartService,
31
+  ProfitChartService,
32
+  TrafficListService,
33
+  PeriodsService,
34
+  EarningService,
35
+  OrdersProfitChartService,
36
+  TrafficBarService,
37
+  ProfitBarAnimationChartService,
38
+  TemperatureHumidityService,
39
+  SolarService,
40
+  TrafficChartService,
41
+  StatsBarService,
42
+  CountryOrderService,
43
+  StatsProgressBarService,
44
+  VisitorsAnalyticsService,
45
+  SecurityCamerasService,
46
+];
47
+
48
+@NgModule({
49
+  imports: [
50
+    CommonModule,
51
+  ],
52
+  providers: [
53
+    ...SERVICES,
54
+  ],
55
+})
56
+export class MockDataModule {
57
+  static forRoot(): ModuleWithProviders {
58
+    return <ModuleWithProviders>{
59
+      ngModule: MockDataModule,
60
+      providers: [
61
+        ...SERVICES,
62
+      ],
63
+    };
64
+  }
65
+}

+ 155
- 0
src/frontend/src/app/@core/mock/orders-chart.service.ts 查看文件

@@ -0,0 +1,155 @@
1
+import { Injectable } from '@angular/core';
2
+import { PeriodsService } from './periods.service';
3
+import { OrdersChart, OrdersChartData } from '../data/orders-chart';
4
+
5
+@Injectable()
6
+export class OrdersChartService extends OrdersChartData {
7
+
8
+  private year = [
9
+    '2012',
10
+    '2013',
11
+    '2014',
12
+    '2015',
13
+    '2016',
14
+    '2017',
15
+    '2018',
16
+  ];
17
+
18
+  private data = { };
19
+
20
+  constructor(private period: PeriodsService) {
21
+    super();
22
+    this.data = {
23
+      week: this.getDataForWeekPeriod(),
24
+      month: this.getDataForMonthPeriod(),
25
+      year: this.getDataForYearPeriod(),
26
+    };
27
+  }
28
+
29
+  private getDataForWeekPeriod(): OrdersChart {
30
+    return {
31
+      chartLabel: this.getDataLabels(42, this.period.getWeeks()),
32
+      linesData: [
33
+        [
34
+          184, 267, 326, 366, 389, 399,
35
+          392, 371, 340, 304, 265, 227,
36
+          191, 158, 130, 108, 95, 91, 97,
37
+          109, 125, 144, 166, 189, 212,
38
+          236, 259, 280, 300, 316, 329,
39
+          338, 342, 339, 329, 312, 288,
40
+          258, 221, 178, 128, 71,
41
+        ],
42
+        [
43
+          158, 178, 193, 205, 212, 213,
44
+          204, 190, 180, 173, 168, 164,
45
+          162, 160, 159, 158, 159, 166,
46
+          179, 195, 215, 236, 257, 276,
47
+          292, 301, 304, 303, 300, 293,
48
+          284, 273, 262, 251, 241, 234,
49
+          232, 232, 232, 232, 232, 232,
50
+        ],
51
+        [
52
+          58, 137, 202, 251, 288, 312,
53
+          323, 324, 311, 288, 257, 222,
54
+          187, 154, 124, 100, 81, 68, 61,
55
+          58, 61, 69, 80, 96, 115, 137,
56
+          161, 186, 210, 233, 254, 271,
57
+          284, 293, 297, 297, 297, 297,
58
+          297, 297, 297, 297, 297,
59
+        ],
60
+      ],
61
+    };
62
+  }
63
+
64
+  private getDataForMonthPeriod(): OrdersChart {
65
+    return {
66
+      chartLabel: this.getDataLabels(47, this.period.getMonths()),
67
+      linesData: [
68
+        [
69
+          5, 63, 113, 156, 194, 225,
70
+          250, 270, 283, 289, 290,
71
+          286, 277, 264, 244, 220,
72
+          194, 171, 157, 151, 150,
73
+          152, 155, 160, 166, 170,
74
+          167, 153, 135, 115, 97,
75
+          82, 71, 64, 63, 62, 61,
76
+          62, 65, 73, 84, 102,
77
+          127, 159, 203, 259, 333,
78
+        ],
79
+        [
80
+          6, 83, 148, 200, 240,
81
+          265, 273, 259, 211,
82
+          122, 55, 30, 28, 36,
83
+          50, 68, 88, 109, 129,
84
+          146, 158, 163, 165,
85
+          173, 187, 208, 236,
86
+          271, 310, 346, 375,
87
+          393, 400, 398, 387,
88
+          368, 341, 309, 275,
89
+          243, 220, 206, 202,
90
+          207, 222, 247, 286, 348,
91
+        ],
92
+        [
93
+          398, 348, 315, 292, 274,
94
+          261, 251, 243, 237, 231,
95
+          222, 209, 192, 172, 152,
96
+          132, 116, 102, 90, 80, 71,
97
+          64, 58, 53, 49, 48, 54, 66,
98
+          84, 104, 125, 142, 156, 166,
99
+          172, 174, 172, 167, 159, 149,
100
+          136, 121, 105, 86, 67, 45, 22,
101
+        ],
102
+      ],
103
+    };
104
+  }
105
+
106
+  private getDataForYearPeriod(): OrdersChart {
107
+    return {
108
+      chartLabel: this.getDataLabels(42, this.year),
109
+      linesData: [
110
+        [
111
+          190, 269, 327, 366, 389, 398,
112
+          396, 387, 375, 359, 343, 327,
113
+          312, 298, 286, 276, 270, 268,
114
+          265, 258, 247, 234, 220, 204,
115
+          188, 172, 157, 142, 128, 116,
116
+          106, 99, 95, 94, 92, 89, 84,
117
+          77, 69, 60, 49, 36, 22,
118
+        ],
119
+        [
120
+          265, 307, 337, 359, 375, 386,
121
+          393, 397, 399, 397, 390, 379,
122
+          365, 347, 326, 305, 282, 261,
123
+          241, 223, 208, 197, 190, 187,
124
+          185, 181, 172, 160, 145, 126,
125
+          105, 82, 60, 40, 26, 19, 22,
126
+          43, 82, 141, 220, 321,
127
+        ],
128
+        [
129
+          9, 165, 236, 258, 244, 206,
130
+          186, 189, 209, 239, 273, 307,
131
+          339, 365, 385, 396, 398, 385,
132
+          351, 300, 255, 221, 197, 181,
133
+          170, 164, 162, 161, 159, 154,
134
+          146, 135, 122, 108, 96, 87,
135
+          83, 82, 82, 82, 82, 82, 82,
136
+        ],
137
+      ],
138
+    };
139
+  }
140
+
141
+  getDataLabels(nPoints: number, labelsArray: string[]): string[] {
142
+    const labelsArrayLength = labelsArray.length;
143
+    const step = Math.round(nPoints / labelsArrayLength);
144
+
145
+    return Array.from(Array(nPoints)).map((item, index) => {
146
+      const dataIndex = Math.round(index / step);
147
+
148
+      return index % step === 0 ? labelsArray[dataIndex] : '';
149
+    });
150
+  }
151
+
152
+  getOrdersChartData(period: string): OrdersChart {
153
+    return this.data[period];
154
+  }
155
+}

+ 45
- 0
src/frontend/src/app/@core/mock/orders-profit-chart.service.ts 查看文件

@@ -0,0 +1,45 @@
1
+import { of as observableOf,  Observable } from 'rxjs';
2
+import { Injectable } from '@angular/core';
3
+import { OrdersChart, OrdersChartData } from '../data/orders-chart';
4
+import { OrderProfitChartSummary, OrdersProfitChartData } from '../data/orders-profit-chart';
5
+import { ProfitChart, ProfitChartData } from '../data/profit-chart';
6
+
7
+@Injectable()
8
+export class OrdersProfitChartService extends OrdersProfitChartData {
9
+
10
+  private summary = [
11
+    {
12
+      title: 'Marketplace',
13
+      value: 3654,
14
+    },
15
+    {
16
+      title: 'Last Month',
17
+      value: 946,
18
+    },
19
+    {
20
+      title: 'Last Week',
21
+      value: 654,
22
+    },
23
+    {
24
+      title: 'Today',
25
+      value: 230,
26
+    },
27
+  ];
28
+
29
+  constructor(private ordersChartService: OrdersChartData,
30
+              private profitChartService: ProfitChartData) {
31
+    super();
32
+  }
33
+
34
+  getOrderProfitChartSummary(): Observable<OrderProfitChartSummary[]> {
35
+    return observableOf(this.summary);
36
+  }
37
+
38
+  getOrdersChartData(period: string): Observable<OrdersChart> {
39
+    return observableOf(this.ordersChartService.getOrdersChartData(period));
40
+  }
41
+
42
+  getProfitChartData(period: string): Observable<ProfitChart> {
43
+    return observableOf(this.profitChartService.getProfitChartData(period));
44
+  }
45
+}

+ 33
- 0
src/frontend/src/app/@core/mock/periods.service.ts 查看文件

@@ -0,0 +1,33 @@
1
+import { Injectable } from '@angular/core';
2
+
3
+@Injectable()
4
+export class PeriodsService {
5
+  getYears() {
6
+    return [
7
+      '2010', '2011', '2012',
8
+      '2013', '2014', '2015',
9
+      '2016', '2017', '2018',
10
+    ];
11
+  }
12
+
13
+  getMonths() {
14
+    return [
15
+      'Jan', 'Feb', 'Mar',
16
+      'Apr', 'May', 'Jun',
17
+      'Jul', 'Aug', 'Sep',
18
+      'Oct', 'Nov', 'Dec',
19
+    ];
20
+  }
21
+
22
+  getWeeks() {
23
+    return [
24
+      'Mon',
25
+      'Tue',
26
+      'Wed',
27
+      'Thu',
28
+      'Fri',
29
+      'Sat',
30
+      'Sun',
31
+    ];
32
+  }
33
+}

+ 43
- 0
src/frontend/src/app/@core/mock/profit-bar-animation-chart.service.ts 查看文件

@@ -0,0 +1,43 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf,  Observable } from 'rxjs';
3
+import { ProfitBarAnimationChartData } from '../data/profit-bar-animation-chart';
4
+
5
+@Injectable()
6
+export class ProfitBarAnimationChartService extends ProfitBarAnimationChartData {
7
+
8
+  private data: any;
9
+
10
+  constructor() {
11
+    super();
12
+    this.data = {
13
+      firstLine: this.getDataForFirstLine(),
14
+      secondLine: this.getDataForSecondLine(),
15
+    };
16
+  }
17
+
18
+  getDataForFirstLine(): number[] {
19
+    return this.createEmptyArray(100)
20
+      .map((_, index) => {
21
+        const oneFifth = index / 5;
22
+
23
+        return (Math.sin(oneFifth) * (oneFifth - 10) + index / 6) * 5;
24
+      });
25
+  }
26
+
27
+  getDataForSecondLine(): number[] {
28
+    return this.createEmptyArray(100)
29
+      .map((_, index) => {
30
+        const oneFifth = index / 5;
31
+
32
+        return (Math.cos(oneFifth) * (oneFifth - 10) + index / 6) * 5;
33
+      });
34
+  }
35
+
36
+  createEmptyArray(nPoints: number) {
37
+    return Array.from(Array(nPoints));
38
+  }
39
+
40
+  getChartData(): Observable<{ firstLine: number[]; secondLine: number[]; }> {
41
+    return observableOf(this.data);
42
+  }
43
+}

+ 77
- 0
src/frontend/src/app/@core/mock/profit-chart.service.ts 查看文件

@@ -0,0 +1,77 @@
1
+import { Injectable } from '@angular/core';
2
+import { PeriodsService } from './periods.service';
3
+import { ProfitChart, ProfitChartData } from '../data/profit-chart';
4
+
5
+@Injectable()
6
+export class ProfitChartService extends ProfitChartData {
7
+
8
+  private year = [
9
+    '2012',
10
+    '2013',
11
+    '2014',
12
+    '2015',
13
+    '2016',
14
+    '2017',
15
+    '2018',
16
+  ];
17
+
18
+  private data = { };
19
+
20
+  constructor(private period: PeriodsService) {
21
+    super();
22
+    this.data = {
23
+      week: this.getDataForWeekPeriod(),
24
+      month: this.getDataForMonthPeriod(),
25
+      year: this.getDataForYearPeriod(),
26
+    };
27
+  }
28
+
29
+  private getDataForWeekPeriod(): ProfitChart {
30
+    const nPoint = this.period.getWeeks().length;
31
+
32
+    return {
33
+      chartLabel: this.period.getWeeks(),
34
+      data: [
35
+        this.getRandomData(nPoint),
36
+        this.getRandomData(nPoint),
37
+        this.getRandomData(nPoint),
38
+      ],
39
+    };
40
+  }
41
+
42
+  private getDataForMonthPeriod(): ProfitChart {
43
+    const nPoint = this.period.getMonths().length;
44
+
45
+    return {
46
+      chartLabel: this.period.getMonths(),
47
+      data: [
48
+        this.getRandomData(nPoint),
49
+        this.getRandomData(nPoint),
50
+        this.getRandomData(nPoint),
51
+      ],
52
+    };
53
+  }
54
+
55
+  private getDataForYearPeriod(): ProfitChart {
56
+    const nPoint = this.year.length;
57
+
58
+    return {
59
+      chartLabel: this.year,
60
+      data: [
61
+        this.getRandomData(nPoint),
62
+        this.getRandomData(nPoint),
63
+        this.getRandomData(nPoint),
64
+      ],
65
+    };
66
+  }
67
+
68
+  private getRandomData(nPoints: number): number[] {
69
+    return Array.from(Array(nPoints)).map(() => {
70
+      return Math.round(Math.random() * 500);
71
+    });
72
+  }
73
+
74
+  getProfitChartData(period: string): ProfitChart {
75
+    return this.data[period];
76
+  }
77
+}

+ 30
- 0
src/frontend/src/app/@core/mock/security-cameras.service.ts 查看文件

@@ -0,0 +1,30 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf, Observable } from 'rxjs';
3
+import { Camera, SecurityCamerasData } from '../data/security-cameras';
4
+
5
+@Injectable()
6
+export class SecurityCamerasService extends SecurityCamerasData {
7
+
8
+  private cameras: Camera[] = [
9
+    {
10
+      title: 'Camera #1',
11
+      source: 'assets/images/camera1.jpg',
12
+    },
13
+    {
14
+      title: 'Camera #2',
15
+      source: 'assets/images/camera2.jpg',
16
+    },
17
+    {
18
+      title: 'Camera #3',
19
+      source: 'assets/images/camera3.jpg',
20
+    },
21
+    {
22
+      title: 'Camera #4',
23
+      source: 'assets/images/camera4.jpg',
24
+    },
25
+  ];
26
+
27
+  getCamerasData(): Observable<Camera[]> {
28
+    return observableOf(this.cameras);
29
+  }
30
+}

+ 432
- 0
src/frontend/src/app/@core/mock/smart-table.service.ts 查看文件

@@ -0,0 +1,432 @@
1
+import { Injectable } from '@angular/core';
2
+import { SmartTableData } from '../data/smart-table';
3
+
4
+@Injectable()
5
+export class SmartTableService extends SmartTableData {
6
+
7
+  data = [{
8
+    id: 1,
9
+    firstName: 'Mark',
10
+    lastName: 'Otto',
11
+    username: '@mdo',
12
+    email: 'mdo@gmail.com',
13
+    age: '28',
14
+  }, {
15
+    id: 2,
16
+    firstName: 'Jacob',
17
+    lastName: 'Thornton',
18
+    username: '@fat',
19
+    email: 'fat@yandex.ru',
20
+    age: '45',
21
+  }, {
22
+    id: 3,
23
+    firstName: 'Larry',
24
+    lastName: 'Bird',
25
+    username: '@twitter',
26
+    email: 'twitter@outlook.com',
27
+    age: '18',
28
+  }, {
29
+    id: 4,
30
+    firstName: 'John',
31
+    lastName: 'Snow',
32
+    username: '@snow',
33
+    email: 'snow@gmail.com',
34
+    age: '20',
35
+  }, {
36
+    id: 5,
37
+    firstName: 'Jack',
38
+    lastName: 'Sparrow',
39
+    username: '@jack',
40
+    email: 'jack@yandex.ru',
41
+    age: '30',
42
+  }, {
43
+    id: 6,
44
+    firstName: 'Ann',
45
+    lastName: 'Smith',
46
+    username: '@ann',
47
+    email: 'ann@gmail.com',
48
+    age: '21',
49
+  }, {
50
+    id: 7,
51
+    firstName: 'Barbara',
52
+    lastName: 'Black',
53
+    username: '@barbara',
54
+    email: 'barbara@yandex.ru',
55
+    age: '43',
56
+  }, {
57
+    id: 8,
58
+    firstName: 'Sevan',
59
+    lastName: 'Bagrat',
60
+    username: '@sevan',
61
+    email: 'sevan@outlook.com',
62
+    age: '13',
63
+  }, {
64
+    id: 9,
65
+    firstName: 'Ruben',
66
+    lastName: 'Vardan',
67
+    username: '@ruben',
68
+    email: 'ruben@gmail.com',
69
+    age: '22',
70
+  }, {
71
+    id: 10,
72
+    firstName: 'Karen',
73
+    lastName: 'Sevan',
74
+    username: '@karen',
75
+    email: 'karen@yandex.ru',
76
+    age: '33',
77
+  }, {
78
+    id: 11,
79
+    firstName: 'Mark',
80
+    lastName: 'Otto',
81
+    username: '@mark',
82
+    email: 'mark@gmail.com',
83
+    age: '38',
84
+  }, {
85
+    id: 12,
86
+    firstName: 'Jacob',
87
+    lastName: 'Thornton',
88
+    username: '@jacob',
89
+    email: 'jacob@yandex.ru',
90
+    age: '48',
91
+  }, {
92
+    id: 13,
93
+    firstName: 'Haik',
94
+    lastName: 'Hakob',
95
+    username: '@haik',
96
+    email: 'haik@outlook.com',
97
+    age: '48',
98
+  }, {
99
+    id: 14,
100
+    firstName: 'Garegin',
101
+    lastName: 'Jirair',
102
+    username: '@garegin',
103
+    email: 'garegin@gmail.com',
104
+    age: '40',
105
+  }, {
106
+    id: 15,
107
+    firstName: 'Krikor',
108
+    lastName: 'Bedros',
109
+    username: '@krikor',
110
+    email: 'krikor@yandex.ru',
111
+    age: '32',
112
+  }, {
113
+    'id': 16,
114
+    'firstName': 'Francisca',
115
+    'lastName': 'Brady',
116
+    'username': '@Gibson',
117
+    'email': 'franciscagibson@comtours.com',
118
+    'age': 11,
119
+  }, {
120
+    'id': 17,
121
+    'firstName': 'Tillman',
122
+    'lastName': 'Figueroa',
123
+    'username': '@Snow',
124
+    'email': 'tillmansnow@comtours.com',
125
+    'age': 34,
126
+  }, {
127
+    'id': 18,
128
+    'firstName': 'Jimenez',
129
+    'lastName': 'Morris',
130
+    'username': '@Bryant',
131
+    'email': 'jimenezbryant@comtours.com',
132
+    'age': 45,
133
+  }, {
134
+    'id': 19,
135
+    'firstName': 'Sandoval',
136
+    'lastName': 'Jacobson',
137
+    'username': '@Mcbride',
138
+    'email': 'sandovalmcbride@comtours.com',
139
+    'age': 32,
140
+  }, {
141
+    'id': 20,
142
+    'firstName': 'Griffin',
143
+    'lastName': 'Torres',
144
+    'username': '@Charles',
145
+    'email': 'griffincharles@comtours.com',
146
+    'age': 19,
147
+  }, {
148
+    'id': 21,
149
+    'firstName': 'Cora',
150
+    'lastName': 'Parker',
151
+    'username': '@Caldwell',
152
+    'email': 'coracaldwell@comtours.com',
153
+    'age': 27,
154
+  }, {
155
+    'id': 22,
156
+    'firstName': 'Cindy',
157
+    'lastName': 'Bond',
158
+    'username': '@Velez',
159
+    'email': 'cindyvelez@comtours.com',
160
+    'age': 24,
161
+  }, {
162
+    'id': 23,
163
+    'firstName': 'Frieda',
164
+    'lastName': 'Tyson',
165
+    'username': '@Craig',
166
+    'email': 'friedacraig@comtours.com',
167
+    'age': 45,
168
+  }, {
169
+    'id': 24,
170
+    'firstName': 'Cote',
171
+    'lastName': 'Holcomb',
172
+    'username': '@Rowe',
173
+    'email': 'coterowe@comtours.com',
174
+    'age': 20,
175
+  }, {
176
+    'id': 25,
177
+    'firstName': 'Trujillo',
178
+    'lastName': 'Mejia',
179
+    'username': '@Valenzuela',
180
+    'email': 'trujillovalenzuela@comtours.com',
181
+    'age': 16,
182
+  }, {
183
+    'id': 26,
184
+    'firstName': 'Pruitt',
185
+    'lastName': 'Shepard',
186
+    'username': '@Sloan',
187
+    'email': 'pruittsloan@comtours.com',
188
+    'age': 44,
189
+  }, {
190
+    'id': 27,
191
+    'firstName': 'Sutton',
192
+    'lastName': 'Ortega',
193
+    'username': '@Black',
194
+    'email': 'suttonblack@comtours.com',
195
+    'age': 42,
196
+  }, {
197
+    'id': 28,
198
+    'firstName': 'Marion',
199
+    'lastName': 'Heath',
200
+    'username': '@Espinoza',
201
+    'email': 'marionespinoza@comtours.com',
202
+    'age': 47,
203
+  }, {
204
+    'id': 29,
205
+    'firstName': 'Newman',
206
+    'lastName': 'Hicks',
207
+    'username': '@Keith',
208
+    'email': 'newmankeith@comtours.com',
209
+    'age': 15,
210
+  }, {
211
+    'id': 30,
212
+    'firstName': 'Boyle',
213
+    'lastName': 'Larson',
214
+    'username': '@Summers',
215
+    'email': 'boylesummers@comtours.com',
216
+    'age': 32,
217
+  }, {
218
+    'id': 31,
219
+    'firstName': 'Haynes',
220
+    'lastName': 'Vinson',
221
+    'username': '@Mckenzie',
222
+    'email': 'haynesmckenzie@comtours.com',
223
+    'age': 15,
224
+  }, {
225
+    'id': 32,
226
+    'firstName': 'Miller',
227
+    'lastName': 'Acosta',
228
+    'username': '@Young',
229
+    'email': 'milleryoung@comtours.com',
230
+    'age': 55,
231
+  }, {
232
+    'id': 33,
233
+    'firstName': 'Johnston',
234
+    'lastName': 'Brown',
235
+    'username': '@Knight',
236
+    'email': 'johnstonknight@comtours.com',
237
+    'age': 29,
238
+  }, {
239
+    'id': 34,
240
+    'firstName': 'Lena',
241
+    'lastName': 'Pitts',
242
+    'username': '@Forbes',
243
+    'email': 'lenaforbes@comtours.com',
244
+    'age': 25,
245
+  }, {
246
+    'id': 35,
247
+    'firstName': 'Terrie',
248
+    'lastName': 'Kennedy',
249
+    'username': '@Branch',
250
+    'email': 'terriebranch@comtours.com',
251
+    'age': 37,
252
+  }, {
253
+    'id': 36,
254
+    'firstName': 'Louise',
255
+    'lastName': 'Aguirre',
256
+    'username': '@Kirby',
257
+    'email': 'louisekirby@comtours.com',
258
+    'age': 44,
259
+  }, {
260
+    'id': 37,
261
+    'firstName': 'David',
262
+    'lastName': 'Patton',
263
+    'username': '@Sanders',
264
+    'email': 'davidsanders@comtours.com',
265
+    'age': 26,
266
+  }, {
267
+    'id': 38,
268
+    'firstName': 'Holden',
269
+    'lastName': 'Barlow',
270
+    'username': '@Mckinney',
271
+    'email': 'holdenmckinney@comtours.com',
272
+    'age': 11,
273
+  }, {
274
+    'id': 39,
275
+    'firstName': 'Baker',
276
+    'lastName': 'Rivera',
277
+    'username': '@Montoya',
278
+    'email': 'bakermontoya@comtours.com',
279
+    'age': 47,
280
+  }, {
281
+    'id': 40,
282
+    'firstName': 'Belinda',
283
+    'lastName': 'Lloyd',
284
+    'username': '@Calderon',
285
+    'email': 'belindacalderon@comtours.com',
286
+    'age': 21,
287
+  }, {
288
+    'id': 41,
289
+    'firstName': 'Pearson',
290
+    'lastName': 'Patrick',
291
+    'username': '@Clements',
292
+    'email': 'pearsonclements@comtours.com',
293
+    'age': 42,
294
+  }, {
295
+    'id': 42,
296
+    'firstName': 'Alyce',
297
+    'lastName': 'Mckee',
298
+    'username': '@Daugherty',
299
+    'email': 'alycedaugherty@comtours.com',
300
+    'age': 55,
301
+  }, {
302
+    'id': 43,
303
+    'firstName': 'Valencia',
304
+    'lastName': 'Spence',
305
+    'username': '@Olsen',
306
+    'email': 'valenciaolsen@comtours.com',
307
+    'age': 20,
308
+  }, {
309
+    'id': 44,
310
+    'firstName': 'Leach',
311
+    'lastName': 'Holcomb',
312
+    'username': '@Humphrey',
313
+    'email': 'leachhumphrey@comtours.com',
314
+    'age': 28,
315
+  }, {
316
+    'id': 45,
317
+    'firstName': 'Moss',
318
+    'lastName': 'Baxter',
319
+    'username': '@Fitzpatrick',
320
+    'email': 'mossfitzpatrick@comtours.com',
321
+    'age': 51,
322
+  }, {
323
+    'id': 46,
324
+    'firstName': 'Jeanne',
325
+    'lastName': 'Cooke',
326
+    'username': '@Ward',
327
+    'email': 'jeanneward@comtours.com',
328
+    'age': 59,
329
+  }, {
330
+    'id': 47,
331
+    'firstName': 'Wilma',
332
+    'lastName': 'Briggs',
333
+    'username': '@Kidd',
334
+    'email': 'wilmakidd@comtours.com',
335
+    'age': 53,
336
+  }, {
337
+    'id': 48,
338
+    'firstName': 'Beatrice',
339
+    'lastName': 'Perry',
340
+    'username': '@Gilbert',
341
+    'email': 'beatricegilbert@comtours.com',
342
+    'age': 39,
343
+  }, {
344
+    'id': 49,
345
+    'firstName': 'Whitaker',
346
+    'lastName': 'Hyde',
347
+    'username': '@Mcdonald',
348
+    'email': 'whitakermcdonald@comtours.com',
349
+    'age': 35,
350
+  }, {
351
+    'id': 50,
352
+    'firstName': 'Rebekah',
353
+    'lastName': 'Duran',
354
+    'username': '@Gross',
355
+    'email': 'rebekahgross@comtours.com',
356
+    'age': 40,
357
+  }, {
358
+    'id': 51,
359
+    'firstName': 'Earline',
360
+    'lastName': 'Mayer',
361
+    'username': '@Woodward',
362
+    'email': 'earlinewoodward@comtours.com',
363
+    'age': 52,
364
+  }, {
365
+    'id': 52,
366
+    'firstName': 'Moran',
367
+    'lastName': 'Baxter',
368
+    'username': '@Johns',
369
+    'email': 'moranjohns@comtours.com',
370
+    'age': 20,
371
+  }, {
372
+    'id': 53,
373
+    'firstName': 'Nanette',
374
+    'lastName': 'Hubbard',
375
+    'username': '@Cooke',
376
+    'email': 'nanettecooke@comtours.com',
377
+    'age': 55,
378
+  }, {
379
+    'id': 54,
380
+    'firstName': 'Dalton',
381
+    'lastName': 'Walker',
382
+    'username': '@Hendricks',
383
+    'email': 'daltonhendricks@comtours.com',
384
+    'age': 25,
385
+  }, {
386
+    'id': 55,
387
+    'firstName': 'Bennett',
388
+    'lastName': 'Blake',
389
+    'username': '@Pena',
390
+    'email': 'bennettpena@comtours.com',
391
+    'age': 13,
392
+  }, {
393
+    'id': 56,
394
+    'firstName': 'Kellie',
395
+    'lastName': 'Horton',
396
+    'username': '@Weiss',
397
+    'email': 'kellieweiss@comtours.com',
398
+    'age': 48,
399
+  }, {
400
+    'id': 57,
401
+    'firstName': 'Hobbs',
402
+    'lastName': 'Talley',
403
+    'username': '@Sanford',
404
+    'email': 'hobbssanford@comtours.com',
405
+    'age': 28,
406
+  }, {
407
+    'id': 58,
408
+    'firstName': 'Mcguire',
409
+    'lastName': 'Donaldson',
410
+    'username': '@Roman',
411
+    'email': 'mcguireroman@comtours.com',
412
+    'age': 38,
413
+  }, {
414
+    'id': 59,
415
+    'firstName': 'Rodriquez',
416
+    'lastName': 'Saunders',
417
+    'username': '@Harper',
418
+    'email': 'rodriquezharper@comtours.com',
419
+    'age': 20,
420
+  }, {
421
+    'id': 60,
422
+    'firstName': 'Lou',
423
+    'lastName': 'Conner',
424
+    'username': '@Sanchez',
425
+    'email': 'lousanchez@comtours.com',
426
+    'age': 16,
427
+  }];
428
+
429
+  getData() {
430
+    return this.data;
431
+  }
432
+}

+ 12
- 0
src/frontend/src/app/@core/mock/solar.service.ts 查看文件

@@ -0,0 +1,12 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf,  Observable } from 'rxjs';
3
+import { SolarData } from '../data/solar';
4
+
5
+@Injectable()
6
+export class SolarService extends SolarData {
7
+  private value = 42;
8
+
9
+  getSolarData(): Observable<number> {
10
+    return observableOf(this.value);
11
+  }
12
+}

+ 16
- 0
src/frontend/src/app/@core/mock/stats-bar.service.ts 查看文件

@@ -0,0 +1,16 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf, Observable } from 'rxjs';
3
+import { StatsBarData } from '../data/stats-bar';
4
+
5
+@Injectable()
6
+export class StatsBarService extends StatsBarData {
7
+
8
+  private statsBarData: number[] = [
9
+    300, 520, 435, 530,
10
+    730, 620, 660, 860,
11
+  ];
12
+
13
+  getStatsBarData(): Observable<number[]> {
14
+    return observableOf(this.statsBarData);
15
+  }
16
+}

+ 31
- 0
src/frontend/src/app/@core/mock/stats-progress-bar.service.ts 查看文件

@@ -0,0 +1,31 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf, Observable } from 'rxjs';
3
+import { ProgressInfo, StatsProgressBarData } from '../data/stats-progress-bar';
4
+
5
+@Injectable()
6
+export class StatsProgressBarService extends StatsProgressBarData {
7
+  private progressInfoData: ProgressInfo[] = [
8
+    {
9
+      title: 'Today’s Profit',
10
+      value: 572900,
11
+      activeProgress: 70,
12
+      description: 'Better than last week (70%)',
13
+    },
14
+    {
15
+      title: 'New Orders',
16
+      value: 6378,
17
+      activeProgress: 30,
18
+      description: 'Better than last week (30%)',
19
+    },
20
+    {
21
+      title: 'New Comments',
22
+      value: 200,
23
+      activeProgress: 55,
24
+      description: 'Better than last week (55%)',
25
+    },
26
+  ];
27
+
28
+  getProgressInfoData(): Observable<ProgressInfo[]> {
29
+    return observableOf(this.progressInfoData);
30
+  }
31
+}

+ 27
- 0
src/frontend/src/app/@core/mock/temperature-humidity.service.ts 查看文件

@@ -0,0 +1,27 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf,  Observable } from 'rxjs';
3
+import { TemperatureHumidityData, Temperature } from '../data/temperature-humidity';
4
+
5
+@Injectable()
6
+export class TemperatureHumidityService extends TemperatureHumidityData {
7
+
8
+  private temperatureDate: Temperature = {
9
+    value: 24,
10
+    min: 12,
11
+    max: 30,
12
+  };
13
+
14
+  private humidityDate: Temperature = {
15
+    value: 87,
16
+    min: 0,
17
+    max: 100,
18
+  };
19
+
20
+  getTemperatureData(): Observable<Temperature> {
21
+    return observableOf(this.temperatureDate);
22
+  }
23
+
24
+  getHumidityData(): Observable<Temperature> {
25
+    return observableOf(this.humidityDate);
26
+  }
27
+}

+ 47
- 0
src/frontend/src/app/@core/mock/traffic-bar.service.ts 查看文件

@@ -0,0 +1,47 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf,  Observable } from 'rxjs';
3
+import { PeriodsService } from './periods.service';
4
+import { TrafficBarData, TrafficBar } from '../data/traffic-bar';
5
+
6
+@Injectable()
7
+export class TrafficBarService extends TrafficBarData {
8
+
9
+  private data = { };
10
+
11
+  constructor(private period: PeriodsService) {
12
+    super();
13
+    this.data = {
14
+      week: this.getDataForWeekPeriod(),
15
+      month: this.getDataForMonthPeriod(),
16
+      year: this.getDataForYearPeriod(),
17
+    };
18
+  }
19
+
20
+  getDataForWeekPeriod(): TrafficBar {
21
+    return {
22
+      data: [10, 15, 19, 7, 20, 13, 15],
23
+      labels: this.period.getWeeks(),
24
+      formatter: '{c0} MB',
25
+    };
26
+  }
27
+
28
+  getDataForMonthPeriod(): TrafficBar {
29
+    return {
30
+      data: [0.5, 0.3, 0.8, 0.2, 0.3, 0.7, 0.8, 1, 0.7, 0.8, 0.6, 0.7],
31
+      labels: this.period.getMonths(),
32
+      formatter: '{c0} GB',
33
+    };
34
+  }
35
+
36
+  getDataForYearPeriod(): TrafficBar {
37
+    return {
38
+      data: [10, 15, 19, 7, 20, 13, 15, 19, 11],
39
+      labels: this.period.getYears(),
40
+      formatter: '{c0} GB',
41
+    };
42
+  }
43
+
44
+  getTrafficBarData(period: string): Observable<TrafficBar> {
45
+    return observableOf(this.data[period]);
46
+  }
47
+}

+ 16
- 0
src/frontend/src/app/@core/mock/traffic-chart.service.ts 查看文件

@@ -0,0 +1,16 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf,  Observable } from 'rxjs';
3
+import { TrafficChartData } from '../data/traffic-chart';
4
+
5
+@Injectable()
6
+export class TrafficChartService extends TrafficChartData {
7
+
8
+  private data: number[] = [
9
+    300, 520, 435, 530,
10
+    730, 620, 660, 860,
11
+  ];
12
+
13
+  getTrafficChartData(): Observable<number[]> {
14
+    return observableOf(this.data);
15
+  }
16
+}

+ 85
- 0
src/frontend/src/app/@core/mock/traffic-list.service.ts 查看文件

@@ -0,0 +1,85 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf,  Observable } from 'rxjs';
3
+import { PeriodsService } from './periods.service';
4
+import { TrafficList, TrafficListData } from '../data/traffic-list';
5
+
6
+@Injectable()
7
+export class TrafficListService extends TrafficListData {
8
+
9
+  private getRandom = (roundTo: number) => Math.round(Math.random() * roundTo);
10
+  private data = {};
11
+
12
+  constructor(private period: PeriodsService) {
13
+    super();
14
+    this.data = {
15
+      week: this.getDataWeek(),
16
+      month: this.getDataMonth(),
17
+      year: this.getDataYear(),
18
+    };
19
+  }
20
+
21
+  private getDataWeek(): TrafficList[] {
22
+    const getFirstDateInPeriod = () => {
23
+      const weeks = this.period.getWeeks();
24
+
25
+      return weeks[weeks.length - 1];
26
+    };
27
+
28
+    return this.reduceData(this.period.getWeeks(), getFirstDateInPeriod);
29
+  }
30
+
31
+  private getDataMonth(): TrafficList[] {
32
+    const getFirstDateInPeriod = () => {
33
+      const months = this.period.getMonths();
34
+
35
+      return months[months.length - 1];
36
+    };
37
+
38
+    return this.reduceData(this.period.getMonths(), getFirstDateInPeriod);
39
+  }
40
+
41
+  private getDataYear(): TrafficList[] {
42
+    const getFirstDateInPeriod = () => {
43
+      const years = this.period.getYears();
44
+
45
+      return `${parseInt(years[0], 10) - 1}`;
46
+    };
47
+
48
+    return this.reduceData(this.period.getYears(), getFirstDateInPeriod);
49
+  }
50
+
51
+  private reduceData(timePeriods: string[], getFirstDateInPeriod: () => string): TrafficList[] {
52
+    return timePeriods.reduce((result, timePeriod, index) => {
53
+      const hasResult = result[index - 1];
54
+      const prevDate = hasResult ?
55
+        result[index - 1].comparison.nextDate :
56
+        getFirstDateInPeriod();
57
+      const prevValue = hasResult ?
58
+        result[index - 1].comparison.nextValue :
59
+        this.getRandom(100);
60
+      const nextValue = this.getRandom(100);
61
+      const deltaValue = prevValue - nextValue;
62
+
63
+      const item = {
64
+        date: timePeriod,
65
+        value: this.getRandom(1000),
66
+        delta: {
67
+          up: deltaValue <= 0,
68
+          value: Math.abs(deltaValue),
69
+        },
70
+        comparison: {
71
+          prevDate,
72
+          prevValue,
73
+          nextDate: timePeriod,
74
+          nextValue,
75
+        },
76
+      };
77
+
78
+      return [...result, item];
79
+    }, []);
80
+  }
81
+
82
+  getTrafficListData(period: string): Observable<TrafficList> {
83
+    return observableOf(this.data[period]);
84
+  }
85
+}

+ 57
- 0
src/frontend/src/app/@core/mock/user-activity.service.ts 查看文件

@@ -0,0 +1,57 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf,  Observable } from 'rxjs';
3
+import { PeriodsService } from './periods.service';
4
+import { UserActive, UserActivityData } from '../data/user-activity';
5
+
6
+@Injectable()
7
+export class UserActivityService extends UserActivityData {
8
+
9
+  private getRandom = (roundTo: number) => Math.round(Math.random() * roundTo);
10
+  private generateUserActivityRandomData(date) {
11
+    return {
12
+      date,
13
+      pagesVisitCount: this.getRandom(1000),
14
+      deltaUp: this.getRandom(1) % 2 === 0,
15
+      newVisits: this.getRandom(100),
16
+    };
17
+  }
18
+
19
+  data = {};
20
+
21
+  constructor(private periods: PeriodsService) {
22
+    super();
23
+    this.data = {
24
+      week: this.getDataWeek(),
25
+      month: this.getDataMonth(),
26
+      year: this.getDataYear(),
27
+    };
28
+  }
29
+
30
+  private getDataWeek(): UserActive[] {
31
+    return this.periods.getWeeks().map((week) => {
32
+      return this.generateUserActivityRandomData(week);
33
+    });
34
+  }
35
+
36
+  private getDataMonth(): UserActive[] {
37
+    const currentDate = new Date();
38
+    const days = currentDate.getDate();
39
+    const month = this.periods.getMonths()[currentDate.getMonth()];
40
+
41
+    return Array.from(Array(days)).map((_, index) => {
42
+      const date = `${index + 1} ${month}`;
43
+
44
+      return this.generateUserActivityRandomData(date);
45
+    });
46
+  }
47
+
48
+  private getDataYear(): UserActive[] {
49
+    return this.periods.getYears().map((year) => {
50
+      return this.generateUserActivityRandomData(year);
51
+    });
52
+  }
53
+
54
+  getUserActivityData(period: string): Observable<UserActive[]> {
55
+    return observableOf(this.data[period]);
56
+  }
57
+}

+ 53
- 0
src/frontend/src/app/@core/mock/users.service.ts 查看文件

@@ -0,0 +1,53 @@
1
+import { of as observableOf,  Observable } from 'rxjs';
2
+import { Injectable } from '@angular/core';
3
+import { Contacts, RecentUsers, UserData } from '../data/users';
4
+
5
+@Injectable()
6
+export class UserService extends UserData {
7
+
8
+  private time: Date = new Date;
9
+
10
+  private users = {
11
+    nick: { name: 'Nick Jones', picture: 'assets/images/nick.png' },
12
+    eva: { name: 'Eva Moor', picture: 'assets/images/eva.png' },
13
+    jack: { name: 'Jack Williams', picture: 'assets/images/jack.png' },
14
+    lee: { name: 'Lee Wong', picture: 'assets/images/lee.png' },
15
+    alan: { name: 'Alan Thompson', picture: 'assets/images/alan.png' },
16
+    kate: { name: 'Kate Martinez', picture: 'assets/images/kate.png' },
17
+  };
18
+  private types = {
19
+    mobile: 'mobile',
20
+    home: 'home',
21
+    work: 'work',
22
+  };
23
+  private contacts: Contacts[] = [
24
+    { user: this.users.nick, type: this.types.mobile },
25
+    { user: this.users.eva, type: this.types.home },
26
+    { user: this.users.jack, type: this.types.mobile },
27
+    { user: this.users.lee, type: this.types.mobile },
28
+    { user: this.users.alan, type: this.types.home },
29
+    { user: this.users.kate, type: this.types.work },
30
+  ];
31
+  private recentUsers: RecentUsers[]  = [
32
+    { user: this.users.alan, type: this.types.home, time: this.time.setHours(21, 12)},
33
+    { user: this.users.eva, type: this.types.home, time: this.time.setHours(17, 45)},
34
+    { user: this.users.nick, type: this.types.mobile, time: this.time.setHours(5, 29)},
35
+    { user: this.users.lee, type: this.types.mobile, time: this.time.setHours(11, 24)},
36
+    { user: this.users.jack, type: this.types.mobile, time: this.time.setHours(10, 45)},
37
+    { user: this.users.kate, type: this.types.work, time: this.time.setHours(9, 42)},
38
+    { user: this.users.kate, type: this.types.work, time: this.time.setHours(9, 31)},
39
+    { user: this.users.jack, type: this.types.mobile, time: this.time.setHours(8, 0)},
40
+  ];
41
+
42
+  getUsers(): Observable<any> {
43
+    return observableOf(this.users);
44
+  }
45
+
46
+  getContacts(): Observable<Contacts[]> {
47
+    return observableOf(this.contacts);
48
+  }
49
+
50
+  getRecentUsers(): Observable<RecentUsers[]> {
51
+    return observableOf(this.recentUsers);
52
+  }
53
+}

+ 57
- 0
src/frontend/src/app/@core/mock/visitors-analytics.service.ts 查看文件

@@ -0,0 +1,57 @@
1
+import { Injectable } from '@angular/core';
2
+import { of as observableOf, Observable } from 'rxjs';
3
+import { PeriodsService } from './periods.service';
4
+import { OutlineData, VisitorsAnalyticsData } from '../data/visitors-analytics';
5
+
6
+@Injectable()
7
+export class VisitorsAnalyticsService extends VisitorsAnalyticsData {
8
+
9
+  constructor(private periodService: PeriodsService) {
10
+    super();
11
+  }
12
+
13
+  private pieChartValue = 75;
14
+  private innerLinePoints: number[] = [
15
+    94, 188, 225, 244, 253, 254, 249, 235, 208,
16
+    173, 141, 118, 105, 97, 94, 96, 104, 121, 147,
17
+    183, 224, 265, 302, 333, 358, 375, 388, 395,
18
+    400, 400, 397, 390, 377, 360, 338, 310, 278,
19
+    241, 204, 166, 130, 98, 71, 49, 32, 20, 13, 9,
20
+  ];
21
+  private outerLinePoints: number[] = [
22
+    85, 71, 59, 50, 45, 42, 41, 44 , 58, 88,
23
+    136 , 199, 267, 326, 367, 391, 400, 397,
24
+    376, 319, 200, 104, 60, 41, 36, 37, 44,
25
+    55, 74, 100 , 131, 159, 180, 193, 199, 200,
26
+    195, 184, 164, 135, 103, 73, 50, 33, 22, 15, 11,
27
+  ];
28
+  private generateOutlineLineData(): OutlineData[] {
29
+    const months = this.periodService.getMonths();
30
+    const outerLinePointsLength = this.outerLinePoints.length;
31
+    const monthsLength = months.length;
32
+
33
+    return this.outerLinePoints.map((p, index) => {
34
+      const monthIndex = Math.round(index / 4);
35
+      const label = (index % Math.round(outerLinePointsLength / monthsLength) === 0)
36
+        ? months[monthIndex]
37
+        : '';
38
+
39
+      return {
40
+        label,
41
+        value: p,
42
+      };
43
+    });
44
+  }
45
+
46
+  getInnerLineChartData(): Observable<number[]> {
47
+    return observableOf(this.innerLinePoints);
48
+  }
49
+
50
+  getOutlineLineChartData(): Observable<OutlineData[]> {
51
+    return observableOf(this.generateOutlineLineData());
52
+  }
53
+
54
+  getPieChartData(): Observable<number> {
55
+    return observableOf(this.pieChartValue);
56
+  }
57
+}

+ 5
- 0
src/frontend/src/app/@core/module-import-guard.ts 查看文件

@@ -0,0 +1,5 @@
1
+export function throwIfAlreadyLoaded(parentModule: any, moduleName: string) {
2
+  if (parentModule) {
3
+    throw new Error(`${moduleName} has already been loaded. Import Core modules in the AppModule only.`);
4
+  }
5
+}

src/frontend/src/app/content-area/main.component.scss → src/frontend/src/app/@core/utils/.gitkeep 查看文件


+ 32
- 0
src/frontend/src/app/@core/utils/analytics.service.ts 查看文件

@@ -0,0 +1,32 @@
1
+import { Injectable } from '@angular/core';
2
+import { NavigationEnd, Router } from '@angular/router';
3
+import { Location } from '@angular/common';
4
+import { filter } from 'rxjs/operators';
5
+
6
+declare const ga: any;
7
+
8
+@Injectable()
9
+export class AnalyticsService {
10
+  private enabled: boolean;
11
+
12
+  constructor(private location: Location, private router: Router) {
13
+    this.enabled = false;
14
+  }
15
+
16
+  trackPageViews() {
17
+    if (this.enabled) {
18
+      this.router.events.pipe(
19
+        filter((event) => event instanceof NavigationEnd),
20
+      )
21
+        .subscribe(() => {
22
+          ga('send', {hitType: 'pageview', page: this.location.path()});
23
+        });
24
+    }
25
+  }
26
+
27
+  trackEvent(eventName: string) {
28
+    if (this.enabled) {
29
+      ga('send', 'event', eventName);
30
+    }
31
+  }
32
+}

+ 11
- 0
src/frontend/src/app/@core/utils/index.ts 查看文件

@@ -0,0 +1,11 @@
1
+import { LayoutService } from './layout.service';
2
+import { AnalyticsService } from './analytics.service';
3
+import { PlayerService } from './player.service';
4
+import { StateService } from './state.service';
5
+
6
+export {
7
+  LayoutService,
8
+  AnalyticsService,
9
+  PlayerService,
10
+  StateService,
11
+};

+ 20
- 0
src/frontend/src/app/@core/utils/layout.service.ts 查看文件

@@ -0,0 +1,20 @@
1
+import { Injectable } from '@angular/core';
2
+import { Observable, Subject } from 'rxjs';
3
+import { delay, share } from 'rxjs/operators';
4
+
5
+@Injectable()
6
+export class LayoutService {
7
+
8
+  protected layoutSize$ = new Subject();
9
+
10
+  changeLayoutSize() {
11
+    this.layoutSize$.next();
12
+  }
13
+
14
+  onChangeLayoutSize(): Observable<any> {
15
+    return this.layoutSize$.pipe(
16
+      share(),
17
+      delay(1),
18
+    );
19
+  }
20
+}

+ 66
- 0
src/frontend/src/app/@core/utils/player.service.ts 查看文件

@@ -0,0 +1,66 @@
1
+import { Injectable } from '@angular/core';
2
+
3
+export class Track {
4
+  name: string;
5
+  artist: string;
6
+  url: string;
7
+  cover: string;
8
+}
9
+
10
+@Injectable()
11
+export class PlayerService {
12
+  current: number;
13
+  playlist: Track[] = [
14
+    {
15
+      name: 'Don\'t Wanna Fight',
16
+      artist: 'Alabama Shakes',
17
+      url: 'https://p.scdn.co/mp3-preview/6156cdbca425a894972c02fca9d76c0b70e001af',
18
+      cover: 'assets/images/cover1.jpg',
19
+    },
20
+    {
21
+      name: 'Harder',
22
+      artist: 'Daft Punk',
23
+      url: 'https://p.scdn.co/mp3-preview/92a04c7c0e96bf93a1b1b1cae7dfff1921969a7b',
24
+      cover: 'assets/images/cover2.jpg',
25
+    },
26
+    {
27
+      name: 'Come Together',
28
+      artist: 'Beatles',
29
+      url: 'https://p.scdn.co/mp3-preview/83090a4db6899eaca689ae35f69126dbe65d94c9',
30
+      cover: 'assets/images/cover3.jpg',
31
+    },
32
+  ];
33
+
34
+  random(): Track {
35
+    this.current = Math.floor(Math.random() * this.playlist.length);
36
+    return this.playlist[this.current];
37
+  }
38
+
39
+  next(): Track {
40
+    return this.getNextTrack();
41
+  }
42
+
43
+  prev() {
44
+    return this.getPrevTrack();
45
+  }
46
+
47
+  private getNextTrack(): Track {
48
+    if (this.current === this.playlist.length - 1) {
49
+      this.current = 0;
50
+    } else {
51
+      this.current++;
52
+    }
53
+
54
+    return this.playlist[this.current];
55
+  }
56
+
57
+  private getPrevTrack(): Track {
58
+    if (this.current === 0) {
59
+      this.current = this.playlist.length - 1;
60
+    } else {
61
+      this.current--;
62
+    }
63
+
64
+    return this.playlist[this.current];
65
+  }
66
+}

+ 92
- 0
src/frontend/src/app/@core/utils/state.service.ts 查看文件

@@ -0,0 +1,92 @@
1
+import { Injectable, OnDestroy } from '@angular/core';
2
+import { of as observableOf,  Observable,  BehaviorSubject } from 'rxjs';
3
+import { takeWhile } from 'rxjs/operators';
4
+
5
+import { NbLayoutDirectionService, NbLayoutDirection } from '@nebular/theme';
6
+
7
+@Injectable()
8
+export class StateService implements OnDestroy {
9
+
10
+  protected layouts: any = [
11
+    {
12
+      name: 'One Column',
13
+      icon: 'nb-layout-default',
14
+      id: 'one-column',
15
+      selected: true,
16
+    },
17
+    {
18
+      name: 'Two Column',
19
+      icon: 'nb-layout-two-column',
20
+      id: 'two-column',
21
+    },
22
+    {
23
+      name: 'Center Column',
24
+      icon: 'nb-layout-centre',
25
+      id: 'center-column',
26
+    },
27
+  ];
28
+
29
+  protected sidebars: any = [
30
+    {
31
+      name: 'Sidebar at layout start',
32
+      icon: 'nb-layout-sidebar-left',
33
+      id: 'start',
34
+      selected: true,
35
+    },
36
+    {
37
+      name: 'Sidebar at layout end',
38
+      icon: 'nb-layout-sidebar-right',
39
+      id: 'end',
40
+    },
41
+  ];
42
+
43
+  protected layoutState$ = new BehaviorSubject(this.layouts[0]);
44
+  protected sidebarState$ = new BehaviorSubject(this.sidebars[0]);
45
+
46
+  alive = true;
47
+
48
+  constructor(directionService: NbLayoutDirectionService) {
49
+    directionService.onDirectionChange()
50
+      .pipe(takeWhile(() => this.alive))
51
+      .subscribe(direction => this.updateSidebarIcons(direction));
52
+
53
+    this.updateSidebarIcons(directionService.getDirection());
54
+  }
55
+
56
+  ngOnDestroy() {
57
+    this.alive = false;
58
+  }
59
+
60
+  private updateSidebarIcons(direction: NbLayoutDirection) {
61
+    const [ startSidebar, endSidebar ] = this.sidebars;
62
+    const isLtr = direction === NbLayoutDirection.LTR;
63
+    const startIconClass = isLtr ? 'nb-layout-sidebar-left' : 'nb-layout-sidebar-right';
64
+    const endIconClass = isLtr ? 'nb-layout-sidebar-right' : 'nb-layout-sidebar-left';
65
+    startSidebar.icon = startIconClass;
66
+    endSidebar.icon = endIconClass;
67
+  }
68
+
69
+  setLayoutState(state: any): any {
70
+    this.layoutState$.next(state);
71
+  }
72
+
73
+  getLayoutStates(): Observable<any[]> {
74
+    return observableOf(this.layouts);
75
+  }
76
+
77
+  onLayoutState(): Observable<any> {
78
+    return this.layoutState$.asObservable();
79
+  }
80
+
81
+  setSidebarState(state: any): any {
82
+    this.sidebarState$.next(state);
83
+  }
84
+
85
+  getSidebarStates(): Observable<any[]> {
86
+    return observableOf(this.sidebars);
87
+  }
88
+
89
+  onSidebarState(): Observable<any> {
90
+    return this.sidebarState$.asObservable();
91
+  }
92
+}

+ 30
- 0
src/frontend/src/app/@theme/components/footer/footer.component.scss 查看文件

@@ -0,0 +1,30 @@
1
+@import '../../styles/themes';
2
+@import '~@nebular/theme/styles/global/breakpoints';
3
+@import '~bootstrap/scss/mixins/breakpoints';
4
+
5
+@include nb-install-component() {
6
+  width: 100%;
7
+  display: flex;
8
+  justify-content: space-between;
9
+  align-items: center;
10
+
11
+  .socials {
12
+    font-size: 2rem;
13
+
14
+    a {
15
+      padding: 0.4rem;
16
+      color: nb-theme(text-hint-color);
17
+      transition: color ease-out 0.1s;
18
+
19
+      &:hover {
20
+        color: nb-theme(text-basic-color);
21
+      }
22
+    }
23
+  }
24
+
25
+  @include media-breakpoint-down(is) {
26
+    .socials {
27
+      font-size: 1.5rem;
28
+    }
29
+  }
30
+}

+ 17
- 0
src/frontend/src/app/@theme/components/footer/footer.component.ts 查看文件

@@ -0,0 +1,17 @@
1
+import { Component } from '@angular/core';
2
+
3
+@Component({
4
+  selector: 'ngx-footer',
5
+  styleUrls: ['./footer.component.scss'],
6
+  template: `
7
+    <span class="created-by">Created with ♥ by <b><a href="https://akveo.com" target="_blank">Akveo</a></b> 2019</span>
8
+    <div class="socials">
9
+      <a href="#" target="_blank" class="ion ion-social-github"></a>
10
+      <a href="#" target="_blank" class="ion ion-social-facebook"></a>
11
+      <a href="#" target="_blank" class="ion ion-social-twitter"></a>
12
+      <a href="#" target="_blank" class="ion ion-social-linkedin"></a>
13
+    </div>
14
+  `,
15
+})
16
+export class FooterComponent {
17
+}

+ 29
- 0
src/frontend/src/app/@theme/components/header/header.component.html 查看文件

@@ -0,0 +1,29 @@
1
+<div class="header-container">
2
+  <div class="logo-container">
3
+    <a (click)="toggleSidebar()" href="#" class="sidebar-toggle">
4
+      <nb-icon icon="menu-2-outline"></nb-icon>
5
+    </a>
6
+    <a class="logo" href="#" (click)="navigateHome()">ngx-<span>admin</span></a>
7
+  </div>
8
+  <nb-select [selected]="currentTheme" (selectedChange)="changeTheme($event)" status="primary">
9
+    <nb-option *ngFor="let theme of themes" [value]="theme.value"> {{ theme.name }}</nb-option>
10
+  </nb-select>
11
+</div>
12
+
13
+<div class="header-container">
14
+  <nb-actions size="small">
15
+
16
+    <nb-action class="control-item">
17
+      <nb-search type="rotate-layout"></nb-search>
18
+    </nb-action>
19
+    <nb-action class="control-item" icon="email-outline"></nb-action>
20
+    <nb-action class="control-item" icon="bell-outline"></nb-action>
21
+    <nb-action class="user-action" *nbIsGranted="['view', 'user']" >
22
+      <nb-user [nbContextMenu]="userMenu"
23
+               [onlyPicture]="userPictureOnly"
24
+               [name]="user?.name"
25
+               [picture]="user?.picture">
26
+      </nb-user>
27
+    </nb-action>
28
+  </nb-actions>
29
+</div>

+ 70
- 0
src/frontend/src/app/@theme/components/header/header.component.scss 查看文件

@@ -0,0 +1,70 @@
1
+@import '~bootstrap/scss/mixins/breakpoints';
2
+@import '~@nebular/theme/styles/global/breakpoints';
3
+@import '../../styles/themes';
4
+
5
+@include nb-install-component() {
6
+  display: flex;
7
+  justify-content: space-between;
8
+  width: 100%;
9
+
10
+  .logo-container {
11
+    display: flex;
12
+    align-items: center;
13
+    width: calc(#{nb-theme(sidebar-width)} - #{nb-theme(header-padding)});
14
+  }
15
+
16
+  nb-action {
17
+    height: auto;
18
+    display: flex;
19
+    align-content: center;
20
+  }
21
+
22
+  nb-user {
23
+    cursor: pointer;
24
+  }
25
+
26
+  ::ng-deep nb-search button {
27
+    padding: 0!important;
28
+  }
29
+
30
+  .header-container {
31
+    display: flex;
32
+    align-items: center;
33
+    width: auto;
34
+
35
+    .sidebar-toggle {
36
+      @include nb-ltr(padding-right, 1.25rem);
37
+      @include nb-rtl(padding-left, 1.25rem);
38
+      text-decoration: none;
39
+      color: nb-theme(text-hint-color);
40
+      nb-icon {
41
+        font-size: 1.75rem;
42
+      }
43
+    }
44
+
45
+    .logo {
46
+      padding: 0 1.25rem;
47
+      font-size: 1.75rem;
48
+      @include nb-ltr(border-left, 1px solid nb-theme(divider-color));
49
+      @include nb-rtl(border-right, 1px solid nb-theme(divider-color));
50
+      white-space: nowrap;
51
+      text-decoration: none;
52
+    }
53
+  }
54
+
55
+  @include media-breakpoint-down(sm) {
56
+    .control-item {
57
+      display: none;
58
+    }
59
+    .user-action {
60
+      border: none;
61
+      padding: 0;
62
+    }
63
+  }
64
+
65
+  @include media-breakpoint-down(is) {
66
+    nb-select {
67
+      display: none;
68
+    }
69
+  }
70
+}

+ 94
- 0
src/frontend/src/app/@theme/components/header/header.component.ts 查看文件

@@ -0,0 +1,94 @@
1
+import { Component, OnDestroy, OnInit } from '@angular/core';
2
+import { NbMediaBreakpointsService, NbMenuService, NbSidebarService, NbThemeService } from '@nebular/theme';
3
+
4
+import { UserData } from '../../../@core/data/users';
5
+import { LayoutService } from '../../../@core/utils';
6
+import { map, takeUntil } from 'rxjs/operators';
7
+import { Subject } from 'rxjs';
8
+
9
+@Component({
10
+  selector: 'ngx-header',
11
+  styleUrls: ['./header.component.scss'],
12
+  templateUrl: './header.component.html',
13
+})
14
+export class HeaderComponent implements OnInit, OnDestroy {
15
+
16
+  private destroy$: Subject<void> = new Subject<void>();
17
+  userPictureOnly: boolean = false;
18
+  user: any;
19
+
20
+  themes = [
21
+    {
22
+      value: 'default',
23
+      name: 'Light',
24
+    },
25
+    {
26
+      value: 'dark',
27
+      name: 'Dark',
28
+    },
29
+    {
30
+      value: 'cosmic',
31
+      name: 'Cosmic',
32
+    },
33
+    {
34
+      value: 'corporate',
35
+      name: 'Corporate',
36
+    },
37
+  ];
38
+
39
+  currentTheme = 'default';
40
+
41
+  userMenu = [ { title: 'Profile' }, { title: 'Log out' } ];
42
+
43
+  constructor(private sidebarService: NbSidebarService,
44
+              private menuService: NbMenuService,
45
+              private themeService: NbThemeService,
46
+              private userService: UserData,
47
+              private layoutService: LayoutService,
48
+              private breakpointService: NbMediaBreakpointsService) {
49
+  }
50
+
51
+  ngOnInit() {
52
+    this.currentTheme = this.themeService.currentTheme;
53
+
54
+    this.userService.getUsers()
55
+      .pipe(takeUntil(this.destroy$))
56
+      .subscribe((users: any) => this.user = users.nick);
57
+
58
+    const { xl } = this.breakpointService.getBreakpointsMap();
59
+    this.themeService.onMediaQueryChange()
60
+      .pipe(
61
+        map(([, currentBreakpoint]) => currentBreakpoint.width < xl),
62
+        takeUntil(this.destroy$),
63
+      )
64
+      .subscribe((isLessThanXl: boolean) => this.userPictureOnly = isLessThanXl);
65
+
66
+    this.themeService.onThemeChange()
67
+      .pipe(
68
+        map(({ name }) => name),
69
+        takeUntil(this.destroy$),
70
+      )
71
+      .subscribe(themeName => this.currentTheme = themeName);
72
+  }
73
+
74
+  ngOnDestroy() {
75
+    this.destroy$.next();
76
+    this.destroy$.complete();
77
+  }
78
+
79
+  changeTheme(themeName: string) {
80
+    this.themeService.changeTheme(themeName);
81
+  }
82
+
83
+  toggleSidebar(): boolean {
84
+    this.sidebarService.toggle(true, 'menu-sidebar');
85
+    this.layoutService.changeLayoutSize();
86
+
87
+    return false;
88
+  }
89
+
90
+  navigateHome() {
91
+    this.menuService.navigateHome();
92
+    return false;
93
+  }
94
+}

+ 4
- 0
src/frontend/src/app/@theme/components/index.ts 查看文件

@@ -0,0 +1,4 @@
1
+export * from './header/header.component';
2
+export * from './footer/footer.component';
3
+export * from './search-input/search-input.component';
4
+export * from './tiny-mce/tiny-mce.component';

+ 33
- 0
src/frontend/src/app/@theme/components/search-input/search-input.component.scss 查看文件

@@ -0,0 +1,33 @@
1
+:host {
2
+  display: flex;
3
+  align-items: center;
4
+
5
+  i.control-icon {
6
+    &::before {
7
+      font-size: 2.3rem;
8
+    }
9
+
10
+    &:hover {
11
+      cursor: pointer;
12
+    }
13
+  }
14
+
15
+  input {
16
+    border: none;
17
+    outline: none;
18
+    margin-left: 1rem;
19
+    width: 15rem;
20
+    transition: width 0.2s ease;
21
+
22
+    &.hidden {
23
+      width: 0;
24
+      margin: 0;
25
+    }
26
+  }
27
+
28
+  ::ng-deep search-input {
29
+    input {
30
+      background: transparent;
31
+    }
32
+  }
33
+}

+ 35
- 0
src/frontend/src/app/@theme/components/search-input/search-input.component.ts 查看文件

@@ -0,0 +1,35 @@
1
+import { Component, ElementRef, EventEmitter, Output, ViewChild } from '@angular/core';
2
+
3
+@Component({
4
+  selector: 'ngx-search-input',
5
+  styleUrls: ['./search-input.component.scss'],
6
+  template: `
7
+    <i class="control-icon ion ion-ios-search"
8
+       (click)="showInput()"></i>
9
+    <input placeholder="Type your search request here..."
10
+           #input
11
+           [class.hidden]="!isInputShown"
12
+           (blur)="hideInput()"
13
+           (input)="onInput($event)">
14
+  `,
15
+})
16
+export class SearchInputComponent {
17
+  @ViewChild('input', { static: true }) input: ElementRef;
18
+
19
+  @Output() search: EventEmitter<string> = new EventEmitter<string>();
20
+
21
+  isInputShown = false;
22
+
23
+  showInput() {
24
+    this.isInputShown = true;
25
+    this.input.nativeElement.focus();
26
+  }
27
+
28
+  hideInput() {
29
+    this.isInputShown = false;
30
+  }
31
+
32
+  onInput(val: string) {
33
+    this.search.emit(val);
34
+  }
35
+}

+ 37
- 0
src/frontend/src/app/@theme/components/tiny-mce/tiny-mce.component.ts 查看文件

@@ -0,0 +1,37 @@
1
+import { Component, OnDestroy, AfterViewInit, Output, EventEmitter, ElementRef } from '@angular/core';
2
+import { LocationStrategy } from '@angular/common';
3
+
4
+@Component({
5
+  selector: 'ngx-tiny-mce',
6
+  template: '',
7
+})
8
+export class TinyMCEComponent implements OnDestroy, AfterViewInit {
9
+
10
+  @Output() editorKeyup = new EventEmitter<any>();
11
+
12
+  editor: any;
13
+
14
+  constructor(
15
+    private host: ElementRef,
16
+    private locationStrategy: LocationStrategy,
17
+  ) { }
18
+
19
+  ngAfterViewInit() {
20
+    tinymce.init({
21
+      target: this.host.nativeElement,
22
+      plugins: ['link', 'paste', 'table'],
23
+      skin_url: `${this.locationStrategy.getBaseHref()}assets/skins/lightgray`,
24
+      setup: editor => {
25
+        this.editor = editor;
26
+        editor.on('keyup', () => {
27
+          this.editorKeyup.emit(editor.getContent());
28
+        });
29
+      },
30
+      height: '320',
31
+    });
32
+  }
33
+
34
+  ngOnDestroy() {
35
+    tinymce.remove(this.editor);
36
+  }
37
+}

src/frontend/src/app/error-display/error-display.component.scss → src/frontend/src/app/@theme/directives/.gitkeep 查看文件


+ 3
- 0
src/frontend/src/app/@theme/layouts/index.ts 查看文件

@@ -0,0 +1,3 @@
1
+export * from './one-column/one-column.layout';
2
+export * from './two-columns/two-columns.layout';
3
+export * from './three-columns/three-columns.layout';

+ 9
- 0
src/frontend/src/app/@theme/layouts/one-column/one-column.layout.scss 查看文件

@@ -0,0 +1,9 @@
1
+@import '../../styles/themes';
2
+@import '~bootstrap/scss/mixins/breakpoints';
3
+@import '~@nebular/theme/styles/global/breakpoints';
4
+
5
+@include nb-install-component() {
6
+  .menu-sidebar ::ng-deep .scrollable {
7
+    padding-top: nb-theme(layout-padding-top);
8
+  }
9
+}

+ 26
- 0
src/frontend/src/app/@theme/layouts/one-column/one-column.layout.ts 查看文件

@@ -0,0 +1,26 @@
1
+import { Component } from '@angular/core';
2
+
3
+@Component({
4
+  selector: 'ngx-one-column-layout',
5
+  styleUrls: ['./one-column.layout.scss'],
6
+  template: `
7
+    <nb-layout windowMode>
8
+      <nb-layout-header fixed>
9
+        <ngx-header></ngx-header>
10
+      </nb-layout-header>
11
+
12
+      <nb-sidebar class="menu-sidebar" tag="menu-sidebar" responsive>
13
+        <ng-content select="nb-menu"></ng-content>
14
+      </nb-sidebar>
15
+
16
+      <nb-layout-column>
17
+        <ng-content select="router-outlet"></ng-content>
18
+      </nb-layout-column>
19
+
20
+      <nb-layout-footer fixed>
21
+        <ngx-footer></ngx-footer>
22
+      </nb-layout-footer>
23
+    </nb-layout>
24
+  `,
25
+})
26
+export class OneColumnLayoutComponent {}

+ 9
- 0
src/frontend/src/app/@theme/layouts/three-columns/three-columns.layout.scss 查看文件

@@ -0,0 +1,9 @@
1
+@import '../../styles/themes';
2
+@import '~bootstrap/scss/mixins/breakpoints';
3
+@import '~@nebular/theme/styles/global/breakpoints';
4
+
5
+@include nb-install-component() {
6
+  .menu-sidebar ::ng-deep .scrollable {
7
+    padding-top: nb-theme(layout-padding-top);
8
+  }
9
+}

+ 32
- 0
src/frontend/src/app/@theme/layouts/three-columns/three-columns.layout.ts 查看文件

@@ -0,0 +1,32 @@
1
+import { Component } from '@angular/core';
2
+
3
+@Component({
4
+  selector: 'ngx-three-columns-layout',
5
+  styleUrls: ['./three-columns.layout.scss'],
6
+  template: `
7
+    <nb-layout windowMode>
8
+      <nb-layout-header fixed>
9
+        <ngx-header></ngx-header>
10
+      </nb-layout-header>
11
+
12
+      <nb-sidebar class="menu-sidebar" tag="menu-sidebar" responsive>
13
+        <ng-content select="nb-menu"></ng-content>
14
+      </nb-sidebar>
15
+
16
+      <nb-layout-column class="small">
17
+      </nb-layout-column>
18
+
19
+      <nb-layout-column>
20
+        <ng-content select="router-outlet"></ng-content>
21
+      </nb-layout-column>
22
+
23
+      <nb-layout-column class="small">
24
+      </nb-layout-column>
25
+
26
+      <nb-layout-footer fixed>
27
+        <ngx-footer></ngx-footer>
28
+      </nb-layout-footer>
29
+    </nb-layout>
30
+  `,
31
+})
32
+export class ThreeColumnsLayoutComponent {}

+ 9
- 0
src/frontend/src/app/@theme/layouts/two-columns/two-columns.layout.scss 查看文件

@@ -0,0 +1,9 @@
1
+@import '../../styles/themes';
2
+@import '~bootstrap/scss/mixins/breakpoints';
3
+@import '~@nebular/theme/styles/global/breakpoints';
4
+
5
+@include nb-install-component() {
6
+  .menu-sidebar ::ng-deep .scrollable {
7
+    padding-top: nb-theme(layout-padding-top);
8
+  }
9
+}

+ 30
- 0
src/frontend/src/app/@theme/layouts/two-columns/two-columns.layout.ts 查看文件

@@ -0,0 +1,30 @@
1
+import { Component } from '@angular/core';
2
+
3
+@Component({
4
+  selector: 'ngx-two-columns-layout',
5
+  styleUrls: ['./two-columns.layout.scss'],
6
+  template: `
7
+    <nb-layout windowMode>
8
+      <nb-layout-header fixed>
9
+        <ngx-header></ngx-header>
10
+      </nb-layout-header>
11
+
12
+      <nb-sidebar class="menu-sidebar" tag="menu-sidebar" responsive>
13
+        <ng-content select="nb-menu"></ng-content>
14
+      </nb-sidebar>
15
+
16
+      <nb-layout-column class="small">
17
+      </nb-layout-column>
18
+
19
+      <nb-layout-column>
20
+        <ng-content select="router-outlet"></ng-content>
21
+      </nb-layout-column>
22
+
23
+      <nb-layout-footer fixed>
24
+        <ngx-footer></ngx-footer>
25
+      </nb-layout-footer>
26
+
27
+    </nb-layout>
28
+  `,
29
+})
30
+export class TwoColumnsLayoutComponent {}

src/frontend/src/app/header-bar/header-bar.component.scss → src/frontend/src/app/@theme/pipes/.gitkeep 查看文件


+ 11
- 0
src/frontend/src/app/@theme/pipes/capitalize.pipe.ts 查看文件

@@ -0,0 +1,11 @@
1
+import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+@Pipe({ name: 'ngxCapitalize' })
4
+export class CapitalizePipe implements PipeTransform {
5
+
6
+  transform(input: string): string {
7
+    return input && input.length
8
+      ? (input.charAt(0).toUpperCase() + input.slice(1).toLowerCase())
9
+      : input;
10
+  }
11
+}

+ 5
- 0
src/frontend/src/app/@theme/pipes/index.ts 查看文件

@@ -0,0 +1,5 @@
1
+export * from './capitalize.pipe';
2
+export * from './plural.pipe';
3
+export * from './round.pipe';
4
+export * from './timing.pipe';
5
+export * from './number-with-commas.pipe';

+ 9
- 0
src/frontend/src/app/@theme/pipes/number-with-commas.pipe.ts 查看文件

@@ -0,0 +1,9 @@
1
+import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+@Pipe({ name: 'ngxNumberWithCommas' })
4
+export class NumberWithCommasPipe implements PipeTransform {
5
+
6
+  transform(input: number): string {
7
+    return new Intl.NumberFormat().format(input);
8
+  }
9
+}

+ 14
- 0
src/frontend/src/app/@theme/pipes/plural.pipe.ts 查看文件

@@ -0,0 +1,14 @@
1
+import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+@Pipe({ name: 'ngxPlural' })
4
+export class PluralPipe implements PipeTransform {
5
+
6
+  transform(input: number, label: string, pluralLabel: string = ''): string {
7
+    input = input || 0;
8
+    return input === 1
9
+      ? `${input} ${label}`
10
+      : pluralLabel
11
+        ? `${input} ${pluralLabel}`
12
+        : `${input} ${label}s`;
13
+  }
14
+}

+ 9
- 0
src/frontend/src/app/@theme/pipes/round.pipe.ts 查看文件

@@ -0,0 +1,9 @@
1
+import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+@Pipe({ name: 'ngxRound' })
4
+export class RoundPipe implements PipeTransform {
5
+
6
+  transform(input: number): number {
7
+    return Math.round(input);
8
+  }
9
+}

+ 18
- 0
src/frontend/src/app/@theme/pipes/timing.pipe.ts 查看文件

@@ -0,0 +1,18 @@
1
+import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+@Pipe({ name: 'timing' })
4
+export class TimingPipe implements PipeTransform {
5
+  transform(time: number): string {
6
+    if (time) {
7
+      const minutes = Math.floor(time / 60);
8
+      const seconds = Math.floor(time % 60);
9
+      return `${this.initZero(minutes)}${minutes}:${this.initZero(seconds)}${seconds}`;
10
+    }
11
+
12
+    return '00:00';
13
+  }
14
+
15
+  private initZero(time: number): string {
16
+    return time < 10 ? '0' : '';
17
+  }
18
+}

+ 8
- 0
src/frontend/src/app/@theme/styles/_layout.scss 查看文件

@@ -0,0 +1,8 @@
1
+@mixin ngx-layout() {
2
+  @include media-breakpoint-down(is) {
3
+    .row {
4
+      margin-left: -10px;
5
+      margin-right: -10px;
6
+    }
7
+  }
8
+}

+ 11
- 0
src/frontend/src/app/@theme/styles/_overrides.scss 查看文件

@@ -0,0 +1,11 @@
1
+@import './themes';
2
+
3
+@mixin nb-overrides() {
4
+  nb-select.size-medium button {
5
+    padding: 0.4375rem 2.2rem 0.4375rem 1.125rem !important;
6
+
7
+    nb-icon {
8
+      right: 0.41rem !important;
9
+    }
10
+  }
11
+}

+ 20
- 0
src/frontend/src/app/@theme/styles/pace.theme.scss 查看文件

@@ -0,0 +1,20 @@
1
+/**
2
+ * @license
3
+ * Copyright Akveo. All Rights Reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ */
6
+
7
+ @mixin ngx-pace-theme() {
8
+
9
+  .pace .pace-progress {
10
+    background: nb-theme(color-primary-default);
11
+  }
12
+
13
+  .pace .pace-progress-inner {
14
+    box-shadow: 0 0 10px nb-theme(color-primary-default), 0 0 5px nb-theme(color-primary-default);
15
+  }
16
+
17
+  .pace .pace-activity {
18
+    display: none;
19
+  }
20
+}

+ 33
- 0
src/frontend/src/app/@theme/styles/styles.scss 查看文件

@@ -0,0 +1,33 @@
1
+@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,600,700&display=swap');
2
+
3
+// themes - our custom or/and out of the box themes
4
+@import 'themes';
5
+
6
+// framework component themes (styles tied to theme variables)
7
+@import '~@nebular/theme/styles/globals';
8
+@import '~@nebular/auth/styles/all';
9
+
10
+@import '~bootstrap/scss/functions';
11
+@import '~bootstrap/scss/variables';
12
+@import '~bootstrap/scss/mixins';
13
+@import '~bootstrap/scss/grid';
14
+
15
+// loading progress bar theme
16
+@import './pace.theme';
17
+
18
+@import './layout';
19
+@import './overrides';
20
+
21
+// install the framework and custom global styles
22
+@include nb-install() {
23
+
24
+  // framework global styles
25
+  @include nb-theme-global();
26
+  @include nb-auth-global();
27
+
28
+  @include ngx-layout();
29
+  // loading progress bar
30
+  @include ngx-pace-theme();
31
+
32
+  @include nb-overrides();
33
+};

+ 0
- 0
src/frontend/src/app/@theme/styles/theme.corporate.ts 查看文件


部分文件因为文件数量过多而无法显示

正在加载...
取消
保存