You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ni...@apache.org on 2018/12/14 02:09:32 UTC
[servicecomb-pack] branch master updated: [SCB-935] Saga
transaction management console UI initial commit (#317)
This is an automated email from the ASF dual-hosted git repository.
ningjiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-pack.git
The following commit(s) were added to refs/heads/master by this push:
new 96b1514 [SCB-935] Saga transaction management console UI initial commit (#317)
96b1514 is described below
commit 96b15140f63a57ee8161bbfb024210188831db8d
Author: Anvith KS <an...@huawei.com>
AuthorDate: Fri Dec 14 07:39:28 2018 +0530
[SCB-935] Saga transaction management console UI initial commit (#317)
* Saga transaction management console UI initial commit
* Removed unnecessary files and updated the icons.
* Updated dashboard as per new ALpha API's.
* Fixed some UI style issues.
* Fixed spelling error.
* Updated ASF headers. removed commented code.
* Docker image reference changed.
* Updated the Readme file.
* Added missing license information
* Added license copies. Added usage information and version number.
* Updated license information in the scss files.
---
.../saga-servicecomb-demo/docker-compose.yaml | 2 +
saga-distribution/src/release/LICENSE | 7 +-
saga-distribution/src/release/NOTICE | 6 +-
.../src/release/licenses/LICENSE-ngxadmin | 21 ++
.../src/main/resources/saga-frontend/README.md | 32 +++
.../src/main/resources/saga-frontend/angular.json | 89 ++++++
.../src/main/resources/saga-frontend/package.json | 77 +++++
.../saga-frontend/src/app/@core/core.module.ts | 80 ++++++
.../src/app/@core/data/data.module.ts | 34 +++
.../src/app/@core/data/saga-events.service.ts | 71 +++++
.../src/app/@core/data/state.service.ts | 97 +++++++
.../src/app/@core/module-import-guard.ts | 21 ++
.../src/app/@core/utils/analytics.service.ts | 37 +++
.../src/app/@core/utils/util.service.ts | 127 +++++++++
.../@theme/components/footer/footer.component.scss | 53 ++++
.../@theme/components/footer/footer.component.ts | 32 +++
.../@theme/components/header/header.component.html | 35 +++
.../@theme/components/header/header.component.scss | 235 ++++++++++++++++
.../@theme/components/header/header.component.ts | 53 ++++
.../src/app/@theme/components/index.ts | 11 +
.../components/switcher/switcher.component.scss | 135 +++++++++
.../components/switcher/switcher.component.ts | 65 +++++
.../theme-settings/theme-settings.component.scss | 94 +++++++
.../theme-settings/theme-settings.component.ts | 63 +++++
.../theme-switcher/theme-switcher.component.html | 21 ++
.../theme-switcher/theme-switcher.component.scss | 73 +++++
.../theme-switcher/theme-switcher.component.ts | 24 ++
.../theme-switcher-list.component.scss | 99 +++++++
.../themes-switcher-list.component.ts | 56 ++++
.../app/@theme/layouts/default/default.layout.scss | 187 ++++++++++++
.../app/@theme/layouts/default/default.layout.ts | 119 ++++++++
.../saga-frontend/src/app/@theme/layouts/index.ts | 7 +
.../layouts/one-column/one-column.layout.scss | 180 ++++++++++++
.../@theme/layouts/one-column/one-column.layout.ts | 56 ++++
.../src/app/@theme/pipes/capitalize.pipe.ts | 16 ++
.../saga-frontend/src/app/@theme/pipes/index.ts | 11 +
.../src/app/@theme/pipes/no-sanitize.pipe.ts | 28 ++
.../app/@theme/pipes/number-with-commas.pipe.ts | 14 +
.../src/app/@theme/pipes/plural.pipe.ts | 19 ++
.../src/app/@theme/pipes/round.pipe.ts | 14 +
.../src/app/@theme/pipes/timing.pipe.ts | 23 ++
.../src/app/@theme/styles/bootstrap-rtl.scss | 216 ++++++++++++++
.../src/app/@theme/styles/font-size.scss | 26 ++
.../src/app/@theme/styles/pace.theme.scss | 40 +++
.../src/app/@theme/styles/styles.scss | 54 ++++
.../src/app/@theme/styles/theme.corporate.ts | 309 ++++++++++++++++++++
.../src/app/@theme/styles/theme.cosmic.ts | 309 ++++++++++++++++++++
.../src/app/@theme/styles/theme.default.ts | 312 +++++++++++++++++++++
.../src/app/@theme/styles/themes.scss | 120 ++++++++
.../saga-frontend/src/app/@theme/theme.module.ts | 124 ++++++++
.../saga-frontend/src/app/app-routing.module.ts | 43 +++
.../saga-frontend/src/app/app.component.ts | 22 ++
.../resources/saga-frontend/src/app/app.module.ts | 38 +++
.../count-cards/count-cards.component.html | 23 ++
.../count-cards/count-cards.component.scss | 37 +++
.../dashboard/count-cards/count-cards.component.ts | 38 +++
.../app/pages/dashboard/dashboard.component.html | 64 +++++
.../app/pages/dashboard/dashboard.component.scss | 44 +++
.../src/app/pages/dashboard/dashboard.component.ts | 196 +++++++++++++
.../src/app/pages/dashboard/dashboard.module.ts | 39 +++
.../recent-table/recent-table.component.html | 60 ++++
.../recent-table/recent-table.component.scss | 19 ++
.../recent-table/recent-table.component.ts | 72 +++++
.../miscellaneous/miscellaneous-routing.module.ts | 30 ++
.../pages/miscellaneous/miscellaneous.component.ts | 15 +
.../pages/miscellaneous/miscellaneous.module.ts | 19 ++
.../not-found/not-found.component.html | 29 ++
.../not-found/not-found.component.scss | 43 +++
.../miscellaneous/not-found/not-found.component.ts | 22 ++
.../saga-frontend/src/app/pages/pages-menu.ts | 63 +++++
.../src/app/pages/pages-routing.module.ts | 70 +++++
.../saga-frontend/src/app/pages/pages.component.ts | 33 +++
.../saga-frontend/src/app/pages/pages.module.ts | 44 +++
.../pages/transactions/customRender.component.ts | 47 ++++
.../transactions/findTransaction.component.html | 48 ++++
.../transactions/findTransaction.component.ts | 79 ++++++
.../pages/transactions/transactions.component.html | 15 +
.../pages/transactions/transactions.component.ts | 53 ++++
.../app/pages/transactions/transactions.module.ts | 36 +++
.../transactions/transactionsTable.component.html | 34 +++
.../transactions/transactionsTable.component.scss | 20 ++
.../transactions/transactionsTable.component.ts | 135 +++++++++
.../src/assets/images/square_pattern.svg | 1 +
.../src/assets/images/square_pattern_cosmic.svg | 1 +
.../src/environments/environment.prod.ts | 9 +
.../saga-frontend/src/environments/environment.ts | 15 +
.../main/resources/saga-frontend/src/favicon.ico | Bin 0 -> 798 bytes
.../main/resources/saga-frontend/src/favicon.png | Bin 0 -> 29126 bytes
.../main/resources/saga-frontend/src/index.html | 48 ++++
.../src/main/resources/saga-frontend/src/main.ts | 17 ++
.../main/resources/saga-frontend/src/polyfills.ts | 76 +++++
.../resources/saga-frontend/src/tsconfig.app.json | 27 ++
.../main/resources/saga-frontend/src/typings.d.ts | 15 +
.../src/main/resources/saga-frontend/tsconfig.json | 22 ++
94 files changed, 5693 insertions(+), 2 deletions(-)
diff --git a/saga-demo/saga-servicecomb-demo/docker-compose.yaml b/saga-demo/saga-servicecomb-demo/docker-compose.yaml
index 036a206..88b4e24 100644
--- a/saga-demo/saga-servicecomb-demo/docker-compose.yaml
+++ b/saga-demo/saga-servicecomb-demo/docker-compose.yaml
@@ -36,6 +36,8 @@ services:
hostname: alpha-server
links:
- "database:postgresql.servicecomb.io"
+ ports:
+ - "8090:8090"
environment:
- JAVA_OPTS=-Dspring.profiles.active=prd
healthcheck:
diff --git a/saga-distribution/src/release/LICENSE b/saga-distribution/src/release/LICENSE
index 1c06754..a440d44 100644
--- a/saga-distribution/src/release/LICENSE
+++ b/saga-distribution/src/release/LICENSE
@@ -262,7 +262,12 @@ org.slf4j:log4j-over-slf4j:jar:1.7.25
This product bundles slf4j libraries which is licensed under the
MIT license.
For details, see http://www.slf4j.org
-You can find a copy of the License at licenses/LICENSE-slf4j
+You can find a copy of the License at licenses/LICENSE-slf4j
+===========================================================================
+This product bundles ngx-admin libraries which is licensed under the
+MIT license.
+For details, see https://github.com/akveo/ngx-admin
+You can find a copy of the License at licenses/LICENSE-ngxadmin
===========================================================================
For com.google.protobuf:protobuf-java:bundle:3.5.1
diff --git a/saga-distribution/src/release/NOTICE b/saga-distribution/src/release/NOTICE
index 5a61c81..19bc823 100644
--- a/saga-distribution/src/release/NOTICE
+++ b/saga-distribution/src/release/NOTICE
@@ -50,4 +50,8 @@ See licenses/NOTICE-tomcat
================================================================
Notice for Netty tcnative
-See licenses/NOTICE-netty-tcnative
+See licenses/NOTICE-netty-tcnative
+================================================================
+Notice for akveo ngx-admin
+
+See licenses/LICENSE-ngxadmin
\ No newline at end of file
diff --git a/saga-distribution/src/release/licenses/LICENSE-ngxadmin b/saga-distribution/src/release/licenses/LICENSE-ngxadmin
new file mode 100644
index 0000000..93092fd
--- /dev/null
+++ b/saga-distribution/src/release/licenses/LICENSE-ngxadmin
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/saga-web/src/main/resources/saga-frontend/README.md b/saga-web/src/main/resources/saga-frontend/README.md
new file mode 100644
index 0000000..ee62a37
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/README.md
@@ -0,0 +1,32 @@
+# Saga Web UI - A Transaction viewer dashboard
+This project is using [ngx-admin](https://github.com/akveo/ngx-admin), [here you can find documentation and other useful articles](https://akveo.github.io/nebular/docs/getting-started/what-is-nebular).
+
+### How to run the UI
+To install saga-frontend on your machine you need to have the following tools installed:
+
+- Git - https://git-scm.com
+- Node.js - https://nodejs.org. Please note the version should be >=8
+- Npm - Node.js package manager, comes with Node.js. Please make sure npm version is >=5
+- You might also need some specific native packages depending on your operating system like build-essential on Ubuntu
+- Clone the repository
+
+```
+git clone https://github.com/apache/servicecomb-pack
+```
+
+- Navigate to the frontend folder
+
+```
+cd servicecomb-pack/saga-web/src/main/resources/saga-frontend && npm i
+```
+- To run a local copy
+```
+npm start
+```
+Go to http://0.0.0.0:4200 or http://localhost:4200 in your browser.
+- To create a bundle in production mode
+```
+npm run build:prod
+```
+This will clear up your **dist** folder (where release files are located) and generate a release build. Now you can copy the sources from the **dist** folder and simply put it under the **static** folder or under a web server.
+
diff --git a/saga-web/src/main/resources/saga-frontend/angular.json b/saga-web/src/main/resources/saga-frontend/angular.json
new file mode 100644
index 0000000..df102a8
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/angular.json
@@ -0,0 +1,89 @@
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "version": 1,
+ "newProjectRoot": "projects",
+ "projects": {
+ "saga-frontend": {
+ "root": "",
+ "sourceRoot": "src",
+ "projectType": "application",
+ "architect": {
+ "build": {
+ "builder": "@angular-devkit/build-angular:browser",
+ "options": {
+ "preserveSymlinks": true,
+ "outputPath": "dist",
+ "index": "src/index.html",
+ "main": "src/main.ts",
+ "tsConfig": "src/tsconfig.app.json",
+ "polyfills": "src/polyfills.ts",
+ "assets": [
+ "src/assets",
+ "src/favicon.ico",
+ "src/favicon.png"
+ ],
+ "styles": [
+ "node_modules/bootstrap/dist/css/bootstrap.css",
+ "node_modules/typeface-exo/index.css",
+ "node_modules/roboto-fontface/css/roboto/roboto-fontface.css",
+ "node_modules/ionicons/scss/ionicons.scss",
+ "node_modules/@fortawesome/fontawesome-free/css/all.css",
+ "node_modules/socicon/css/socicon.css",
+ "node_modules/nebular-icons/scss/nebular-icons.scss",
+ "node_modules/pace-js/templates/pace-theme-flash.tmpl.css",
+ "src/app/@theme/styles/styles.scss"
+ ],
+ "scripts": [
+ "node_modules/pace-js/pace.min.js" ]
+ },
+ "configurations": {
+ "production": {
+ "optimization": true,
+ "outputHashing": "all",
+ "sourceMap": false,
+ "extractCss": true,
+ "namedChunks": false,
+ "aot": true,
+ "extractLicenses": true,
+ "vendorChunk": false,
+ "buildOptimizer": true,
+ "fileReplacements": [
+ {
+ "replace": "src/environments/environment.ts",
+ "with": "src/environments/environment.prod.ts"
+ }
+ ]
+ }
+ }
+ },
+ "serve": {
+ "builder": "@angular-devkit/build-angular:dev-server",
+ "options": {
+ "browserTarget": "saga-frontend:build"
+ },
+ "configurations": {
+ "production": {
+ "browserTarget": "saga-frontend:build:production"
+ }
+ }
+ },
+ "extract-i18n": {
+ "builder": "@angular-devkit/build-angular:extract-i18n",
+ "options": {
+ "browserTarget": "saga-frontend:build"
+ }
+ }
+ }
+ }
+ },
+ "defaultProject": "saga-frontend",
+ "schematics": {
+ "@schematics/angular:component": {
+ "prefix": "ngx",
+ "styleext": "scss"
+ },
+ "@schematics/angular:directive": {
+ "prefix": "ngx"
+ }
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/package.json b/saga-web/src/main/resources/saga-frontend/package.json
new file mode 100644
index 0000000..c8c9d92
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/package.json
@@ -0,0 +1,77 @@
+{
+ "name": "saga-frontend",
+ "version": "0.2.0",
+ "license": "Apache 2.0",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/apache/incubator-servicecomb-saga.git"
+ },
+ "bugs": {
+ "url": "https://github.com/apache/incubator-servicecomb-saga/issues"
+ },
+ "browserslist": [
+ "> 1%",
+ "last 2 versions",
+ "IE 11"
+ ],
+ "scripts": {
+ "ng": "ng",
+ "conventional-changelog": "conventional-changelog",
+ "start": "ng serve --port 5200",
+ "build": "ng build",
+ "build:prod": "npm run build -- --prod --aot",
+ "prepush": "npm run lint:ci",
+ "release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s"
+ },
+ "dependencies": {
+ "@angular/animations": "6.0.0",
+ "@angular/common": "6.0.0",
+ "@angular/compiler": "6.0.0",
+ "@angular/core": "6.0.0",
+ "@angular/forms": "6.0.0",
+ "@angular/http": "6.0.0",
+ "@angular/platform-browser": "6.0.0",
+ "@angular/platform-browser-dynamic": "6.0.0",
+ "@angular/router": "6.0.0",
+ "@nebular/auth": "2.0.0-rc.10",
+ "@nebular/security": "2.0.0-rc.10",
+ "@nebular/theme": "2.0.0-rc.10",
+ "@ng-bootstrap/ng-bootstrap": "1.0.0",
+ "angular2-toaster": "6.0.0",
+ "bootstrap": "4.0.0",
+ "classlist.js": "1.1.20150312",
+ "core-js": "2.5.1",
+ "intl": "1.2.5",
+ "ionicons": "2.0.1",
+ "nebular-icons": "1.0.9",
+ "ng2-completer": "2.0.8",
+ "ng2-smart-table": "1.3.5",
+ "normalize.css": "6.0.0",
+ "pace-js": "1.0.2",
+ "roboto-fontface": "0.8.0",
+ "rxjs": "^6.1.0",
+ "rxjs-compat": "^6.1.0",
+ "socicon": "3.0.5",
+ "typeface-exo": "0.0.22",
+ "underscore": "^1.9.1",
+ "web-animations-js": "2.2.5",
+ "zone.js": "^0.8.26"
+ },
+ "devDependencies": {
+ "@angular/cli": "6.0.0",
+ "@angular/compiler-cli": "6.0.0",
+ "@angular/language-service": "6.0.0",
+ "@compodoc/compodoc": "1.0.1",
+ "@fortawesome/fontawesome-free": "^5.2.0",
+ "@types/d3-color": "1.0.5",
+ "@types/node": "6.0.90",
+ "codelyzer": "4.0.2",
+ "conventional-changelog-cli": "1.3.4",
+ "husky": "0.13.3",
+ "npm-run-all": "4.0.2",
+ "rimraf": "2.6.1",
+ "ts-node": "3.2.2",
+ "typescript": "2.7.2",
+ "@angular-devkit/build-angular": "~0.6.0"
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@core/core.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/@core/core.module.ts
new file mode 100644
index 0000000..011ea3c
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@core/core.module.ts
@@ -0,0 +1,80 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { NbAuthModule, NbDummyAuthStrategy } from '@nebular/auth';
+import { NbSecurityModule, NbRoleProvider } from '@nebular/security';
+import { of as observableOf } from 'rxjs';
+
+import { throwIfAlreadyLoaded } from './module-import-guard';
+import { DataModule } from './data/data.module';
+import { AnalyticsService } from './utils/analytics.service';
+import { UtilService } from './utils/util.service';
+
+
+export class NbSimpleRoleProvider extends NbRoleProvider {
+ getRole() {
+ // here you could provide any role based on any auth flow
+ return observableOf('guest');
+ }
+}
+
+export const NB_CORE_PROVIDERS = [
+ ...DataModule.forRoot().providers,
+ ...NbAuthModule.forRoot({
+
+ strategies: [
+ NbDummyAuthStrategy.setup({
+ name: 'email',
+ delay: 3000,
+ }),
+ ],
+ }).providers,
+
+ NbSecurityModule.forRoot({
+ accessControl: {
+ guest: {
+ view: '*',
+ },
+ user: {
+ parent: 'guest',
+ create: '*',
+ edit: '*',
+ remove: '*',
+ },
+ },
+ }).providers,
+
+ {
+ provide: NbRoleProvider, useClass: NbSimpleRoleProvider,
+ },
+ AnalyticsService,
+ UtilService,
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ ],
+ exports: [
+ NbAuthModule,
+ ],
+ declarations: [],
+})
+export class CoreModule {
+ constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
+ throwIfAlreadyLoaded(parentModule, 'CoreModule');
+ }
+
+ static forRoot(): ModuleWithProviders {
+ return <ModuleWithProviders>{
+ ngModule: CoreModule,
+ providers: [
+ ...NB_CORE_PROVIDERS,
+ ],
+ };
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@core/data/data.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/@core/data/data.module.ts
new file mode 100644
index 0000000..221e392
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@core/data/data.module.ts
@@ -0,0 +1,34 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { NgModule, ModuleWithProviders } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { SagaeventsService } from './saga-events.service';
+import { StateService } from './state.service';
+
+const SERVICES = [
+ SagaeventsService,
+ StateService,
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ ],
+ providers: [
+ ...SERVICES,
+ ],
+})
+export class DataModule {
+ static forRoot(): ModuleWithProviders {
+ return <ModuleWithProviders>{
+ ngModule: DataModule,
+ providers: [
+ ...SERVICES,
+ ],
+ };
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@core/data/saga-events.service.ts b/saga-web/src/main/resources/saga-frontend/src/app/@core/data/saga-events.service.ts
new file mode 100644
index 0000000..3517d70
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@core/data/saga-events.service.ts
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs/Observable';
+
+
+import { environment } from '../../../environments/environment';
+
+const API_URL = environment.apiUrl;
+
+@Injectable()
+export class SagaeventsService {
+
+ private events;
+
+ constructor(private http: HttpClient) {
+
+ }
+
+ public getRecentTransactions(status: any, count?: number): Observable<any> {
+ let url;
+ if(status && status.length){
+ if(count){
+ url = API_URL + '/saga/recent?status=' + status + '&count=' + count;
+ } else{
+ url = API_URL + '/saga/recent?status=' + status + '&count=5';
+ }
+ }
+ return this.http.get(url, {observe: 'response'});
+ }
+
+ public findTransaction(gid?: any, name?: string): Observable<any> {
+ let url;
+ if(gid){
+ url = API_URL + '/saga/findTransactions?globalTxID=' + gid ;
+ } else if(name){
+ url = API_URL + '/saga/findTransactions?microServiceName=' + name ;
+ } else {
+ url = API_URL + '/saga/findTransactions';
+ }
+ return this.http.get(url, {observe: 'response'});
+ }
+
+ public getTransactions(status: any): Observable<any> {
+ let url;
+ if(status && status.length){
+ url = API_URL + '/saga/transactions?status=' + status;
+ }
+ return this.http.get(url, {observe: 'response'});
+ }
+
+ public getAllStats(): Observable<any> {
+ return this.http.get(API_URL + '/saga/stats', {observe: 'response'});
+ }
+
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@core/data/state.service.ts b/saga-web/src/main/resources/saga-frontend/src/app/@core/data/state.service.ts
new file mode 100644
index 0000000..2bb676b
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@core/data/state.service.ts
@@ -0,0 +1,97 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Injectable, OnDestroy } from '@angular/core';
+import { of as observableOf, Observable, BehaviorSubject } from 'rxjs';
+import { takeWhile } from 'rxjs/operators';
+
+import { NbLayoutDirectionService, NbLayoutDirection } from '@nebular/theme';
+
+@Injectable()
+export class StateService implements OnDestroy {
+
+ protected layouts: any = [
+ {
+ name: 'One Column',
+ icon: 'nb-layout-default',
+ id: 'one-column',
+ selected: true,
+ },
+ {
+ name: 'Two Column',
+ icon: 'nb-layout-two-column',
+ id: 'two-column',
+ },
+ {
+ name: 'Center Column',
+ icon: 'nb-layout-centre',
+ id: 'center-column',
+ },
+ ];
+
+ protected sidebars: any = [
+ {
+ name: 'Sidebar at layout start',
+ icon: 'nb-layout-sidebar-left',
+ id: 'start',
+ selected: true,
+ },
+ {
+ name: 'Sidebar at layout end',
+ icon: 'nb-layout-sidebar-right',
+ id: 'end',
+ },
+ ];
+
+ protected layoutState$ = new BehaviorSubject(this.layouts[0]);
+ protected sidebarState$ = new BehaviorSubject(this.sidebars[0]);
+
+ alive = true;
+
+ constructor(directionService: NbLayoutDirectionService) {
+ directionService.onDirectionChange()
+ .pipe(takeWhile(() => this.alive))
+ .subscribe(direction => this.updateSidebarIcons(direction));
+
+ this.updateSidebarIcons(directionService.getDirection());
+ }
+
+ ngOnDestroy() {
+ this.alive = false;
+ }
+
+ private updateSidebarIcons(direction: NbLayoutDirection) {
+ const [ startSidebar, endSidebar ] = this.sidebars;
+ const isLtr = direction === NbLayoutDirection.LTR;
+ const startIconClass = isLtr ? 'nb-layout-sidebar-left' : 'nb-layout-sidebar-right';
+ const endIconClass = isLtr ? 'nb-layout-sidebar-right' : 'nb-layout-sidebar-left';
+ startSidebar.icon = startIconClass;
+ endSidebar.icon = endIconClass;
+ }
+
+ setLayoutState(state: any): any {
+ this.layoutState$.next(state);
+ }
+
+ getLayoutStates(): Observable<any[]> {
+ return observableOf(this.layouts);
+ }
+
+ onLayoutState(): Observable<any> {
+ return this.layoutState$.asObservable();
+ }
+
+ setSidebarState(state: any): any {
+ this.sidebarState$.next(state);
+ }
+
+ getSidebarStates(): Observable<any[]> {
+ return observableOf(this.sidebars);
+ }
+
+ onSidebarState(): Observable<any> {
+ return this.sidebarState$.asObservable();
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@core/module-import-guard.ts b/saga-web/src/main/resources/saga-frontend/src/app/@core/module-import-guard.ts
new file mode 100644
index 0000000..932555e
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@core/module-import-guard.ts
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export function throwIfAlreadyLoaded(parentModule: any, moduleName: string) {
+ if (parentModule) {
+ throw new Error(`${moduleName} has already been loaded. Import Core modules in the AppModule only.`);
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@core/utils/analytics.service.ts b/saga-web/src/main/resources/saga-frontend/src/app/@core/utils/analytics.service.ts
new file mode 100644
index 0000000..592947f
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@core/utils/analytics.service.ts
@@ -0,0 +1,37 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Injectable } from '@angular/core';
+import { NavigationEnd, Router } from '@angular/router';
+import { Location } from '@angular/common';
+import { filter } from 'rxjs/operators';
+
+declare const ga: any;
+
+@Injectable()
+export class AnalyticsService {
+ private enabled: boolean;
+
+ constructor(private location: Location, private router: Router) {
+ this.enabled = false;
+ }
+
+ trackPageViews() {
+ if (this.enabled) {
+ this.router.events.pipe(
+ filter((event) => event instanceof NavigationEnd),
+ )
+ .subscribe(() => {
+ ga('send', {hitType: 'pageview', page: this.location.path()});
+ });
+ }
+ }
+
+ trackEvent(eventName: string) {
+ if (this.enabled) {
+ ga('send', 'event', eventName);
+ }
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@core/utils/util.service.ts b/saga-web/src/main/resources/saga-frontend/src/app/@core/utils/util.service.ts
new file mode 100644
index 0000000..ed75fd1
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@core/utils/util.service.ts
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Injectable } from '@angular/core';
+import { ToasterService, ToasterConfig, Toast, BodyOutputType } from 'angular2-toaster';
+import 'style-loader!angular2-toaster/toaster.css';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class UtilService {
+
+ constructor(private toasterService: ToasterService) { }
+
+ config: ToasterConfig;
+
+ positions = 'toast-bottom-right';
+ animationType = 'fade';
+ timeout = 5000;
+ toastsLimit = 5;
+
+ isNewestOnTop = true;
+ isHideOnClick = true;
+ isDuplicatesPrevented = false;
+ isCloseButton = true;
+
+ success (title: string, body: string, params?: any) {
+ this.config = new ToasterConfig({
+ positionClass: params && params.position?params.position:this.positions,
+ timeout: params && params.timeout?params.timeout:this.timeout,
+ newestOnTop: params && params.isNewestOnTop?params.isNewestOnTop:this.isNewestOnTop,
+ tapToDismiss: params && params.isHideOnClick?params.isHideOnClick:this.isHideOnClick,
+ preventDuplicates: params && params.isDuplicatesPrevented?params.isDuplicatesPrevented:this.isDuplicatesPrevented,
+ animation: params && params.animationType?params.animationType:this.animationType,
+ limit: params && params.toastsLimit?params.toastsLimit:this.toastsLimit,
+ });
+ const toast: Toast = {
+ type: 'success',
+ title: title,
+ body: body,
+ timeout: params && params.timeout?params.timeout:this.timeout,
+ showCloseButton: params && params.isCloseButton?params.isCloseButton:this.isCloseButton,
+ bodyOutputType: BodyOutputType.TrustedHtml,
+ };
+ this.toasterService.popAsync(toast);
+ }
+
+ error (title: string, body: string, params?: any) {
+ this.config = new ToasterConfig({
+ positionClass: params && params.position?params.position:this.positions,
+ timeout: params && params.timeout?params.timeout:this.timeout,
+ newestOnTop: params && params.isNewestOnTop?params.isNewestOnTop:this.isNewestOnTop,
+ tapToDismiss: params && params.isHideOnClick?params.isHideOnClick:this.isHideOnClick,
+ preventDuplicates: params && params.isDuplicatesPrevented?params.isDuplicatesPrevented:this.isDuplicatesPrevented,
+ animation: params && params.animationType?params.animationType:this.animationType,
+ limit: params && params.toastsLimit?params.toastsLimit:this.toastsLimit,
+ });
+ const toast: Toast = {
+ type: 'error',
+ title: title,
+ body: body,
+ timeout: params && params.timeout?params.timeout:this.timeout,
+ showCloseButton: params && params.isCloseButton?params.isCloseButton:this.isCloseButton,
+ bodyOutputType: BodyOutputType.TrustedHtml,
+ };
+ this.toasterService.popAsync(toast);
+ }
+
+ warning (title: string, body: string, params?: any) {
+ this.config = new ToasterConfig({
+ positionClass: params && params.position?params.position:this.positions,
+ timeout: params && params.timeout?params.timeout:this.timeout,
+ newestOnTop: params && params.isNewestOnTop?params.isNewestOnTop:this.isNewestOnTop,
+ tapToDismiss: params && params.isHideOnClick?params.isHideOnClick:this.isHideOnClick,
+ preventDuplicates: params && params.isDuplicatesPrevented?params.isDuplicatesPrevented:this.isDuplicatesPrevented,
+ animation: params && params.animationType?params.animationType:this.animationType,
+ limit: params && params.toastsLimit?params.toastsLimit:this.toastsLimit,
+ });
+ const toast: Toast = {
+ type: 'warning',
+ title: title,
+ body: body,
+ timeout: params && params.timeout?params.timeout:this.timeout,
+ showCloseButton: params && params.isCloseButton?params.isCloseButton:this.isCloseButton,
+ bodyOutputType: BodyOutputType.TrustedHtml,
+ };
+ this.toasterService.popAsync(toast);
+ }
+
+ info (title: string, body: string, params?: any) {
+ this.config = new ToasterConfig({
+ positionClass: params && params.position?params.position:this.positions,
+ timeout: params && params.timeout?params.timeout:this.timeout,
+ newestOnTop: params && params.isNewestOnTop?params.isNewestOnTop:this.isNewestOnTop,
+ tapToDismiss: params && params.isHideOnClick?params.isHideOnClick:this.isHideOnClick,
+ preventDuplicates: params && params.isDuplicatesPrevented?params.isDuplicatesPrevented:this.isDuplicatesPrevented,
+ animation: params && params.animationType?params.animationType:this.animationType,
+ limit: params && params.toastsLimit?params.toastsLimit:this.toastsLimit,
+ });
+ const toast: Toast = {
+ type: 'info',
+ title: title,
+ body: body,
+ timeout: params && params.timeout?params.timeout:this.timeout,
+ showCloseButton: params && params.isCloseButton?params.isCloseButton:this.isCloseButton,
+ bodyOutputType: BodyOutputType.TrustedHtml,
+ };
+ this.toasterService.popAsync(toast);
+ }
+
+ clearToasts() {
+ this.toasterService.clear();
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/footer/footer.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/footer/footer.component.scss
new file mode 100644
index 0000000..a7a85b4
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/footer/footer.component.scss
@@ -0,0 +1,53 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+@import '../../styles/themes';
+@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
+@import '~bootstrap/scss/mixins/breakpoints';
+
+@include nb-install-component() {
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .socials {
+ font-size: 2rem;
+
+ a {
+ padding: 0.4rem;
+ color: nb-theme(color-fg);
+ transition: color ease-out 0.1s;
+
+ &:hover {
+ color: nb-theme(color-fg-heading);
+ }
+ }
+ }
+
+ @include media-breakpoint-down(is) {
+ .socials {
+ font-size: 1.5rem;
+ }
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/footer/footer.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/footer/footer.component.ts
new file mode 100644
index 0000000..68ab67c
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/footer/footer.component.ts
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'ngx-footer',
+ styleUrls: ['./footer.component.scss'],
+ template: `
+ <span class="created-by">© 2018 <b><a href="http://servicecomb.apache.org/" target="_blank">ServiceComb</a></b></span>
+ <div class="socials">
+ <a title="GitHub" href="https://github.com/apache?q=ServiceComb" target="_blank" class="ion ion-social-github"></a>
+ <a title="Twitter" href="https://twitter.com/servicecomb" target="_blank" class="ion ion-social-twitter"></a>
+ <a title="Gitter" href="https://gitter.im/ServiceCombUsers/Saga" target="_blank" class="fab fa-gitter"></a>
+ </div>
+ `,
+})
+export class FooterComponent {
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/header/header.component.html b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/header/header.component.html
new file mode 100644
index 0000000..5def2e4
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/header/header.component.html
@@ -0,0 +1,35 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<div class="header-container"
+ [class.left]="position === 'normal'"
+ [class.right]="position === 'inverse'">
+ <div class="logo-containter mr-5">
+ <a (click)="toggleSidebar()" href="#" class="navigation"><i class="nb-menu"></i></a>
+ <div class="logo" (click)="goToHome()">ServiceComb<span> Saga</span></div>
+
+ </div>
+ <small class="ml-5">
+ A transaction management system for servicecomb
+ </small>
+</div>
+
+<div class="header-container">
+ <ngx-theme-switcher></ngx-theme-switcher>
+ <nb-actions
+ size="medium"
+ [class.right]="position === 'normal'"
+ [class.left]="position === 'inverse'">
+ </nb-actions>
+</div>
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/header/header.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/header/header.component.scss
new file mode 100644
index 0000000..3300a90
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/header/header.component.scss
@@ -0,0 +1,235 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+@import '../../styles/themes';
+@import '~bootstrap/scss/mixins/breakpoints';
+@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
+
+@include nb-install-component() {
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+
+ .left {
+ display: flex;
+ width: 100%;
+ order: 0;
+ flex-direction: row;
+ }
+ .right {
+ order: 1;
+ flex-direction: row-reverse;
+ }
+
+ .logo-containter {
+ display: flex;
+ align-items: center;
+ width: calc(#{nb-theme(sidebar-width)} - #{nb-theme(header-padding)});
+ }
+
+ .control-item {
+ display: block;
+ }
+
+ .header-container {
+ display: flex;
+ align-items: center;
+ width: auto;
+
+ .navigation {
+ @include nb-ltr(padding-right, nb-theme(padding));
+ @include nb-rtl(padding-left, nb-theme(padding));
+ font-size: 2.5rem;
+ text-decoration: none;
+
+ i {
+ display: block;
+ }
+
+ }
+
+ .logo {
+ padding: 0 nb-theme(padding);
+ font-size: 1.75rem;
+ font-weight: nb-theme(font-weight-bolder);
+ @include nb-ltr(border-left, 1px solid nb-theme(separator));
+ @include nb-rtl(border-right, 1px solid nb-theme(separator));
+ white-space: nowrap;
+
+ span {
+ font-weight: nb-theme(font-weight-normal);
+ }
+ }
+ }
+
+ @include nb-for-theme(corporate) {
+ $menu-action-separator-color: #3f4550;
+
+ nb-action {
+ @include nb-ltr(border-left-color, $menu-action-separator-color);
+ @include nb-rtl(border-right-color, $menu-action-separator-color);
+ }
+
+ .header-container .logo {
+ @include nb-ltr(border, none);
+ @include nb-rtl(border, none);
+ }
+
+ .header-container /deep/ ngx-theme-switcher .dropdown-toggle {
+ color: nb-theme(color-white);
+ background: transparent;
+ }
+ }
+
+ ngx-layout-direction-switcher {
+ margin: 0 1.5rem;
+ }
+
+ ngx-theme-switcher {
+ margin: nb-theme(layout-padding);
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+
+ @include media-breakpoint-down(xl) {
+ ngx-layout-direction-switcher {
+ display: none;
+ }
+ }
+
+ .toggle-layout /deep/ a {
+ display: block;
+ text-decoration: none;
+ line-height: 1;
+
+ i {
+ color: nb-theme(color-fg-highlight);
+ font-size: 2.25rem;
+ border-radius: 50%;
+ position: relative;
+ animation-name: pulse-light;
+
+ &::after {
+ content: ' ';
+ // hack to be able to set border-radius
+ background-image: url('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
+ border-radius: 50%;
+ pointer-events: none;
+
+ position: absolute;
+ top: 52.3%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 13%;
+ height: 13%;
+
+ animation: 3s linear infinite pulse;
+
+ @include nb-for-theme(default) {
+ animation-name: pulse-light;
+ }
+ }
+ }
+ }
+
+ @include keyframes(pulse) {
+ 0% {
+ box-shadow: 0 0 1px 0 rgba(nb-theme(color-fg-highlight), 0);
+ }
+ 20% {
+ box-shadow: 0 0 3px 10px rgba(nb-theme(color-fg-highlight), 0.4);
+ }
+ 100% {
+ box-shadow: 0 0 5px 20px rgba(nb-theme(color-fg-highlight), 0);
+ }
+ }
+
+ @include keyframes(pulse-light) {
+ 0% {
+ box-shadow: 0 0 1px 0 rgba(115, 255, 208, 0);
+ }
+ 20% {
+ box-shadow: 0 0 3px 10px rgba(115, 255, 208, 0.4);
+ }
+ 100% {
+ box-shadow: 0 0 5px 20px rgba(115, 255, 208, 0);
+ }
+ }
+
+ @include media-breakpoint-down(md) {
+
+ nb-action:not(.toggle-layout) {
+ border: none;
+ }
+
+ .control-item {
+ display: none;
+ }
+
+ .toggle-layout {
+ padding: 0;
+ }
+
+ ngx-layout-direction-switcher {
+ display: none;
+ }
+
+ ngx-theme-switcher {
+ margin: 0 0.5rem;
+ }
+ }
+
+ @include media-breakpoint-down(sm) {
+
+ nb-user /deep/ .user-name {
+ display: none;
+ }
+ }
+
+ @include media-breakpoint-down(is) {
+
+ .header-container {
+ .logo {
+ font-size: 1.25rem;
+ }
+ }
+
+ .toggle-layout {
+ display: none;
+ }
+
+ ngx-theme-switcher {
+ display: none;
+ }
+
+ nb-action:not(.toggle-layout) {
+ padding: 0;
+ }
+ }
+
+ @include media-breakpoint-down(xs) {
+ .right /deep/ {
+ display: none;
+ }
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/header/header.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/header/header.component.ts
new file mode 100644
index 0000000..1aab1d4
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/header/header.component.ts
@@ -0,0 +1,53 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Component, Input, OnInit } from '@angular/core';
+
+import { NbMenuService, NbSidebarService } from '@nebular/theme';
+import { AnalyticsService } from '../../../@core/utils/analytics.service';
+
+@Component({
+ selector: 'ngx-header',
+ styleUrls: ['./header.component.scss'],
+ templateUrl: './header.component.html',
+})
+export class HeaderComponent implements OnInit {
+
+ @Input() position = 'normal';
+
+ user: any;
+
+ userMenu = [{ title: 'Profile' }, { title: 'Log out' }];
+
+ constructor(private sidebarService: NbSidebarService,
+ private menuService: NbMenuService,
+ private analyticsService: AnalyticsService,
+ ) {
+ }
+
+ ngOnInit() {
+
+ }
+
+ toggleSidebar(): boolean {
+ this.sidebarService.toggle(true, 'menu-sidebar');
+
+ return false;
+ }
+
+ toggleSettings(): boolean {
+ this.sidebarService.toggle(false, 'settings-sidebar');
+
+ return false;
+ }
+
+ goToHome() {
+ this.menuService.navigateHome();
+ }
+
+ startSearch() {
+ this.analyticsService.trackEvent('startSearch');
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/index.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/index.ts
new file mode 100644
index 0000000..5446283
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/index.ts
@@ -0,0 +1,11 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+export * from './header/header.component';
+export * from './footer/footer.component';
+export * from './theme-settings/theme-settings.component';
+export * from './theme-switcher/theme-switcher.component';
+export * from './switcher/switcher.component';
+export * from './theme-switcher/themes-switcher-list/themes-switcher-list.component';
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/switcher/switcher.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/switcher/switcher.component.scss
new file mode 100644
index 0000000..ec95d29
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/switcher/switcher.component.scss
@@ -0,0 +1,135 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+@import '../../styles/themes';
+@import '~@nebular/theme/styles/global/bootstrap/hero-buttons';
+@import '~bootstrap/scss/mixins/breakpoints';
+@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
+
+@include nb-install-component() {
+ .switch-label {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ cursor: pointer;
+ margin: 0;
+
+ &.vertical {
+ flex-direction: column;
+ align-items: flex-start;
+
+ .first,
+ .second {
+ padding: 0;
+ }
+
+ .switch {
+ margin-top: 0.5em;
+ }
+ }
+
+ & > span {
+ font-size: 1.125rem;
+ font-weight: nb-theme(font-weight-bold);
+ transition: opacity 0.3s ease;
+ color: nb-theme(color-fg);
+
+ &.first {
+ @include nb-ltr(padding-right, 10px);
+ @include nb-rtl(padding-left, 10px);
+ }
+
+ &.second {
+ @include nb-ltr(padding-left, 10px);
+ @include nb-rtl(padding-right, 10px);
+ }
+
+ &.active {
+ color: nb-theme(color-fg-text);
+ }
+
+ @include nb-for-theme(cosmic) {
+ color: nb-theme(color-fg);
+
+ &.active {
+ color: nb-theme(color-white);
+ }
+ }
+
+ &:active {
+ opacity: 0.78;
+ }
+ }
+ }
+
+ .switch {
+ position: relative;
+ display: inline-block;
+ width: 3rem;
+ height: 1.5rem;
+ margin: 0;
+
+ input {
+ display: none;
+
+ &:checked + .slider::before {
+ @include nb-ltr(transform, translateX(1.5rem));
+ @include nb-rtl(transform, translateX(-1.5rem));
+ }
+ }
+
+ .slider {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ border-radius: 1.75rem;
+ background-color: nb-theme(layout-bg);
+ }
+
+ .slider::before {
+ position: absolute;
+ content: '';
+ height: 1.5rem;
+ width: 1.5rem;
+ border-radius: 50%;
+ background-color: nb-theme(color-success);
+ transition: 0.2s;
+
+ box-shadow: 0 0 0.25rem 0 rgba(nb-theme(color-fg), 0.4);
+
+ @include nb-for-theme(cosmic) {
+ @include btn-hero-primary-gradient();
+ }
+
+ @include nb-for-theme(corporate) {
+ background-color: nb-theme(color-primary);
+ }
+ }
+ }
+
+ @include media-breakpoint-down(xs) {
+ align-items: flex-end;
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/switcher/switcher.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/switcher/switcher.component.ts
new file mode 100644
index 0000000..377d639
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/switcher/switcher.component.ts
@@ -0,0 +1,65 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+
+@Component({
+ selector: 'ngx-switcher',
+ styleUrls: ['./switcher.component.scss'],
+ template: `
+ <label class="switch-label" [class.vertical]="vertical">
+ <span class="first" [class.active]="vertical || isFirstValue()">
+ {{vertical ? currentValueLabel() : firstValueLabel}}
+ </span>
+
+ <div class="switch">
+ <input type="checkbox" [checked]="isSecondValue()" (change)="changeValue()">
+ <span class="slider"></span>
+ </div>
+
+ <span
+ *ngIf="!vertical"
+ class="second"
+ [class.active]="isSecondValue()"
+ >
+ {{secondValueLabel}}
+ </span>
+ </label>
+ `,
+})
+export class SwitcherComponent {
+ @Input() firstValue: any;
+ @Input() secondValue: any;
+
+ @Input() firstValueLabel: string;
+ @Input() secondValueLabel: string;
+
+ @Input() vertical: boolean;
+
+ @Input() value: any;
+ @Output() valueChange = new EventEmitter<any>();
+
+ isFirstValue() {
+ return this.value === this.firstValue;
+ }
+
+ isSecondValue() {
+ return this.value === this.secondValue;
+ }
+
+ currentValueLabel() {
+ return this.isFirstValue()
+ ? this.firstValueLabel
+ : this.secondValueLabel;
+ }
+
+ changeValue() {
+ this.value = this.isFirstValue()
+ ? this.secondValue
+ : this.firstValue;
+
+ this.valueChange.emit(this.value);
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-settings/theme-settings.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-settings/theme-settings.component.scss
new file mode 100644
index 0000000..054a3cf
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-settings/theme-settings.component.scss
@@ -0,0 +1,94 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+@import '../../styles/themes';
+
+@include nb-install-component() {
+ h6 {
+ margin-bottom: 0.5rem;
+ }
+
+ .settings-row {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+
+ width: 90%;
+ margin: 0 0 1rem;
+
+ a {
+ text-decoration: none;
+ font-size: 2.25rem;
+
+ color: nb-theme(color-fg);
+
+ &.selected {
+ color: nb-theme(color-success);
+ }
+
+ @include nb-for-theme(cosmic) {
+ &.selected {
+ color: nb-theme(link-color);
+ }
+ }
+ }
+ }
+
+ .settings {
+ margin-bottom: 1em;
+ }
+
+ .switcher {
+ margin-bottom: 1rem;
+
+ /deep/ ngx-switcher {
+ .switch-label span {
+ font-size: 1em;
+ font-weight: normal;
+ }
+
+ .switch {
+ height: 1.5em;
+ width: 3em;
+
+ .slider::before {
+ height: 1.5em;
+ width: 1.5em;
+ }
+
+ input:checked + .slider::before {
+ @include nb-ltr(transform, translateX(1.5rem)!important);
+ @include nb-rtl(transform, translateX(-1.5rem)!important);
+ }
+ }
+
+ @include nb-for-theme(cosmic) {
+ .switch .slider {
+ background-color: nb-theme(color-bg);
+ }
+ }
+ }
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-settings/theme-settings.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-settings/theme-settings.component.ts
new file mode 100644
index 0000000..edd7092
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-settings/theme-settings.component.ts
@@ -0,0 +1,63 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Component } from '@angular/core';
+
+import { StateService } from '../../../@core/data/state.service';
+
+@Component({
+ selector: 'ngx-theme-settings',
+ styleUrls: ['./theme-settings.component.scss'],
+ template: `
+ <h6>SIDEBAR</h6>
+ <div class="settings-row">
+ <a *ngFor="let sidebar of sidebars"
+ href="#"
+ [class.selected]="sidebar.selected"
+ [attr.title]="sidebar.name"
+ (click)="sidebarSelect(sidebar)">
+ <i [attr.class]="sidebar.icon"></i>
+ </a>
+ </div>
+ <!-- <div class="switcher">
+ <ngx-layout-direction-switcher [vertical]="true"></ngx-layout-direction-switcher>
+ </div> -->
+ `,
+})
+export class ThemeSettingsComponent {
+
+ layouts = [];
+ sidebars = [];
+
+ constructor(protected stateService: StateService) {
+ this.stateService.getLayoutStates()
+ .subscribe((layouts: any[]) => this.layouts = layouts);
+
+ this.stateService.getSidebarStates()
+ .subscribe((sidebars: any[]) => this.sidebars = sidebars);
+ }
+
+ layoutSelect(layout: any): boolean {
+ this.layouts = this.layouts.map((l: any) => {
+ l.selected = false;
+ return l;
+ });
+
+ layout.selected = true;
+ this.stateService.setLayoutState(layout);
+ return false;
+ }
+
+ sidebarSelect(sidebars: any): boolean {
+ this.sidebars = this.sidebars.map((s: any) => {
+ s.selected = false;
+ return s;
+ });
+
+ sidebars.selected = true;
+ this.stateService.setSidebarState(sidebars);
+ return false;
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/theme-switcher.component.html b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/theme-switcher.component.html
new file mode 100644
index 0000000..355b1e6
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/theme-switcher.component.html
@@ -0,0 +1,21 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<div class="themes-switcher"
+ [nbPopover]="switcherListComponent"
+ nbPopoverPlacement="bottom"
+ [nbPopoverContext]="{popover: popover}">
+ <i class="nb-drops"></i>
+ <span *ngIf="showTitle">Themes</span>
+</div>
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/theme-switcher.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/theme-switcher.component.scss
new file mode 100644
index 0000000..93222db
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/theme-switcher.component.scss
@@ -0,0 +1,73 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+@import '../../styles/themes';
+@import '~@nebular/theme/styles/core/mixins';
+@import '~@nebular/theme/styles/core/functions';
+
+@include nb-install-component() {
+ .themes-switcher {
+ display: flex;
+ font-size: 1.25rem;
+ padding: 0.8rem 1.25rem;
+ align-items: center;
+ cursor: pointer;
+ background-color: nb-theme(switcher-background);
+ border-radius: nb-theme(radius);
+
+ &:hover {
+ $color: nb-theme(switcher-background);
+ $percentage: nb-theme(switcher-background-percentage);
+
+ background-color: tint($color, $percentage);
+ }
+
+ span {
+ margin: 0 1.2rem;
+ }
+
+ i {
+ color: nb-theme(color-primary);
+ font-size: 1.8rem;
+ border-radius: 50%;
+ position: relative;
+
+ @include nb-for-theme(default) {
+ color: nb-theme(color-success);
+ }
+
+ @include nb-for-theme(corporate) {
+ color: nb-theme(color-fg-highlight);
+ }
+
+ &::before {
+ // Hack for IE11, IE11 should not set background
+ background: nb-theme(drops-icon-line-gadient);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ }
+ }
+ }
+}
+
+
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/theme-switcher.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/theme-switcher.component.ts
new file mode 100644
index 0000000..7680a1b
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/theme-switcher.component.ts
@@ -0,0 +1,24 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Component, Input, ViewChild } from '@angular/core';
+import { NbPopoverDirective } from '@nebular/theme';
+import { NbJSThemeOptions } from '@nebular/theme/services/js-themes/theme.options';
+
+import { ThemeSwitcherListComponent } from './themes-switcher-list/themes-switcher-list.component';
+
+@Component({
+ selector: 'ngx-theme-switcher',
+ templateUrl: './theme-switcher.component.html',
+ styleUrls: ['./theme-switcher.component.scss'],
+})
+export class ThemeSwitcherComponent {
+ @ViewChild(NbPopoverDirective) popover: NbPopoverDirective;
+
+ @Input() showTitle: boolean = true;
+
+ switcherListComponent = ThemeSwitcherListComponent;
+ theme: NbJSThemeOptions;
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/themes-switcher-list/theme-switcher-list.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/themes-switcher-list/theme-switcher-list.component.scss
new file mode 100644
index 0000000..b63f1b1
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/themes-switcher-list/theme-switcher-list.component.scss
@@ -0,0 +1,99 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+@import '../../../styles/themes';
+@import '~@nebular/theme/styles/core/mixins';
+@import '~bootstrap/scss/mixins/breakpoints';
+@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
+
+$icon-color-default: #0bbb79;
+$icon-color-cosmic: #7958fa;
+$icon-color-corporate: #a7a2be;
+
+$icon-top-color-default: #01dbb5;
+$icon-top-color-cosmic: #a258fe;
+$icon-top-color-corporate: #e9e8eb;
+
+@include nb-install-component() {
+ /deep/ .themes-switcher-list {
+ padding: 1rem 2rem 1.25rem 0.5rem;
+ margin: 0;
+
+ @include nb-ltr(text-align, start);
+ @include nb-rtl(text-align, end);
+
+ .themes-switcher-item {
+ list-style: none;
+ cursor: pointer;
+
+ &:hover span {
+ opacity: 0.5;
+ }
+
+ i {
+ font-size: 2rem;
+
+ &.drop-icon-default {
+ color: $icon-color-default;
+
+ // Hack for IE11, IE11 should not set background
+ background: -webkit-linear-gradient($icon-top-color-default, $icon-color-default);
+ }
+
+ &.drop-icon-cosmic {
+ color: $icon-color-cosmic;
+
+ // Hack for IE11, IE11 should not set background
+ background: -webkit-linear-gradient($icon-top-color-cosmic, $icon-color-cosmic);
+ }
+
+ &.drop-icon-corporate {
+ color: $icon-color-corporate;
+
+ // Hack for IE11, IE11 should not set background
+ background: -webkit-linear-gradient($icon-top-color-corporate, $icon-color-corporate);
+ }
+
+ &.drop-icon-default,
+ &.drop-icon-cosmic,
+ &.drop-icon-corporate {
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ }
+ }
+
+ span {
+ font-weight: 300;
+ vertical-align: super;
+ padding-left: 1rem;
+ color: nb-theme(color-fg-heading);
+ }
+ }
+ }
+
+ @include media-breakpoint-down(is) {
+ /deep/ .themes-switcher-list {
+ display: none;
+ }
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/themes-switcher-list/themes-switcher-list.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/themes-switcher-list/themes-switcher-list.component.ts
new file mode 100644
index 0000000..3b410ea
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/components/theme-switcher/themes-switcher-list/themes-switcher-list.component.ts
@@ -0,0 +1,56 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import {Component, Input} from '@angular/core';
+import { NbThemeService, NbPopoverDirective } from '@nebular/theme';
+import { AnalyticsService } from '../../../../@core/utils/analytics.service';
+import { NbJSThemeOptions } from '@nebular/theme/services/js-themes/theme.options';
+
+@Component({
+ selector: 'ngx-theme-switcher-list',
+ template: `
+ <ul class="themes-switcher-list">
+ <li class="themes-switcher-item"
+ *ngFor="let theme of themes"
+ (click)="onToggleTheme(theme.key)">
+ <i class="nb-drop" [ngClass]="'drop-icon-' + theme.key"></i>
+ <span>{{ theme.title }}</span>
+ </li>
+ </ul>
+ `,
+ styleUrls: ['./theme-switcher-list.component.scss'],
+})
+export class ThemeSwitcherListComponent {
+
+ @Input() popover: NbPopoverDirective;
+
+ theme: NbJSThemeOptions;
+
+ themes = [
+ {
+ title: 'Light',
+ key: 'default',
+ },
+ {
+ title: 'Cosmic',
+ key: 'cosmic',
+ },
+ {
+ title: 'Corporate',
+ key: 'corporate',
+ },
+ ];
+
+ constructor(
+ private themeService: NbThemeService,
+ private analyticsService: AnalyticsService,
+ ) {}
+
+ onToggleTheme(themeKey: string) {
+ this.themeService.changeTheme(themeKey);
+ this.analyticsService.trackEvent('switchTheme');
+ this.popover.hide();
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/default/default.layout.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/default/default.layout.scss
new file mode 100644
index 0000000..45e0401
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/default/default.layout.scss
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@import '../../styles/themes';
+@import '~bootstrap/scss/mixins/breakpoints';
+@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
+
+@include nb-install-component() {
+ nb-layout-column.small {
+ flex: 0.15 !important;
+ }
+
+ nb-sidebar.settings-sidebar {
+ $sidebar-width: 7.5rem;
+
+ transition: width 0.3s ease;
+ width: $sidebar-width;
+ overflow: hidden;
+
+ &.collapsed {
+ width: 0;
+
+ /deep/ .main-container {
+ width: 0;
+
+ .scrollable {
+ width: $sidebar-width;
+ padding: 1.25rem;
+ }
+ }
+ }
+
+ /deep/ .main-container {
+ width: $sidebar-width;
+ background: nb-theme(color-bg);
+ transition: width 0.3s ease;
+ overflow: hidden;
+
+ .scrollable {
+ width: $sidebar-width;
+ }
+
+ @include nb-for-theme(cosmic) {
+ background: nb-theme(layout-bg);
+ }
+ }
+ }
+
+ nb-sidebar.menu-sidebar {
+
+ margin-top: nb-theme(sidebar-header-gap);
+
+ @include nb-for-theme(corporate) {
+ margin-top: 0;
+ }
+
+ /deep/ .main-container {
+ height:
+ calc(#{nb-theme(sidebar-height)} - #{nb-theme(header-height)} - #{nb-theme(sidebar-header-gap)}) !important;
+ @include nb-ltr(border-top-right-radius, nb-theme(radius));
+ @include nb-rtl(border-top-left-radius, nb-theme(radius));
+
+ @include nb-for-theme(corporate) {
+ border: 1px solid nb-theme(separator);
+ height:
+ calc(#{nb-theme(sidebar-height)} - #{nb-theme(header-height)}) !important;
+ }
+ }
+
+ /deep/ .scrollable {
+ @include nb-for-theme(corporate) {
+ padding-top: 0;
+
+ .menu-item:first-child {
+ border-top: none;
+ }
+ }
+ }
+
+ /deep/ nb-sidebar-header {
+ padding-bottom: 0.5rem;
+ text-align: center;
+ }
+
+ background: transparent;
+
+ .main-btn {
+ padding: 0.75rem 2.5rem;
+ margin-top: -2rem;
+ font-weight: bold;
+ transition: padding 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.48);
+
+ @include nb-for-theme(corporate) {
+ border-radius: nb-theme(radius);
+ }
+
+ i {
+ font-size: 2rem;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
+ }
+ span {
+ @include nb-ltr(padding-left, 0.25rem);
+ @include nb-rtl(padding-right, 0.25rem);
+ }
+
+ i, span {
+ vertical-align: middle;
+ }
+ }
+
+ /deep/ nb-menu {
+ & > .menu-items {
+ & > .menu-item:first-child {
+ .menu-title {
+
+ }
+ }
+ }
+
+ .nb-e-commerce {
+ font-size: 2rem;
+ }
+ }
+
+ &.compacted {
+
+ /deep/ nb-sidebar-header {
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ .main-btn {
+ width: 46px;
+ height: 44px;
+ padding: 0.375rem;
+ border-radius: 5px;
+ transition: none;
+
+ span {
+ display: none;
+ }
+ }
+ }
+ }
+
+ @include media-breakpoint-down(xs) {
+ .main-content {
+ padding: 0.75rem !important;
+
+ }
+ }
+
+ @include media-breakpoint-down(sm) {
+
+ nb-sidebar.menu-sidebar {
+
+ margin-top: 0;
+
+ /deep/ .main-container {
+ height: calc(#{nb-theme(sidebar-height)} - #{nb-theme(header-height)}) !important;
+ @include nb-ltr(border-top-right-radius, 0);
+ @include nb-rtl(border-top-left-radius, 0);
+
+ .scrollable {
+ padding-top: 0;
+ }
+ }
+ }
+
+ .main-btn {
+ display: none;
+ }
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/default/default.layout.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/default/default.layout.ts
new file mode 100644
index 0000000..3d6f308
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/default/default.layout.ts
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, OnDestroy } from '@angular/core';
+import { delay, withLatestFrom, takeWhile } from 'rxjs/operators';
+import {
+ NbMediaBreakpoint,
+ NbMediaBreakpointsService,
+ NbMenuItem,
+ NbMenuService,
+ NbSidebarService,
+ NbThemeService,
+} from '@nebular/theme';
+
+import { StateService } from '../../../@core/data/state.service';
+
+// TODO: move layouts into the framework
+@Component({
+ selector: 'ngx-default-layout',
+ styleUrls: ['./default.layout.scss'],
+ template: `
+ <nb-layout [center]="layout.id === 'center-column'" windowMode>
+ <nb-layout-header fixed>
+ <ngx-header [position]="sidebar.id === 'start' ? 'normal': 'inverse'"></ngx-header>
+ </nb-layout-header>
+
+ <nb-sidebar class="menu-sidebar"
+ tag="menu-sidebar"
+ responsive
+ [end]="sidebar.id === 'end'">
+ <nb-sidebar-header *ngIf="currentTheme !== 'corporate'">
+ <a href="https://gitter.im/ServiceCombUsers/Saga" target='_blank' class="btn btn-hero-success main-btn">
+ <i class="fab fa-gitter"></i> <span>Talk to Us</span>
+ </a>
+ </nb-sidebar-header>
+ <ng-content select="nb-menu"></ng-content>
+ </nb-sidebar>
+
+ <nb-layout-column class="main-content">
+ <ng-content select="router-outlet"></ng-content>
+ </nb-layout-column>
+
+ <nb-layout-footer fixed>
+ <ngx-footer></ngx-footer>
+ </nb-layout-footer>
+
+ <nb-sidebar class="settings-sidebar"
+ tag="settings-sidebar"
+ state="collapsed"
+ fixed
+ [end]="sidebar.id !== 'end'">
+ <ngx-theme-settings></ngx-theme-settings>
+ </nb-sidebar>
+ </nb-layout>
+ `,
+})
+export class DefaultLayoutComponent implements OnDestroy {
+
+
+ layout: any = {};
+ sidebar: any = {};
+
+ private alive = true;
+
+ currentTheme: string;
+
+ constructor(protected stateService: StateService,
+ protected menuService: NbMenuService,
+ protected themeService: NbThemeService,
+ protected bpService: NbMediaBreakpointsService,
+ protected sidebarService: NbSidebarService) {
+ this.stateService.onLayoutState()
+ .pipe(takeWhile(() => this.alive))
+ .subscribe((layout: string) => this.layout = layout);
+
+ this.stateService.onSidebarState()
+ .pipe(takeWhile(() => this.alive))
+ .subscribe((sidebar: string) => {
+ this.sidebar = sidebar;
+ });
+
+ const isBp = this.bpService.getByName('is');
+ this.menuService.onItemSelect()
+ .pipe(
+ takeWhile(() => this.alive),
+ withLatestFrom(this.themeService.onMediaQueryChange()),
+ delay(20),
+ )
+ .subscribe(([item, [bpFrom, bpTo]]: [any, [NbMediaBreakpoint, NbMediaBreakpoint]]) => {
+
+ if (bpTo.width <= isBp.width) {
+ this.sidebarService.collapse('menu-sidebar');
+ }
+ });
+
+ this.themeService.getJsTheme()
+ .pipe(takeWhile(() => this.alive))
+ .subscribe(theme => {
+ this.currentTheme = theme.name;
+ });
+ }
+
+ ngOnDestroy() {
+ this.alive = false;
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/index.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/index.ts
new file mode 100644
index 0000000..359dd9c
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/index.ts
@@ -0,0 +1,7 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+export * from './one-column/one-column.layout';
+export * from './default/default.layout';
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/one-column/one-column.layout.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/one-column/one-column.layout.scss
new file mode 100644
index 0000000..f8ab596
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/one-column/one-column.layout.scss
@@ -0,0 +1,180 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+@import '../../styles/themes';
+@import '~bootstrap/scss/mixins/breakpoints';
+@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
+
+@include nb-install-component() {
+ nb-layout-column.small {
+ flex: 0.15 !important;
+ }
+
+ nb-sidebar.settings-sidebar {
+ $sidebar-width: 7.5rem;
+
+ transition: width 0.3s ease;
+ width: $sidebar-width;
+ overflow: hidden;
+
+ &.collapsed {
+ width: 0;
+
+ /deep/ .main-container {
+ width: 0;
+
+ .scrollable {
+ width: $sidebar-width;
+ padding: 1.25rem;
+ }
+ }
+ }
+
+ /deep/ .main-container {
+ width: $sidebar-width;
+ background: nb-theme(color-bg);
+ transition: width 0.3s ease;
+ overflow: hidden;
+
+ .scrollable {
+ width: $sidebar-width;
+ }
+
+ @include nb-for-theme(cosmic) {
+ background: nb-theme(layout-bg);
+ }
+ }
+ }
+
+ nb-sidebar.menu-sidebar {
+
+ margin-top: nb-theme(sidebar-header-gap);
+
+ @include nb-for-theme(corporate) {
+ margin-top: 0;
+ }
+
+ /deep/ .main-container {
+ height:
+ calc(#{nb-theme(sidebar-height)} - #{nb-theme(header-height)} - #{nb-theme(sidebar-header-gap)}) !important;
+ @include nb-ltr(border-top-right-radius, nb-theme(radius));
+ @include nb-rtl(border-top-left-radius, nb-theme(radius));
+
+ @include nb-for-theme(corporate) {
+ border: 1px solid nb-theme(separator);
+ height:
+ calc(#{nb-theme(sidebar-height)} - #{nb-theme(header-height)}) !important;
+ }
+ }
+
+ /deep/ .scrollable {
+ @include nb-for-theme(corporate) {
+ padding-top: 0;
+
+ .menu-item:first-child {
+ border-top: none;
+ }
+ }
+ }
+
+ /deep/ nb-sidebar-header {
+ padding-bottom: 0.5rem;
+ text-align: center;
+ }
+
+ background: transparent;
+
+ .main-btn {
+ padding: 0.75rem 2.5rem;
+ margin-top: -2rem;
+ font-weight: bold;
+ transition: padding 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.48);
+
+ @include nb-for-theme(corporate) {
+ border-radius: nb-theme(radius);
+ }
+
+ i {
+ font-size: 2rem;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
+ }
+ span {
+ @include nb-ltr(padding-left, 0.25rem);
+ @include nb-rtl(padding-right, 0.25rem);
+ }
+
+ i, span {
+ vertical-align: middle;
+ }
+ }
+
+ &.compacted {
+
+ /deep/ nb-sidebar-header {
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ .main-btn {
+ width: 46px;
+ height: 44px;
+ padding: 0.375rem;
+ border-radius: 5px;
+ transition: none;
+
+ span {
+ display: none;
+ }
+ }
+ }
+ }
+
+ @include media-breakpoint-down(xs) {
+ .main-content {
+ padding: 0.75rem !important;
+
+ }
+ }
+
+ @include media-breakpoint-down(sm) {
+
+ nb-sidebar.menu-sidebar {
+
+ margin-top: 0;
+
+ /deep/ .main-container {
+ height: calc(#{nb-theme(sidebar-height)} - #{nb-theme(header-height)}) !important;
+ @include nb-ltr(border-top-right-radius, 0);
+ @include nb-rtl(border-top-left-radius, 0);
+
+ .scrollable {
+ padding-top: 0;
+ }
+ }
+ }
+
+ .main-btn {
+ display: none;
+ }
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/one-column/one-column.layout.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/one-column/one-column.layout.ts
new file mode 100644
index 0000000..cc227c5
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/layouts/one-column/one-column.layout.ts
@@ -0,0 +1,56 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Component, OnDestroy } from '@angular/core';
+import { NbThemeService } from '@nebular/theme';
+import { takeWhile } from 'rxjs/operators/takeWhile';
+
+// TODO: move layouts into the framework
+@Component({
+ selector: 'ngx-one-column-layout',
+ styleUrls: ['./one-column.layout.scss'],
+ template: `
+ <nb-layout>
+ <nb-layout-header fixed>
+ <ngx-header></ngx-header>
+ </nb-layout-header>
+
+ <nb-sidebar class="menu-sidebar" tag="menu-sidebar" responsive>
+ <nb-sidebar-header *ngIf="currentTheme !== 'corporate'">
+ <a href="https://github.com/apache/incubator-servicecomb-saga" target='_blank' class="btn btn-hero-success main-btn">
+ <i class="ion ion-social-github"></i> <span>Support Us</span>
+ </a>
+ </nb-sidebar-header>
+ <ng-content select="nb-menu"></ng-content>
+ </nb-sidebar>
+
+ <nb-layout-column>
+ <ng-content select="router-outlet"></ng-content>
+ </nb-layout-column>
+
+ <nb-layout-footer fixed>
+ <ngx-footer></ngx-footer>
+ </nb-layout-footer>
+ </nb-layout>
+ `,
+})
+export class OneColumnLayoutComponent implements OnDestroy {
+
+ private alive = true;
+
+ currentTheme: string;
+
+ constructor(protected themeService: NbThemeService) {
+ this.themeService.getJsTheme()
+ .pipe(takeWhile(() => this.alive))
+ .subscribe(theme => {
+ this.currentTheme = theme.name;
+ });
+ }
+
+ ngOnDestroy() {
+ this.alive = false;
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/capitalize.pipe.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/capitalize.pipe.ts
new file mode 100644
index 0000000..d7fe7f0
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/capitalize.pipe.ts
@@ -0,0 +1,16 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'ngxCapitalize' })
+export class CapitalizePipe implements PipeTransform {
+
+ transform(input: string): string {
+ return input && input.length
+ ? (input.charAt(0).toUpperCase() + input.slice(1).toLowerCase())
+ : input;
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/index.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/index.ts
new file mode 100644
index 0000000..c36c380
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/index.ts
@@ -0,0 +1,11 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+export * from './capitalize.pipe';
+export * from './plural.pipe';
+export * from './round.pipe';
+export * from './timing.pipe';
+export * from './number-with-commas.pipe';
+export * from './no-sanitize.pipe';
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/no-sanitize.pipe.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/no-sanitize.pipe.ts
new file mode 100644
index 0000000..5c03f4d
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/no-sanitize.pipe.ts
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Pipe, PipeTransform } from '@angular/core';
+import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
+
+@Pipe({ name: 'noSanitize' })
+export class NoSanitizePipe implements PipeTransform {
+ constructor(private domSanitizer: DomSanitizer) {
+
+ }
+ transform(html: string): SafeHtml {
+ return this.domSanitizer.bypassSecurityTrustHtml(html);
+ }
+}
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/number-with-commas.pipe.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/number-with-commas.pipe.ts
new file mode 100644
index 0000000..fe42335
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/number-with-commas.pipe.ts
@@ -0,0 +1,14 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'ngxNumberWithCommas' })
+export class NumberWithCommasPipe implements PipeTransform {
+
+ transform(input: number): string {
+ return new Intl.NumberFormat().format(input);
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/plural.pipe.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/plural.pipe.ts
new file mode 100644
index 0000000..aaad2fd
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/plural.pipe.ts
@@ -0,0 +1,19 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'ngxPlural' })
+export class PluralPipe implements PipeTransform {
+
+ transform(input: number, label: string, pluralLabel: string = ''): string {
+ input = input || 0;
+ return input === 1
+ ? `${input} ${label}`
+ : pluralLabel
+ ? `${input} ${pluralLabel}`
+ : `${input} ${label}s`;
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/round.pipe.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/round.pipe.ts
new file mode 100644
index 0000000..9264a4f
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/round.pipe.ts
@@ -0,0 +1,14 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'ngxRound' })
+export class RoundPipe implements PipeTransform {
+
+ transform(input: number): number {
+ return Math.round(input);
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/timing.pipe.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/timing.pipe.ts
new file mode 100644
index 0000000..04b9f5e
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/pipes/timing.pipe.ts
@@ -0,0 +1,23 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'timing' })
+export class TimingPipe implements PipeTransform {
+ transform(time: number): string {
+ if (time) {
+ const minutes = Math.floor(time / 60);
+ const seconds = Math.floor(time % 60);
+ return `${this.initZero(minutes)}${minutes}:${this.initZero(seconds)}${seconds}`;
+ }
+
+ return '00:00';
+ }
+
+ private initZero(time: number): string {
+ return time < 10 ? '0' : '';
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/bootstrap-rtl.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/bootstrap-rtl.scss
new file mode 100644
index 0000000..d28ee4c
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/bootstrap-rtl.scss
@@ -0,0 +1,216 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+@import './themes';
+
+@mixin bootstrap-rtl() {
+ .btn-group:not(.btn-divided-group) > .btn:not(.dropdown-toggle) {
+ &:first-child {
+ @include nb-ltr() {
+ border-top-left-radius: nb-theme(btn-border-radius);
+ border-bottom-left-radius: nb-theme(btn-border-radius);
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ };
+ @include nb-rtl() {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ border-top-right-radius: nb-theme(btn-border-radius);
+ border-bottom-right-radius: nb-theme(btn-border-radius);
+ };
+ }
+ &:last-child {
+ @include nb-ltr() {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ border-top-right-radius: nb-theme(btn-border-radius);
+ border-bottom-right-radius: nb-theme(btn-border-radius);
+ };
+ @include nb-rtl() {
+ border-top-left-radius: nb-theme(btn-border-radius);
+ border-bottom-left-radius: nb-theme(btn-border-radius);
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ };
+ }
+ }
+
+ .btn-group.dropdown {
+ & > .btn:first-of-type.dropdown-toggle {
+ @include nb-ltr() {
+ border-top-left-radius: nb-theme(btn-border-radius);
+ border-top-right-radius: 0;
+ };
+ @include nb-rtl() {
+ border-top-left-radius: 0;
+ border-top-right-radius: nb-theme(btn-border-radius);
+ };
+ }
+ & > .btn:last-of-type.dropdown-toggle {
+ @include nb-ltr() {
+ border-top-left-radius: 0;
+ border-top-right-radius: nb-theme(btn-border-radius);
+ };
+ @include nb-rtl() {
+ border-top-left-radius: nb-theme(btn-border-radius);
+ border-top-right-radius: 0;
+ };
+ }
+
+ &:not(.show) {
+ & > .btn:first-of-type.dropdown-toggle {
+ @include nb-ltr() {
+ border-bottom-left-radius: nb-theme(btn-border-radius);
+ border-bottom-right-radius: 0;
+ };
+ @include nb-rtl() {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: nb-theme(btn-border-radius);
+ };
+ }
+ & > .btn:last-of-type.dropdown-toggle {
+ @include nb-ltr() {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: nb-theme(btn-border-radius);
+ };
+ @include nb-rtl() {
+ border-bottom-left-radius: nb-theme(btn-border-radius);
+ border-bottom-right-radius: 0;
+ };
+ }
+ }
+ }
+
+ .btn-group.dropup {
+ & > .btn:first-of-type.dropdown-toggle {
+ @include nb-ltr() {
+ border-bottom-left-radius: nb-theme(btn-border-radius);
+ border-bottom-right-radius: 0;
+ };
+ @include nb-rtl() {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: nb-theme(btn-border-radius);
+ };
+ }
+ & > .btn:last-of-type.dropdown-toggle {
+ @include nb-ltr() {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: nb-theme(btn-border-radius);
+ };
+ @include nb-rtl() {
+ border-bottom-left-radius: nb-theme(btn-border-radius);
+ border-bottom-right-radius: 0;
+ };
+ }
+
+ &:not(.show) {
+ & > .btn:first-of-type.dropdown-toggle {
+ @include nb-ltr() {
+ border-top-left-radius: nb-theme(btn-border-radius);
+ border-top-right-radius: 0;
+ };
+ @include nb-rtl() {
+ border-top-left-radius: 0;
+ border-top-right-radius: nb-theme(btn-border-radius);
+ };
+ }
+ & > .btn:last-of-type.dropdown-toggle {
+ @include nb-ltr() {
+ border-top-left-radius: 0;
+ border-top-right-radius: nb-theme(btn-border-radius);
+ };
+ @include nb-rtl() {
+ border-top-left-radius: nb-theme(btn-border-radius);
+ border-top-right-radius: 0;
+ };
+ }
+ }
+ }
+
+ .btn-divided-group {
+ .btn:not(:first-child) {
+ @include nb-ltr(margin-left, 0.5rem);
+ @include nb-rtl(margin-right, 0.5rem);
+ border-radius: nb-theme(btn-border-radius);
+ }
+ }
+
+ .input-group-addon,
+ .input-group-icon {
+ @include nb-ltr() {
+ border-left: nb-theme(form-control-border);
+ border-right: none;
+ };
+ @include nb-rtl() {
+ border-left: none;
+ border-right: nb-theme(form-control-border);
+ };
+ }
+
+ .input-group {
+ .form-control:first-child:not(:only-child),
+ .input-group-addon:first-child,
+ .input-group-prepend .btn:first-child,
+ .input-group-btn .btn:first-child {
+ @include nb-ltr() {
+ border-top-left-radius: nb-theme(form-control-border-radius);
+ border-bottom-left-radius: nb-theme(form-control-border-radius);
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ };
+ @include nb-rtl() {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ border-top-right-radius: nb-theme(form-control-border-radius);
+ border-bottom-right-radius: nb-theme(form-control-border-radius);
+ };
+ }
+ .form-control:last-child:not(:only-child),
+ .input-group-addon:last-child,
+ .input-group-append .btn:last-child,
+ .input-group-btn .btn:last-child {
+ @include nb-ltr() {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ border-top-right-radius: nb-theme(form-control-border-radius);
+ border-bottom-right-radius: nb-theme(form-control-border-radius);
+ };
+ @include nb-rtl() {
+ border-top-left-radius: nb-theme(form-control-border-radius);
+ border-bottom-left-radius: nb-theme(form-control-border-radius);
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ };
+ }
+
+ .dropdown.show .btn.dropdown-toggle {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+
+ .dropup.show .btn.dropdown-toggle {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ }
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/font-size.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/font-size.scss
new file mode 100644
index 0000000..ed0c1ad
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/font-size.scss
@@ -0,0 +1,26 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+html {
+ font-size: 14px;
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/pace.theme.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/pace.theme.scss
new file mode 100644
index 0000000..b39cd67
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/pace.theme.scss
@@ -0,0 +1,40 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+
+ @mixin ngx-pace-theme() {
+
+ .pace .pace-progress {
+ background: nb-theme(color-fg-highlight);
+ }
+
+ .pace .pace-progress-inner {
+ box-shadow: 0 0 10px nb-theme(color-fg-highlight), 0 0 5px nb-theme(color-fg-highlight);
+ }
+
+ .pace .pace-activity {
+ display: none;
+ // border-top-color: nb-theme(color-fg-highlight);
+ // border-left-color: nb-theme(color-fg-highlight);
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/styles.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/styles.scss
new file mode 100644
index 0000000..ed8ca9a
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/styles.scss
@@ -0,0 +1,54 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+// themes - our custom or/and out of the box themes
+@import 'themes';
+
+// framework component themes (styles tied to theme variables)
+@import '~@nebular/theme/styles/globals';
+@import '~@nebular/auth/styles/all';
+
+// global app font size
+@import './font-size';
+
+// loading progress bar theme
+@import './pace.theme';
+
+@import './bootstrap-rtl';
+
+// install the framework and custom global styles
+@include nb-install() {
+
+ // framework global styles
+ @include nb-theme-global();
+ @include nb-auth-global();
+
+ // loading progress bar
+ @include ngx-pace-theme();
+
+ // fixed in rc.9 and can be removed after upgrade
+ .custom-control .custom-control-indicator {
+ border-radius: 50%; // TODO: quickfix for https://github.com/akveo/nebular/issues/275
+ }
+ @include bootstrap-rtl();
+};
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/theme.corporate.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/theme.corporate.ts
new file mode 100644
index 0000000..2bc58e7
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/theme.corporate.ts
@@ -0,0 +1,309 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+export const CORPORATE_THEME = {
+ name: 'corporate',
+ base: 'default',
+ variables: {
+ temperature: [
+ '#ffa36b',
+ '#ffa36b',
+ '#ff9e7a',
+ '#ff9888',
+ '#ff8ea0',
+ ],
+
+ solar: {
+ gradientLeft: '#ff8ea0',
+ gradientRight: '#ffa36b',
+ shadowColor: 'rgba(0, 0, 0, 0)',
+ radius: ['80%', '90%'],
+ },
+
+ traffic: {
+ colorBlack: '#ffffff',
+ tooltipBg: '#eef2f5',
+ tooltipBorderColor: '#eef2f5',
+ tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: '400',
+
+ lineBg: '#cae6f3',
+ lineShadowBlur: '0',
+ itemColor: '#bcc3cc',
+ itemBorderColor: '#bcc3cc',
+ itemEmphasisBorderColor: '#74a2ff',
+ shadowLineDarkBg: 'rgba(0, 0, 0, 0)',
+ shadowLineShadow: 'rgba(0, 0, 0, 0)',
+ gradFrom: '#ffffff',
+ gradTo: '#ffffff',
+ },
+
+ electricity: {
+ tooltipBg: '#edf0f4',
+ tooltipLineColor: '#bdc4cd',
+ tooltipLineWidth: '0',
+ tooltipBorderColor: '#ebeef2',
+ tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+
+ axisLineColor: 'rgba(0, 0, 0, 0)',
+ xAxisTextColor: '#2a2a2a',
+ yAxisSplitLine: '#ebeef2',
+
+ itemBorderColor: '#73a1ff',
+ lineStyle: 'solid',
+ lineWidth: '4',
+ lineGradFrom: '#bdc4cd',
+ lineGradTo: '#c0c8d1',
+ lineShadow: 'rgba(0, 0, 0, 0)',
+
+ areaGradFrom: 'rgba(255, 255, 255, 0)',
+ areaGradTo: 'rgba(255, 255, 255, 0)',
+ shadowLineDarkBg: 'rgba(255, 255, 255, 0)',
+ },
+
+ bubbleMap: {
+ titleColor: '#484848',
+ areaColor: '#dddddd',
+ areaHoverColor: '#cccccc',
+ areaBorderColor: '#ebeef2',
+ },
+
+ profitBarAnimationEchart: {
+ textColor: '#b2bac2',
+
+ firstAnimationBarColor: '#719efc',
+ secondAnimationBarColor: '#5dcfe3',
+
+ splitLineStyleOpacity: '0.06',
+ splitLineStyleWidth: '1',
+ splitLineStyleColor: '#000000',
+
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: '400',
+ tooltipFontSize: '16',
+ tooltipBg: '#eef2f5',
+ tooltipBorderColor: '#eef2f5',
+ tooltipBorderWidth: '3',
+ tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
+ },
+
+ trafficBarEchart: {
+ gradientFrom: '#ff8ea0',
+ gradientTo: '#ffa36b',
+ shadow: 'rgba(0, 0, 0, 0)',
+ shadowBlur: '0',
+
+ axisTextColor: '#b2bac2',
+ axisFontSize: '12',
+
+ tooltipBg: '#edf0f4',
+ tooltipBorderColor: '#ebeef2',
+ tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+ },
+
+ countryOrders: {
+ countryBorderColor: 'rgba(255, 255, 255, 1)',
+ countryFillColor: 'rgba(236, 242, 245, 1)',
+ countryBorderWidth: '1',
+ hoveredCountryBorderColor: 'rgba(113, 158, 252, 1)',
+ hoveredCountryFillColor: 'rgba(199, 216, 247, 1)',
+ hoveredCountryBorderWidth: '3',
+
+ chartAxisLineColor: 'rgba(0, 0, 0, 0)',
+ chartAxisTextColor: '#b2bac2',
+ chartAxisFontSize: '16',
+ chartGradientTo: 'rgba(113, 158, 252, 1)',
+ chartGradientFrom: 'rgba(113, 158, 252, 1)',
+ chartAxisSplitLine: '#ebeef2',
+ chartShadowLineColor: '#2f296b',
+
+ chartLineBottomShadowColor: 'rgba(113, 158, 252, 1)',
+
+ chartInnerLineColor: '#eceff4',
+ },
+
+ echarts: {
+ bg: '#ffffff',
+ textColor: '#484848',
+ axisLineColor: '#bbbbbb',
+ splitLineColor: '#ebeef2',
+ itemHoverShadowColor: 'rgba(0, 0, 0, 0.5)',
+ tooltipBackgroundColor: '#6a7985',
+ areaOpacity: '0.7',
+ },
+
+ chartjs: {
+ axisLineColor: '#cccccc',
+ textColor: '#484848',
+ },
+
+ orders: {
+ tooltipBg: '#ffffff',
+ tooltipLineColor: 'rgba(0, 0, 0, 0)',
+ tooltipLineWidth: '0',
+ tooltipBorderColor: '#ebeef2',
+ tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+ tooltipFontSize: '20',
+
+ axisLineColor: 'rgba(161, 161 ,229, 0.3)',
+ axisFontSize: '16',
+ axisTextColor: '#b2bac2',
+ yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
+
+ itemBorderColor: '#73a1ff',
+ lineStyle: 'solid',
+ lineWidth: '4',
+
+ // first line
+ firstAreaGradFrom: 'rgba(227, 236, 254, 0.7)',
+ firstAreaGradTo: 'rgba(227, 236, 254, 0.7)',
+ firstShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
+
+ // second line
+ secondLineGradFrom: 'rgba(93, 207, 227, 1)',
+ secondLineGradTo: 'rgba(93, 207, 227, 1)',
+
+ secondAreaGradFrom: 'rgba(0, 0, 0, 0)',
+ secondAreaGradTo: 'rgba(0, 0, 0, 0)',
+ secondShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
+
+ // third line
+ thirdLineGradFrom: 'rgba(113, 158, 252, 1)',
+ thirdLineGradTo: 'rgba(113, 158, 252, 1)',
+
+ thirdAreaGradFrom: 'rgba(0, 0, 0, 0)',
+ thirdAreaGradTo: 'rgba(0, 0, 0, 0)',
+ thirdShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
+ },
+
+ profit: {
+ bg: '#ffffff',
+ textColor: '#ffffff',
+ axisLineColor: 'rgba(161, 161 ,229, 0.3)',
+ splitLineColor: 'rgba(161, 161 ,229, 0.2)',
+ areaOpacity: '1',
+
+ axisFontSize: '16',
+ axisTextColor: '#b2bac2',
+
+ // first bar
+ firstLineGradFrom: '#719efc',
+ firstLineGradTo: '#719efc',
+ firstLineShadow: 'rgba(14, 16, 48, 0.4)',
+
+ // second bar
+ secondLineGradFrom: '#5dcfe3',
+ secondLineGradTo: '#5dcfe3',
+ secondLineShadow: 'rgba(14, 16, 48, 0.4)',
+
+ // third bar
+ thirdLineGradFrom: '#e3ecfe',
+ thirdLineGradTo: '#e3ecfe',
+ thirdLineShadow: 'rgba(14, 16, 48, 0.4)',
+ },
+
+ orderProfitLegend: {
+ firstItem: '#719efc',
+ secondItem: '#5dcfe3',
+ thirdItem: '#e3ecfe',
+ },
+
+ visitors: {
+ tooltipBg: '#ffffff',
+ tooltipLineColor: 'rgba(0, 0, 0, 0)',
+ tooltipLineWidth: '0',
+ tooltipBorderColor: '#ebeef2',
+ tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+ tooltipFontSize: '20',
+
+ axisLineColor: 'rgba(161, 161 ,229, 0.3)',
+ axisFontSize: '16',
+ axisTextColor: '#b2bac2',
+ yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
+
+ itemBorderColor: '#73a1ff',
+ lineStyle: 'dotted',
+ lineWidth: '6',
+ lineGradFrom: '#73a1ff',
+ lineGradTo: '#73a1ff',
+ lineShadow: 'rgba(0, 0, 0, 0)',
+
+ areaGradFrom: 'rgba(146, 181, 252, 1)',
+ areaGradTo: 'rgba(146, 181, 252, 1)',
+ shadowLineDarkBg: '#a695ff',
+
+ innerLineStyle: 'solid',
+ innerLineWidth: '1',
+
+ innerAreaGradFrom: 'rgba(227, 236, 254, 1)',
+ innerAreaGradTo: 'rgba(227, 236, 254, 1)',
+ },
+
+ visitorsLegend: {
+ firstIcon: '#e3ecfe',
+ secondIcon: '#719efc',
+ },
+
+ visitorsPie: {
+ firstPieGradientLeft: '#94e2ed',
+ firstPieGradientRight: '#94e2ed',
+ firstPieShadowColor: 'rgba(0, 0, 0, 0)',
+ firstPieRadius: ['65%', '90%'],
+
+ secondPieGradientLeft: '#719efc',
+ secondPieGradientRight: '#719efc',
+ secondPieShadowColor: '#b2cafb',
+ secondPieRadius: ['63%', '92%'],
+ shadowOffsetX: '-4',
+ shadowOffsetY: '-4',
+ },
+
+ visitorsPieLegend: {
+ firstSection: '#719efc',
+ secondSection: '#99e5ee',
+ },
+
+ earningPie: {
+ radius: ['65%', '100%'],
+ center: ['50%', '50%'],
+
+ fontSize: '22',
+
+ firstPieGradientLeft: '#719efc',
+ firstPieGradientRight: '#719efc',
+ firstPieShadowColor: 'rgba(0, 0, 0, 0)',
+
+ secondPieGradientLeft: '#ff9f6f',
+ secondPieGradientRight: '#ff9f6f',
+ secondPieShadowColor: 'rgba(0, 0, 0, 0)',
+
+ thirdPieGradientLeft: '#ff5e83',
+ thirdPieGradientRight: '#ff5e83',
+ thirdPieShadowColor: 'rgba(0, 0, 0, 0)',
+ },
+
+ earningLine: {
+ gradFrom: '#e3ecfe',
+ gradTo: '#e3ecfe',
+
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: '400',
+ tooltipFontSize: '16',
+ tooltipBg: '#eef2f5',
+ tooltipBorderColor: '#eef2f5',
+ tooltipBorderWidth: '3',
+ tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
+ },
+ },
+};
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/theme.cosmic.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/theme.cosmic.ts
new file mode 100644
index 0000000..27b1d69
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/theme.cosmic.ts
@@ -0,0 +1,309 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+export const COSMIC_THEME = {
+ name: 'cosmic',
+ base: 'default',
+ variables: {
+
+ temperature: [
+ '#2ec7fe',
+ '#31ffad',
+ '#7bff24',
+ '#fff024',
+ '#f7bd59',
+ ],
+
+ solar: {
+ gradientLeft: '#7bff24',
+ gradientRight: '#2ec7fe',
+ shadowColor: '#19977E',
+ radius: ['70%', '90%'],
+ },
+
+ traffic: {
+ colorBlack: '#000000',
+ tooltipBg: 'rgba(0, 255, 170, 0.35)',
+ tooltipBorderColor: '#00d977',
+ tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 4px 16px;',
+ tooltipTextColor: '#ffffff',
+ tooltipFontWeight: 'normal',
+
+ lineBg: '#d1d1ff',
+ lineShadowBlur: '14',
+ itemColor: '#BEBBFF',
+ itemBorderColor: '#ffffff',
+ itemEmphasisBorderColor: '#ffffff',
+ shadowLineDarkBg: '#655ABD',
+ shadowLineShadow: 'rgba(33, 7, 77, 0.5)',
+ gradFrom: 'rgba(118, 89, 255, 0.4)',
+ gradTo: 'rgba(164, 84, 255, 0.5)',
+ },
+
+ electricity: {
+ tooltipBg: 'rgba(0, 255, 170, 0.35)',
+ tooltipLineColor: 'rgba(255, 255, 255, 0.1)',
+ tooltipLineWidth: '1',
+ tooltipBorderColor: '#00d977',
+ tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 8px 24px;',
+ tooltipTextColor: '#ffffff',
+ tooltipFontWeight: 'normal',
+
+ axisLineColor: 'rgba(161, 161 ,229, 0.3)',
+ xAxisTextColor: '#a1a1e5',
+ yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
+
+ itemBorderColor: '#ffffff',
+ lineStyle: 'dotted',
+ lineWidth: '6',
+ lineGradFrom: '#00ffaa',
+ lineGradTo: '#fff835',
+ lineShadow: 'rgba(14, 16, 48, 0.4)',
+
+ areaGradFrom: 'rgba(188, 92, 255, 0.5)',
+ areaGradTo: 'rgba(188, 92, 255, 0)',
+ shadowLineDarkBg: '#a695ff',
+ },
+
+ bubbleMap: {
+ titleColor: '#ffffff',
+ areaColor: '#2c2961',
+ areaHoverColor: '#a1a1e5',
+ areaBorderColor: '#654ddb',
+ },
+
+ profitBarAnimationEchart: {
+ textColor: '#ffffff',
+
+ firstAnimationBarColor: '#0088ff',
+ secondAnimationBarColor: '#7659ff',
+
+ splitLineStyleOpacity: '0.06',
+ splitLineStyleWidth: '1',
+ splitLineStyleColor: '#000000',
+
+ tooltipTextColor: '#ffffff',
+ tooltipFontWeight: 'normal',
+ tooltipFontSize: '16',
+ tooltipBg: 'rgba(0, 255, 170, 0.35)',
+ tooltipBorderColor: '#00d977',
+ tooltipBorderWidth: '3',
+ tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 4px 16px;',
+ },
+
+ trafficBarEchart: {
+ gradientFrom: '#fc0',
+ gradientTo: '#ffa100',
+ shadow: '#ffb600',
+ shadowBlur: '5',
+
+ axisTextColor: '#a1a1e5',
+ axisFontSize: '12',
+
+ tooltipBg: 'rgba(0, 255, 170, 0.35)',
+ tooltipBorderColor: '#00d977',
+ tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 4px 16px;',
+ tooltipTextColor: '#ffffff',
+ tooltipFontWeight: 'normal',
+ },
+
+ countryOrders: {
+ countryBorderColor: '#525dbd',
+ countryFillColor: '#4f41a6',
+ countryBorderWidth: '2',
+ hoveredCountryBorderColor: '#00f9a6',
+ hoveredCountryFillColor: '#377aa7',
+ hoveredCountryBorderWidth: '3',
+
+ chartAxisLineColor: 'rgba(161, 161 ,229, 0.3)',
+ chartAxisTextColor: '#a1a1e5',
+ chartAxisFontSize: '16',
+ chartGradientTo: '#00c7c7',
+ chartGradientFrom: '#00d977',
+ chartAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
+ chartShadowBarColor: '#2f296b',
+
+ chartLineBottomShadowColor: '#00977e',
+
+ chartInnerLineColor: '#2f296b',
+ },
+
+ echarts: {
+ bg: '#3d3780',
+ textColor: '#ffffff',
+ axisLineColor: '#a1a1e5',
+ splitLineColor: '#342e73',
+ itemHoverShadowColor: 'rgba(0, 0, 0, 0.5)',
+ tooltipBackgroundColor: '#6a7985',
+ areaOpacity: '1',
+ },
+
+ chartjs: {
+ axisLineColor: '#a1a1e5',
+ textColor: '#ffffff',
+ },
+
+ orders: {
+ tooltipBg: 'rgba(0, 255, 170, 0.35)',
+ tooltipLineColor: 'rgba(255, 255, 255, 0.1)',
+ tooltipLineWidth: '1',
+ tooltipBorderColor: '#00d977',
+ tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 8px 24px;',
+ tooltipTextColor: '#ffffff',
+ tooltipFontWeight: 'normal',
+ tooltipFontSize: '20',
+
+ axisLineColor: 'rgba(161, 161 ,229, 0.3)',
+ axisFontSize: '16',
+ axisTextColor: '#a1a1e5',
+ yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
+
+ itemBorderColor: '#ffffff',
+ lineStyle: 'solid',
+ lineWidth: '4',
+
+ // first line
+ firstAreaGradFrom: 'rgba(78, 64, 164, 1)',
+ firstAreaGradTo: 'rgba(78, 64, 164, 1)',
+ firstShadowLineDarkBg: '#018dff',
+
+ // second line
+ secondLineGradFrom: '#00bece',
+ secondLineGradTo: '#00da78',
+
+ secondAreaGradFrom: 'rgba(38, 139, 145, 0.8)',
+ secondAreaGradTo: 'rgba(38, 139, 145, 0.5)',
+ secondShadowLineDarkBg: '#2c5a85',
+
+ // third line
+ thirdLineGradFrom: '#8069ff',
+ thirdLineGradTo: '#8357ff',
+
+ thirdAreaGradFrom: 'rgba(118, 73, 208, 0.7)',
+ thirdAreaGradTo: 'rgba(188, 92, 255, 0.4)',
+ thirdShadowLineDarkBg: '#a695ff',
+ },
+
+ profit: {
+ bg: '#3d3780',
+ textColor: '#ffffff',
+ axisLineColor: '#a1a1e5',
+ splitLineColor: '#342e73',
+ areaOpacity: '1',
+
+ axisFontSize: '16',
+ axisTextColor: '#a1a1e5',
+
+ // first bar
+ firstLineGradFrom: '#00bece',
+ firstLineGradTo: '#00da78',
+ firstLineShadow: 'rgba(14, 16, 48, 0.4)',
+
+ // second bar
+ secondLineGradFrom: '#8069ff',
+ secondLineGradTo: '#8357ff',
+ secondLineShadow: 'rgba(14, 16, 48, 0.4)',
+
+ // third bar
+ thirdLineGradFrom: '#4e40a4',
+ thirdLineGradTo: '#4e40a4',
+ thirdLineShadow: 'rgba(14, 16, 48, 0.4)',
+ },
+
+ orderProfitLegend: {
+ firstItem: 'linear-gradient(90deg, #00c7c7 0%, #00d977 100%)',
+ secondItem: 'linear-gradient(90deg, #a454ff 0%, #7659ff 100%)',
+ thirdItem: '#4e40a4',
+ },
+
+ visitors: {
+ tooltipBg: 'rgba(0, 255, 170, 0.35)',
+ tooltipLineColor: 'rgba(255, 255, 255, 0.1)',
+ tooltipLineWidth: '1',
+ tooltipBorderColor: '#00d977',
+ tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 8px 24px;',
+ tooltipTextColor: '#ffffff',
+ tooltipFontWeight: 'normal',
+
+ axisLineColor: 'rgba(161, 161 ,229, 0.3)',
+ axisFontSize: '16',
+ axisTextColor: '#a1a1e5',
+ yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
+
+ itemBorderColor: '#ffffff',
+ lineStyle: 'dotted',
+ lineWidth: '6',
+ lineGradFrom: '#ffffff',
+ lineGradTo: '#ffffff',
+ lineShadow: 'rgba(14, 16, 48, 0.4)',
+
+ areaGradFrom: 'rgba(188, 92, 255, 1)',
+ areaGradTo: 'rgba(188, 92, 255, 0.5)',
+ shadowLineDarkBg: '#a695ff',
+
+ innerLineStyle: 'solid',
+ innerLineWidth: '1',
+
+ innerAreaGradFrom: 'rgba(59, 165, 243, 1)',
+ innerAreaGradTo: 'rgba(4, 133, 243 , 1)',
+ },
+
+ visitorsLegend: {
+ firstIcon: 'linear-gradient(90deg, #0088ff 0%, #3dafff 100%)',
+ secondIcon: 'linear-gradient(90deg, #a454ff 0%, #7659ff 100%)',
+ },
+
+ visitorsPie: {
+ firstPieGradientLeft: '#7bff24',
+ firstPieGradientRight: '#2ec7fe',
+ firstPieShadowColor: '#19977E',
+ firstPieRadius: ['70%', '90%'],
+
+ secondPieGradientLeft: '#ff894a',
+ secondPieGradientRight: '#ffcc10',
+ secondPieShadowColor: '#cf7c1c',
+ secondPieRadius: ['60%', '95%'],
+ shadowOffsetX: '0',
+ shadowOffsetY: '3',
+ },
+
+ visitorsPieLegend: {
+ firstSection: 'linear-gradient(90deg, #ffcb17 0%, #ff874c 100%)',
+ secondSection: 'linear-gradient(90deg, #00c7c7 0%, #00d977 100%)',
+ },
+
+ earningPie: {
+ radius: ['65%', '100%'],
+ center: ['50%', '50%'],
+
+ fontSize: '22',
+
+ firstPieGradientLeft: '#00d77f',
+ firstPieGradientRight: '#00d77f',
+ firstPieShadowColor: 'rgba(0, 0, 0, 0)',
+
+ secondPieGradientLeft: '#7756f7',
+ secondPieGradientRight: '#7756f7',
+ secondPieShadowColor: 'rgba(0, 0, 0, 0)',
+
+ thirdPieGradientLeft: '#ffca00',
+ thirdPieGradientRight: '#ffca00',
+ thirdPieShadowColor: 'rgba(0, 0, 0, 0)',
+ },
+
+ earningLine: {
+ gradFrom: 'rgba(118, 89, 255, 0.4)',
+ gradTo: 'rgba(164, 84, 255, 0.5)',
+
+ tooltipTextColor: '#ffffff',
+ tooltipFontWeight: 'normal',
+ tooltipFontSize: '16',
+ tooltipBg: 'rgba(0, 255, 170, 0.35)',
+ tooltipBorderColor: '#00d977',
+ tooltipBorderWidth: '3',
+ tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 4px 16px;',
+ },
+ },
+};
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/theme.default.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/theme.default.ts
new file mode 100644
index 0000000..7a98e5c
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/theme.default.ts
@@ -0,0 +1,312 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+export const DEFAULT_THEME = {
+ name: 'default',
+ base: null,
+ variables: {
+
+ // Safari fix
+ temperature: [
+ '#42db7d',
+ '#42db7d',
+ '#42db7d',
+ '#42db7d',
+ '#42db7d',
+ ],
+
+ solar: {
+ gradientLeft: '#42db7d',
+ gradientRight: '#42db7d',
+ shadowColor: 'rgba(0, 0, 0, 0)',
+ radius: ['80%', '90%'],
+ },
+
+ traffic: {
+ colorBlack: '#000000',
+ tooltipBg: '#ffffff',
+ tooltipBorderColor: '#c0c8d1',
+ tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+
+ lineBg: '#c0c8d1',
+ lineShadowBlur: '1',
+ itemColor: '#bcc3cc',
+ itemBorderColor: '#bcc3cc',
+ itemEmphasisBorderColor: '#42db7d',
+ shadowLineDarkBg: 'rgba(0, 0, 0, 0)',
+ shadowLineShadow: 'rgba(0, 0, 0, 0)',
+ gradFrom: '#ebeef2',
+ gradTo: '#ebeef2',
+ },
+
+ electricity: {
+ tooltipBg: '#ffffff',
+ tooltipLineColor: 'rgba(0, 0, 0, 0)',
+ tooltipLineWidth: '0',
+ tooltipBorderColor: '#ebeef2',
+ tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+
+ axisLineColor: 'rgba(0, 0, 0, 0)',
+ xAxisTextColor: '#2a2a2a',
+ yAxisSplitLine: '#ebeef2',
+
+ itemBorderColor: '#42db7d',
+ lineStyle: 'solid',
+ lineWidth: '4',
+ lineGradFrom: '#42db7d',
+ lineGradTo: '#42db7d',
+ lineShadow: 'rgba(0, 0, 0, 0)',
+
+ areaGradFrom: 'rgba(235, 238, 242, 0.5)',
+ areaGradTo: 'rgba(235, 238, 242, 0.5)',
+ shadowLineDarkBg: 'rgba(0, 0, 0, 0)',
+ },
+
+ bubbleMap: {
+ titleColor: '#484848',
+ areaColor: '#dddddd',
+ areaHoverColor: '#cccccc',
+ areaBorderColor: '#ebeef2',
+ },
+
+ profitBarAnimationEchart: {
+ textColor: '#484848',
+
+ firstAnimationBarColor: '#3edd81',
+ secondAnimationBarColor: '#8d7fff',
+
+ splitLineStyleOpacity: '0.06',
+ splitLineStyleWidth: '1',
+ splitLineStyleColor: '#000000',
+
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+ tooltipFontSize: '16',
+ tooltipBg: '#ffffff',
+ tooltipBorderColor: '#c0c8d1',
+ tooltipBorderWidth: '3',
+ tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
+ },
+
+ trafficBarEchart: {
+ gradientFrom: '#fc0',
+ gradientTo: '#ffa100',
+ shadow: '#ffb600',
+ shadowBlur: '0',
+
+ axisTextColor: '#b2bac2',
+ axisFontSize: '12',
+
+ tooltipBg: '#ffffff',
+ tooltipBorderColor: '#c0c8d1',
+ tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+ },
+
+ countryOrders: {
+ countryBorderColor: 'rgba(255, 255, 255, 1)',
+ countryFillColor: 'rgba(236, 242, 245, 1)',
+ countryBorderWidth: '1',
+ hoveredCountryBorderColor: '#40dc7e',
+ hoveredCountryFillColor: '#c7f4d9',
+ hoveredCountryBorderWidth: '3',
+
+ chartAxisLineColor: 'rgba(0, 0, 0, 0)',
+ chartAxisTextColor: '#b2bac2',
+ chartAxisFontSize: '16',
+ chartGradientTo: '#3edd81',
+ chartGradientFrom: '#3bddaf',
+ chartAxisSplitLine: '#ebeef2',
+ chartShadowLineColor: '#2f296b',
+
+ chartLineBottomShadowColor: '#eceff4',
+
+ chartInnerLineColor: '#eceff4',
+ },
+
+ echarts: {
+ bg: '#ffffff',
+ textColor: '#484848',
+ axisLineColor: '#bbbbbb',
+ splitLineColor: '#ebeef2',
+ itemHoverShadowColor: 'rgba(0, 0, 0, 0.5)',
+ tooltipBackgroundColor: '#6a7985',
+ areaOpacity: '0.7',
+ },
+
+ chartjs: {
+ axisLineColor: '#cccccc',
+ textColor: '#484848',
+ },
+
+ orders: {
+ tooltipBg: '#ffffff',
+ tooltipLineColor: 'rgba(0, 0, 0, 0)',
+ tooltipLineWidth: '0',
+ tooltipBorderColor: '#ebeef2',
+ tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+ tooltipFontSize: '20',
+
+ axisLineColor: 'rgba(161, 161 ,229, 0.3)',
+ axisFontSize: '16',
+ axisTextColor: '#b2bac2',
+ yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
+
+ itemBorderColor: '#42db7d',
+ lineStyle: 'solid',
+ lineWidth: '4',
+
+ // first line
+ firstAreaGradFrom: 'rgba(236, 242, 245, 0.8)',
+ firstAreaGradTo: 'rgba(236, 242, 245, 0.8)',
+ firstShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
+
+ // second line
+ secondLineGradFrom: 'rgba(164, 123, 255, 1)',
+ secondLineGradTo: 'rgba(164, 123, 255, 1)',
+
+ secondAreaGradFrom: 'rgba(188, 92, 255, 0.2)',
+ secondAreaGradTo: 'rgba(188, 92, 255, 0)',
+ secondShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
+
+ // third line
+ thirdLineGradFrom: 'rgba(55, 220, 136, 1)',
+ thirdLineGradTo: 'rgba(55, 220, 136, 1)',
+
+ thirdAreaGradFrom: 'rgba(31 ,106, 124, 0.2)',
+ thirdAreaGradTo: 'rgba(4, 126, 230, 0)',
+ thirdShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
+ },
+
+ // TODO: need design for default theme
+ profit: {
+ bg: '#ffffff',
+ textColor: '#ffffff',
+ axisLineColor: 'rgba(161, 161 ,229, 0.3)',
+ splitLineColor: 'rgba(161, 161 ,229, 0.2)',
+ areaOpacity: '1',
+
+ axisFontSize: '16',
+ axisTextColor: '#b2bac2',
+
+ // first bar
+ firstLineGradFrom: '#00bece',
+ firstLineGradTo: '#00da78',
+ firstLineShadow: 'rgba(14, 16, 48, 0.4)',
+
+ // second bar
+ secondLineGradFrom: '#8069ff',
+ secondLineGradTo: '#8357ff',
+ secondLineShadow: 'rgba(14, 16, 48, 0.4)',
+
+ // third bar
+ thirdLineGradFrom: 'rgba(236, 242, 245, 0.8)',
+ thirdLineGradTo: 'rgba(236, 242, 245, 0.8)',
+ thirdLineShadow: 'rgba(14, 16, 48, 0.4)',
+ },
+
+ orderProfitLegend: {
+ firstItem: 'linear-gradient(90deg, #3edd81 0%, #3bddad 100%)',
+ secondItem: 'linear-gradient(90deg, #8d7fff 0%, #b17fff 100%)',
+ thirdItem: 'rgba(236, 242, 245, 0.8)',
+ },
+
+ visitors: {
+ tooltipBg: '#ffffff',
+ tooltipLineColor: 'rgba(0, 0, 0, 0)',
+ tooltipLineWidth: '0',
+ tooltipBorderColor: '#ebeef2',
+ tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+ tooltipFontSize: '20',
+
+ axisLineColor: 'rgba(161, 161 ,229, 0.3)',
+ axisFontSize: '16',
+ axisTextColor: '#b2bac2',
+ yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)',
+
+ itemBorderColor: '#42db7d',
+ lineStyle: 'dotted',
+ lineWidth: '6',
+ lineGradFrom: '#ffffff',
+ lineGradTo: '#ffffff',
+ lineShadow: 'rgba(14, 16, 48, 0)',
+
+ areaGradFrom: 'rgba(188, 92, 255, 1)',
+ areaGradTo: 'rgba(188, 92, 255, 0.5)',
+ shadowLineDarkBg: '#a695ff',
+
+ innerLineStyle: 'solid',
+ innerLineWidth: '1',
+
+ innerAreaGradFrom: 'rgba(60, 221, 156, 1)',
+ innerAreaGradTo: 'rgba(60, 221, 156, 1)',
+ },
+
+ visitorsLegend: {
+ firstIcon: 'linear-gradient(90deg, #3edd81 0%, #3bddad 100%)',
+ secondIcon: 'linear-gradient(90deg, #8d7fff 0%, #b17fff 100%)',
+ },
+
+ visitorsPie: {
+ firstPieGradientLeft: '#8defbb',
+ firstPieGradientRight: '#8defbb',
+ firstPieShadowColor: 'rgba(0, 0, 0, 0)',
+ firstPieRadius: ['70%', '90%'],
+
+ secondPieGradientLeft: '#ff894a',
+ secondPieGradientRight: '#ffcc10',
+ secondPieShadowColor: 'rgba(0, 0, 0, 0)',
+ secondPieRadius: ['60%', '97%'],
+ shadowOffsetX: '0',
+ shadowOffsetY: '0',
+ },
+
+ visitorsPieLegend: {
+ firstSection: 'linear-gradient(90deg, #ffcb17 0%, #ff874c 100%)',
+ secondSection: '#8defbb',
+ },
+
+ earningPie: {
+ radius: ['65%', '100%'],
+ center: ['50%', '50%'],
+
+ fontSize: '22',
+
+ firstPieGradientLeft: '#00d77f',
+ firstPieGradientRight: '#00d77f',
+ firstPieShadowColor: 'rgba(0, 0, 0, 0)',
+
+ secondPieGradientLeft: '#7756f7',
+ secondPieGradientRight: '#7756f7',
+ secondPieShadowColor: 'rgba(0, 0, 0, 0)',
+
+ thirdPieGradientLeft: '#ffca00',
+ thirdPieGradientRight: '#ffca00',
+ thirdPieShadowColor: 'rgba(0, 0, 0, 0)',
+ },
+
+ earningLine: {
+ gradFrom: 'rgba(188, 92, 255, 0.5)',
+ gradTo: 'rgba(188, 92, 255, 0.5)',
+
+ tooltipTextColor: '#2a2a2a',
+ tooltipFontWeight: 'bolder',
+ tooltipFontSize: '16',
+ tooltipBg: '#ffffff',
+ tooltipBorderColor: '#c0c8d1',
+ tooltipBorderWidth: '3',
+ tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
+ },
+ },
+};
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/themes.scss b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/themes.scss
new file mode 100644
index 0000000..d3012c3
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/styles/themes.scss
@@ -0,0 +1,120 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+// @nebular theming framework
+@import '~@nebular/theme/styles/theming';
+// @nebular out of the box themes
+@import '~@nebular/theme/styles/themes';
+
+// which themes you what to enable (empty to enable all)
+$nb-enabled-themes: (default, cosmic, corporate);
+
+$nb-themes: nb-register-theme((
+ // app wise variables for each theme
+ sidebar-header-gap: 2rem,
+ sidebar-header-height: initial,
+ layout-content-width: 1400px,
+
+ font-main: Roboto,
+ font-secondary: Exo,
+
+ switcher-background: #ebeff5,
+ switcher-background-percentage: 50%,
+ drops-icon-line-gadient: -webkit-linear-gradient(#01dbb5, #0bbb79),
+
+ list-item-border-width: 1px,
+
+ slide-out-container-width: 30%,
+ slide-out-background: linear-gradient(270deg, #f7fafb 0%, #ecf2f5 100%),
+ slide-out-shadow-color: 0 4px 14px 0 #a2d2c8,
+ slide-out-shadow-color-rtl: 0 4px 14px 0 #a2d2c8,
+
+ chart-panel-summary-box-shadow: none,
+ chart-panel-summary-background-color: #ecf2f5,
+ chart-panel-summary-border-color: #ebeff1,
+ chart-panel-summary-border-width: 1px,
+
+ ecommerce-card-border-width: 1px,
+
+ progress-bar-background: linear-gradient(90deg, #3edd81 0%, #3bddaf 100%),
+), default, default);
+
+$nb-themes: nb-register-theme((
+ // app wise variables for each theme
+ sidebar-header-gap: 2rem,
+ sidebar-header-height: initial,
+ layout-content-width: 1400px,
+
+ font-main: Roboto,
+ font-secondary: Exo,
+
+ switcher-background: #4e41a5,
+ switcher-background-percentage: 14%,
+ drops-icon-line-gadient: -webkit-linear-gradient(#a258fe, #7958fa),
+
+ list-item-border-width: 1px,
+
+ slide-out-container-width: 30%,
+ slide-out-background: radial-gradient(circle, #302c6e 0%, #423f8c 100%),
+ slide-out-shadow-color: 2px 0 3px rgba(19, 19, 94, 0.9),
+ slide-out-shadow-color-rtl: -2px 0 3px rgba(19, 19, 94, 0.9),
+
+ chart-panel-summary-box-shadow: none,
+ chart-panel-summary-background-color: rgba(0, 0, 0, 0.1),
+ chart-panel-summary-border-color: #332e73,
+ chart-panel-summary-border-width: 1px,
+
+ ecommerce-card-border-width: 1px,
+
+ progress-bar-background: linear-gradient(90deg, #00c7c7 0%, #00d977 100%),
+), cosmic, cosmic);
+
+$nb-themes: nb-register-theme((
+ // app wise variables for each theme
+ sidebar-header-gap: 2rem,
+ sidebar-header-height: initial,
+ layout-content-width: 1400px,
+
+ font-main: Roboto,
+ font-secondary: Exo,
+
+ switcher-background: #2b2d34,
+ switcher-background-percentage: 14%,
+ drops-icon-line-gadient: -webkit-linear-gradient(#e9e8eb, #a7a2be),
+
+ list-item-border-width: 1px,
+
+ slide-out-container-width: 30%,
+ slide-out-background: linear-gradient(270deg, #f7fafb 0%, #ecf2f5 100%),
+ slide-out-shadow-color: 0 4px 14px 0 #a2d2c8,
+ slide-out-shadow-color-rtl: 0 4px 14px 0 #a2d2c8,
+
+ chart-panel-summary-box-shadow: none,
+ chart-panel-summary-background-color: #f7fafb,
+ chart-panel-summary-border-color: #ebeff1,
+ chart-panel-summary-border-width: 1px,
+
+ ecommerce-card-border-width: 1px,
+
+ progress-bar-background: linear-gradient(90deg, #ff9f6f 0%, #ff8b97 100%),
+), corporate, corporate);
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/@theme/theme.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/@theme/theme.module.ts
new file mode 100644
index 0000000..c816a96
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/@theme/theme.module.ts
@@ -0,0 +1,124 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { ModuleWithProviders, NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+
+import {
+ NbActionsModule,
+ NbCardModule,
+ NbLayoutModule,
+ NbMenuModule,
+ NbRouteTabsetModule,
+ NbSearchModule,
+ NbSidebarModule,
+ NbTabsetModule,
+ NbThemeModule,
+ NbUserModule,
+ NbCheckboxModule,
+ NbPopoverModule,
+ NbContextMenuModule,
+ NbProgressBarModule,
+ NbSpinnerModule,
+} from '@nebular/theme';
+
+import { NbSecurityModule } from '@nebular/security';
+
+import {
+ FooterComponent,
+ HeaderComponent,
+ ThemeSettingsComponent,
+ SwitcherComponent,
+ ThemeSwitcherComponent,
+ ThemeSwitcherListComponent,
+} from './components';
+import {
+ CapitalizePipe,
+ PluralPipe,
+ RoundPipe,
+ TimingPipe,
+ NumberWithCommasPipe,
+ NoSanitizePipe
+} from './pipes';
+import {
+ OneColumnLayoutComponent,
+ DefaultLayoutComponent,
+} from './layouts';
+import { DEFAULT_THEME } from './styles/theme.default';
+import { COSMIC_THEME } from './styles/theme.cosmic';
+import { CORPORATE_THEME } from './styles/theme.corporate';
+
+const BASE_MODULES = [CommonModule, FormsModule, ReactiveFormsModule];
+
+const NB_MODULES = [
+ NbCardModule,
+ NbLayoutModule,
+ NbTabsetModule,
+ NbRouteTabsetModule,
+ NbMenuModule,
+ NbUserModule,
+ NbActionsModule,
+ NbSearchModule,
+ NbSidebarModule,
+ NbCheckboxModule,
+ NbPopoverModule,
+ NbContextMenuModule,
+ NgbModule,
+ NbSecurityModule, // *nbIsGranted directive,
+ NbProgressBarModule,
+ NbSpinnerModule
+];
+
+const COMPONENTS = [
+ SwitcherComponent,
+ ThemeSwitcherComponent,
+ ThemeSwitcherListComponent,
+ HeaderComponent,
+ FooterComponent,
+ ThemeSettingsComponent,
+ OneColumnLayoutComponent,
+ DefaultLayoutComponent,
+];
+
+const ENTRY_COMPONENTS = [
+ ThemeSwitcherListComponent,
+];
+
+const PIPES = [
+ CapitalizePipe,
+ PluralPipe,
+ RoundPipe,
+ TimingPipe,
+ NumberWithCommasPipe,
+ NoSanitizePipe,
+];
+
+const NB_THEME_PROVIDERS = [
+ ...NbThemeModule.forRoot(
+ {
+ name: 'default',
+ },
+ [ DEFAULT_THEME, COSMIC_THEME, CORPORATE_THEME ],
+ ).providers,
+ ...NbSidebarModule.forRoot().providers,
+ ...NbMenuModule.forRoot().providers,
+];
+
+@NgModule({
+ imports: [...BASE_MODULES, ...NB_MODULES],
+ exports: [...BASE_MODULES, ...NB_MODULES, ...COMPONENTS, ...PIPES],
+ declarations: [...COMPONENTS, ...PIPES],
+ entryComponents: [...ENTRY_COMPONENTS],
+})
+export class ThemeModule {
+ static forRoot(): ModuleWithProviders {
+ return <ModuleWithProviders>{
+ ngModule: ThemeModule,
+ providers: [...NB_THEME_PROVIDERS],
+ };
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/app-routing.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/app-routing.module.ts
new file mode 100644
index 0000000..ccc05ca
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/app-routing.module.ts
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ExtraOptions, RouterModule, Routes } from '@angular/router';
+import { NgModule } from '@angular/core';
+import {
+ NbAuthComponent,
+ NbLoginComponent,
+ NbLogoutComponent,
+ NbRegisterComponent,
+ NbRequestPasswordComponent,
+ NbResetPasswordComponent,
+} from '@nebular/auth';
+
+const routes: Routes = [
+ { path: 'pages', loadChildren: 'app/pages/pages.module#PagesModule' },
+ { path: '', redirectTo: 'pages', pathMatch: 'full' },
+ { path: '**', redirectTo: 'pages' },
+];
+
+const config: ExtraOptions = {
+ useHash: true,
+};
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes, config)],
+ exports: [RouterModule],
+})
+export class AppRoutingModule {
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/app.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/app.component.ts
new file mode 100644
index 0000000..8ceed64
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/app.component.ts
@@ -0,0 +1,22 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Component, OnInit } from '@angular/core';
+import { AnalyticsService } from './@core/utils/analytics.service';
+
+@Component({
+ selector: 'ngx-app',
+ template: `<toaster-container [toasterconfig]="config"></toaster-container>
+ <router-outlet></router-outlet>`,
+})
+export class AppComponent implements OnInit {
+
+ constructor(private analytics: AnalyticsService) {
+ }
+
+ ngOnInit(): void {
+ this.analytics.trackPageViews();
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/app.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/app.module.ts
new file mode 100644
index 0000000..d93b5bc
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/app.module.ts
@@ -0,0 +1,38 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { APP_BASE_HREF } from '@angular/common';
+import { BrowserModule } from '@angular/platform-browser';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { NgModule } from '@angular/core';
+import { HttpClientModule } from '@angular/common/http';
+import { CoreModule } from './@core/core.module';
+import { ToasterModule } from 'angular2-toaster';
+
+import { AppComponent } from './app.component';
+import { AppRoutingModule } from './app-routing.module';
+import { ThemeModule } from './@theme/theme.module';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+
+@NgModule({
+ declarations: [AppComponent],
+ imports: [
+ BrowserModule,
+ BrowserAnimationsModule,
+ HttpClientModule,
+ AppRoutingModule,
+ ToasterModule.forRoot(),
+
+ NgbModule.forRoot(),
+ ThemeModule.forRoot(),
+ CoreModule.forRoot(),
+ ],
+ bootstrap: [AppComponent],
+ providers: [
+ { provide: APP_BASE_HREF, useValue: '/' },
+ ],
+})
+export class AppModule {
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/count-cards/count-cards.component.html b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/count-cards/count-cards.component.html
new file mode 100644
index 0000000..e662f15
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/count-cards/count-cards.component.html
@@ -0,0 +1,23 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<nb-card status="{{type}}">
+ <nb-card-header>
+ <div class="count-card-icon-container">
+ <i class="count-card-icon {{iconClass}}"></i>
+ </div>
+ {{title}}
+ <span class="float-right count-card-number">{{number}}</span>
+ </nb-card-header>
+</nb-card>
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/count-cards/count-cards.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/count-cards/count-cards.component.scss
new file mode 100644
index 0000000..97274b1
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/count-cards/count-cards.component.scss
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+.count-card-icon-container {
+ border-radius: 3px;
+ background-color: #414141;
+ padding: 15px;
+ margin-top: -30px;
+ margin-right: 15px;
+ float: left;
+ box-shadow: 0 4px 20px 0 rgba(0,0,0,.14),0 7px 10px -5px rgba(255,152,0,.4);
+
+ .count-card-icon {
+ font-size: 1.25em;
+ color: #fff;
+ }
+
+}
+.count-card-number{
+ font-size: 1.25em;
+ font-weight: 700;
+ color: #fff;
+ text-shadow: 1px 1px #7d7d7d;
+}
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/count-cards/count-cards.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/count-cards/count-cards.component.ts
new file mode 100644
index 0000000..9b4692e
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/count-cards/count-cards.component.ts
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, OnInit, Input } from '@angular/core';
+
+@Component({
+ selector: 'ngx-count-card',
+ templateUrl: './count-cards.component.html',
+ styleUrls: ['./count-cards.component.scss']
+})
+export class CountCardsComponent implements OnInit {
+
+ @Input() title: string;
+ @Input() footer: string;
+ @Input() number: number;
+ @Input() iconClass: string;
+ @Input() footerIconClass: string;
+ @Input() type: string;
+
+ constructor() { }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.component.html b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.component.html
new file mode 100644
index 0000000..c849fa3
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.component.html
@@ -0,0 +1,64 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<div [nbSpinner]="loading" nbSpinnerStatus="success" nbSpinnerSize="xxlarge">
+ <div class="row">
+ <div class="col-md-12">
+ <nb-card class="quick-stats-container-card" status="default">
+ <nb-card-header>
+ <div class="d-flex justify-content-between">
+ <div class="">
+ <span class="card-header-title">Quick Stats</span>
+ <small class="ml-2 text-muted"><i class="far fa-clock"></i> Last updated {{updatedAt}}</small>
+ </div>
+ <div class="">
+ <nb-actions size="medium">
+ <nb-action nbPopover="Refresh" nbPopoverMode="hint" icon="fas fa-sync-alt" (click)="refresh()" shape="semi-round" outline status="info"></nb-action>
+ </nb-actions>
+ </div>
+ </div>
+ </nb-card-header>
+ </nb-card>
+ </div>
+
+ </div>
+ <div class="row">
+ <div class="col-xxxl-2 col-md-2" *ngFor="let statusCard of statusCards">
+ <ngx-count-card [title]="statusCard.title"
+ [type]="statusCard.type"
+ [iconClass]="statusCard.iconClass"
+ [number]="statusCard.number"
+ [footerIconClass]="statusCard.footerIconClass"
+ [footer]="statusCard.footer">
+ </ngx-count-card>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col-md-6">
+ <ngx-recent-table [transactionStatus]="'COMMITTED'" [showTitle]='true' [showActions]='true'></ngx-recent-table>
+ </div>
+ <div class="col-md-6">
+ <ngx-recent-table [transactionStatus]="'PENDING'" [showTitle]='true' [showActions]='true'></ngx-recent-table>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-6">
+ <ngx-recent-table [transactionStatus]="'COMPENSATING'" [showTitle]='true' [showActions]='true'></ngx-recent-table>
+ </div>
+ <div class="col-md-6">
+ <ngx-recent-table [transactionStatus]="'ROLLBACKED'" [showTitle]='true' [showActions]='true'></ngx-recent-table>
+ </div>
+ </div>
+</div>
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.component.scss
new file mode 100644
index 0000000..464b99c
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.component.scss
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+.count-card-icon-container {
+ border-radius: 3px;
+ background-color: #414141;
+ padding: 15px;
+ margin-top: -30px;
+ margin-right: 15px;
+ float: left;
+ box-shadow: 0 4px 20px 0 rgba(0,0,0,.14),0 7px 10px -5px rgba(255,152,0,.4);
+
+
+}
+.count-card-icon {
+ font-size: 1.25em;
+ color: #fff;
+ text-align: center;
+}
+
+.count-card-number{
+ font-size: 1.25em;
+ font-weight: 700;
+ color: #fff;
+ text-shadow: 1px 1px #7d7d7d;
+}
+.quick-stats-container-card{
+ background: none;
+ box-shadow: none;
+}
+
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.component.ts
new file mode 100644
index 0000000..01c404a
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.component.ts
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { NbThemeService, NbSpinnerService } from '@nebular/theme';
+import { takeWhile } from 'rxjs/operators/takeWhile' ;
+import { SagaeventsService } from '../../@core/data/saga-events.service';
+import { UtilService } from '../../@core/utils/util.service';
+import * as _ from 'underscore';
+import { RecentTableComponent } from './recent-table/recent-table.component';
+
+interface CountCardSettings {
+ title: string;
+ footer: string;
+ number: number;
+ iconClass: string;
+ footerIconClass: string;
+ type: string;
+}
+
+@Component({
+ selector: 'ngx-dashboard',
+ templateUrl: './dashboard.component.html',
+ styleUrls: ['./dashboard.component.scss'],
+})
+export class DashboardComponent implements OnDestroy {
+
+ private alive = true;
+ private loading = false;
+ succTransactionsArr = [];
+ failedTransactionsArr = [];
+ committedTransArr =[];
+ pendingTransArr =[];
+ compensatingTransArr =[];
+ rollbackedTransArr =[];
+ successTxSettings: any;
+ failedTxSettings: any;
+ isGlobalTx: boolean = true;
+ committedTransactions: number;
+ compensatingTransactions: number;
+ failureRate: number;
+ pendingTransactions: number;
+ rollbackTransactions: number;
+ totalTransactions: number;
+ updatedAt: any;
+
+ totalTxCard: CountCardSettings = {
+ title: 'Total',
+ footer: 'Last updated few hours ago',
+ type: 'info',
+ number: 0,
+ iconClass: 'fas fa-exchange-alt',
+ footerIconClass: 'far fa-clock',
+ };
+
+ committedTxCard: CountCardSettings = {
+ title: 'Committed',
+ footer: 'Last updated few hours ago',
+ type: 'success',
+ number: 0,
+ iconClass: 'far fa-check-circle',
+ footerIconClass: 'far fa-clock',
+ };
+
+ pendingTxCard: CountCardSettings = {
+ title: 'Pending',
+ footer: 'Last updated few hours ago',
+ type: 'danger',
+ number: 0,
+ iconClass: 'fas fa-exclamation-circle',
+ footerIconClass: 'far fa-clock',
+ };
+
+ compensatingTxCard: CountCardSettings = {
+ title: 'Compensating',
+ footer: 'Last updated few hours ago',
+ type: 'danger',
+ number: 0,
+ iconClass: 'fas fa-exclamation-circle',
+ footerIconClass: 'far fa-clock',
+ };
+
+ rollbackTxCard: CountCardSettings = {
+ title: 'Rollback',
+ footer: 'Last updated few hours ago',
+ type: 'danger',
+ number: 0,
+ iconClass: 'fas fa-exclamation-circle',
+ footerIconClass: 'far fa-clock',
+ };
+
+ rateTxCard: CountCardSettings = {
+ title: 'Failure Rate',
+ footer: 'Last updated few hours ago',
+ type: 'primary',
+ number: 0,
+ iconClass: 'fas fa-percent',
+ footerIconClass: 'far fa-clock',
+ };
+
+
+ statusCards: string;
+
+ commonStatusCardsSet: CountCardSettings[] = [
+ this.totalTxCard,
+ this.committedTxCard,
+ this.pendingTxCard,
+ this.compensatingTxCard,
+ this.rollbackTxCard,
+ this.rateTxCard,
+ ];
+
+ statusCardsByThemes: {
+ default: CountCardSettings[];
+ cosmic: CountCardSettings[];
+ corporate: CountCardSettings[];
+ } = {
+ default: this.commonStatusCardsSet,
+ cosmic: this.commonStatusCardsSet,
+ corporate: [
+ {
+ ...this.totalTxCard,
+ type: 'info',
+ },
+ {
+ ...this.committedTxCard,
+ type: 'success',
+ },
+ {
+ ...this.pendingTxCard,
+ type: 'danger',
+ },
+ {
+ ...this.compensatingTxCard,
+ type: 'danger',
+ },
+ {
+ ...this.rollbackTxCard,
+ type: 'danger',
+ },
+ {
+ ...this.rateTxCard,
+ type: 'primary',
+ },
+ ],
+ };
+
+ constructor(private themeService: NbThemeService, private events: SagaeventsService, private util: UtilService) {
+ this.themeService.getJsTheme()
+ .pipe(takeWhile(() => this.alive))
+ .subscribe(theme => {
+ this.statusCards = this.statusCardsByThemes[theme.name];
+ });
+ this.getAllStats();
+ }
+ refresh(){
+ this.getAllStats();
+ }
+
+ //Get all stats
+ getAllStats(){
+ this.loading = true;
+ this.events.getAllStats().subscribe(data =>{
+ this.totalTxCard.number = data.body.totalTransactions;
+ this.committedTxCard.number = data.body.committedTransactions;
+ this.pendingTxCard.number = data.body.pendingTransactions;
+ this.compensatingTxCard.number = data.body.compensatingTransactions;
+ this.rollbackTxCard.number = data.body.rollbackTransactions;
+ this.rateTxCard.number = data.body.failureRate;
+ this.updatedAt = new Date(data.body.updatedAt);
+ this.loading = false;
+ },
+ (error) => {
+ this.util.error("Error!", "Something went wrong. The stats could not be fetched.");
+ this.loading = false;
+ });
+ }
+
+ ngOnDestroy() {
+ this.alive = false;
+ this.util.clearToasts();
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.module.ts
new file mode 100644
index 0000000..ebfe6eb
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/dashboard.module.ts
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { NgModule } from '@angular/core';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+
+import { ThemeModule } from '../../@theme/theme.module';
+import { DashboardComponent } from './dashboard.component';
+
+import { CountCardsComponent } from './count-cards/count-cards.component';
+import { RecentTableComponent } from './recent-table/recent-table.component';
+
+@NgModule({
+ imports: [
+ ThemeModule,
+ Ng2SmartTableModule,
+ ],
+ declarations: [
+ DashboardComponent,
+ CountCardsComponent,
+ RecentTableComponent,
+ ],
+ providers: [
+ ],
+})
+export class DashboardModule { }
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/recent-table/recent-table.component.html b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/recent-table/recent-table.component.html
new file mode 100644
index 0000000..a2476a9
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/recent-table/recent-table.component.html
@@ -0,0 +1,60 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<nb-card status="info" class="recent-card">
+ <nb-card-header>
+ <div class="d-flex justify-content-between">
+ <div class="">
+ <span *ngIf="showTitle" class="card-header-title">Recent {{transactionStatus}} transactions</span>
+ </div>
+ <div class="">
+ <nb-actions *ngIf="showActions" size="small" inverse="true">
+ <nb-action nbPopover="View All Transactions" nbPopoverMode="hint" icon="fas fa-external-link-alt" (click)="showAll(transactionStatus)"></nb-action>
+ <nb-action nbPopover="Refresh" nbPopoverMode="hint" icon="fas fa-sync-alt" (click)="refreshTable()"></nb-action>
+ </nb-actions>
+ </div>
+ </div>
+ </nb-card-header>
+ <nb-card-body>
+ <div class="table-responsive-sm">
+ <table class="table">
+ <thead>
+ <tr class="d-flex">
+ <th class="col-1">ID</th>
+ <th class="col-1">Service Name</th>
+ <th class="col-1">Instance ID</th>
+ <th class="col-2">Global Tx ID</th>
+ <th class="col-2">Local Tx ID</th>
+ <th class="col-2">Created At</th>
+ <th class="col-3">Compensation Method</th>
+ </tr>
+ </thead>
+ <tbody *ngIf="txCount">
+ <tr *ngFor="let event of recTransactionsArr | slice:0:5" class="d-flex">
+ <td class="col-1">{{event.surrogateId}}</td>
+ <td class="col-1">{{event.serviceName}}</td>
+ <td class="col-1">{{event.instanceId}}</td>
+ <td class="col-2">{{event.globalTxId}}</td>
+ <td class="col-2">{{event.localTxId}}</td>
+ <td class="col-2">{{event.creationTime | date:'MMMM d, y, h:mm:ss a zzzz'}}</td>
+ <td nbPopover="{{event.compensationMethod}}" nbPopoverMode="hover" class="col-3 text-truncate">{{event.compensationMethod?event.compensationMethod:'-'}}</td>
+ </tr>
+ </tbody>
+ <tbody *ngIf='!txCount'>
+ <p class="d-flex justify-content-center align-items-center mt-5">No Data Available.</p>
+ </tbody>
+ </table>
+ </div>
+ </nb-card-body>
+ </nb-card>
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/recent-table/recent-table.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/recent-table/recent-table.component.scss
new file mode 100644
index 0000000..54519b9
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/recent-table/recent-table.component.scss
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+.recent-card{
+ min-height: 527px;
+}
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/recent-table/recent-table.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/recent-table/recent-table.component.ts
new file mode 100644
index 0000000..a0b7808
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/dashboard/recent-table/recent-table.component.ts
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, OnInit, Input } from '@angular/core';
+import { SagaeventsService } from '../../../@core/data/saga-events.service';
+import { UtilService } from '../../../@core/utils/util.service';
+import { NavigationEnd, ActivatedRoute, Router } from '@angular/router';
+import * as _ from 'underscore';
+
+@Component({
+ selector: 'ngx-recent-table',
+ templateUrl: './recent-table.component.html',
+ styleUrls: ['./recent-table.component.scss']
+})
+export class RecentTableComponent implements OnInit {
+ @Input() transactionStatus: string;
+ @Input() cardType: string;
+ @Input() showTitle: boolean;
+ @Input() showActions: boolean;
+ private loading = false;
+ recTransactionsArr: any;
+ txCount: number;
+
+
+ constructor(private events: SagaeventsService, private util: UtilService, private router: Router) {
+ }
+
+ ngOnInit() {
+ this.getRecentTransactions(this.transactionStatus);
+ }
+
+ refreshTable(){
+ this.getRecentTransactions(this.transactionStatus);
+ }
+
+ showAll(status: any){
+ let url = '/pages/transactions/' + status.toLowerCase();
+ this.router.navigate([url], {queryParams: {type: status}});
+ }
+
+ getRecentTransactions(status: any){
+ this.loading = true;
+ this.events.getRecentTransactions(status).subscribe(data => {
+ this.recTransactionsArr = _.sortBy(data.body, function(item){
+ return -item['creationTime']; //Sorted by Create time
+ });
+ this.txCount = data.body.length;
+ this.loading = false;
+ let message = 'All recent ' + status + ' transactions have been fetched.';
+ this.util.success("", message);
+ },
+ (error) => {
+ this.recTransactionsArr = [];
+ let message = 'Something went wrong. The ' + status + ' transactions could not be fetched.';
+ this.util.error("Error!", message);
+ });
+ }
+
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/miscellaneous-routing.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/miscellaneous-routing.module.ts
new file mode 100644
index 0000000..aa2885b
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/miscellaneous-routing.module.ts
@@ -0,0 +1,30 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { MiscellaneousComponent } from './miscellaneous.component';
+import { NotFoundComponent } from './not-found/not-found.component';
+
+const routes: Routes = [{
+ path: '',
+ component: MiscellaneousComponent,
+ children: [{
+ path: '404',
+ component: NotFoundComponent,
+ }],
+}];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule],
+})
+export class MiscellaneousRoutingModule { }
+
+export const routedComponents = [
+ MiscellaneousComponent,
+ NotFoundComponent,
+];
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/miscellaneous.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/miscellaneous.component.ts
new file mode 100644
index 0000000..a75713e
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/miscellaneous.component.ts
@@ -0,0 +1,15 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'ngx-miscellaneous',
+ template: `
+ <router-outlet></router-outlet>
+ `,
+})
+export class MiscellaneousComponent {
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/miscellaneous.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/miscellaneous.module.ts
new file mode 100644
index 0000000..2c37c39
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/miscellaneous.module.ts
@@ -0,0 +1,19 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { NgModule } from '@angular/core';
+import { ThemeModule } from '../../@theme/theme.module';
+import { MiscellaneousRoutingModule, routedComponents } from './miscellaneous-routing.module';
+
+@NgModule({
+ imports: [
+ ThemeModule,
+ MiscellaneousRoutingModule,
+ ],
+ declarations: [
+ ...routedComponents,
+ ],
+})
+export class MiscellaneousModule { }
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/not-found/not-found.component.html b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/not-found/not-found.component.html
new file mode 100644
index 0000000..0a50058
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/not-found/not-found.component.html
@@ -0,0 +1,29 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<div class="row">
+ <div class="col-md-12">
+ <nb-card>
+ <nb-card-body>
+ <div class="flex-centered col-xl-4 col-lg-6 col-md-8 col-sm-12">
+ <h2 class="title">404 Page Not Found</h2>
+ <small class="sub-title">The page you were looking for doesn't exist</small>
+ <button (click)="goToHome()" type="button" class="btn btn-block btn-hero-primary">
+ Take me home
+ </button>
+ </div>
+ </nb-card-body>
+ </nb-card>
+ </div>
+</div>
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/not-found/not-found.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/not-found/not-found.component.scss
new file mode 100644
index 0000000..5321a4f
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/not-found/not-found.component.scss
@@ -0,0 +1,43 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+.flex-centered {
+ margin: auto;
+}
+nb-card-body {
+ display: flex;
+}
+
+.title {
+ text-align: center;
+}
+
+.sub-title {
+ text-align: center;
+ display: block;
+ margin-bottom: 3rem;
+}
+
+.btn {
+ margin-bottom: 2rem;
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/not-found/not-found.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/not-found/not-found.component.ts
new file mode 100644
index 0000000..cea8863
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/miscellaneous/not-found/not-found.component.ts
@@ -0,0 +1,22 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { NbMenuService } from '@nebular/theme';
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'ngx-not-found',
+ styleUrls: ['./not-found.component.scss'],
+ templateUrl: './not-found.component.html',
+})
+export class NotFoundComponent {
+
+ constructor(private menuService: NbMenuService) {
+ }
+
+ goToHome() {
+ this.menuService.navigateHome();
+ }
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/pages-menu.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/pages-menu.ts
new file mode 100644
index 0000000..7979100
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/pages-menu.ts
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { NbMenuItem } from '@nebular/theme';
+
+export const MENU_ITEMS: NbMenuItem[] = [
+ {
+ title: 'Dashboard',
+ icon: 'icon ion-speedometer',
+ link: '/pages/dashboard',
+ home: true,
+ },
+ {
+ title: 'Saga Transactions',
+ icon: 'fas fa-exchange-alt',
+ link: '/pages/transactions',
+ expanded: false,
+ children: [
+ {
+ title: 'All Transactions',
+ link: '/pages/transactions',
+ queryParams: {type: "ALL"},
+ },
+ {
+ title: 'Committed Transactions',
+ link: '/pages/transactions/committed',
+ queryParams: {type: "COMMITTED"},
+ },
+ {
+ title: 'Pending Transactions',
+ link: '/pages/transactions/pending',
+ queryParams: {type: "PENDING"},
+ },
+ {
+ title: 'Compensating Transactions',
+ link: '/pages/transactions/compensating',
+ queryParams: {type: "COMPENSATING"},
+ },
+ {
+ title: 'Rollback Transactions',
+ link: '/pages/transactions/rollbacked',
+ queryParams: {type: "ROLLBACKED"},
+ }
+ ],
+ },{
+ title: 'Find Transactions',
+ icon: 'icon ion-search',
+ link: '/pages/find',
+ },
+];
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/pages-routing.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/pages-routing.module.ts
new file mode 100644
index 0000000..55dc5a1
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/pages-routing.module.ts
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { RouterModule, Routes } from '@angular/router';
+import { NgModule } from '@angular/core';
+
+import { PagesComponent } from './pages.component';
+import { DashboardComponent } from './dashboard/dashboard.component';
+import {TransactionsComponent} from './transactions/transactions.component';
+import {FindTransactionComponent} from './transactions/findTransaction.component'
+import { NotFoundComponent } from './miscellaneous/not-found/not-found.component';
+
+const routes: Routes = [{
+ path: '',
+ component: PagesComponent,
+ children: [{
+ path: 'dashboard',
+ component: DashboardComponent,
+ }, {
+ path: 'transactions',
+ component: TransactionsComponent,
+ },{
+ path: 'transactions/committed',
+ component: TransactionsComponent,
+ },
+ {
+ path: 'transactions/pending',
+ component: TransactionsComponent,
+ },
+ {
+ path: 'transactions/compensating',
+ component: TransactionsComponent,
+ },
+ {
+ path: 'transactions/rollbacked',
+ component: TransactionsComponent,
+ },
+ {
+ path: 'find',
+ component: FindTransactionComponent,
+ },
+ {
+ path: '',
+ redirectTo: 'dashboard',
+ pathMatch: 'full',
+ }, {
+ path: '**',
+ component: NotFoundComponent,
+ }],
+}];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule],
+})
+export class PagesRoutingModule {
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/pages.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/pages.component.ts
new file mode 100644
index 0000000..b44d069
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/pages.component.ts
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */import { Component } from '@angular/core';
+
+import { MENU_ITEMS } from './pages-menu';
+
+@Component({
+ selector: 'ngx-pages',
+ template: `
+ <ngx-default-layout>
+ <nb-menu [items]="menu"></nb-menu>
+
+ <router-outlet></router-outlet>
+ </ngx-default-layout>
+ `,
+})
+export class PagesComponent {
+
+ menu = MENU_ITEMS;
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/pages.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/pages.module.ts
new file mode 100644
index 0000000..2df6cc8
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/pages.module.ts
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { NgModule } from '@angular/core';
+
+import { PagesComponent } from './pages.component';
+import { DashboardModule } from './dashboard/dashboard.module';
+import { TransactionsModule } from './transactions/transactions.module';
+import { PagesRoutingModule } from './pages-routing.module';
+import { ThemeModule } from '../@theme/theme.module';
+
+import { MiscellaneousModule } from './miscellaneous/miscellaneous.module';
+
+const PAGES_COMPONENTS = [
+ PagesComponent,
+];
+
+@NgModule({
+ imports: [
+ PagesRoutingModule,
+ ThemeModule,
+ DashboardModule,
+ TransactionsModule,
+ MiscellaneousModule,
+ ],
+ declarations: [
+ ...PAGES_COMPONENTS,
+ ],
+})
+export class PagesModule {
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/customRender.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/customRender.component.ts
new file mode 100644
index 0000000..0d5c6db
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/customRender.component.ts
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, Input, OnInit } from '@angular/core';
+import { NavigationEnd, ActivatedRoute, Router } from '@angular/router';
+import { ViewCell } from 'ng2-smart-table';
+
+@Component({
+ template: `
+ <a href="javascript:void(0)" title="View Transactions" class="nav-link" (click)="showDetails(value)">{{value}}</a>
+ `,
+})
+export class CustomRenderComponent implements ViewCell, OnInit {
+
+ renderValue: string;
+
+ @Input() value: string | number;
+ @Input() rowData: any;
+
+ constructor(private router: Router){}
+
+ ngOnInit() {
+ }
+ showDetails(gid: any){
+ if(this.value=== this.rowData.globalTxId){
+ this.router.navigate(['/pages/find'], {queryParams: {gid: this.value}});
+ }
+ if(this.value === this.rowData.serviceName){
+ this.router.navigate(['/pages/find'], {queryParams: {serviceName: this.rowData.serviceName}});
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/findTransaction.component.html b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/findTransaction.component.html
new file mode 100644
index 0000000..78068ca
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/findTransaction.component.html
@@ -0,0 +1,48 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<nb-card [nbSpinner]="loading" nbSpinnerStatus="success" nbSpinnerSize="xxlarge" status="info">
+ <nb-card-header>
+ <div class="row">
+ <div class="col-4">
+ Find Transactions
+ </div>
+ </div>
+ </nb-card-header>
+ <nb-card-body>
+ <form>
+ <div class="form-group form-row align-items-center">
+ <div class="col">
+ <label class="sr-only" for="findGID">Global ID</label>
+ <input [disabled]="hideGID" (change)="hideName=true;hideGID=false" [(ngModel)]="globalID" required nbInput type="text" class="form-control mb-2" id="findGID" name="findGID" placeholder="Global ID">
+ <small class="form-text text-muted">Please enter either Global ID or Micro Service Name.</small>
+ </div>
+ <div class="col">
+ <label class="sr-only" for="findName">Micro Service Name</label>
+ <div class="input-group mb-4">
+ <input [disabled]="hideName" (change)="hideGID=true;hideName=false" [(ngModel)]="serviceName" required nbInput type="text" class="form-control" id="findName" name="findName" placeholder="Micro Service Name">
+ </div>
+ </div>
+ <div class="col-auto">
+ <input nbButton status="success" size="small" class="mb-4" (click)="findTransaction(globalID, serviceName)" value="Find"/>
+ <input nbButton status="info" size="small" class="mb-4 ml-2" (click)="resetForm()" value="Clear" />
+ </div>
+ </div>
+ </form>
+ <div>
+ <ngx-transactions-table [showTitle]="false" [title]="Transactions" [showCount]="false" [hideRefresh]="true" [tableData]= "transactionsArr"></ngx-transactions-table>
+ </div>
+
+ </nb-card-body>
+ </nb-card>
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/findTransaction.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/findTransaction.component.ts
new file mode 100644
index 0000000..0586f08
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/findTransaction.component.ts
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {ActivatedRoute, Params} from '@angular/router';
+import { Component, OnInit, Input, OnDestroy, ViewChild } from '@angular/core';
+import { ElementRef, Renderer2 } from '@angular/core';
+import { SagaeventsService } from '../../@core/data/saga-events.service';
+import {TransactionsTableComponent} from './transactionsTable.component';
+import { UtilService } from '../../@core/utils/util.service';
+import * as _ from 'underscore';
+
+@Component({
+ selector: 'ngx-find-transactions',
+ templateUrl: './findTransaction.component.html',
+ })
+ export class FindTransactionComponent implements OnInit {
+ transactionsArr = [];
+ type: any;
+ globalID: any;
+ serviceName: any;
+ hideGID: boolean;
+ hideName: boolean;
+ loading: boolean;
+
+ constructor(private events: SagaeventsService, private util: UtilService, private activatedRoute: ActivatedRoute){
+ this.activatedRoute.queryParams.subscribe(params => {
+ this.globalID = params['gid'];
+ });
+ }
+
+ findTransaction(gid?:any, name?: any) {
+ this.loading = true;
+ this.util.clearToasts();
+ this.events.findTransaction(gid, name).subscribe(data => {
+ this.transactionsArr = data.body;
+ if(this.transactionsArr.length){
+ this.util.success("", "All transactions have been fetched");
+ }
+ this.loading = false;
+ },
+ (error) => {
+ this.util.error("Error!", "Something went wrong. The transactions could not be fetched.");
+ this.loading = false;
+ });
+ }
+ resetForm(){
+ this.transactionsArr = [];
+ this.globalID = "";
+ this.serviceName = "";
+ this.hideGID = false;
+ this.hideName = false;
+ }
+
+ ngOnInit() {
+ this.activatedRoute.queryParams.subscribe(params => {
+ this.globalID = params['gid'];
+ this.serviceName = params['serviceName'];
+ if(params['gid']){
+ this.findTransaction(params['gid']);
+ }
+ if(params['serviceName']){
+ this.findTransaction(undefined, params['serviceName']);
+ }
+ });
+ }
+ }
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactions.component.html b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactions.component.html
new file mode 100644
index 0000000..22e0cf4
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactions.component.html
@@ -0,0 +1,15 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<ngx-transactions-table [showTitle]="true" [title]="type" [showCount]="true" [hideRefresh]="false" [tableData]= "transactionsArr"></ngx-transactions-table>
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactions.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactions.component.ts
new file mode 100644
index 0000000..ac7a7ef
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactions.component.ts
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Router, ActivatedRoute, Params} from '@angular/router';
+import { Component, OnInit, Input, OnDestroy } from '@angular/core';
+import { SagaeventsService } from '../../@core/data/saga-events.service';
+import {TransactionsTableComponent} from './transactionsTable.component';
+import { UtilService } from '../../@core/utils/util.service';
+import * as _ from 'underscore';
+
+@Component({
+ selector: 'ngx-all-transactions',
+ templateUrl: './transactions.component.html',
+ })
+ export class TransactionsComponent implements OnInit {
+ transactionsArr = [];
+ type: any;
+ constructor(private events: SagaeventsService, private util: UtilService, private activatedRoute: ActivatedRoute){
+ this.activatedRoute.queryParams.subscribe(params => {
+ this.type = params['type'];
+ });
+ this.getAllTransactions(this.type);
+ }
+
+ getAllTransactions(status: any) {
+ this.events.getTransactions(status).subscribe(data => {
+ this.transactionsArr = data.body;
+ this.util.success("Transactions fetched!", "All transactions have been fetched");
+ },
+ (error) => {
+ this.util.error("Error!", "Something went wrong. The transactions could not be fetched.");
+ });
+ }
+
+ ngOnInit() {
+ this.activatedRoute.queryParams.subscribe(params => {
+ this.type = params['type'];
+ });
+ }
+ }
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactions.module.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactions.module.ts
new file mode 100644
index 0000000..aab07db
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactions.module.ts
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { CustomRenderComponent } from './customRender.component';
+
+import { ThemeModule } from '../../@theme/theme.module';
+import { TransactionsTableComponent} from './transactionsTable.component';
+import { TransactionsComponent} from './transactions.component';
+import { FindTransactionComponent } from './findTransaction.component';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ ThemeModule,
+ Ng2SmartTableModule,
+ ],
+ entryComponents: [CustomRenderComponent],
+ declarations: [TransactionsTableComponent , TransactionsComponent, FindTransactionComponent, CustomRenderComponent]
+})
+export class TransactionsModule { }
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactionsTable.component.html b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactionsTable.component.html
new file mode 100644
index 0000000..b2e5973
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactionsTable.component.html
@@ -0,0 +1,34 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<div *ngIf="!hideRefresh" class="row">
+ <div class="col-xxxl-12 col-md-12 mb-4">
+ <button title="Refresh" (click)="refresh()" class="float-left" nbButton shape="semi-round" outline status="info"><i class="fas fa-sync-alt"></i></button>
+ </div>
+</div>
+<nb-card status="info">
+ <nb-card-header>
+ <div class="row">
+ <div class="col-4">
+ <span *ngIf="showTitle">{{title}}</span>
+ </div>
+ <div class=" float-right offset-4 col-4">
+ <span *ngIf="showCount">Total Transactions: {{tableData.length}}</span>
+ </div>
+ </div>
+ </nb-card-header>
+ <nb-card-body>
+ <ng2-smart-table [settings]='settings' [source]='tableData'></ng2-smart-table>
+ </nb-card-body>
+</nb-card>
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactionsTable.component.scss b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactionsTable.component.scss
new file mode 100644
index 0000000..4bedc40
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactionsTable.component.scss
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+:host /deep/ ng2-smart-table table tr td{
+ /* font-size:0.9em; */
+ word-break: break-all;
+ }
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactionsTable.component.ts b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactionsTable.component.ts
new file mode 100644
index 0000000..581c493
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/app/pages/transactions/transactionsTable.component.ts
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, OnInit, Input, OnDestroy } from '@angular/core';
+import { DomSanitizer, SafeResourceUrl, SafeUrl} from '@angular/platform-browser';
+import { RouterModule, Routes, Router } from '@angular/router';
+import { NbThemeService } from '@nebular/theme';
+import { takeWhile } from 'rxjs/operators/takeWhile' ;
+import { SagaeventsService } from '../../@core/data/saga-events.service';
+import { UtilService } from '../../@core/utils/util.service';
+import * as _ from 'underscore';
+
+import { CustomRenderComponent } from './customRender.component';
+
+
+
+@Component({
+ selector: 'ngx-transactions-table',
+ templateUrl: './transactionsTable.component.html',
+ styleUrls: ['./transactionsTable.component.scss']
+})
+export class TransactionsTableComponent implements OnInit, OnDestroy {
+ @Input() tableData: any;
+ @Input() hideRefresh: boolean;
+ @Input() showTitle: boolean;
+ @Input() title: string;
+ @Input() showCount: boolean;
+
+ private alive = true;
+ transactions: any;
+ settings: any;
+
+ constructor(private events: SagaeventsService, private util: UtilService, private router: Router, private sanitizer: DomSanitizer) { }
+
+ ngOnInit() {
+ this.transactions = this.tableData;
+ this.setTransactionsTable();
+ }
+
+ refresh(){
+ this. setTransactionsTable();
+ }
+
+ setTransactionsTable() {
+ this.settings = {
+ pager: {
+ perPage: 10,
+ },
+ add: {
+ addButtonContent: '<i class="nb-plus"></i>',
+ createButtonContent: '<i class="nb-checkmark"></i>',
+ cancelButtonContent: '<i class="nb-close"></i>',
+ },
+ edit: {
+ editButtonContent: '<i class="nb-edit"></i>',
+ saveButtonContent: '<i class="nb-checkmark"></i>',
+ cancelButtonContent: '<i class="nb-close"></i>',
+ },
+ delete: {
+ deleteButtonContent: '<i class="nb-trash"></i>',
+ confirmDelete: true,
+ },
+ actions: {
+ add: false,
+ edit: false,
+ delete: false,
+ position: 'right',
+ },
+ columns: {
+ surrogateId: {
+ title: 'ID',
+ filter: true,
+ sort: true,
+ editable: false,
+ width: '5%',
+ },
+ serviceName: {
+ title: 'Service Name',
+ filter: true,
+ sort: true,
+ editable: false,
+ width: '9%',
+ type:'custom',
+ renderComponent: CustomRenderComponent,
+ },
+ instanceId: {
+ title: 'Instance ID',
+ editable: false,
+ width: '11%',
+ },
+ globalTxId: {
+ title: 'Global ID',
+ filter: true,
+ sort: true,
+ width: '16%',
+ editable: false,
+ type:'custom',
+ renderComponent: CustomRenderComponent,
+ },
+ localTxId: {
+ title: 'Local ID',
+ filter: true,
+ sort: true,
+ width: '15%',
+ editable: false
+ },
+ compensationMethod: {
+ title: 'Compensation Method',
+ editable: false,
+ width: '20%',
+ },
+ },
+
+ };
+ }
+
+ ngOnDestroy() {
+ this.alive = false;
+ this.util.clearToasts();
+ }
+
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/assets/images/square_pattern.svg b/saga-web/src/main/resources/saga-frontend/src/assets/images/square_pattern.svg
new file mode 100644
index 0000000..dda0f42
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/assets/images/square_pattern.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 150"><defs><style>.cls-1{fill:none;stroke:#ebeef2;stroke-miterlimit:10;}</style></defs><title>Asset 2_svg</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><line class="cls-1" x1="75" x2="75" y2="150"/><line class="cls-1" x1="150" y1="75" y2="75"/></g></g></svg>
\ No newline at end of file
diff --git a/saga-web/src/main/resources/saga-frontend/src/assets/images/square_pattern_cosmic.svg b/saga-web/src/main/resources/saga-frontend/src/assets/images/square_pattern_cosmic.svg
new file mode 100644
index 0000000..fa11141
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/assets/images/square_pattern_cosmic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 150"><defs><style>.cls-1{fill:none;stroke:#342e73;stroke-miterlimit:10;}</style></defs><title>Asset 2_svg</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><line class="cls-1" x1="75" x2="75" y2="150"/><line class="cls-1" x1="150" y1="75" y2="75"/></g></g></svg>
diff --git a/saga-web/src/main/resources/saga-frontend/src/environments/environment.prod.ts b/saga-web/src/main/resources/saga-frontend/src/environments/environment.prod.ts
new file mode 100644
index 0000000..868175c
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/environments/environment.prod.ts
@@ -0,0 +1,9 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+export const environment = {
+ production: true,
+ apiUrl: 'http://127.0.0.1:8090',
+};
diff --git a/saga-web/src/main/resources/saga-frontend/src/environments/environment.ts b/saga-web/src/main/resources/saga-frontend/src/environments/environment.ts
new file mode 100644
index 0000000..29ab5e3
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/environments/environment.ts
@@ -0,0 +1,15 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+// The file contents for the current environment will overwrite these during build.
+// The build system defaults to the dev environment which uses `environment.ts`, but if you do
+// `ng build --env=prod` then `environment.prod.ts` will be used instead.
+// The list of which env maps to which file can be found in `.angular-cli.json`.
+
+export const environment = {
+ production: false,
+
+ apiUrl: 'http://127.0.0.1:8090',
+};
diff --git a/saga-web/src/main/resources/saga-frontend/src/favicon.ico b/saga-web/src/main/resources/saga-frontend/src/favicon.ico
new file mode 100644
index 0000000..a4895ba
Binary files /dev/null and b/saga-web/src/main/resources/saga-frontend/src/favicon.ico differ
diff --git a/saga-web/src/main/resources/saga-frontend/src/favicon.png b/saga-web/src/main/resources/saga-frontend/src/favicon.png
new file mode 100644
index 0000000..49f3f2a
Binary files /dev/null and b/saga-web/src/main/resources/saga-frontend/src/favicon.png differ
diff --git a/saga-web/src/main/resources/saga-frontend/src/index.html b/saga-web/src/main/resources/saga-frontend/src/index.html
new file mode 100644
index 0000000..7deef5f
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/index.html
@@ -0,0 +1,48 @@
+<!--
+The MIT License (MIT)
+
+Copyright (c) 2017 akveo.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ -->
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ServiceComb Saga</title>
+
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="icon" type="image/png" href="favicon.png">
+ <link rel="icon" type="image/x-icon" href="favicon.ico">
+</head>
+<body>
+ <ngx-app>Loading...</ngx-app>
+
+ <style>@-webkit-keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@-moz-keyframes spin{0%{-moz-transform:rotate(0)}100%{-moz-transform:rotate(360deg)}}@keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.spinner{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1003;background: #000000;overflow:hidden} .spinner div:first-child{display:block;position:relative;left:50%;top:50%;width:150px;height:150px;margin:-75px 0 0 -75px;border-radius:50% [...]
+ <div id="nb-global-spinner" class="spinner">
+ <div class="blob blob-0"></div>
+ <div class="blob blob-1"></div>
+ <div class="blob blob-2"></div>
+ <div class="blob blob-3"></div>
+ <div class="blob blob-4"></div>
+ <div class="blob blob-5"></div>
+ </div>
+
+</body>
+</html>
diff --git a/saga-web/src/main/resources/saga-frontend/src/main.ts b/saga-web/src/main/resources/saga-frontend/src/main.ts
new file mode 100644
index 0000000..359bdbd
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/main.ts
@@ -0,0 +1,17 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.error(err));
diff --git a/saga-web/src/main/resources/saga-frontend/src/polyfills.ts b/saga-web/src/main/resources/saga-frontend/src/polyfills.ts
new file mode 100644
index 0000000..421e93e
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/polyfills.ts
@@ -0,0 +1,76 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+import 'core-js/es6/symbol';
+import 'core-js/es6/object';
+import 'core-js/es6/function';
+import 'core-js/es6/parse-int';
+import 'core-js/es6/parse-float';
+import 'core-js/es6/number';
+import 'core-js/es6/math';
+import 'core-js/es6/string';
+import 'core-js/es6/date';
+import 'core-js/es6/array';
+import 'core-js/es6/regexp';
+import 'core-js/es6/map';
+import 'core-js/es6/set';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following to support `@angular/animation`. */
+import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+
+/** Evergreen browsers require these. **/
+import 'core-js/es6/reflect';
+import 'core-js/es7/reflect';
+
+
+/** ALL Firefox browsers require the following to support `@angular/animation`. **/
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+
+/***************************************************************************************************
+ * Zone JS is required by Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
+
+/**
+ * Date, currency, decimal and percent pipes.
+ * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
+ */
+import 'core-js/es7/array';
+import 'core-js/es7/object';
+
+if (typeof SVGElement.prototype.contains === 'undefined') {
+ SVGElement.prototype.contains = HTMLDivElement.prototype.contains;
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/tsconfig.app.json b/saga-web/src/main/resources/saga-frontend/src/tsconfig.app.json
new file mode 100644
index 0000000..d2f5b48
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/tsconfig.app.json
@@ -0,0 +1,27 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../out-tsc/app",
+ "module": "es2015",
+ "baseUrl": "./",
+ "types": [],
+ "paths": {
+ "@angular/*": [
+ "../node_modules/@angular/*"
+ ],
+ "@nebular/*": [
+ "../node_modules/@nebular/*"
+ ]
+ }
+ },
+ "exclude": [
+ "test.ts",
+ "**/*.spec.ts",
+ "../node_modules/@nebular/**/*.spec.ts"
+ ],
+ "include": [
+ "../src/*.ts",
+ "../src/**/*.ts",
+ "../node_modules/@nebular/**/*.ts"
+ ]
+}
diff --git a/saga-web/src/main/resources/saga-frontend/src/typings.d.ts b/saga-web/src/main/resources/saga-frontend/src/typings.d.ts
new file mode 100644
index 0000000..47a1963
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/src/typings.d.ts
@@ -0,0 +1,15 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See licenses/LICENSE-ngxadmin for license information.
+ */
+
+/* SystemJS module definition */
+declare var module: NodeModule;
+interface NodeModule {
+ id: string;
+}
+
+declare var tinymce: any;
+
+declare var echarts: any;
diff --git a/saga-web/src/main/resources/saga-frontend/tsconfig.json b/saga-web/src/main/resources/saga-frontend/tsconfig.json
new file mode 100644
index 0000000..d82d78b
--- /dev/null
+++ b/saga-web/src/main/resources/saga-frontend/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "outDir": "./dist/out-tsc",
+ "sourceMap": true,
+ "declaration": false,
+ "moduleResolution": "node",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "target": "es5",
+ "typeRoots": [
+ "node_modules/@types"
+ ],
+ "lib": [
+ "es2017",
+ "dom"
+ ],
+ "plugins": [
+ { "name": "tslint-language-service"}
+ ]
+ }
+}