Compare commits
52 Commits
42e7ee39be
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 3325c63631 | |||
| 3a12b6a4ab | |||
| 9ea2278dfb | |||
| a4391c84d0 | |||
| ee6dc58a21 | |||
| 0d3995764b | |||
| f30c41afba | |||
| 2ef9968920 | |||
| d8db9f650b | |||
| 349510db56 | |||
| b44834343a | |||
| b41c01e6f7 | |||
| 9ad991a70e | |||
| 6806eeff8a | |||
| 3c3c880a3c | |||
| 542f24c12d | |||
| 23e6da2808 | |||
| 61604355c1 | |||
| b4d52283aa | |||
| b24753afe7 | |||
| 89cb09adb6 | |||
| 716e25f0ba | |||
| d20f2a37c4 | |||
| 701c36112c | |||
| e5933104cc | |||
| 46ec236ed5 | |||
| b8b35645ac | |||
| ed3c116d13 | |||
| 719108fd6a | |||
| f88cd21b33 | |||
| fd32ae5dcc | |||
| 9fec45a91f | |||
| ba3ad023ad | |||
| ed90250876 | |||
| cdceaab2fd | |||
| 70aa8adbba | |||
| cd9021d9c0 | |||
| 0ee2e7e545 | |||
| 8961490ff8 | |||
| d1039a409b | |||
| cca0de9812 | |||
| 3b37d7d798 | |||
| e2f55f0b8b | |||
| 56d2bd17e4 | |||
| c68d9ba749 | |||
| dc49c0a958 | |||
| 853b7069f9 | |||
| dfc1f269a0 | |||
| d486fe9594 | |||
| 6a031ca478 | |||
| b46392bc41 | |||
| 6301d6008b |
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"kendoai": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@progress/kendo-angular-mcp@latest"],
|
||||
"env": {
|
||||
"TELERIK_LICENSE_PATH":"C:/Users/Chris/AppData/Roaming/Telerik/telerik-license.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
!.vscode/extensions.json
|
||||
|
||||
# misc
|
||||
/.angular/cache
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": [
|
||||
"development"
|
||||
],
|
||||
"hints": {
|
||||
"axe/text-alternatives": [
|
||||
"default",
|
||||
{
|
||||
"image-alt": "off"
|
||||
}
|
||||
]
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults",
|
||||
"not ie 11",
|
||||
"not ie <= 11"
|
||||
]
|
||||
}
|
||||
Vendored
+1
@@ -5,6 +5,7 @@
|
||||
"git.decorations.enabled": false,
|
||||
"workbench.editor.decorations.badges": false,
|
||||
"workbench.editor.decorations.colors": false,
|
||||
"typescript.preferences.includePackageJsonAutoImports": "on",
|
||||
"vscode_custom_css.imports": [
|
||||
"file:///C:/VScode/custom.css"
|
||||
],
|
||||
|
||||
+12
-51
@@ -24,8 +24,8 @@
|
||||
"src/web.config",
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "node_modules/leaflet/dist/images",
|
||||
"output": "/assets/img/markers"
|
||||
"input": "node_modules/tinymce",
|
||||
"output": "/tinymce/"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
@@ -38,16 +38,13 @@
|
||||
"node_modules/nebular-icons/scss/nebular-icons.scss",
|
||||
"node_modules/pace-js/templates/pace-theme-flash.tmpl.css",
|
||||
"node_modules/leaflet/dist/leaflet.css",
|
||||
"node_modules/@progress/kendo-theme-default/dist/all.css",
|
||||
"src/app/@theme/styles/styles.scss",
|
||||
"src/assets/styles/site.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/pace-js/pace.min.js",
|
||||
"node_modules/tinymce/tinymce.min.js",
|
||||
"node_modules/tinymce/themes/modern/theme.min.js",
|
||||
"node_modules/tinymce/plugins/link/plugin.min.js",
|
||||
"node_modules/tinymce/plugins/paste/plugin.min.js",
|
||||
"node_modules/tinymce/plugins/table/plugin.min.js",
|
||||
"node_modules/echarts/dist/echarts.min.js",
|
||||
"node_modules/echarts/dist/extension/bmap.min.js",
|
||||
"node_modules/chart.js/dist/Chart.min.js"
|
||||
@@ -57,7 +54,10 @@
|
||||
"echarts",
|
||||
"lodash",
|
||||
"zrender/lib/svg/svg",
|
||||
"zrender/lib/vml/vml"
|
||||
"zrender/lib/vml/vml",
|
||||
"file-saver",
|
||||
"eva-icons",
|
||||
"rxjs-compat"
|
||||
],
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
@@ -94,18 +94,18 @@
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "ngx-admin-demo:build"
|
||||
"buildTarget": "ngx-admin-demo:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "ngx-admin-demo:build:production"
|
||||
"buildTarget": "ngx-admin-demo:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "ngx-admin-demo:build"
|
||||
"buildTarget": "ngx-admin-demo:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
@@ -118,10 +118,6 @@
|
||||
"scripts": [
|
||||
"node_modules/pace-js/pace.min.js",
|
||||
"node_modules/tinymce/tinymce.min.js",
|
||||
"node_modules/tinymce/themes/modern/theme.min.js",
|
||||
"node_modules/tinymce/plugins/link/plugin.min.js",
|
||||
"node_modules/tinymce/plugins/paste/plugin.min.js",
|
||||
"node_modules/tinymce/plugins/table/plugin.min.js",
|
||||
"node_modules/echarts/dist/echarts.min.js",
|
||||
"node_modules/echarts/dist/extension/bmap.min.js",
|
||||
"node_modules/chart.js/dist/Chart.min.js"
|
||||
@@ -144,50 +140,15 @@
|
||||
"src/web.config",
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "node_modules/leaflet/dist/images",
|
||||
"output": "/assets/img/markers"
|
||||
"input": "node_modules/tinymce",
|
||||
"output": "/tinymce/"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"src/tsconfig.app.json",
|
||||
"src/tsconfig.spec.json"
|
||||
],
|
||||
"typeCheck": true,
|
||||
"exclude": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ngx-admin-demo-e2e": {
|
||||
"root": "",
|
||||
"sourceRoot": "",
|
||||
"projectType": "application",
|
||||
"architect": {
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "./protractor.conf.js",
|
||||
"devServerTarget": "ngx-admin-demo:serve"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"e2e/tsconfig.e2e.json"
|
||||
],
|
||||
"exclude": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "ngx-admin-demo",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"prefix": "ngx",
|
||||
|
||||
Generated
+60418
-46416
File diff suppressed because it is too large
Load Diff
+120
-118
@@ -1,120 +1,122 @@
|
||||
{
|
||||
"name": "ngx-admin",
|
||||
"version": "8.0.0",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/akveo/ngx-admin.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/akveo/ngx-admin/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"conventional-changelog": "conventional-changelog",
|
||||
"start": "ng serve",
|
||||
"build": "ng build --output-path \\\\ArkNAS\\docker\\nginx-proxy\\data\\ChurchAngular --prod",
|
||||
"build:prod": "npm run build -- --configuration production --aot",
|
||||
"test": "ng test",
|
||||
"test:coverage": "rimraf coverage && npm run test -- --code-coverage",
|
||||
"lint": "ng lint",
|
||||
"lint:fix": "ng lint ngx-admin-demo --fix",
|
||||
"lint:styles": "stylelint ./src/**/*.scss",
|
||||
"lint:ci": "npm run lint && npm run lint:styles",
|
||||
"pree2e": "webdriver-manager update --standalone false --gecko false",
|
||||
"e2e": "ng e2e",
|
||||
"docs": "compodoc -p src/tsconfig.app.json -d docs",
|
||||
"docs:serve": "compodoc -p src/tsconfig.app.json -d docs -s",
|
||||
"prepush": "npm run lint:ci",
|
||||
"release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s",
|
||||
"postinstall": "ngcc --properties es2015 es5 browser module main --first-only --create-ivy-entry-points --tsconfig \"./src/tsconfig.app.json\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^12.2.16",
|
||||
"@angular/cdk": "12.1.0",
|
||||
"@angular/common": "^12.2.16",
|
||||
"@angular/compiler": "^12.2.16",
|
||||
"@angular/core": "^12.2.16",
|
||||
"@angular/forms": "^12.2.16",
|
||||
"@angular/google-maps": "^12.2.13",
|
||||
"@angular/platform-browser": "^12.2.16",
|
||||
"@angular/platform-browser-dynamic": "^12.2.16",
|
||||
"@angular/router": "^12.2.16",
|
||||
"@asymmetrik/ngx-leaflet": "3.0.1",
|
||||
"@microsoft/signalr": "^6.0.8",
|
||||
"@nebular/auth": "8.0.0",
|
||||
"@nebular/date-fns": "^9.0.3",
|
||||
"@nebular/eva-icons": "8.0.0",
|
||||
"@nebular/security": "8.0.0",
|
||||
"@nebular/theme": "8.0.0",
|
||||
"@swimlane/ngx-charts": "^14.0.0",
|
||||
"angular2-chartjs": "0.4.1",
|
||||
"angular2-qrcode": "^2.0.3",
|
||||
"bootstrap": "4.3.1",
|
||||
"chart.js": "2.7.1",
|
||||
"ckeditor": "4.7.3",
|
||||
"classlist.js": "1.1.20150312",
|
||||
"core-js": "2.5.1",
|
||||
"echarts": "^4.9.0",
|
||||
"eva-icons": "^1.1.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"intl": "1.2.5",
|
||||
"ionicons": "2.0.1",
|
||||
"leaflet": "1.2.0",
|
||||
"nebular-icons": "1.1.0",
|
||||
"ng-in-viewport": "^13.0.1",
|
||||
"ng2-ckeditor": "~1.2.9",
|
||||
"ng2-completer": "^9.0.1",
|
||||
"ng2-smart-table": "^1.6.0",
|
||||
"ngx-echarts": "^4.2.2",
|
||||
"ngx-infinite-scroll": "^13.0.2",
|
||||
"ngx-mask": "^12.0.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"normalize.css": "6.0.0",
|
||||
"pace-js": "1.0.2",
|
||||
"roboto-fontface": "0.8.0",
|
||||
"rxjs": "6.6.2",
|
||||
"rxjs-compat": "6.3.0",
|
||||
"socicon": "3.0.5",
|
||||
"style-loader": "^1.3.0",
|
||||
"tinymce": "4.5.7",
|
||||
"tslib": "^2.3.1",
|
||||
"typeface-exo": "0.0.22",
|
||||
"typeit": "^8.7.0",
|
||||
"web-animations-js": "^2.3.2",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^12.1.4",
|
||||
"@angular/cli": "^12.2.17",
|
||||
"@angular/compiler-cli": "^12.2.16",
|
||||
"@angular/language-service": "12.1.0",
|
||||
"@compodoc/compodoc": "1.0.1",
|
||||
"@fortawesome/fontawesome-free": "^5.2.0",
|
||||
"@schematics/angular": "^14.1.3",
|
||||
"@types/d3-color": "1.0.5",
|
||||
"@types/jasmine": "~3.3.0",
|
||||
"@types/jasminewd2": "2.0.3",
|
||||
"@types/leaflet": "1.2.3",
|
||||
"@types/node": "^12.12.70",
|
||||
"codelyzer": "^6.0.2",
|
||||
"conventional-changelog-cli": "1.3.4",
|
||||
"husky": "0.13.3",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~6.3.19",
|
||||
"karma-chrome-launcher": "~3.1.1",
|
||||
"karma-cli": "1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.2",
|
||||
"karma-jasmine-html-reporter": "^1.7.0",
|
||||
"npm-run-all": "4.0.2",
|
||||
"protractor": "~7.0.0",
|
||||
"rimraf": "2.6.1",
|
||||
"stylelint": "7.13.0",
|
||||
"ts-node": "3.2.2",
|
||||
"tslint": "~6.1.0",
|
||||
"tslint-language-service": "^0.9.9",
|
||||
"typescript": "~4.2.3||~4.3.0"
|
||||
}
|
||||
"name": "ngx-admin",
|
||||
"version": "8.0.0",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/akveo/ngx-admin.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/akveo/ngx-admin/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"conventional-changelog": "conventional-changelog",
|
||||
"start": "ng serve --host=127.0.0.1",
|
||||
"build": "ng build",
|
||||
"build:prod": "ng build --output-path \\\\ArkNAS\\docker\\nginx-proxy\\data\\ChurchAngular --configuration production --aot",
|
||||
"test": "ng test",
|
||||
"test:coverage": "rimraf coverage && npm run test -- --code-coverage",
|
||||
"lint": "ng lint",
|
||||
"lint:fix": "ng lint ngx-admin-demo --fix",
|
||||
"lint:styles": "stylelint ./src/**/*.scss",
|
||||
"lint:ci": "npm run lint && npm run lint:styles",
|
||||
"pree2e": "webdriver-manager update --standalone false --gecko false",
|
||||
"e2e": "ng e2e",
|
||||
"docs": "compodoc -p src/tsconfig.app.json -d docs",
|
||||
"docs:serve": "compodoc -p src/tsconfig.app.json -d docs -s",
|
||||
"prepush": "echo 'Pre-push hook disabled - style linting skipped'",
|
||||
"release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^17.3.3",
|
||||
"@angular/cdk": "17.3.3",
|
||||
"@angular/common": "^17.3.3",
|
||||
"@angular/compiler": "^17.3.3",
|
||||
"@angular/core": "^17.3.3",
|
||||
"@angular/forms": "^17.3.3",
|
||||
"@angular/platform-browser": "^17.3.3",
|
||||
"@angular/platform-browser-dynamic": "^17.3.3",
|
||||
"@angular/router": "^17.3.3",
|
||||
"@asymmetrik/ngx-leaflet": "3.0.1",
|
||||
"@microsoft/signalr": "^6.0.8",
|
||||
"@nebular/auth": "13.0.0",
|
||||
"@nebular/date-fns": "^13.0.0",
|
||||
"@nebular/eva-icons": "13.0.0",
|
||||
"@nebular/security": "13.0.0",
|
||||
"@nebular/theme": "13.0.0",
|
||||
"@progress/kendo-angular-buttons": "^20.1.1",
|
||||
"@progress/kendo-angular-dialog": "^20.1.1",
|
||||
"@progress/kendo-angular-dropdowns": "^20.1.1",
|
||||
"@progress/kendo-angular-editor": "^20.1.1",
|
||||
"@progress/kendo-angular-grid": "^20.1.1",
|
||||
"@progress/kendo-angular-inputs": "^20.1.1",
|
||||
"@progress/kendo-angular-toolbar": "^20.1.1",
|
||||
"@progress/kendo-licensing": "^1.7.1",
|
||||
"@progress/kendo-svg-icons": "^4.5.0",
|
||||
"@progress/kendo-theme-default": "^12.2.0",
|
||||
"@tinymce/tinymce-angular": "^7.0.0",
|
||||
"angular2-chartjs": "0.4.1",
|
||||
"angular2-qrcode": "^2.0.3",
|
||||
"bootstrap": "4.3.1",
|
||||
"chart.js": "2.7.1",
|
||||
"core-js": "2.5.1",
|
||||
"echarts": "^4.9.0",
|
||||
"eva-icons": "^1.1.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"intl": "1.2.5",
|
||||
"ionicons": "2.0.1",
|
||||
"leaflet": "1.2.0",
|
||||
"nebular-icons": "1.1.0",
|
||||
"ng-in-viewport": "^13.0.1",
|
||||
"ng2-completer": "^9.0.1",
|
||||
"ngx-echarts": "^4.2.2",
|
||||
"ngx-infinite-scroll": "^17.0.0",
|
||||
"ngx-mask": "^12.0.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"normalize.css": "6.0.0",
|
||||
"pace-js": "1.0.2",
|
||||
"roboto-fontface": "0.8.0",
|
||||
"rxjs": "6.6.2",
|
||||
"rxjs-compat": "6.3.0",
|
||||
"socicon": "3.0.5",
|
||||
"style-loader": "^1.3.0",
|
||||
"tinymce": "^7.0.0",
|
||||
"tslib": "^2.3.1",
|
||||
"typeface-exo": "0.0.22",
|
||||
"typeit": "^8.7.0",
|
||||
"zone.js": "~0.14.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.3.3",
|
||||
"@angular/cli": "^17.3.3",
|
||||
"@angular/compiler-cli": "^17.3.3",
|
||||
"@angular/language-service": "17.3.3",
|
||||
"@angular/localize": "^17.3.3",
|
||||
"@compodoc/compodoc": "1.0.1",
|
||||
"@fortawesome/fontawesome-free": "^5.2.0",
|
||||
"@schematics/angular": "^14.1.3",
|
||||
"@types/d3-color": "1.0.5",
|
||||
"@types/jasmine": "~3.3.0",
|
||||
"@types/jasminewd2": "2.0.3",
|
||||
"@types/leaflet": "1.2.3",
|
||||
"@types/node": "^18.19.30",
|
||||
"codelyzer": "^6.0.2",
|
||||
"conventional-changelog-cli": "1.3.4",
|
||||
"husky": "0.13.3",
|
||||
"jasmine-core": "~5.1.2",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~6.3.19",
|
||||
"karma-chrome-launcher": "~3.1.1",
|
||||
"karma-cli": "1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "^2.1.0",
|
||||
"npm-run-all": "4.0.2",
|
||||
"protractor": "~7.0.0",
|
||||
"rimraf": "2.6.1",
|
||||
"stylelint": "7.13.0",
|
||||
"ts-node": "3.2.2",
|
||||
"typescript": "~5.4.4"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
LayoutService,
|
||||
PlayerService,
|
||||
SeoService,
|
||||
StateService,
|
||||
StateServiceForNB,
|
||||
} from './utils';
|
||||
import { UserData } from './data/users';
|
||||
import { ElectricityData } from './data/electricity';
|
||||
@@ -142,7 +142,7 @@ export const NB_CORE_PROVIDERS = [
|
||||
LayoutService,
|
||||
PlayerService,
|
||||
SeoService,
|
||||
StateService,
|
||||
StateServiceForNB,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { LayoutService } from './layout.service';
|
||||
import { AnalyticsService } from './analytics.service';
|
||||
import { PlayerService } from './player.service';
|
||||
import { StateService } from './state.service';
|
||||
import { StateServiceForNB } from './state.service';
|
||||
import { SeoService } from './seo.service';
|
||||
|
||||
export {
|
||||
@@ -9,5 +9,5 @@ export {
|
||||
AnalyticsService,
|
||||
PlayerService,
|
||||
SeoService,
|
||||
StateService,
|
||||
StateServiceForNB,
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import { of as observableOf, Observable, BehaviorSubject } from 'rxjs';
|
||||
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 {
|
||||
export class StateServiceForNB implements OnDestroy {
|
||||
|
||||
protected layouts: any = [
|
||||
{
|
||||
@@ -58,7 +58,7 @@ export class StateService implements OnDestroy {
|
||||
}
|
||||
|
||||
private updateSidebarIcons(direction: NbLayoutDirection) {
|
||||
const [ startSidebar, endSidebar ] = this.sidebars;
|
||||
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';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@import '../../styles/themes';
|
||||
@import '~@nebular/theme/styles/global/breakpoints';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import "../../styles/themes";
|
||||
@import "@nebular/theme/styles/global/breakpoints";
|
||||
@import "bootstrap/scss/mixins/breakpoints";
|
||||
|
||||
@include nb-install-component() {
|
||||
width: 100%;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
export const DEFAULT_MEDIA_BREAKPOINTS = [
|
||||
{
|
||||
name: 'xs',
|
||||
width: 0,
|
||||
},
|
||||
{
|
||||
name: 'is',
|
||||
width: 400,
|
||||
},
|
||||
{
|
||||
name: 'sm',
|
||||
width: 576,
|
||||
},
|
||||
{
|
||||
name: 'md',
|
||||
width: 768,
|
||||
},
|
||||
{
|
||||
name: 'lg',
|
||||
width: 992,
|
||||
},
|
||||
{
|
||||
name: 'xl',
|
||||
width: 1200,
|
||||
},
|
||||
{
|
||||
name: 'xxl',
|
||||
width: 1400,
|
||||
},
|
||||
{
|
||||
name: 'xxxl',
|
||||
width: 1600,
|
||||
},
|
||||
];
|
||||
@@ -1,6 +1,6 @@
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/breakpoints';
|
||||
@import '../../styles/themes';
|
||||
@import "bootstrap/scss/mixins/breakpoints";
|
||||
@import "@nebular/theme/styles/global/breakpoints";
|
||||
@import "../../styles/themes";
|
||||
|
||||
@include nb-install-component() {
|
||||
display: flex;
|
||||
@@ -24,7 +24,7 @@
|
||||
}
|
||||
|
||||
::ng-deep nb-search button {
|
||||
padding: 0!important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
|
||||
@@ -6,9 +6,10 @@ import { LayoutService } from '../../../@core/utils';
|
||||
import { map, takeUntil, first } from 'rxjs/operators';
|
||||
import { Subject } from 'rxjs';
|
||||
import { HeaderService } from '../../../services/header.service';
|
||||
import { NbAuthService } from '@nebular/auth';
|
||||
import { AuthService } from '../../../services/auth.service';
|
||||
import { UserProfileAction } from '../../../entity/Auth';
|
||||
import { Router } from '@angular/router';
|
||||
import { LoginUserService } from '../../../services/login-user.service';
|
||||
import { AuthService } from '../../../services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-header',
|
||||
@@ -19,6 +20,7 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
header: string = '';
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
userPictureOnly: boolean = false;
|
||||
isLessThanMd: boolean = false;
|
||||
|
||||
themes = [
|
||||
{
|
||||
@@ -52,7 +54,7 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
}];
|
||||
|
||||
public get user() {
|
||||
return this.authService.userAccess;
|
||||
return this.loginUserService.userAccess;
|
||||
}
|
||||
public get userFullName() {
|
||||
if (this.user) {
|
||||
@@ -68,24 +70,22 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
constructor(private sidebarService: NbSidebarService,
|
||||
constructor(
|
||||
private sidebarService: NbSidebarService,
|
||||
private menuService: NbMenuService,
|
||||
private themeService: NbThemeService,
|
||||
private userService: UserData,
|
||||
private layoutService: LayoutService,
|
||||
private breakpointService: NbMediaBreakpointsService,
|
||||
private headerService: HeaderService,
|
||||
private oAuthService: NbAuthService,
|
||||
private loginUserService: LoginUserService,
|
||||
private authService: AuthService,
|
||||
protected router: Router,
|
||||
) {
|
||||
|
||||
this.headerService.headerChange$.pipe(takeUntil(this.destroy$)).subscribe(result => {
|
||||
this.header = result;
|
||||
});
|
||||
this.menuService.onItemClick().pipe(takeUntil(this.destroy$))
|
||||
.subscribe(result => {
|
||||
if (result.item.title == 'Log out') this.logout();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -93,13 +93,18 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
ngOnInit() {
|
||||
this.currentTheme = this.themeService.currentTheme;
|
||||
|
||||
const { xl } = this.breakpointService.getBreakpointsMap();
|
||||
const { md, xl } = this.breakpointService.getBreakpointsMap();
|
||||
this.themeService.onMediaQueryChange()
|
||||
.pipe(
|
||||
map(([, currentBreakpoint]) => currentBreakpoint.width < xl),
|
||||
map(([, currentBreakpoint]) => currentBreakpoint.width),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe((isLessThanXl: boolean) => this.userPictureOnly = isLessThanXl);
|
||||
.subscribe((screenWidth: number) => {
|
||||
let isLessThanXl = screenWidth < xl;
|
||||
this.isLessThanMd = screenWidth < md;
|
||||
this.userPictureOnly = isLessThanXl
|
||||
|
||||
});
|
||||
|
||||
this.themeService.onThemeChange()
|
||||
.pipe(
|
||||
@@ -108,6 +113,22 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
)
|
||||
.subscribe(themeName => this.currentTheme = themeName);
|
||||
|
||||
this.menuService.onItemClick().subscribe(result => {
|
||||
if (this.isLessThanMd && result.tag == 'NavMenu' && result.item.link) {
|
||||
this.toggleSidebar();
|
||||
} else if (result.tag == 'UserProfileMenu') {
|
||||
switch (result.item.data as UserProfileAction) {
|
||||
case UserProfileAction.None: break;
|
||||
case UserProfileAction.GoToProfile:
|
||||
this.router.navigate(["/myapp/profile"]);
|
||||
break;
|
||||
case UserProfileAction.LogOut:
|
||||
this.logout();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@import '../../styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/breakpoints';
|
||||
@import "../../styles/themes";
|
||||
@import "bootstrap/scss/mixins/breakpoints";
|
||||
@import "@nebular/theme/styles/global/breakpoints";
|
||||
|
||||
@include nb-install-component() {
|
||||
.menu-sidebar ::ng-deep .scrollable {
|
||||
|
||||
@@ -23,4 +23,4 @@ import { Component } from '@angular/core';
|
||||
</nb-layout>
|
||||
`,
|
||||
})
|
||||
export class OneColumnLayoutComponent {}
|
||||
export class OneColumnLayoutComponent { }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@import '../../styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/breakpoints';
|
||||
@import "../../styles/themes";
|
||||
@import "bootstrap/scss/mixins/breakpoints";
|
||||
@import "@nebular/theme/styles/global/breakpoints";
|
||||
|
||||
@include nb-install-component() {
|
||||
.menu-sidebar ::ng-deep .scrollable {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@import '../../styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/breakpoints';
|
||||
@import "../../styles/themes";
|
||||
@import "bootstrap/scss/mixins/breakpoints";
|
||||
@import "@nebular/theme/styles/global/breakpoints";
|
||||
|
||||
@include nb-install-component() {
|
||||
.menu-sidebar ::ng-deep .scrollable {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@import '../../styles/themes';
|
||||
@import '~bootstrap/scss/mixins/breakpoints';
|
||||
@import '~@nebular/theme/styles/global/breakpoints';
|
||||
@import "../../styles/themes";
|
||||
@import "bootstrap/scss/mixins/breakpoints";
|
||||
@import "@nebular/theme/styles/global/breakpoints";
|
||||
|
||||
@include nb-install-component() {
|
||||
.menu-sidebar ::ng-deep .scrollable {
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,600,700&display=swap');
|
||||
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,600,700&display=swap");
|
||||
|
||||
// themes - our custom or/and out of the box themes
|
||||
@import 'themes';
|
||||
@import "themes";
|
||||
|
||||
// framework component themes (styles tied to theme variables)
|
||||
@import '~@nebular/theme/styles/globals';
|
||||
@import '~@nebular/auth/styles/globals';
|
||||
@import "@nebular/theme/styles/globals";
|
||||
@import "@nebular/auth/styles/globals";
|
||||
|
||||
@import '~bootstrap/scss/functions';
|
||||
@import '~bootstrap/scss/variables';
|
||||
@import '~bootstrap/scss/mixins';
|
||||
@import '~bootstrap/scss/grid';
|
||||
@import "bootstrap/scss/functions";
|
||||
@import "bootstrap/scss/variables";
|
||||
@import "bootstrap/scss/mixins";
|
||||
@import "bootstrap/scss/grid";
|
||||
|
||||
// loading progress bar theme
|
||||
@import './pace.theme';
|
||||
@import "./pace.theme";
|
||||
|
||||
@import './layout';
|
||||
@import './overrides';
|
||||
@import "./layout";
|
||||
@import "./overrides";
|
||||
|
||||
// install the framework and custom global styles
|
||||
@include nb-install() {
|
||||
|
||||
// framework global styles
|
||||
@include nb-theme-global();
|
||||
@include nb-auth-global();
|
||||
@@ -30,4 +29,4 @@
|
||||
@include ngx-pace-theme();
|
||||
|
||||
@include nb-overrides();
|
||||
};
|
||||
} ;
|
||||
|
||||
@@ -1,88 +1,104 @@
|
||||
// @nebular theming framework
|
||||
@import '~@nebular/theme/styles/theming';
|
||||
@import "@nebular/theme/styles/theming";
|
||||
// @nebular out of the box themes
|
||||
@import '~@nebular/theme/styles/themes';
|
||||
@import "@nebular/theme/styles/themes";
|
||||
|
||||
$nb-themes: nb-register-theme((
|
||||
layout-padding-top: 2.25rem,
|
||||
$nb-themes: nb-register-theme(
|
||||
(
|
||||
layout-padding-top: 2.25rem,
|
||||
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
|
||||
card-height-tiny: 13.5rem,
|
||||
card-height-small: 21.1875rem,
|
||||
card-height-medium: 28.875rem,
|
||||
card-height-large: 36.5625rem,
|
||||
card-height-giant: 44.25rem,
|
||||
card-margin-bottom: 1.875rem,
|
||||
card-header-with-select-padding-top: 0.5rem,
|
||||
card-header-with-select-padding-bottom: 0.5rem,
|
||||
card-height-tiny: 13.5rem,
|
||||
card-height-small: 21.1875rem,
|
||||
card-height-medium: 28.875rem,
|
||||
card-height-large: 36.5625rem,
|
||||
card-height-giant: 44.25rem,
|
||||
card-margin-bottom: 1.875rem,
|
||||
card-header-with-select-padding-top: 0.5rem,
|
||||
card-header-with-select-padding-bottom: 0.5rem,
|
||||
|
||||
select-min-width: 6rem,
|
||||
select-min-width: 6rem,
|
||||
|
||||
slide-out-background: #f7f9fc,
|
||||
slide-out-shadow-color: 0 4px 14px 0 #8f9bb3,
|
||||
slide-out-shadow-color-rtl: 0 4px 14px 0 #8f9bb3,
|
||||
), default, default);
|
||||
slide-out-background: #f7f9fc,
|
||||
slide-out-shadow-color: 0 4px 14px 0 #8f9bb3,
|
||||
slide-out-shadow-color-rtl: 0 4px 14px 0 #8f9bb3,
|
||||
),
|
||||
default,
|
||||
default
|
||||
);
|
||||
|
||||
$nb-themes: nb-register-theme((
|
||||
layout-padding-top: 2.25rem,
|
||||
$nb-themes: nb-register-theme(
|
||||
(
|
||||
layout-padding-top: 2.25rem,
|
||||
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
|
||||
card-height-tiny: 13.5rem,
|
||||
card-height-small: 21.1875rem,
|
||||
card-height-medium: 28.875rem,
|
||||
card-height-large: 36.5625rem,
|
||||
card-height-giant: 44.25rem,
|
||||
card-margin-bottom: 1.875rem,
|
||||
card-header-with-select-padding-top: 0.5rem,
|
||||
card-header-with-select-padding-bottom: 0.5rem,
|
||||
card-height-tiny: 13.5rem,
|
||||
card-height-small: 21.1875rem,
|
||||
card-height-medium: 28.875rem,
|
||||
card-height-large: 36.5625rem,
|
||||
card-height-giant: 44.25rem,
|
||||
card-margin-bottom: 1.875rem,
|
||||
card-header-with-select-padding-top: 0.5rem,
|
||||
card-header-with-select-padding-bottom: 0.5rem,
|
||||
|
||||
select-min-width: 6rem,
|
||||
select-min-width: 6rem,
|
||||
|
||||
slide-out-background: #252547,
|
||||
slide-out-shadow-color: 2px 0 3px #29157a,
|
||||
slide-out-shadow-color-rtl: -2px 0 3px #29157a,
|
||||
), cosmic, cosmic);
|
||||
slide-out-background: #252547,
|
||||
slide-out-shadow-color: 2px 0 3px #29157a,
|
||||
slide-out-shadow-color-rtl: -2px 0 3px #29157a,
|
||||
),
|
||||
cosmic,
|
||||
cosmic
|
||||
);
|
||||
|
||||
$nb-themes: nb-register-theme((
|
||||
layout-padding-top: 2.25rem,
|
||||
$nb-themes: nb-register-theme(
|
||||
(
|
||||
layout-padding-top: 2.25rem,
|
||||
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
|
||||
card-height-tiny: 13.5rem,
|
||||
card-height-small: 21.1875rem,
|
||||
card-height-medium: 28.875rem,
|
||||
card-height-large: 36.5625rem,
|
||||
card-height-giant: 44.25rem,
|
||||
card-margin-bottom: 1.875rem,
|
||||
card-header-with-select-padding-top: 0.5rem,
|
||||
card-header-with-select-padding-bottom: 0.5rem,
|
||||
card-height-tiny: 13.5rem,
|
||||
card-height-small: 21.1875rem,
|
||||
card-height-medium: 28.875rem,
|
||||
card-height-large: 36.5625rem,
|
||||
card-height-giant: 44.25rem,
|
||||
card-margin-bottom: 1.875rem,
|
||||
card-header-with-select-padding-top: 0.5rem,
|
||||
card-header-with-select-padding-bottom: 0.5rem,
|
||||
|
||||
select-min-width: 6rem,
|
||||
select-min-width: 6rem,
|
||||
|
||||
slide-out-background: linear-gradient(270deg, #edf1f7 0%, #e4e9f2 100%),
|
||||
slide-out-shadow-color: 0 4px 14px 0 #8f9bb3,
|
||||
slide-out-shadow-color-rtl: 0 4px 14px 0 #8f9bb3,
|
||||
), corporate, corporate);
|
||||
slide-out-background: linear-gradient(270deg, #edf1f7 0%, #e4e9f2 100%),
|
||||
slide-out-shadow-color: 0 4px 14px 0 #8f9bb3,
|
||||
slide-out-shadow-color-rtl: 0 4px 14px 0 #8f9bb3,
|
||||
),
|
||||
corporate,
|
||||
corporate
|
||||
);
|
||||
|
||||
$nb-themes: nb-register-theme((
|
||||
layout-padding-top: 2.25rem,
|
||||
$nb-themes: nb-register-theme(
|
||||
(
|
||||
layout-padding-top: 2.25rem,
|
||||
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
|
||||
card-height-tiny: 13.5rem,
|
||||
card-height-small: 21.1875rem,
|
||||
card-height-medium: 28.875rem,
|
||||
card-height-large: 36.5625rem,
|
||||
card-height-giant: 44.25rem,
|
||||
card-margin-bottom: 1.875rem,
|
||||
card-header-with-select-padding-top: 0.5rem,
|
||||
card-header-with-select-padding-bottom: 0.5rem,
|
||||
card-height-tiny: 13.5rem,
|
||||
card-height-small: 21.1875rem,
|
||||
card-height-medium: 28.875rem,
|
||||
card-height-large: 36.5625rem,
|
||||
card-height-giant: 44.25rem,
|
||||
card-margin-bottom: 1.875rem,
|
||||
card-header-with-select-padding-top: 0.5rem,
|
||||
card-header-with-select-padding-bottom: 0.5rem,
|
||||
|
||||
select-min-width: 6rem,
|
||||
select-min-width: 6rem,
|
||||
|
||||
slide-out-background: linear-gradient(270deg, #222b45 0%, #151a30 100%),
|
||||
slide-out-shadow-color: 0 4px 14px 0 #8f9bb3,
|
||||
slide-out-shadow-color-rtl: 0 4px 14px 0 #8f9bb3,
|
||||
), dark, dark);
|
||||
slide-out-background: linear-gradient(270deg, #222b45 0%, #151a30 100%),
|
||||
slide-out-shadow-color: 0 4px 14px 0 #8f9bb3,
|
||||
slide-out-shadow-color-rtl: 0 4px 14px 0 #8f9bb3,
|
||||
),
|
||||
dark,
|
||||
dark
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<ngx-one-column-layout>
|
||||
<nb-menu [items]="MENU_ITEMS"></nb-menu>
|
||||
<nb-menu [items]="MENU_ITEMS" tag="NavMenu"></nb-menu>
|
||||
<router-outlet></router-outlet>
|
||||
</ngx-one-column-layout>
|
||||
@@ -6,16 +6,12 @@ import { AdminComponent } from './admin.component';
|
||||
import { NbMenuModule, NbInputModule, NbCardModule, NbButtonModule, NbActionsModule, NbCheckboxModule, NbRadioModule, NbDatepickerModule, NbSelectModule, NbIconModule, NbTagModule, NbStepperModule, NbListModule, NbSpinnerModule, NbDialogModule, NbUserModule } from '@nebular/theme';
|
||||
import { ThemeModule } from '../@theme/theme.module';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BestListDlgComponent } from './happiness-groups/best-list-dlg/best-list-dlg.component';
|
||||
import { AlertDlgModule } from '../ui/alert-dlg/alert-dlg.module';
|
||||
import { HappinessWeekEditorComponent } from './happiness-groups/happiness-week-editor/happiness-week-editor.component';
|
||||
import { HappinessWeekListDlgComponent } from './happiness-groups/happiness-week-list-dlg/happiness-week-list-dlg.component';
|
||||
import { CellGroupRoutineEventsComponent } from './cell-group-routine-events/cell-group-routine-events.component';
|
||||
import { FancyTableModule } from '../ui/fancy-table/fancy-table.module';
|
||||
import { FamilyMembersComponent } from './family-members/family-members.component';
|
||||
import { FamilyMemberEditorComponent } from './family-members/family-member-editor/family-member-editor.component';
|
||||
import { DropDownListModule } from '../ui/drop-down-list/drop-down-list.module';
|
||||
import { NgxMaskModule } from 'ngx-mask';
|
||||
import { PastoralDomainsComponent } from './pastoral-domains/pastoral-domains.component';
|
||||
import { PastoralDomainEditorComponent } from './pastoral-domains/pastoral-domain-editor/pastoral-domain-editor.component';
|
||||
import { AssignMemberCellGroupComponent } from './family-members/assign-member-cell-group/assign-member-cell-group.component';
|
||||
@@ -26,14 +22,9 @@ import { MaskDirectiveModule } from '../directives/mask-directive/mask-directive
|
||||
import { DateInputModule } from '../ui/date-input/date-input.module';
|
||||
import { LineMessagingAccountComponent } from './lines/line-messaging-account/line-messaging-account.component';
|
||||
import { LineMessagingAccountEditorComponent } from './lines/line-messaging-account-editor/line-messaging-account-editor.component';
|
||||
import { WeekTaskEditorComponent } from './happiness-groups/week-task-editor/week-task-editor.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AdminComponent,
|
||||
BestListDlgComponent,
|
||||
HappinessWeekEditorComponent,
|
||||
HappinessWeekListDlgComponent,
|
||||
CellGroupRoutineEventsComponent,
|
||||
FamilyMembersComponent,
|
||||
FamilyMemberEditorComponent,
|
||||
@@ -44,7 +35,7 @@ import { WeekTaskEditorComponent } from './happiness-groups/week-task-editor/wee
|
||||
LogDetailComponent,
|
||||
LineMessagingAccountComponent,
|
||||
LineMessagingAccountEditorComponent,
|
||||
WeekTaskEditorComponent],
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
@@ -69,7 +60,6 @@ import { WeekTaskEditorComponent } from './happiness-groups/week-task-editor/wee
|
||||
AlertDlgModule,
|
||||
FancyTableModule,
|
||||
DropDownListModule,
|
||||
NgxMaskModule,
|
||||
CurrencyInputModule,
|
||||
MaskDirectiveModule,
|
||||
DateInputModule
|
||||
|
||||
@@ -3,14 +3,12 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { inherits } from 'util';
|
||||
import { StateService } from '../../@core/utils';
|
||||
import { CellGroupRoutineEvents } from '../../entity/CellGroupRoutineEvents';
|
||||
import { CellGroupRoutineEventsService } from '../../services/crudServices/cell-group-routine-events.service';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { FancySettings } from '../../ui/fancy-table/fancy-settings.model';
|
||||
import { FancyTableComponent } from '../../ui/fancy-table/fancy-table.component';
|
||||
import { ObjectUtils } from '../../utilities/object-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-cell-group-routine-events',
|
||||
|
||||
+1
-2
@@ -2,11 +2,10 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { NbDialogRef } from '@nebular/theme';
|
||||
import { DropDownOption } from '../../../entity/dropDownOption';
|
||||
import { DomainMemberRelationship, PastoralDomain } from '../../../entity/PastoralDomain';
|
||||
import { DialogComponent } from '../../../pages/modal-overlays/dialog/dialog.component';
|
||||
import { DomainMemberShipService } from '../../../services/crudServices/pastoral-domain.service';
|
||||
import { ArrayUtils } from '../../../utilities/array-utils';
|
||||
import { first } from "rxjs/operators"
|
||||
import { FamilyMember } from '../../../entity/Member';
|
||||
import { DomainMemberShipService } from '../../../services/crudServices/domain-member-ship.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-assign-member-cell-group',
|
||||
|
||||
@@ -3,12 +3,12 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { StateService } from '../../@core/utils';
|
||||
import { FamilyMember, Gender } from '../../entity/Member';
|
||||
import { PastoralDomain } from '../../entity/PastoralDomain';
|
||||
import { FamilyMemberService } from '../../services/crudServices/family-member.service';
|
||||
import { PastoralDomainService } from '../../services/crudServices/pastoral-domain.service';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { FancySettings } from '../../ui/fancy-table/fancy-settings.model';
|
||||
import { FancyTableComponent } from '../../ui/fancy-table/fancy-table.component';
|
||||
import { ObjectUtils } from '../../utilities/object-utils';
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
nb-card {
|
||||
max-height: 90vh;
|
||||
width: 700px;
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NbDialogRef, NbToastrService } from '@nebular/theme';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { HappinessBEST } from '../../../entity/HappinessGroup';
|
||||
import { PastoralDomain } from '../../../entity/PastoralDomain';
|
||||
import { BestService } from '../../../services/crudServices/best.service';
|
||||
import { HappinessGroupService } from '../../../services/crudServices/happiness-group.service';
|
||||
import { LineGroup, LineService } from '../../../services/line.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { ADButtons, AlertDlgComponent } from '../../../ui/alert-dlg/alert-dlg.component';
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-best-list-dlg',
|
||||
templateUrl: './best-list-dlg.component.html',
|
||||
styleUrls: ['./best-list-dlg.component.scss']
|
||||
})
|
||||
export class BestListDlgComponent implements OnInit {
|
||||
|
||||
processing: boolean = false;
|
||||
isAdding: boolean = false;
|
||||
group: PastoralDomain;
|
||||
datum: HappinessBEST;
|
||||
constructor(
|
||||
private dlgRef: NbDialogRef<BestListDlgComponent>,
|
||||
private msgBpxService: MsgBoxService,
|
||||
private happinessGroupService: HappinessGroupService,
|
||||
private toastrService: NbToastrService,
|
||||
private bestService: BestService,
|
||||
private lineService: LineService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.group.bests = this.group.bests.sort((a, b) => 0 - (a.name < b.name ? 1 : -1));
|
||||
}
|
||||
|
||||
close() {
|
||||
this.dlgRef.close();
|
||||
}
|
||||
update() {
|
||||
this.dlgRef.close(this.datum);
|
||||
}
|
||||
addBest() {
|
||||
this.msgBpxService.showInputbox('Add Best', 'Please input Best\'s Name').pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
let obj = { groupId: this.group.id, id: '', name: result } as HappinessBEST;
|
||||
this.bestService.createOrUpdate(obj).pipe(first()).subscribe(result => {
|
||||
this.dlgRef.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
copyBestUrl(best: HappinessBEST) {
|
||||
|
||||
var dummy = document.createElement("textarea");
|
||||
document.body.appendChild(dummy);
|
||||
dummy.value = `${environment.invitationUrl}${best.id}`;
|
||||
dummy.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(dummy);
|
||||
this.toastrService.success(dummy.value, "複製地址至剪貼簿!");
|
||||
}
|
||||
copyBestList() {
|
||||
let list = "".concat(...this.group.bests.map(b => `${b.name}\t\t: ${environment.invitationUrl}${b.id}\r\n`));
|
||||
var dummy = document.createElement("textarea");
|
||||
document.body.appendChild(dummy);
|
||||
dummy.value = list;
|
||||
dummy.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(dummy);
|
||||
this.toastrService.success(dummy.value, "複製地址至剪貼簿!");
|
||||
|
||||
this.lineService.pushLineMessage(LineGroup.Chris, list);
|
||||
|
||||
}
|
||||
downloadBestQRCode(best: HappinessBEST) {
|
||||
|
||||
this.happinessGroupService.getBestQrCode(best.name + ".png", best.id);
|
||||
}
|
||||
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
nb-card {
|
||||
max-height: 90vh;
|
||||
width: 700px;
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NbDialogRef, NbDialogService } from '@nebular/theme';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { HappinessWeek } from '../../../entity/HappinessGroup';
|
||||
import { PastoralDomain } from '../../../entity/PastoralDomain';
|
||||
import { NumberUtils } from '../../../utilities/number-utils';
|
||||
import { HappinessWeekEditorComponent } from '../happiness-week-editor/happiness-week-editor.component';
|
||||
import { WeekTaskEditorComponent } from '../week-task-editor/week-task-editor.component';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-happiness-week-list-dlg',
|
||||
templateUrl: './happiness-week-list-dlg.component.html',
|
||||
styleUrls: ['./happiness-week-list-dlg.component.scss']
|
||||
})
|
||||
export class HappinessWeekListDlgComponent implements OnInit {
|
||||
|
||||
group: PastoralDomain;
|
||||
constructor(
|
||||
private dlgRef: NbDialogRef<any>,
|
||||
private dlgService: NbDialogService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
close() {
|
||||
this.dlgRef.close();
|
||||
}
|
||||
openEditDlg(week: HappinessWeek) {
|
||||
this.dlgService.open(HappinessWeekEditorComponent, {
|
||||
context: {
|
||||
data: week
|
||||
}
|
||||
}).onClose.pipe(first()).subscribe(result => {
|
||||
if (result) this.close();
|
||||
});
|
||||
}
|
||||
openTaskDlg(week: HappinessWeek) {
|
||||
this.dlgService.open(WeekTaskEditorComponent, {
|
||||
context: {
|
||||
data: week,
|
||||
allData: this.group.happinessWeeks
|
||||
}
|
||||
}).onClose.pipe(first()).subscribe(result => {
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
weekDisplay(seq: number) {
|
||||
return `W${seq} ${this.group.happinessWeeks[seq - 1].topic}`;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<form #form="ngForm">
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
第 {{data.seq}} 周 {{data.topic}} 分工
|
||||
</nb-card-header>
|
||||
<nb-card-body *ngIf="data">
|
||||
<div class="row" *ngFor="let task of data.tasks;let i = index">
|
||||
<div class="col-md-4">
|
||||
<div class='form-group'>
|
||||
<label for='tasker{{i}}' class='label'>{{getTaskTitle(task.type)}}</label>
|
||||
<op-drop-down name="tasker{{i}}" [editable]="true" [(ngModel)]="task.tasker"
|
||||
[source]="taskrOptions">
|
||||
</op-drop-down>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="form-group">
|
||||
<label for="content{{i}}" class="label">內容</label>
|
||||
<input type="text" name="content{{i}}" nbInput fullWidth id="content{{i}}"
|
||||
[(ngModel)]="task.content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</nb-card-body>
|
||||
<nb-card-footer>
|
||||
<button class="float-right" nbButton hero status="danger" size="small" (click)="close()"
|
||||
[nbSpinner]="processing">
|
||||
Close
|
||||
</button>
|
||||
<button class="float-right mr-2" nbButton hero status="primary" size="small"
|
||||
(click)="!form.invalid&&!processing&&update()" [disabled]="form.invalid" [nbSpinner]="processing">
|
||||
Save
|
||||
</button>
|
||||
</nb-card-footer>
|
||||
</nb-card>
|
||||
</form>
|
||||
@@ -1,79 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NbDialogRef } from '@nebular/theme';
|
||||
import { HappinessTask, HappinessTaskType, HappinessWeek } from '../../../entity/HappinessGroup';
|
||||
import { HappinessTaskService } from '../../../services/crudServices/happiness-group.service';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { DropDownOption } from '../../../entity/dropDownOption';
|
||||
import { StringUtils } from '../../../utilities/string-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-week-task-editor',
|
||||
templateUrl: './week-task-editor.component.html',
|
||||
styleUrls: ['./week-task-editor.component.scss']
|
||||
})
|
||||
export class WeekTaskEditorComponent implements OnInit {
|
||||
processing: boolean = false;
|
||||
allData: HappinessWeek[];
|
||||
data: HappinessWeek;
|
||||
taskrOptions: DropDownOption[] = [];
|
||||
constructor(
|
||||
private happinessTaskService: HappinessTaskService,
|
||||
private dlgRef: NbDialogRef<WeekTaskEditorComponent>
|
||||
) {
|
||||
|
||||
}
|
||||
ngOnInit(): void {
|
||||
if (this.data.tasks.length == 0) {
|
||||
this.data.tasks = [];
|
||||
for (let i = HappinessTaskType.IceBreak; i <= HappinessTaskType.Dessert; i++) {
|
||||
this.data.tasks.push(
|
||||
{
|
||||
weekId: this.data.id,
|
||||
id: 'new',
|
||||
type: i as HappinessTaskType,
|
||||
tasker: '',
|
||||
content: ''
|
||||
} as HappinessTask
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
let names = [] as string[];
|
||||
this.allData.forEach(week => {
|
||||
if (week.tasks) {
|
||||
names = names.concat(week.tasks.map(t => t.tasker.trim()));
|
||||
|
||||
}
|
||||
});
|
||||
this.taskrOptions = names.filter(function (item, pos) {
|
||||
return !StringUtils.isNullOrWhitespace(item) && names.indexOf(item) == pos;
|
||||
}).map(name => new DropDownOption(name, name));
|
||||
}
|
||||
|
||||
close() {
|
||||
this.dlgRef.close();
|
||||
}
|
||||
update() {
|
||||
if (this.processing == false) {
|
||||
|
||||
this.processing = true;
|
||||
|
||||
this.happinessTaskService.createOrUpdateAll(this.data.tasks).pipe(first()).subscribe(result => {
|
||||
this.processing = false;
|
||||
this.dlgRef.close(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getTaskTitle(t: HappinessTaskType) {
|
||||
switch (t) {
|
||||
case HappinessTaskType.IceBreak: return '帶遊戲';
|
||||
case HappinessTaskType.Worship: return '唱歌';
|
||||
case HappinessTaskType.Testimony: return '見證';
|
||||
case HappinessTaskType.Message: return '信息';
|
||||
case HappinessTaskType.Gift: return '準備禮物';
|
||||
case HappinessTaskType.Dessert: return '準備點心';
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -33,7 +33,7 @@ export class LineMessagingAccountEditorComponent implements OnInit {
|
||||
|
||||
this.processing = true;
|
||||
|
||||
let func = this.isAdding ? this.lineMessagingAccountService.createOrUpdate(this.data) : this.lineMessagingAccountService.update(this.data);
|
||||
let func = this.lineMessagingAccountService.createOrUpdate(this.data);//this.isAdding ? this.lineMessagingAccountService.createOrUpdate(this.data) : this.lineMessagingAccountService.update(this.data);
|
||||
func.pipe(first()).subscribe(result => {
|
||||
this.processing = false;
|
||||
if (result) {
|
||||
|
||||
@@ -3,11 +3,11 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { StateService } from '../../@core/utils';
|
||||
import { Log, LogLevel } from '../../entity/Log';
|
||||
import { ScreenBase } from '../../ScreenBase';
|
||||
import { LogService } from '../../services/crudServices/log.service';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { FancyTag } from '../../ui/fancy-table/fancy-row-column.model';
|
||||
import { FancySettings } from '../../ui/fancy-table/fancy-settings.model';
|
||||
import { FancyTableComponent } from '../../ui/fancy-table/fancy-table.component';
|
||||
|
||||
@@ -3,17 +3,18 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { StateService } from '../../@core/utils';
|
||||
import { FamilyMember } from '../../entity/Member';
|
||||
import { DomainType, PastoralDomain } from '../../entity/PastoralDomain';
|
||||
import { BestListDlgComponent } from '../../happiness/best-list-dlg/best-list-dlg.component';
|
||||
import { HappinessWeekListDlgComponent } from '../../happiness/happiness-week-list-dlg/happiness-week-list-dlg.component';
|
||||
import { DomainMemberShipService } from '../../services/crudServices/domain-member-ship.service';
|
||||
import { FamilyMemberService } from '../../services/crudServices/family-member.service';
|
||||
import { PastoralDomainService } from '../../services/crudServices/pastoral-domain.service';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { FancySettings } from '../../ui/fancy-table/fancy-settings.model';
|
||||
import { FancyTableComponent } from '../../ui/fancy-table/fancy-table.component';
|
||||
import { ObjectUtils } from '../../utilities/object-utils';
|
||||
import { BestListDlgComponent } from '../happiness-groups/best-list-dlg/best-list-dlg.component';
|
||||
import { HappinessWeekListDlgComponent } from '../happiness-groups/happiness-week-list-dlg/happiness-week-list-dlg.component';
|
||||
import { PastoralDomainEditorComponent } from './pastoral-domain-editor/pastoral-domain-editor.component';
|
||||
|
||||
@Component({
|
||||
@@ -35,7 +36,8 @@ export class PastoralDomainsComponent implements OnInit {
|
||||
private dlgService: NbDialogService,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
private memberService: FamilyMemberService
|
||||
private memberService: FamilyMemberService,
|
||||
private memberShipService: DomainMemberShipService
|
||||
|
||||
) {
|
||||
}
|
||||
@@ -87,6 +89,15 @@ export class PastoralDomainsComponent implements OnInit {
|
||||
callback: (datum, element) => {
|
||||
this.openHappinessWeekListDlg(datum);
|
||||
},
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
id: 'members',
|
||||
title: 'Members',
|
||||
icon: 'people-outline',
|
||||
callback: (datum, element) => {
|
||||
this.openMemberAssignDlg(datum);
|
||||
},
|
||||
}
|
||||
],
|
||||
onContextMenuOpening: (datum, menuItems) => {
|
||||
@@ -94,9 +105,18 @@ export class PastoralDomainsComponent implements OnInit {
|
||||
menuItems.find(i => i.id == 'edit').visible = !!datum;
|
||||
menuItems.find(i => i.id == 'happinessBest').visible = datum.type == DomainType.HappinessGroup;
|
||||
menuItems.find(i => i.id == 'happinessWeek').visible = datum.type == DomainType.HappinessGroup;
|
||||
//menuItems.find(i => i.id == 'members').visible = datum.type == DomainType.HappinessGroup;
|
||||
return datum;
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'type',
|
||||
title: 'Type',
|
||||
type: 'number',
|
||||
group: true,
|
||||
visible: false,
|
||||
groupValuePrepareFunction: (v) => this.getDomainTypeName(v.type)
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
title: 'Name',
|
||||
@@ -108,7 +128,7 @@ export class PastoralDomainsComponent implements OnInit {
|
||||
name: 'description',
|
||||
title: 'Description',
|
||||
type: 'text',
|
||||
widthPx: 200,
|
||||
widthPct: 70,
|
||||
//valuePrepareFunction: (item) => ,
|
||||
},
|
||||
{
|
||||
@@ -136,7 +156,6 @@ export class PastoralDomainsComponent implements OnInit {
|
||||
// },
|
||||
|
||||
],
|
||||
|
||||
};
|
||||
//#endregion
|
||||
|
||||
@@ -222,5 +241,33 @@ export class PastoralDomainsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
private getDomainTypeName(type: DomainType): string {
|
||||
switch (type) {
|
||||
case DomainType.CellGroup: return "細胞小組";
|
||||
case DomainType.HappinessGroup: return "幸福小組";
|
||||
case DomainType.CellGroupCoworker: return "細胞同工";
|
||||
case DomainType.ChurchCoworker: return "教會事工";
|
||||
case DomainType.Administrator: return "管理員";
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
private openMemberAssignDlg(datum: PastoralDomain) {
|
||||
|
||||
// this.msgBoxService.showListBox<FamilyMember>(
|
||||
// `Members in ${datum.name}`,
|
||||
// this.members,
|
||||
// 'id',
|
||||
// datum.familyMembers.map(m => m.id),
|
||||
// (e) => `${e.firstName} ${e.lastName} (${e.email})`
|
||||
// ).pipe(first()).subscribe(result => {
|
||||
// if (result) {
|
||||
|
||||
// datum.familyMembers = result;
|
||||
// this.memberShipService.updateMembersInGroup(datum).pipe(first()).subscribe(result => {
|
||||
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,6 @@ export const routes: Routes = [
|
||||
loadChildren: () => import('./invitation/invitation.module')
|
||||
.then(m => m.InvitationModule),
|
||||
},
|
||||
{
|
||||
path: 'pages',
|
||||
loadChildren: () => import('./pages/pages.module')
|
||||
.then(m => m.PagesModule),
|
||||
},
|
||||
{
|
||||
path: 'auth',
|
||||
component: NbAuthComponent,
|
||||
@@ -73,11 +68,11 @@ export const routes: Routes = [
|
||||
path: 'games', loadChildren: () => import('./games/games.module').then(m => m.GamesModule),
|
||||
canActivateChild: [AuthGuardService],
|
||||
data: {
|
||||
roles: Role.Admin | Role.Pastor
|
||||
roles: Role.All
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'CellGroup', loadChildren: () => import('./cell-group/cell-group.module').then(m => m.CellGroupModule),
|
||||
path: 'myapp', loadChildren: () => import('./cell-group/cell-group.module').then(m => m.CellGroupModule),
|
||||
canActivateChild: [AuthGuardService],
|
||||
data: {
|
||||
roles: Role.All
|
||||
|
||||
@@ -70,7 +70,10 @@ const socialLinks = [
|
||||
NbChatModule.forRoot({
|
||||
messageGoogleMapKey: 'AIzaSyA_wNuCzia92MAmdLRemailRGvCF7wCZPY',
|
||||
}),
|
||||
NgxMaskModule.forRoot(maskConfig),
|
||||
NbDialogModule.forRoot({
|
||||
closeOnBackdropClick: false,
|
||||
closeOnEsc: false
|
||||
}),
|
||||
NbDateFnsDateModule.forRoot({ format: 'MM/dd/yyyy' }),
|
||||
CoreModule.forRoot(),
|
||||
ThemeModule.forRoot(),
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import { PastoralDomainService } from "../services/crudServices/pastoral-domain.service";
|
||||
import { StateService } from "../services/state.service";
|
||||
import { first } from 'rxjs/operators';
|
||||
import { PastoralDomain } from "../entity/PastoralDomain";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
@Injectable()
|
||||
export abstract class MyAppBase {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected pastoralDomainService: PastoralDomainService
|
||||
) {
|
||||
// super(pastoralDomainService, stateService, route);
|
||||
// this.isSnapshotView = true;
|
||||
}
|
||||
isLoading: boolean = true;
|
||||
redirectIfNoDomains: boolean = true;
|
||||
|
||||
pageGroupId: string;
|
||||
|
||||
private domainInitialized = new Subject();;
|
||||
ngOnInit(): void {
|
||||
|
||||
this.isLoading = true;
|
||||
// this.route.paramMap.subscribe((params: ParamMap) => {
|
||||
// this.id = +params.get('id')
|
||||
// });
|
||||
this.route.paramMap.pipe(first()).subscribe(params => {
|
||||
if (params.get('groupId')) {
|
||||
this.pageGroupId = params.get('groupId');
|
||||
}
|
||||
|
||||
this.pastoralDomainService.initialized.takeUntil(this.domainInitialized).subscribe(result => {
|
||||
if (result) {
|
||||
this.domainInitialized.next();
|
||||
if (this.redirectIfNoDomains) {
|
||||
if (this.pageGroupId && !this.domains.find(d => d.id == this.pageGroupId)) {
|
||||
this.pastoralDomainService.joinDomain(this.pageGroupId).pipe(first()).subscribe(result => {
|
||||
this.pageOnInit();
|
||||
});
|
||||
return;
|
||||
} else if (this.domains.length == 0) {
|
||||
//TODO:SetUser
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.pageOnInit();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public get domains(): PastoralDomain[] {
|
||||
return this.pastoralDomainService.domains;
|
||||
}
|
||||
|
||||
abstract pageOnInit(): void;
|
||||
|
||||
get saveMethod(): Observable<any> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
public get happinessGroup(): PastoralDomain {
|
||||
return this.pastoralDomainService.myHappinessGroup;
|
||||
}
|
||||
public get cellGroup(): PastoralDomain {
|
||||
return this.pastoralDomainService.myCellGroup;
|
||||
}
|
||||
|
||||
public get currentDomain(): PastoralDomain {
|
||||
return this.domains.find(d => d.id == this.pageGroupId);
|
||||
}
|
||||
public get currentDomainName(): string {
|
||||
if (this.domains && this.pageGroupId) {
|
||||
//let group = this.domains.find(d => d.id == this.pageGroupId);
|
||||
|
||||
return this.domains.find(d => d.id == this.pageGroupId)?.name;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -2,25 +2,58 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { NbMenuItem } from '@nebular/theme';
|
||||
import { Role } from '../entity/Auth';
|
||||
import { DomainType } from '../entity/PastoralDomain';
|
||||
import { CellGroupComponent } from './cell-group.component';
|
||||
import { DinnerComponent } from './dinner/dinner.component';
|
||||
import { FinanceComponent } from './finance/finance.component';
|
||||
import { GoogleLoginComponent } from './google-login/google-login.component';
|
||||
import { BestListComponent, HappinessWeekListComponent } from './happiness-group/happiness-group.component';
|
||||
import { PrayerComponent } from './prayer/prayer.component';
|
||||
import { UserProfileComponent } from './user-profile/user-profile.component';
|
||||
export class CellGroupRoutingConfig {
|
||||
public static MENU_ITEMS: NbMenuItem[] = [
|
||||
|
||||
{
|
||||
title: '小組禱告',
|
||||
title: '細胞小組',
|
||||
icon: 'people-outline',
|
||||
link: '/CellGroup/prayer',
|
||||
home: true,
|
||||
children: [
|
||||
|
||||
{
|
||||
title: '小組禱告',
|
||||
//icon: 'people-outline',
|
||||
link: '/myapp/prayer'
|
||||
},
|
||||
{
|
||||
title: '小組晚餐',
|
||||
//icon: 'people-outline',
|
||||
link: '/myapp/dinner',
|
||||
},
|
||||
],
|
||||
data: DomainType.CellGroup
|
||||
},
|
||||
{
|
||||
title: '小組晚餐',
|
||||
icon: 'people-outline',
|
||||
link: '/CellGroup/dinner',
|
||||
},
|
||||
title: '幸福小組',
|
||||
icon: 'smiling-face-outline',
|
||||
children: [
|
||||
|
||||
{
|
||||
title: 'Bests',
|
||||
//icon: 'people-outline',
|
||||
link: '/myapp/bests'
|
||||
},
|
||||
{
|
||||
title: '幸福周',
|
||||
//icon: 'people-outline',
|
||||
link: '/myapp/happinessWeeks',
|
||||
},
|
||||
{
|
||||
title: '財務表',
|
||||
//icon: 'people-outline',
|
||||
link: '/myapp/finance',
|
||||
},
|
||||
],
|
||||
data: DomainType.HappinessGroup
|
||||
},
|
||||
]
|
||||
}
|
||||
const routes: Routes = [
|
||||
@@ -29,7 +62,16 @@ const routes: Routes = [
|
||||
children:
|
||||
[
|
||||
{ path: 'dinner', component: DinnerComponent, },
|
||||
{ path: 'dinner/:groupId', component: DinnerComponent, },
|
||||
{ path: 'prayer', component: PrayerComponent, },
|
||||
{ path: 'prayer/:groupId', component: PrayerComponent, },
|
||||
{ path: 'profile', component: UserProfileComponent, },
|
||||
{ path: 'bests', component: BestListComponent, },
|
||||
{ path: 'bests/:groupId', component: BestListComponent, },
|
||||
{ path: 'happinessWeeks', component: HappinessWeekListComponent, },
|
||||
{ path: 'happinessWeeks/:groupId', component: HappinessWeekListComponent, },
|
||||
{ path: 'finance/:groupId', component: FinanceComponent, },
|
||||
|
||||
{
|
||||
path: 'management', component: GoogleLoginComponent,
|
||||
data: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<ngx-one-column-layout>
|
||||
<nb-menu [items]="MENU_ITEMS"></nb-menu>
|
||||
<nb-menu [items]="MENU_ITEMS" tag="NavMenu"></nb-menu>
|
||||
<router-outlet></router-outlet>
|
||||
</ngx-one-column-layout>
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NbMenuItem } from '@nebular/theme';
|
||||
import { PastoralDomainService } from '../services/crudServices/pastoral-domain.service';
|
||||
import { ObjectUtils } from '../utilities/object-utils';
|
||||
import { CellGroupRoutingConfig } from './cell-group-routing.module';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-cell-group',
|
||||
@@ -8,10 +12,45 @@ import { CellGroupRoutingConfig } from './cell-group-routing.module';
|
||||
})
|
||||
export class CellGroupComponent implements OnInit {
|
||||
|
||||
MENU_ITEMS = CellGroupRoutingConfig.MENU_ITEMS;
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
MENU_ITEMS = ObjectUtils.CloneValue(CellGroupRoutingConfig.MENU_ITEMS);
|
||||
constructor(
|
||||
private pastoralDomainService: PastoralDomainService
|
||||
) {
|
||||
let subscription = pastoralDomainService.initialized.subscribe(result => {
|
||||
if (result) {
|
||||
this.initializeSideMenu();
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
|
||||
private initializeSideMenu() {
|
||||
|
||||
for (let i = 0; i < this.MENU_ITEMS.length; i++) {
|
||||
const item = this.MENU_ITEMS[i];
|
||||
this.checkDisplayItem(item);
|
||||
}
|
||||
}
|
||||
private checkDisplayItem(item: NbMenuItem) {
|
||||
if (item.data != undefined) {
|
||||
var group = this.pastoralDomainService.domains.find(d => d.type == item.data);
|
||||
if (!group) {
|
||||
item.hidden = true;
|
||||
return;
|
||||
}
|
||||
item.title = `${group.name}`;
|
||||
item.children.forEach(element => {
|
||||
element.link += `/${group.id}`;
|
||||
});
|
||||
|
||||
} else if (item.children) {
|
||||
item.children.forEach(element => {
|
||||
this.checkDisplayItem(element);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,19 @@ import { CellGroupRoutingModule } from './cell-group-routing.module';
|
||||
import { CellGroupComponent } from './cell-group.component';
|
||||
import { DinnerComponent } from './dinner/dinner.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NbButtonModule, NbCardModule, NbIconModule, NbInputModule, NbMenuModule, NbSpinnerModule, NbToastrModule, NbToggleModule } from '@nebular/theme';
|
||||
import { NbActionsModule, NbButtonModule, NbCardModule, NbCheckboxModule, NbIconModule, NbInputModule, NbMenuModule, NbSpinnerModule, NbToastrModule, NbToggleModule } from '@nebular/theme';
|
||||
import { ThemeModule } from '../@theme/theme.module';
|
||||
import { GoogleLoginComponent } from './google-login/google-login.component';
|
||||
import { PrayerComponent } from './prayer/prayer.component';
|
||||
import { UserProfileComponent } from './user-profile/user-profile.component';
|
||||
import { DateInputModule } from '../ui/date-input/date-input.module';
|
||||
import { DropDownListModule } from '../ui/drop-down-list/drop-down-list.module';
|
||||
import { HappinessModule } from '../happiness/happiness.module';
|
||||
import { BestListComponent, HappinessWeekListComponent } from './happiness-group/happiness-group.component';
|
||||
import { JoinDomainComponent } from './join-domain/join-domain.component';
|
||||
import { FinanceComponent } from './finance/finance.component';
|
||||
import { AddContributionDlgComponent } from './finance/add-contribution-dlg/add-contribution-dlg.component';
|
||||
import { FancyTableModule } from '../ui/fancy-table/fancy-table.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -18,7 +26,12 @@ import { UserProfileComponent } from './user-profile/user-profile.component';
|
||||
DinnerComponent,
|
||||
GoogleLoginComponent,
|
||||
PrayerComponent,
|
||||
UserProfileComponent
|
||||
UserProfileComponent,
|
||||
BestListComponent,
|
||||
HappinessWeekListComponent,
|
||||
JoinDomainComponent,
|
||||
FinanceComponent,
|
||||
AddContributionDlgComponent
|
||||
],
|
||||
imports: [
|
||||
ThemeModule,
|
||||
@@ -32,8 +45,13 @@ import { UserProfileComponent } from './user-profile/user-profile.component';
|
||||
NbToastrModule,
|
||||
NbToggleModule,
|
||||
NbIconModule,
|
||||
NbMenuModule
|
||||
|
||||
NbMenuModule,
|
||||
NbActionsModule,
|
||||
DateInputModule,
|
||||
NbCheckboxModule,
|
||||
DropDownListModule,
|
||||
HappinessModule,
|
||||
FancyTableModule
|
||||
]
|
||||
})
|
||||
export class CellGroupModule { }
|
||||
|
||||
@@ -7,18 +7,21 @@ import { SessionService } from '../../services/session.service';
|
||||
import { LineService } from '../../services/line.service';
|
||||
import { HeaderService } from '../../services/header.service';
|
||||
import { StringUtils } from '../../utilities/string-utils';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { DateUtils } from '../../utilities/date-utils';
|
||||
import { NumberUtils } from '../../utilities/number-utils';
|
||||
import { CellGroupRoutineEventsService } from '../../services/crudServices/cell-group-routine-events.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { PastoralDomainService } from '../../services/crudServices/pastoral-domain.service';
|
||||
import { MyAppBase } from '../MyAppBase';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { LoginUserService } from '../../services/login-user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-dinner',
|
||||
templateUrl: './dinner.component.html',
|
||||
styleUrls: ['./dinner.component.scss']
|
||||
})
|
||||
export class DinnerComponent implements OnInit {
|
||||
|
||||
export class DinnerComponent extends MyAppBase {
|
||||
constructor(
|
||||
private cellGroupRoutineEventsService: CellGroupRoutineEventsService,
|
||||
private messageService: MsgBoxService,
|
||||
@@ -26,10 +29,12 @@ export class DinnerComponent implements OnInit {
|
||||
private sessionService: SessionService,
|
||||
private lineService: LineService,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
private headerService: HeaderService,
|
||||
private authService: AuthService
|
||||
private loginUserService: LoginUserService,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected pastoralDomainService: PastoralDomainService
|
||||
) {
|
||||
|
||||
super(stateService, route, pastoralDomainService);
|
||||
}
|
||||
|
||||
cellGroupEvent: CellGroupRoutineEvents;
|
||||
@@ -40,20 +45,22 @@ export class DinnerComponent implements OnInit {
|
||||
isLoading: boolean = true;
|
||||
processing: boolean = false;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.headerService.setHeader("晚宴系統")
|
||||
pageOnInit(): void {
|
||||
this.stateService.SetPageTitle("晚宴系統");
|
||||
this.getAllData();
|
||||
|
||||
}
|
||||
|
||||
getAllData() {
|
||||
|
||||
this.isLoading = true;
|
||||
this.cellGroupRoutineEventsService.getComingEvent().pipe(first()).subscribe(result => {
|
||||
this.cellGroupEvent = result;
|
||||
|
||||
this.data = this.cellGroupEvent.attendees.find(a => a.id == this.authService.userAccess.memberId);
|
||||
this.data = this.cellGroupEvent.attendees.find(a => a.id == this.loginUserService.userAccess.memberId);
|
||||
if (!this.data) {
|
||||
this.data = new CellGroupRoutineEventAttendee(this.cellGroupEvent.id, this.authService.userAccess.memberId);
|
||||
this.data.name = this.authService.userAccess.firstName;
|
||||
this.data = new CellGroupRoutineEventAttendee(this.cellGroupEvent.id, this.loginUserService.userAccess.memberId);
|
||||
this.data.name = this.loginUserService.userAccess.firstName;
|
||||
this.cellGroupEvent.attendees.push(this.data);
|
||||
} else {
|
||||
this.potluckItems = this.data.potluckItem.split("|").map(s => new DinnerInfo(s));
|
||||
@@ -70,7 +77,7 @@ export class DinnerComponent implements OnInit {
|
||||
|
||||
this.processing = false;
|
||||
this.toastrService.success('菜單更新完成!');
|
||||
this.lineService.pushCommandMessage('Cac4ac5a8d7fc52daa444d71dc7c360a9', 'dinner');
|
||||
this.lineService.pushCommandMessage(this.cellGroupEvent.pastoralDomainId, 'dinner');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,9 +95,6 @@ export class DinnerComponent implements OnInit {
|
||||
this.potluckItems.push(new DinnerInfo(''));
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
logout() {
|
||||
this.authService.logout();
|
||||
}
|
||||
}
|
||||
|
||||
export class DinnerInfo {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<nb-card status="success">
|
||||
<nb-card-header>
|
||||
新增奉獻
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="row form-group">
|
||||
<div class="col-6 col-md-4">
|
||||
<div class='form-group'>
|
||||
<label for='contributor' class='label'>奉獻人</label>
|
||||
|
||||
<input type="text" name="contributor" nbInput fullWidth id="contributor"
|
||||
[(ngModel)]="contribution.contributor">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="form-group">
|
||||
<label for="costAmount" class="label">金額</label>
|
||||
<input type="number" name="costAmount" nbInput fullWidth id="costAmount"
|
||||
[(ngModel)]="contribution.amount">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-5">
|
||||
<div class="form-group">
|
||||
<label for="costContent" class="label">備註</label>
|
||||
<input type="text" name="costContent" nbInput fullWidth id="costContent"
|
||||
[(ngModel)]="contribution.comment">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
<nb-card-footer>
|
||||
<button class="float-right" nbButton hero status="danger" size="small" (click)="close()">Close</button>
|
||||
<button class="float-right mr-2" nbButton hero status="primary" size="small" (click)="submit()">Submit</button>
|
||||
</nb-card-footer>
|
||||
</nb-card>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AddContributionDlgComponent } from './add-contribution-dlg.component';
|
||||
|
||||
describe('AddContributionDlgComponent', () => {
|
||||
let component: AddContributionDlgComponent;
|
||||
let fixture: ComponentFixture<AddContributionDlgComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AddContributionDlgComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AddContributionDlgComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NbDialogRef } from '@nebular/theme';
|
||||
import { Contribution } from '../../../entity/contribution.model';
|
||||
import { DropDownOption } from '../../../entity/dropDownOption';
|
||||
import { AuthService } from '../../../services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-add-contribution-dlg',
|
||||
templateUrl: './add-contribution-dlg.component.html',
|
||||
styleUrls: ['./add-contribution-dlg.component.scss']
|
||||
})
|
||||
export class AddContributionDlgComponent implements OnInit {
|
||||
|
||||
taskrOptions: DropDownOption[] = [];
|
||||
contribution: Contribution;
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private dlgRef: NbDialogRef<AddContributionDlgComponent>
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.contribution = { time: new Date() } as Contribution;
|
||||
}
|
||||
close() {
|
||||
this.dlgRef.close();
|
||||
}
|
||||
submit() {
|
||||
if (this.contribution.contributor && this.contribution.amount > 0) {
|
||||
this.dlgRef.close(this.contribution);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<nb-card [nbSpinner]="isLoading" nbSpinnerStatus="info" nbSpinnerSize="giant">
|
||||
<nb-card-header>
|
||||
{{currentDomainName}} 財務表
|
||||
<nb-actions size="small" fullWidth class="float-right">
|
||||
<nb-action icon="plus-outline" title="Add New" (click)="addContribution()"></nb-action>
|
||||
</nb-actions>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<ng-container *ngIf="!isLoading">
|
||||
<fancy-table [data]="contributions" [settings]="contributionSettings" [footerData]="summary"></fancy-table>
|
||||
</ng-container>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { FinanceComponent } from './finance.component';
|
||||
|
||||
describe('FinanceComponent', () => {
|
||||
let component: FinanceComponent;
|
||||
let fixture: ComponentFixture<FinanceComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ FinanceComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FinanceComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,208 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService, NbToastrService } from '@nebular/theme';
|
||||
import { Contribution } from '../../entity/contribution.model';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { ContributionService } from '../../services/crudServices/contribution.service';
|
||||
import { PastoralDomainService } from '../../services/crudServices/pastoral-domain.service';
|
||||
import { LineService } from '../../services/line.service';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { SessionService } from '../../services/session.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { MyAppBase } from '../MyAppBase';
|
||||
import { AddContributionDlgComponent } from './add-contribution-dlg/add-contribution-dlg.component';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { FancySettings } from '../../ui/fancy-table/fancy-settings.model';
|
||||
import { ObjectUtils } from '../../utilities/object-utils';
|
||||
import { NumberUtils } from '../../utilities/number-utils';
|
||||
import { StringUtils } from '../../utilities/string-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-finance',
|
||||
templateUrl: './finance.component.html',
|
||||
styleUrls: ['./finance.component.scss']
|
||||
})
|
||||
export class FinanceComponent extends MyAppBase {
|
||||
|
||||
contributions: Contribution[];
|
||||
summary: Contribution[];
|
||||
//#region Table Setting
|
||||
contributionSettings = <FancySettings<Contribution>>{
|
||||
contextMenuItems: [
|
||||
// {
|
||||
// id: 'add',
|
||||
// enabled: true,
|
||||
// title: 'Add New',
|
||||
// icon: 'plus-circle-outline',
|
||||
// callback: (datum, element) => {
|
||||
// this.openEditingDialog(true);
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// id: 'edit',
|
||||
// enabled: true,
|
||||
// title: 'Edit',
|
||||
// icon: 'edit',
|
||||
// callback: (datum, element) => {
|
||||
// this.openEditingDialog(false);
|
||||
// },
|
||||
// },
|
||||
{
|
||||
enabled: true,
|
||||
id: 'delete',
|
||||
title: 'Delete',
|
||||
icon: 'trash-2-outline',
|
||||
callback: (datum, element) => {
|
||||
this.contributionService.delete(datum.id).pipe(first()).subscribe(result => {
|
||||
|
||||
});
|
||||
},
|
||||
}],
|
||||
onContextMenuOpening: (datum, menuItems) => {
|
||||
menuItems.find(i => i.id == 'delete').visible = !!datum;
|
||||
//menuItems.find(i => i.id == 'edit').visible = !!datum;
|
||||
return datum;
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'time',
|
||||
title: 'Time',
|
||||
type: 'date',
|
||||
widthPx: 120,
|
||||
//valuePrepareFunction: (item) => ,
|
||||
},
|
||||
{
|
||||
name: 'contributor',
|
||||
title: 'Contributor',
|
||||
type: 'text',
|
||||
widthPx: 150,
|
||||
//valuePrepareFunction: (item) => ,
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
title: 'Amount',
|
||||
type: 'text',
|
||||
widthPx: 100,
|
||||
class: 'text-right',
|
||||
footerClass: 'text-right',
|
||||
valuePrepareFunction: getAmountDisplay,
|
||||
},
|
||||
{
|
||||
name: 'comment',
|
||||
title: 'Comment',
|
||||
type: 'text',
|
||||
widthPx: 200,
|
||||
//valuePrepareFunction: (item) => ,
|
||||
},
|
||||
|
||||
],
|
||||
showFooter: true
|
||||
};
|
||||
//#endregion
|
||||
|
||||
constructor(
|
||||
private contributionService: ContributionService,
|
||||
private messageService: MsgBoxService,
|
||||
private toastrService: NbToastrService,
|
||||
private sessionService: SessionService,
|
||||
private lineService: LineService,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
private authService: AuthService,
|
||||
private dlgService: NbDialogService,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected pastoralDomainService: PastoralDomainService
|
||||
) {
|
||||
super(stateService, route, pastoralDomainService);
|
||||
}
|
||||
|
||||
pageOnInit(): void {
|
||||
this.initializeData();
|
||||
}
|
||||
|
||||
addContribution() {
|
||||
this.dlgService.open(AddContributionDlgComponent, {
|
||||
context: {
|
||||
|
||||
}
|
||||
}).onClose.pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
result.groupId = this.currentDomain.id;
|
||||
this.isLoading = true;
|
||||
this.contributionService.createOrUpdate(result).pipe(first()).subscribe(result => {
|
||||
this.refreshData();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private refreshData() {
|
||||
this.isLoading = true;
|
||||
this.pastoralDomainService.initialize().pipe(first()).subscribe(result => {
|
||||
this.initializeData();
|
||||
});
|
||||
}
|
||||
private initializeData() {
|
||||
this.contributions = ObjectUtils.CloneValue(this.currentDomain.contributions);
|
||||
this.summary = [];
|
||||
|
||||
let totalComment = '';
|
||||
|
||||
let totalAmount = this.contributions.map(d => d.amount).reduce((a, b) => a + b, 0);
|
||||
//Append Cost
|
||||
if (this.currentDomain.happinessWeeks) {
|
||||
|
||||
for (let i = 0; i < this.currentDomain.happinessWeeks.length; i++) {
|
||||
const week = this.currentDomain.happinessWeeks[i];
|
||||
let weekCost = 0;
|
||||
for (let j = 0; j < week.costs.length; j++) {
|
||||
const weekCostItem = week.costs[j];
|
||||
this.contributions.push(
|
||||
{
|
||||
time: week.date,
|
||||
contributor: `W${i + 1} - ${weekCostItem.tasker}`,
|
||||
amount: -1 * weekCostItem.amount,
|
||||
comment: weekCostItem.content
|
||||
} as Contribution);
|
||||
weekCost += weekCostItem.amount;
|
||||
}
|
||||
|
||||
if (weekCost > 0) {
|
||||
|
||||
this.contributions.push(
|
||||
{
|
||||
time: week.date,
|
||||
contributor: `${week.topic} - Sub Total`,
|
||||
amount: 0,
|
||||
comment: '$ ' + NumberUtils.FormatCurrency(weekCost)
|
||||
} as Contribution);
|
||||
}
|
||||
}
|
||||
let remainWeeks = this.currentDomain.happinessWeeks.filter(w => w.date >= new Date()).length;
|
||||
|
||||
totalAmount = this.contributions.map(d => d.amount).reduce((a, b) => a + b, 0);
|
||||
totalComment = StringUtils.getHtmlBadge(`剩餘每周(${remainWeeks}) 預算: $${NumberUtils.FormatCurrency(totalAmount / remainWeeks)}`, 'info');
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.summary.push(
|
||||
{
|
||||
time: null,
|
||||
contributor: `Summary`,
|
||||
amount: totalAmount,
|
||||
comment: totalComment
|
||||
} as Contribution);
|
||||
this.isLoading = false;
|
||||
|
||||
}
|
||||
}
|
||||
function getAmountDisplay(item: Contribution): string {
|
||||
|
||||
if (item.amount != 0) {
|
||||
return StringUtils.getHtmlBadge(NumberUtils.FormatCurrency(item.amount), item.amount > 0 ? 'success' : 'warning');
|
||||
}
|
||||
|
||||
return "---------------";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { PastoralDomainService } from '../../services/crudServices/pastoral-domain.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { MyAppBase } from '../MyAppBase';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-happiness-group',
|
||||
template: `
|
||||
<best-list [group]="happinessGroup"></best-list>
|
||||
`,
|
||||
styleUrls: ['./happiness-group.component.scss']
|
||||
})
|
||||
export class BestListComponent extends MyAppBase {
|
||||
|
||||
constructor(
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected pastoralDomainService: PastoralDomainService
|
||||
) {
|
||||
super(stateService, route, pastoralDomainService);
|
||||
}
|
||||
pageOnInit(): void {
|
||||
this.stateService.SetPageTitle("Best");
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-happiness-week-group',
|
||||
template: `<happiness-week-list [group]="happinessGroup"></happiness-week-list>
|
||||
`,
|
||||
styleUrls: ['./happiness-group.component.scss']
|
||||
})
|
||||
export class HappinessWeekListComponent extends MyAppBase {
|
||||
|
||||
constructor(
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected pastoralDomainService: PastoralDomainService
|
||||
) {
|
||||
super(stateService, route, pastoralDomainService);
|
||||
}
|
||||
pageOnInit(): void {
|
||||
this.stateService.SetPageTitle("Happiness Weeks");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<nb-card status="success">
|
||||
<nb-card-header>
|
||||
加入小組
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class='col-12 col-md-6'>
|
||||
<div class='form-group'>
|
||||
<label for='domain' class='label'>小組</label>
|
||||
<op-drop-down name='domain' id='domain' [(ngModel)]='domainId' [source]='domainOptions'></op-drop-down>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
<nb-card-footer>
|
||||
<button class="float-right mr-2" fullWidth nbButton hero status="primary" size="large"
|
||||
(click)="joinDomain()">加入小組</button>
|
||||
</nb-card-footer>
|
||||
</nb-card>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { JoinDomainComponent } from './join-domain.component';
|
||||
|
||||
describe('JoinDomainComponent', () => {
|
||||
let component: JoinDomainComponent;
|
||||
let fixture: ComponentFixture<JoinDomainComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ JoinDomainComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(JoinDomainComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DomainType, PastoralDomain } from '../../entity/PastoralDomain';
|
||||
import { PastoralDomainService } from '../../services/crudServices/pastoral-domain.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { MyAppBase } from '../MyAppBase';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { DropDownOption } from '../../entity/dropDownOption';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-join-domain',
|
||||
templateUrl: './join-domain.component.html',
|
||||
styleUrls: ['./join-domain.component.scss']
|
||||
})
|
||||
export class JoinDomainComponent extends MyAppBase {
|
||||
|
||||
constructor(
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected pastoralDomainService: PastoralDomainService
|
||||
) {
|
||||
super(stateService, route, pastoralDomainService);
|
||||
this.redirectIfNoDomains = false;
|
||||
}
|
||||
domainId: string;
|
||||
allDomains: PastoralDomain[];
|
||||
domainOptions: DropDownOption[] = [];
|
||||
pageOnInit(): void {
|
||||
this.isLoading = true;
|
||||
this.pastoralDomainService.getAll().pipe(first()).subscribe(result => {
|
||||
this.allDomains = result.filter(d => d.type == DomainType.CellGroup || d.type == DomainType.HappinessGroup);
|
||||
this.domainOptions = this.allDomains.map(d => new DropDownOption(d.id, d.name));
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
joinDomain() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,40 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbToastrService } from '@nebular/theme';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { BindingHelper } from '../../entity/BindingHelper';
|
||||
import { CellGroupRoutineEvents, CellGroupRoutineEventPrayer, CellGroupRoutineEventAttendee } from '../../entity/CellGroupRoutineEvents';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { CellGroupRoutineEventsService } from '../../services/crudServices/cell-group-routine-events.service';
|
||||
import { PastoralDomainService } from '../../services/crudServices/pastoral-domain.service';
|
||||
import { HeaderService } from '../../services/header.service';
|
||||
import { LineService } from '../../services/line.service';
|
||||
import { LoginUserService } from '../../services/login-user.service';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { SessionService } from '../../services/session.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { DateUtils } from '../../utilities/date-utils';
|
||||
import { NumberUtils } from '../../utilities/number-utils';
|
||||
import { DinnerInfo } from '../dinner/dinner.component';
|
||||
import { MyAppBase } from '../MyAppBase';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-prayer',
|
||||
templateUrl: './prayer.component.html',
|
||||
styleUrls: ['./prayer.component.scss']
|
||||
})
|
||||
export class PrayerComponent implements OnInit {
|
||||
|
||||
|
||||
export class PrayerComponent extends MyAppBase {
|
||||
constructor(
|
||||
private cellGroupRoutineEventsService: CellGroupRoutineEventsService,
|
||||
private messageService: MsgBoxService,
|
||||
private toastrService: NbToastrService,
|
||||
private sessionService: SessionService,
|
||||
private lineService: LineService,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
private headerService: HeaderService,
|
||||
private authService: AuthService
|
||||
private loginUserService: LoginUserService,
|
||||
private authService: AuthService,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected pastoralDomainService: PastoralDomainService
|
||||
) {
|
||||
|
||||
super(stateService, route, pastoralDomainService);
|
||||
}
|
||||
|
||||
cellGroupEvent: CellGroupRoutineEvents;
|
||||
@@ -42,8 +45,8 @@ export class PrayerComponent implements OnInit {
|
||||
isLoading: boolean = true;
|
||||
processing: boolean = false;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.headerService.setHeader("禱告中心")
|
||||
pageOnInit(): void {
|
||||
this.stateService.SetPageTitle("禱告中心");
|
||||
this.getAllData();
|
||||
}
|
||||
getAllData() {
|
||||
@@ -52,9 +55,9 @@ export class PrayerComponent implements OnInit {
|
||||
this.cellGroupRoutineEventsService.getLastEvent().pipe(first()).subscribe(result => {
|
||||
this.cellGroupEvent = result;
|
||||
|
||||
this.data = this.cellGroupEvent.prayers.find(a => a.memberId == this.authService.userAccess.memberId);
|
||||
this.data = this.cellGroupEvent.prayers.find(a => a.memberId == this.loginUserService.userAccess.memberId);
|
||||
if (!this.data) {
|
||||
this.data = new CellGroupRoutineEventPrayer(this.cellGroupEvent.id, this.authService.userAccess.memberId);
|
||||
this.data = new CellGroupRoutineEventPrayer(this.cellGroupEvent.id, this.loginUserService.userAccess.memberId);
|
||||
this.cellGroupEvent.prayers.push(this.data);
|
||||
this.prayer = new BindingHelper(['']);
|
||||
} else {
|
||||
@@ -92,7 +95,7 @@ export class PrayerComponent implements OnInit {
|
||||
// message += "\n======= 備註 =======" + "\n" + comment;
|
||||
// }
|
||||
|
||||
// message += "\n請使用方舟晚宴系統新增菜單唷!" + "\n" + "https://happiness.tours/CellGroup/dinner?openExternalBrowser=1"
|
||||
// message += "\n請使用方舟晚宴系統新增菜單唷!" + "\n" + "https://golife.love/CellGroup/dinner?openExternalBrowser=1"
|
||||
// this.lineService.pushLineMessage(message);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,20 +5,21 @@
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="row" *ngIf="data">
|
||||
<div class="col-md-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="form-group">
|
||||
<label for="firstName" class="label">First Name</label>
|
||||
<input type="text" name="firstName" nbInput fullWidth id="firstName"
|
||||
[(ngModel)]="data.firstName">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="form-group">
|
||||
<label for="lastName" class="label">Last Name</label>
|
||||
<input type="text" name="lastName" nbInput fullWidth id="lastName" [(ngModel)]="data.lastName">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
|
||||
<div class="col-4 col-md-2">
|
||||
<div class="form-group">
|
||||
<label for="gender" class="label">Gender</label>
|
||||
<op-drop-down id="gender" name="gender" [(ngModel)]='data.gender' [source]="GenderOptions">
|
||||
@@ -27,25 +28,21 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<nb-checkbox id="married" name="married" [(ngModel)]="data.married">Married</nb-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<nb-checkbox id="baptized" name="baptized" [(ngModel)]="data.baptized">Baptized</nb-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="col-8 col-md-4">
|
||||
<div class='form-group'>
|
||||
<label for='birthday' class='label'>Birthday</label>
|
||||
<op-date-input id='birthday' name='birthday' [(ngModel)]='data.birthday'></op-date-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="email" class="label">Email</label>
|
||||
<input type="text" name="email" nbInput fullWidth id="email" [(ngModel)]="data.email" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="address" class="label">Address</label>
|
||||
<input type="text" name="address" nbInput fullWidth id="address" [(ngModel)]="data.address">
|
||||
@@ -58,30 +55,30 @@
|
||||
[(ngModel)]="data.avatarImage">
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="col-md-4">
|
||||
<div class='form-group'>
|
||||
<label for='birthday' class='label'>Birthday</label>
|
||||
<input type='text' nbInput fullWidth id='birthday' name='birthday' [(ngModel)]='data.birthday'
|
||||
[nbDatepicker]="dateTimePickerbirthday">
|
||||
<nb-date-timepicker format="yyyy/MM/dd" #dateTimePickerbirthday></nb-date-timepicker>
|
||||
|
||||
|
||||
<div class="col-6 col-md-2 align-self-md-end">
|
||||
<div class="form-group">
|
||||
<nb-checkbox id="married" name="married" [(ngModel)]="data.married">Married</nb-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class='form-group'>
|
||||
<label for='dateOfBaptized' class='label'>Date Of Baptized</label>
|
||||
<input type='text' nbInput fullWidth id='dateOfBaptized' name='dateOfBaptized'
|
||||
[(ngModel)]='data.dateOfBaptized' [nbDatepicker]="dateTimePickerdateOfBaptized">
|
||||
<nb-datepicker format="yyyy/MM/dd" #dateTimePickerdateOfBaptized></nb-datepicker>
|
||||
<div class="col-6 col-md-2 align-self-md-end">
|
||||
<div class="form-group">
|
||||
<nb-checkbox id="baptized" name="baptized" [(ngModel)]="data.baptized">Baptized</nb-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="col-12 col-md-4">
|
||||
<div class='form-group'>
|
||||
<label for='dateOfWalkIn' class='label'>Date Of Walk In</label>
|
||||
<input type='text' nbInput fullWidth id='dateOfWalkIn' name='dateOfWalkIn'
|
||||
[(ngModel)]='data.dateOfWalkIn' [nbDatepicker]="dateTimePickerdateOfWalkIn">
|
||||
<nb-date-timepicker format="yyyy/MM/dd" #dateTimePickerdateOfWalkIn></nb-date-timepicker>
|
||||
<op-date-input id='dateOfWalkIn' name='dateOfWalkIn' [(ngModel)]='data.dateOfWalkIn'>
|
||||
</op-date-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<div class='form-group' *ngIf="data.baptized">
|
||||
<label for='dateOfBaptized' class='label'>Date Of Baptized</label>
|
||||
<op-date-input id='dateOfBaptized' name='dateOfBaptized' [(ngModel)]='data.dateOfBaptized'>
|
||||
</op-date-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -89,10 +86,10 @@
|
||||
|
||||
</nb-card-body>
|
||||
<nb-card-footer>
|
||||
<button class="float-right" nbButton hero status="danger" size="small" (click)="close()"
|
||||
<!-- <button class="float-right" nbButton hero status="danger" size="small" (click)="close()"
|
||||
[nbSpinner]="processing">
|
||||
Cancel
|
||||
</button>
|
||||
</button> -->
|
||||
<button class="float-right mr-2" nbButton hero status="primary" size="small"
|
||||
(click)="!form.invalid&&!processing&&update()" [disabled]="form.invalid" [nbSpinner]="processing">
|
||||
Save
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FamilyMember } from '../../entity/Member';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { FamilyMemberService } from '../../services/crudServices/family-member.service';
|
||||
import { SessionService } from '../../services/session.service';
|
||||
import { first } from "rxjs/operators";
|
||||
import { DropDownOptions } from '../../entity/dropDownOption';
|
||||
import { NbToastrService } from '@nebular/theme';
|
||||
import { LoginUserService } from '../../services/login-user.service';
|
||||
@Component({
|
||||
selector: 'ngx-user-profile',
|
||||
templateUrl: './user-profile.component.html',
|
||||
styleUrls: ['./user-profile.component.scss']
|
||||
})
|
||||
export class UserProfileComponent implements OnInit {
|
||||
GenderOptions = DropDownOptions.GenderOptions;
|
||||
|
||||
constructor(
|
||||
private memberService: FamilyMemberService,
|
||||
private authService: AuthService
|
||||
private loginUserService: LoginUserService,
|
||||
private toastrService: NbToastrService,
|
||||
) { }
|
||||
data: FamilyMember;
|
||||
processing: boolean;
|
||||
ngOnInit(): void {
|
||||
this.memberService.getById(this.authService.userAccess.memberId).pipe(first()).subscribe(result => {
|
||||
this.memberService.getById(this.loginUserService.userAccess.memberId).pipe(first()).subscribe(result => {
|
||||
this.data = result;
|
||||
if (this.data.dateOfBaptized == undefined) this.data.dateOfBaptized = null;
|
||||
if (this.data.dateOfWalkIn == undefined) this.data.dateOfWalkIn = null;
|
||||
if (this.data.birthday == undefined) this.data.birthday = null;
|
||||
});
|
||||
|
||||
}
|
||||
@@ -29,6 +35,7 @@ export class UserProfileComponent implements OnInit {
|
||||
update() {
|
||||
this.memberService.createOrUpdate(this.data).pipe(first()).subscribe(result => {
|
||||
|
||||
this.toastrService.success('更新完成!', '個人資料');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,12 @@ import { ContextMenuModule } from '../../ui/context-menu/context-menu.module';
|
||||
const components = [RightClickMenuDirective,];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
entryComponents: [
|
||||
ContextMenuComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
NbDialogModule,
|
||||
ContextMenuModule
|
||||
],
|
||||
exports: [...components],
|
||||
declarations: [...components],
|
||||
imports: [
|
||||
CommonModule,
|
||||
NbDialogModule,
|
||||
ContextMenuModule
|
||||
],
|
||||
exports: [...components]
|
||||
})
|
||||
export class RightClickMenuModule { }
|
||||
|
||||
@@ -20,6 +20,8 @@ export interface LoginTokenViewModel {
|
||||
avatarImage: string;
|
||||
role: Role;
|
||||
cellGroup: PastoralDomain;
|
||||
signalRConnectionId;
|
||||
sessionTabId: string;
|
||||
}
|
||||
|
||||
export interface GoogleUserInfo {
|
||||
|
||||
@@ -4,6 +4,7 @@ export interface CellGroupRoutineEvents {
|
||||
address: string;
|
||||
attendees: CellGroupRoutineEventAttendee[];
|
||||
prayers: CellGroupRoutineEventPrayer[];
|
||||
pastoralDomainId: string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { HappinessCost } from "./happiness-cost.model";
|
||||
import { PastoralDomain } from "./PastoralDomain";
|
||||
|
||||
export interface HappinessWeek {
|
||||
@@ -11,6 +12,7 @@ export interface HappinessWeek {
|
||||
seq: number;
|
||||
updateRestWeekDate: boolean;
|
||||
tasks: HappinessTask[];
|
||||
costs: HappinessCost[];
|
||||
topic: string
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Contribution } from "./contribution.model";
|
||||
import { HappinessBEST, HappinessWeek } from "./HappinessGroup";
|
||||
import { FamilyMember } from "./Member";
|
||||
|
||||
@@ -6,7 +7,7 @@ export enum DomainType {
|
||||
HappinessGroup,
|
||||
CellGroupCoworker,
|
||||
ChurchCoworker,
|
||||
Person = 99
|
||||
Administrator = 99
|
||||
}
|
||||
|
||||
export interface PastoralDomain {
|
||||
@@ -27,6 +28,7 @@ export interface PastoralDomain {
|
||||
|
||||
bests: HappinessBEST[];
|
||||
happinessWeeks: HappinessWeek[];
|
||||
contributions: Contribution[];
|
||||
|
||||
serviceAddress: AddressInfo;
|
||||
type: DomainType;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Contribution } from './contribution.model';
|
||||
|
||||
describe('Contribution', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new Contribution()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface Contribution {
|
||||
groupId: string;
|
||||
id: string;
|
||||
contributor: string;
|
||||
amount: number;
|
||||
comment: string;
|
||||
time: Date;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { HappinessCost } from './happiness-cost.model';
|
||||
|
||||
describe('HappinessCost', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new HappinessCost()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
export class HappinessCost {
|
||||
weekId: string;
|
||||
id: string;
|
||||
tasker: string;
|
||||
content: string;
|
||||
amount: number;
|
||||
}
|
||||
@@ -8,12 +8,12 @@ import * as signalR from "@microsoft/signalr"
|
||||
import { StringUtils } from '../../utilities/string-utils';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { ADButtons, ADIcon } from '../../ui/alert-dlg/alert-dlg.component';
|
||||
import { UuidUtils } from '../../utilities/uuid-utils';
|
||||
import { ObjectUtils } from '../../utilities/object-utils';
|
||||
import { NbToastrService } from '@nebular/theme';
|
||||
import { AvalonBase } from './avalonBase';
|
||||
import { HeaderService } from '../../services/header.service';
|
||||
import { ADIcon, ADButtons } from '../../ui/alert-dlg/alert-dlg.model';
|
||||
|
||||
const minimumPlayers = 5;
|
||||
const maximumPlayers = 10;
|
||||
@@ -30,7 +30,7 @@ const teamSize: number[][] = [
|
||||
const fourthQuestNeed2Failed = 7;
|
||||
const SIGNAL_R_URL = (id: string = null) => { return `${environment.signalRUrl}/${id}Hub` }
|
||||
//const SIGNAL_R_URL = (id: string = null) => { return `http://localhost:12071/hub` }
|
||||
//const SIGNAL_R_URL = (id: string = null) => { return `http://happiness.tours:8088/${id}hub` }
|
||||
//const SIGNAL_R_URL = (id: string = null) => { return `http://golife.love:8088/${id}hub` }
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-avalon',
|
||||
@@ -181,7 +181,7 @@ export class AvalonComponent extends AvalonBase implements OnInit {
|
||||
|
||||
public showMyRole() {
|
||||
let roleInfo = this.getRoleInfoByRole(this.me.role);
|
||||
this.msgBoxService.show(roleInfo.name, roleInfo.description, ADIcon.INFO)
|
||||
this.msgBoxService.show(roleInfo.name, { text: roleInfo.description, icon: ADIcon.INFO });
|
||||
}
|
||||
public showMySecret() {
|
||||
//let roleInfo = this.getRoleInfoByRole(this.me.role);
|
||||
@@ -208,7 +208,7 @@ export class AvalonComponent extends AvalonBase implements OnInit {
|
||||
break;
|
||||
}
|
||||
|
||||
this.msgBoxService.show('噓...', description, ADIcon.INFO);
|
||||
this.msgBoxService.show('噓...', { text: description, icon: ADIcon.INFO });
|
||||
}
|
||||
|
||||
private initializeQuestInfo() {
|
||||
@@ -251,7 +251,7 @@ export class AvalonComponent extends AvalonBase implements OnInit {
|
||||
|
||||
previousStage() {
|
||||
|
||||
this.msgBoxService.show('上一棟', '你確定?', ADIcon.QUESTION, ADButtons.YesNo).pipe(first()).subscribe(result => {
|
||||
this.msgBoxService.show('上一棟', { text: '你確定?', icon: ADIcon.QUESTION, buttons: ADButtons.YesNo }).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
this.avalonService.data.stage -= 1;
|
||||
this.clearVoteStatus();
|
||||
@@ -264,14 +264,14 @@ export class AvalonComponent extends AvalonBase implements OnInit {
|
||||
switch (this.avalonService.data.stage) {
|
||||
case AvalonStage.JoinGame:
|
||||
if (this.players.length < 5) {
|
||||
this.msgBoxService.show("人數不足!", "遊戲最少需要五人");
|
||||
this.msgBoxService.show("人數不足!", { text: "遊戲最少需要五人" });
|
||||
return;
|
||||
}
|
||||
|
||||
this.gameSize = Math.max(0, this.players.length - 6);
|
||||
|
||||
if (this.avalonService.data.roles.filter(r => r.enabled && false == r.isGood).length != players_evil[this.gameSize]) {
|
||||
this.msgBoxService.show("反派人數錯誤!", "反派人數需要 " + players_evil[this.gameSize].toString() + " 人");
|
||||
this.msgBoxService.show("反派人數錯誤!", { text: "反派人數需要 " + players_evil[this.gameSize].toString() + " 人" });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { first } from 'rxjs/operators';
|
||||
import { GameInfo, Player, Role, RoleInfo } from '../../../entity/Avalon';
|
||||
import { AvalonService } from '../../../services/avalon.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { ADButtons, ADIcon } from '../../../ui/alert-dlg/alert-dlg.component';
|
||||
import { ADButtons, ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { AvalonBase } from '../avalonBase';
|
||||
|
||||
@Component({
|
||||
@@ -38,7 +38,7 @@ export class ChooseCharacterComponent extends AvalonBase implements OnInit {
|
||||
// return;
|
||||
this.ngZone.run(
|
||||
_ => {
|
||||
this.msgBoxService.show(roleInfo.name, roleInfo.description + '<br>你確定你是?', ADIcon.QUESTION, ADButtons.YesNo).pipe(first()).subscribe(result => {
|
||||
this.msgBoxService.show(roleInfo.name, { text: roleInfo.description + '<br>你確定你是?', icon: ADIcon.QUESTION, buttons: ADButtons.YesNo }).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
if (this.players.find(p => p.role == roleInfo.role) && roleInfo.role != Role.ArthurKnight) {
|
||||
this.toastrService.danger('腳色重複:' + roleInfo.name, '你確定沒有選錯腳色??')
|
||||
|
||||
@@ -26,5 +26,5 @@
|
||||
<ng-template #WaitingMessage>
|
||||
<h1>等待遊戲開始中...</h1>
|
||||
|
||||
<qr-code *ngIf="isHost" [size]="qrCodeWidth" [value]="'http://happiness.tours/games/avalon'"></qr-code>
|
||||
<qr-code *ngIf="isHost" [size]="qrCodeWidth" [value]="'http://golife.love/games/avalon'"></qr-code>
|
||||
</ng-template>
|
||||
@@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, NgZone, OnInit } from '@angular/core';
|
||||
import { Player, AvalonStage } from '../../../entity/Avalon';
|
||||
import { AvalonService } from '../../../services/avalon.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.component';
|
||||
import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { AvalonBase } from '../avalonBase';
|
||||
|
||||
@Component({
|
||||
@@ -38,7 +38,7 @@ export class PickTeammateComponent extends AvalonBase implements OnInit {
|
||||
this.avalonService.applyCdChange$.next();
|
||||
} else {
|
||||
this.ngZone.run(_ => {
|
||||
this.msgBoxService.show('任務編組錯誤!', `本次任務出場人數為 ${this.currentQuest.teamSize} 人!`, ADIcon.WARNING);
|
||||
this.msgBoxService.show('任務編組錯誤!', { text: `本次任務出場人數為 ${this.currentQuest.teamSize} 人!`, icon: ADIcon.WARNING });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,43 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { NbMenuItem } from '@nebular/theme';
|
||||
import { AvalonComponent } from './avalon/avalon.component';
|
||||
import { GamesComponent } from './games.component';
|
||||
import { HeroDashboardComponent } from './massive-darkness2/hero-dashboard/hero-dashboard.component';
|
||||
import { MassiveDarkness2Component } from './massive-darkness2/massive-darkness2.component';
|
||||
import { MD2MobInfoMaintenanceComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-info-maintenance.component';
|
||||
import { MD2HeroProfileMaintenanceComponent } from './massive-darkness2/md2-hero-profile-maintenance/md2-hero-profile-maintenance.component';
|
||||
export class GameRoomMenuConfig {
|
||||
public static HostMenu: NbMenuItem[] = [
|
||||
|
||||
{
|
||||
title: 'Dashboard',
|
||||
icon: 'people-outline',
|
||||
children: [
|
||||
{
|
||||
title: '小組禱告',
|
||||
//icon: 'people-outline',
|
||||
link: '/games/MD2'
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
public static PlayerMenu: NbMenuItem[] = [
|
||||
|
||||
{
|
||||
title: 'Hero Dashboard',
|
||||
icon: 'people-outline',
|
||||
children: [
|
||||
{
|
||||
title: '小組禱告',
|
||||
//icon: 'people-outline',
|
||||
link: '/myapp/prayer'
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '', component: GamesComponent
|
||||
@@ -12,6 +47,10 @@ const routes: Routes = [
|
||||
[
|
||||
{ path: 'avalon', component: AvalonComponent },
|
||||
{ path: 'avalonHost', component: AvalonComponent },
|
||||
{ path: 'MD2', component: MassiveDarkness2Component },
|
||||
{ path: 'MD2_Hero/:roomId', component: HeroDashboardComponent },
|
||||
{ path: 'MD2MobInfo', component: MD2MobInfoMaintenanceComponent },
|
||||
{ path: 'MD2HeroProfile', component: MD2HeroProfileMaintenanceComponent },
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<ngx-one-column-layout>
|
||||
<!-- <nb-menu [items]="MENU_ITEMS"></nb-menu> -->
|
||||
<ngx-plain-layout>
|
||||
<!-- <nb-menu [items]="gameRoomService.sideMenu"></nb-menu> -->
|
||||
<router-outlet></router-outlet>
|
||||
</ngx-one-column-layout>
|
||||
</ngx-plain-layout>
|
||||
|
||||
<div kendoDialogContainer></div>
|
||||
|
||||
<!-- ngx-plain-layout ngx-one-column-layout-->
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { GameRoomService } from '../services/game-room.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-games',
|
||||
@@ -8,7 +9,9 @@ import { Title } from '@angular/platform-browser';
|
||||
})
|
||||
export class GamesComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
constructor(
|
||||
public gameRoomService: GameRoomService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
//this.browserTitleService.setTitle('我是遊戲王');
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
export interface IGamePlayer {
|
||||
id: string;
|
||||
name: string;
|
||||
gameRoomId: string;
|
||||
isPlayer: boolean;
|
||||
signalRClientId: string;
|
||||
tabId: string;
|
||||
isDisconnected: boolean;
|
||||
}
|
||||
|
||||
export class GamePlayer implements IGamePlayer {
|
||||
id: string;
|
||||
name: string;
|
||||
gameRoomId: string;
|
||||
isPlayer: boolean;
|
||||
signalRClientId: string;
|
||||
tabId: string;
|
||||
isDisconnected: boolean;
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { QRCodeModule } from 'angular2-qrcode';
|
||||
import { GamesRoutingModule } from './games-routing.module';
|
||||
import { GamesComponent } from './games.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NbMenuModule, NbInputModule, NbCardModule, NbButtonModule, NbActionsModule, NbCheckboxModule, NbRadioModule, NbDatepickerModule, NbSelectModule, NbIconModule, NbTagModule, NbStepperModule, NbListModule, NbUserModule, NbSpinnerModule, NbDialogModule, NbToggleModule } from '@nebular/theme';
|
||||
import { NbMenuModule, NbInputModule, NbCardModule, NbButtonModule, NbActionsModule, NbCheckboxModule, NbRadioModule, NbDatepickerModule, NbSelectModule, NbIconModule, NbTagModule, NbStepperModule, NbListModule, NbUserModule, NbSpinnerModule, NbDialogModule, NbToggleModule, NbAccordionModule, NbBadgeModule, NbAlertModule } from '@nebular/theme';
|
||||
import { ThemeModule } from '../@theme/theme.module';
|
||||
import { AdminRoutingModule } from '../admin/admin-routing.module';
|
||||
import { AlertDlgModule } from '../ui/alert-dlg/alert-dlg.module';
|
||||
@@ -16,6 +15,46 @@ import { TeamVoteComponent } from './avalon/team-vote/team-vote.component';
|
||||
import { QuestVoteComponent } from './avalon/quest-vote/quest-vote.component';
|
||||
import { VoteResultComponent } from './avalon/vote-result/vote-result.component';
|
||||
import { QuestTableComponent } from './avalon/quest-table/quest-table.component';
|
||||
import { MassiveDarkness2Component } from './massive-darkness2/massive-darkness2.component';
|
||||
import { TreasureBagComponent } from './massive-darkness2/treasure-bag/treasure-bag.component';
|
||||
import { MobsComponent } from './massive-darkness2/mobs/mobs.component';
|
||||
import { CurrencyInputModule } from '../ui/currency-input/currency-input.module';
|
||||
import { DoorEventsComponent } from './massive-darkness2/door-events/door-events.component';
|
||||
import { HeroDashboardComponent } from './massive-darkness2/hero-dashboard/hero-dashboard.component';
|
||||
import { SpawnMobDlgComponent } from './massive-darkness2/mobs/spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
import { MD2IconComponent } from './massive-darkness2/md2-icon/md2-icon.component';
|
||||
import { MobDetailInfoComponent } from './massive-darkness2/mobs/mob-detail-info/mob-detail-info.component';
|
||||
import { MD2HeroSelectComponent } from './massive-darkness2/md2-hero-select/md2-hero-select.component';
|
||||
import { DropDownListModule } from '../ui/drop-down-list/drop-down-list.module';
|
||||
import { BossFightComponent } from './massive-darkness2/boss-fight/boss-fight.component';
|
||||
import { BossActivationComponent } from './massive-darkness2/boss-fight/boss-activation/boss-activation.component';
|
||||
import { MobAttackInfoComponent } from './massive-darkness2/mobs/mob-detail-info/mob-attack-info/mob-attack-info.component';
|
||||
import { MobDefInfoComponent } from './massive-darkness2/mobs/mob-detail-info/mob-def-info/mob-def-info.component';
|
||||
import { MobCombatInfoComponent } from './massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component';
|
||||
import { MobStandInfoComponent } from './massive-darkness2/mobs/mob-stand-info/mob-stand-info.component';
|
||||
import { HtmlEditorModule } from '../ui/html-editor/html-editor.module';
|
||||
import { EditorModule } from '@tinymce/tinymce-angular';
|
||||
import { MD2HtmlEditorComponent } from './massive-darkness2/md2-html-editor/md2-html-editor.component';
|
||||
import { MD2IconPickerDlgComponent } from './massive-darkness2/md2-html-editor/md2-icon-picker-dlg.component';
|
||||
import { EditorModule as KendoEditorModule } from '@progress/kendo-angular-editor';
|
||||
import { ToolBarModule } from '@progress/kendo-angular-toolbar';
|
||||
import { ButtonsModule } from '@progress/kendo-angular-buttons';
|
||||
import { GridModule } from '@progress/kendo-angular-grid';
|
||||
import { DialogModule } from '@progress/kendo-angular-dialog';
|
||||
import { InputsModule } from '@progress/kendo-angular-inputs';
|
||||
import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
|
||||
import { LayoutModule } from '@progress/kendo-angular-layout';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MD2MobInfoMaintenanceComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-info-maintenance.component';
|
||||
import { MD2MobInfoEditorComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-info-editor/md2-mob-info-editor.component';
|
||||
import { MD2MobInfoDetailComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component';
|
||||
import { MD2MobSkillEditorComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-skill-editor/md2-mob-skill-editor.component';
|
||||
import { MD2MobLevelEditorComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-level-editor/md2-mob-level-editor.component';
|
||||
import { MD2BossFightEditorComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component';
|
||||
import { MD2PhaseBuffEditorComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component';
|
||||
import { MD2HeroProfileMaintenanceComponent } from './massive-darkness2/md2-hero-profile-maintenance/md2-hero-profile-maintenance.component';
|
||||
import { MD2HeroProfileEditorComponent } from './massive-darkness2/md2-hero-profile-maintenance/md2-hero-profile-editor/md2-hero-profile-editor.component';
|
||||
import { GameInitDlgComponent } from './massive-darkness2/game-init-dlg/game-init-dlg.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -28,12 +67,40 @@ import { QuestTableComponent } from './avalon/quest-table/quest-table.component'
|
||||
TeamVoteComponent,
|
||||
QuestVoteComponent,
|
||||
VoteResultComponent,
|
||||
QuestTableComponent
|
||||
QuestTableComponent,
|
||||
MassiveDarkness2Component,
|
||||
TreasureBagComponent,
|
||||
MobsComponent,
|
||||
DoorEventsComponent,
|
||||
HeroDashboardComponent,
|
||||
SpawnMobDlgComponent,
|
||||
MD2IconComponent,
|
||||
MobDetailInfoComponent,
|
||||
MD2HeroSelectComponent,
|
||||
BossFightComponent,
|
||||
BossActivationComponent,
|
||||
MobAttackInfoComponent,
|
||||
MobDefInfoComponent,
|
||||
MobCombatInfoComponent,
|
||||
MobStandInfoComponent,
|
||||
MD2HtmlEditorComponent,
|
||||
MD2IconPickerDlgComponent,
|
||||
MD2MobInfoMaintenanceComponent,
|
||||
MD2MobInfoEditorComponent,
|
||||
MD2MobInfoDetailComponent,
|
||||
MD2MobSkillEditorComponent,
|
||||
MD2MobLevelEditorComponent,
|
||||
MD2BossFightEditorComponent,
|
||||
MD2PhaseBuffEditorComponent,
|
||||
MD2HeroProfileMaintenanceComponent,
|
||||
MD2HeroProfileEditorComponent,
|
||||
GameInitDlgComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
GamesRoutingModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
AdminRoutingModule,
|
||||
ThemeModule,
|
||||
NbMenuModule,
|
||||
@@ -52,9 +119,23 @@ import { QuestTableComponent } from './avalon/quest-table/quest-table.component'
|
||||
NbToggleModule,
|
||||
NbUserModule,
|
||||
NbSpinnerModule,
|
||||
NbAccordionModule,
|
||||
NbBadgeModule,
|
||||
NbAlertModule,
|
||||
CurrencyInputModule,
|
||||
NbDialogModule.forRoot(),
|
||||
AlertDlgModule,
|
||||
QRCodeModule,
|
||||
DropDownListModule,
|
||||
HtmlEditorModule,
|
||||
EditorModule,
|
||||
KendoEditorModule,
|
||||
ToolBarModule,
|
||||
ButtonsModule,
|
||||
GridModule,
|
||||
DialogModule,
|
||||
InputsModule,
|
||||
DropDownsModule,
|
||||
LayoutModule
|
||||
]
|
||||
})
|
||||
export class GamesModule { }
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
import { ChangeDetectorRef, Injectable } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { Subject } from "rxjs";
|
||||
import { first, takeUntil } from "rxjs/operators";
|
||||
import { MD2GameInfo, MD2Service } from "../../services/MD2/md2.service";
|
||||
import { SignalRMessage } from "../../services/signal-r.service";
|
||||
import { StateService } from "../../services/state.service";
|
||||
import { ADIcon, MessageBoxConfig } from "../../ui/alert-dlg/alert-dlg.model";
|
||||
import { MD2HeroInfo, MD2Icon, MobInfo, RoundPhase } from "./massive-darkness2.model";
|
||||
import { LoginUserService } from "../../services/login-user.service";
|
||||
import { GamePlayer } from "../games.model";
|
||||
|
||||
@Injectable()
|
||||
export abstract class MD2Base {
|
||||
MD2Icon = MD2Icon;
|
||||
|
||||
public get gameInfo() {
|
||||
return this.md2Service.info;
|
||||
}
|
||||
|
||||
protected isHeroDashboard: boolean = false;
|
||||
protected roomId: string;
|
||||
protected destroy$: Subject<void> = new Subject<void>();
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
protected md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.route.paramMap.pipe(first()).subscribe(params => {
|
||||
if (params.get('roomId')) {
|
||||
this.roomId = params.get('roomId');
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
this.md2Service.refreshUI$.pipe(takeUntil(this.destroy$)).subscribe(result => {
|
||||
this.cdRef.detectChanges();
|
||||
});
|
||||
this.stateService.loginUserService.signalRInitialized.pipe(first()).subscribe(result => {
|
||||
console.log('signalRInitialized');
|
||||
|
||||
this.signalRInitialized();
|
||||
});
|
||||
this.md2Service.signalRService.ReceivedSignalRMessageSubject.pipe(takeUntil(this.destroy$)).subscribe(result => this.handleSignalRCallback(result));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
signalRInitialized() {
|
||||
|
||||
}
|
||||
|
||||
imgUrl(imgPath: string) {
|
||||
return this.md2Service.imgUrl(imgPath);
|
||||
}
|
||||
fileList(folderPath: string) {
|
||||
return this.md2Service.fileList(folderPath);
|
||||
}
|
||||
|
||||
iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
return this.md2Service.iconHtml(icon, cssClass);
|
||||
}
|
||||
|
||||
imgHtml(imgFile: string, cssClass = '') {
|
||||
return `<img src="${this.imgUrl(imgFile)}" class='${cssClass}'>`
|
||||
}
|
||||
detectChanges() {
|
||||
if (!this.cdRef['destroyed']) {
|
||||
this.cdRef.detectChanges();
|
||||
this.refreshUI();
|
||||
this.md2Service.refreshUI$.next();
|
||||
}
|
||||
|
||||
}
|
||||
abstract refreshUI();
|
||||
handleSignalRCallback(message: SignalRMessage): void {
|
||||
console.log('handleSignalRCallback', message);
|
||||
if (message.from) {
|
||||
if (message.from.isGroup) {
|
||||
if (!this.isHeroDashboard) return;
|
||||
} else {
|
||||
if (this.isHeroDashboard && this.md2Service.playerHero?.playerInfo?.signalRClientId == message.from.connectionId) return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.isHeroDashboard) {
|
||||
|
||||
}
|
||||
switch (message.actionType) {
|
||||
case 'hero':
|
||||
let heroInfo = new MD2HeroInfo(JSON.parse(message.parameters['hero']));
|
||||
switch (message.actionName) {
|
||||
case 'join':
|
||||
this.md2Service.heros.push(heroInfo);
|
||||
break;
|
||||
case 'update':
|
||||
this.updateHeroInfo(heroInfo);
|
||||
//Object.assign(heroInfo, exitingHero);
|
||||
break;
|
||||
case 'updateMyHero':
|
||||
if (this.isHeroDashboard) {
|
||||
this.md2Service.playerHero = heroInfo;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.detectChanges();
|
||||
break;
|
||||
case 'heroes':
|
||||
switch (message.actionName) {
|
||||
case 'updateAll':
|
||||
if (this.isHeroDashboard) {
|
||||
let allHeroes = (JSON.parse(message.parameters['heros']) as MD2HeroInfo[]).map(h => new MD2HeroInfo(h));
|
||||
//Remove heroes that are not in the list
|
||||
this.md2Service.info.heros = this.md2Service.heros.filter(h => allHeroes.some(h2 => h2.playerInfo.tabId == h.playerInfo.tabId));
|
||||
allHeroes.forEach(heroInfo => {
|
||||
this.updateHeroInfo(heroInfo);
|
||||
});
|
||||
this.detectChanges();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'GameRoom':
|
||||
switch (message.actionName) {
|
||||
case 'Leaving':
|
||||
let leavingPlayerInfo = message.value as GamePlayer;
|
||||
let leavingHero = this.md2Service.heros.find(h => h.playerInfo.tabId == leavingPlayerInfo.tabId);
|
||||
if (leavingHero) {
|
||||
leavingHero.playerInfo.isDisconnected = true;
|
||||
}
|
||||
//var disconnectHero = this.md2Service.heros.splice(this.md2Service.heros.findIndex(h => h.playerInfo.signalRClientId == leavingPlayerInfo.signalRClientId));
|
||||
//this.md2Service.info.disconnectedHeroes.push(...disconnectHero);
|
||||
this.detectChanges();
|
||||
break;
|
||||
case 'update':
|
||||
if (this.isHeroDashboard) {
|
||||
//Before update game info check the current Hero level
|
||||
let playerHeroLevel = this.md2Service.playerHero?.level;
|
||||
this.md2Service.info = new MD2GameInfo(JSON.parse(message.parameters['gameInfo']) as MD2GameInfo);
|
||||
let playerHero = this.md2Service.heros.find(h => h.playerInfo.tabId == this.stateService.loginUserService.sessionTabId);
|
||||
if (playerHero) {
|
||||
|
||||
playerHero.playerInfo = this.md2Service.gameRoomService.currentPlayer();
|
||||
playerHero.playerInfo.isDisconnected = false;
|
||||
this.md2Service.playerHero = playerHero;
|
||||
this.md2Service.broadcastMyHeroInfo();
|
||||
//When fetch game info, if the hero level is changed, show the level up message
|
||||
if (playerHeroLevel && playerHeroLevel != playerHero.level) {
|
||||
//do i--
|
||||
for (let i = playerHero.level; i > playerHeroLevel; i--) {
|
||||
this.md2Service.msgBoxService.show(`Level Up Lv.${i}`, { text: 'Please do a skill level up!', icon: ADIcon.INFO });
|
||||
}
|
||||
}
|
||||
}
|
||||
this.detectChanges();
|
||||
}
|
||||
break;
|
||||
case 'phase':
|
||||
if (this.isHeroDashboard) {
|
||||
this.md2Service.info.roundPhase = JSON.parse(message.parameters['phase']);
|
||||
this.detectChanges();
|
||||
}
|
||||
break;
|
||||
case 'getGameInfo':
|
||||
|
||||
if (!this.isHeroDashboard) {
|
||||
|
||||
this.md2Service.broadcastGameInfo();
|
||||
}
|
||||
break;
|
||||
case 'sendJoinInfo':
|
||||
//When hero join the game, or reconnect to the game will receive this message
|
||||
if (this.isHeroDashboard && this.md2Service.playerHero) {
|
||||
this.md2Service.playerHero.playerInfo.signalRClientId = message.parameters['signalrconnid'];
|
||||
//Send fetch game info to the hero
|
||||
this.md2Service.broadcastFetchGameInfo();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'message':
|
||||
switch (message.actionName) {
|
||||
case 'popup':
|
||||
let msg = JSON.parse(message.parameters['msg']) as MessageBoxConfig;
|
||||
this.md2Service.msgBoxService.show(msg.title, msg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'roundPhase':
|
||||
switch (message.actionName) {
|
||||
case 'bossFight':
|
||||
this.gameInfo.isBossFight = true;
|
||||
break;
|
||||
default:
|
||||
this.gameInfo.roundPhase = Number.parseInt(message.parameters['phase']);
|
||||
break;
|
||||
}
|
||||
this.detectChanges();
|
||||
break;
|
||||
case 'mobs':
|
||||
if (this.isHeroDashboard) {
|
||||
this.gameInfo.roamingMonsters = JSON.parse(message.parameters['roamingMonsters']);
|
||||
this.gameInfo.mobs = JSON.parse(message.parameters['mobs']);
|
||||
this.detectChanges();
|
||||
}
|
||||
break;
|
||||
case 'heroAction':
|
||||
if (!this.isHeroDashboard) {
|
||||
//this.md2Service.currentActivateHero = this.md2Service.heros.find(h => h.playerInfo.tabId == message.parameters['tabId']);
|
||||
switch (message.actionName) {
|
||||
case 'attackAction':
|
||||
if (this.gameInfo.isBossFight) {
|
||||
this.md2Service.heroAttackingSubject.next(this.md2Service.currentActivateHero);
|
||||
} else {
|
||||
this.gameInfo.showAttackBtn = true;
|
||||
}
|
||||
break;
|
||||
case 'openDoor':
|
||||
//Door component listen for it
|
||||
break;
|
||||
case 'tradeAction':
|
||||
this.md2Service.msgBoxService.show('Trade and Equip', {
|
||||
text: `every one in the <b>same zone with ${this.md2Service.heroFullName(this.md2Service.currentActivateHero)}</b> may freely trade and
|
||||
equip items!`,
|
||||
icon: ADIcon.INFO
|
||||
});
|
||||
break;
|
||||
default:
|
||||
//this.md2Service.roundPhase = Number.parseInt(message.parameters['phase']);
|
||||
break;
|
||||
}
|
||||
this.heroAction(this.md2Service.currentActivateHero, message.actionName);
|
||||
this.detectChanges();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateHeroInfo(heroInfo: MD2HeroInfo) {
|
||||
let exitingHero = this.md2Service.heros.find(h => h.playerInfo.tabId == heroInfo.playerInfo.tabId);
|
||||
if (exitingHero) {
|
||||
//For boss fight, if the hero finished activating, activate the boss
|
||||
let activateBoss = exitingHero.uiActivating && !heroInfo.uiActivating;
|
||||
|
||||
this.md2Service.heros[this.md2Service.heros.indexOf(exitingHero)] = heroInfo;
|
||||
//My hero update
|
||||
if (this.isHeroDashboard && this.md2Service.loginUserService.sessionTabId == heroInfo.playerInfo.tabId) {
|
||||
this.md2Service.playerHero = heroInfo;
|
||||
}
|
||||
if (!this.isHeroDashboard && this.md2Service.info.isBossFight && activateBoss) {
|
||||
this.md2Service.activateBoss();
|
||||
}
|
||||
} else {
|
||||
this.md2Service.heros.push(heroInfo);
|
||||
}
|
||||
if (!this.isHeroDashboard) {
|
||||
if (this.gameInfo.roundPhase == RoundPhase.HeroPhase) {
|
||||
if (!this.md2Service.heros.some(h => h.remainActions > 0) && !this.md2Service.heros.some(h => h.uiActivating)) {
|
||||
if (!this.md2Service.info.isBossFight) {
|
||||
if (this.md2Service.mobs.length > 0 || this.md2Service.roamingMonsters.length > 0) {
|
||||
this.md2Service.msgBoxService.show('Enemy Phase', { icon: ADIcon.WARNING }).pipe(first()).subscribe(result => {
|
||||
this.md2Service.runNextPhase();
|
||||
});
|
||||
} else {
|
||||
this.md2Service.runNextPhase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract heroAction(hero: MD2HeroInfo, action: string);
|
||||
}
|
||||
|
||||
|
||||
@Injectable()
|
||||
export abstract class MD2ComponentBase {
|
||||
protected roomId: string;
|
||||
protected destroy$: Subject<void> = new Subject<void>();
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
protected md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.md2Service.refreshUI$.pipe(takeUntil(this.destroy$)).subscribe(result => {
|
||||
this.cdRef.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
imgUrl(imgPath: string) {
|
||||
return this.md2Service.imgUrl(imgPath);
|
||||
}
|
||||
fileList(folderPath: string) {
|
||||
return this.md2Service.fileList(folderPath);
|
||||
}
|
||||
|
||||
iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
return this.md2Service.iconHtml(icon, cssClass);
|
||||
}
|
||||
detectChanges() {
|
||||
if (!this.cdRef['destroyed']) {
|
||||
this.cdRef.detectChanges();
|
||||
this.refreshUI();
|
||||
this.md2Service.refreshUI$.next();
|
||||
}
|
||||
|
||||
}
|
||||
refreshUI() {
|
||||
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<nb-card>
|
||||
|
||||
<nb-card-body class="g-overflow-hidden">
|
||||
<div class="row form-group">
|
||||
<div class="col-md-5 g-height-700px">
|
||||
<md2-mob-stand-info [mob]="boss" [mode]="mode"></md2-mob-stand-info>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<label class="MD2text g-font-size-40 mt-4" [innerHtml]="bossAction.name">
|
||||
|
||||
</label>
|
||||
<label class="mt-2 g-font-size-20 my-3 MD2IconContainer-lg" [innerHtml]="bossAction.description">
|
||||
|
||||
</label>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
|
||||
<md2-mob-attack-info [mob]="boss"></md2-mob-attack-info>
|
||||
</div>
|
||||
<div class="col-md-8 MD2IconContainer-lg">
|
||||
|
||||
<md2-mob-combat-info [mob]="boss"></md2-mob-combat-info>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
<nb-card-footer>
|
||||
<button class="float-right" nbButton hero status="danger" size="small" (click)="close()">Close</button>
|
||||
<button class="float-right mr-2" nbButton hero status="primary" size="small" (click)="close()">Submit</button>
|
||||
</nb-card-footer>
|
||||
</nb-card>
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
nb-card {
|
||||
height: 70vh;
|
||||
width: 80vw;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BossActivationComponent } from './boss-activation.component';
|
||||
|
||||
describe('BossActivationComponent', () => {
|
||||
let component: BossActivationComponent;
|
||||
let fixture: ComponentFixture<BossActivationComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ BossActivationComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BossActivationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogRef } from '@nebular/theme';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { MD2Service } from '../../../../services/MD2/md2.service';
|
||||
import { MsgBoxService } from '../../../../services/msg-box.service';
|
||||
import { StateService } from '../../../../services/state.service';
|
||||
import { MobDlgType, MD2Icon, MD2HeroInfo, RoundPhase, MobInfo } from '../../massive-darkness2.model';
|
||||
import { MobSkill, IBossFight } from '../../massive-darkness2.model.boss';
|
||||
import { MD2ComponentBase } from '../../MD2Base';
|
||||
import { SpawnMobDlgComponent } from '../../mobs/spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
import { MD2MobInfo, MD2MobSkill } from '../../massive-darkness2.db.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-boss-activation',
|
||||
templateUrl: './boss-activation.component.html',
|
||||
styleUrls: ['./boss-activation.component.scss']
|
||||
})
|
||||
export class BossActivationComponent implements OnInit {
|
||||
boss: MobInfo;
|
||||
bossAction: MD2MobSkill;
|
||||
currentAction: number;
|
||||
allActions: number;
|
||||
MobDlgType = MobDlgType;
|
||||
mode: MobDlgType = MobDlgType.Activating;
|
||||
|
||||
title: string;
|
||||
titleHtml: string;
|
||||
actionHtml: string;
|
||||
MD2Icon = MD2Icon;
|
||||
|
||||
beenAttackedHero = [] as MD2HeroInfo[];
|
||||
attackTarget: string;
|
||||
otherAttackTarget: string;
|
||||
constructor(
|
||||
private dlgRef: NbDialogRef<SpawnMobDlgComponent>,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
this.md2Service.refreshUI$.pipe(takeUntil(this.destroy$)).subscribe(result => {
|
||||
if (!this.cdRef['destroyed']) {
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
close() {
|
||||
//this.boss.standUrl
|
||||
this.md2Service.info.roundPhase = RoundPhase.HeroPhase;
|
||||
this.dlgRef.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<nb-card>
|
||||
<nb-card-header class="MD2text g-font-size-28">
|
||||
{{boss.name}}
|
||||
<button nbButton hero status="primary" (click)="activate()">Action</button>
|
||||
</nb-card-header>
|
||||
<nb-card-body class="g-overflow-hidden">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<!-- <img src="{{boss.standUrl}}" class="w-100 bossStandImg"> -->
|
||||
|
||||
<md2-mob-stand-info [mob]="boss" [mode]="MobDlgType.PreView"></md2-mob-stand-info>
|
||||
<!-- HP and Mana Bars -->
|
||||
<div class="hero-stats-overlay">
|
||||
<div class="stat-bar-overlay hp-bar-overlay">
|
||||
<div class="stat-bar-label-overlay">
|
||||
<md2-icon [icon]="MD2Icon.HP_Color" size="sm"></md2-icon>
|
||||
<span class="stat-value-overlay">{{boss.unitRemainHp}}/{{boss.hp}}</span>
|
||||
</div>
|
||||
<div class="stat-progress-bar-overlay">
|
||||
<div class="stat-progress-fill-overlay hp-fill-overlay"
|
||||
[style.width.%]="(boss.unitRemainHp / boss.hp) * 100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<adj-number-input name="mob{{boss.name}}" [(ngModel)]="boss.unitRemainHp" minimum="0"
|
||||
class="mb-3" title="Boss HP" (hitMinimum)="WIN()">
|
||||
</adj-number-input>
|
||||
<md2-mob-attack-info [mob]="boss">
|
||||
</md2-mob-attack-info>
|
||||
<md2-mob-def-info [mob]="boss"></md2-mob-def-info>
|
||||
</div>
|
||||
<div class="col-md-9 bossSpecialRules" *ngIf="boss.bossFightProfile.specialRules">
|
||||
<div [innerHtml]="boss.bossFightProfile.specialRules"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<md2-mob-combat-info [mob]="boss"></md2-mob-combat-info>
|
||||
<!--
|
||||
<button nbButton hero status="danger" size="small" (click)="attack(boss)">Attack It</button> -->
|
||||
|
||||
<!-- <label class="MD2Text mt-3" [innerHtml]="boss.combatSkill.skillName">
|
||||
</label>
|
||||
<label class="MD2Text" [innerHtml]="boss.combatInfo.skillDescription">
|
||||
</label> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
@@ -0,0 +1,137 @@
|
||||
nb-card {
|
||||
height: 80vh;
|
||||
//width: 80vw;
|
||||
}
|
||||
.bossStandImg {
|
||||
max-height: 67vh;
|
||||
object-fit: contain;
|
||||
}
|
||||
::ng-deep .bossSpecialRules {
|
||||
.MD2Icon {
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
// HP and Mana Bars Overlay
|
||||
.hero-stats-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.5) 70%, transparent 100%);
|
||||
border-radius: 0 0 8px 8px;
|
||||
z-index: 2;
|
||||
width: 95%;
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-bar-overlay {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-bar-label-overlay {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.25rem;
|
||||
gap: 0.5rem;
|
||||
|
||||
md2-icon {
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.stat-value-overlay {
|
||||
color: white;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
margin-bottom: 0.15rem;
|
||||
gap: 0.3rem;
|
||||
|
||||
md2-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.stat-value-overlay {
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stat-progress-bar-overlay {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
height: 16px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-progress-fill-overlay {
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
transition: width 0.5s ease-out;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
animation: shimmer 2s infinite;
|
||||
}
|
||||
|
||||
&.full-stat {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.hp-fill-overlay {
|
||||
background: linear-gradient(90deg, #ff6b6b, #ee5a6f);
|
||||
box-shadow: 0 0 8px rgba(238, 90, 111, 0.6);
|
||||
}
|
||||
|
||||
.mp-fill-overlay {
|
||||
background: linear-gradient(90deg, #4ecdc4, #44a08d);
|
||||
box-shadow: 0 0 8px rgba(68, 160, 141, 0.6);
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BossFightComponent } from './boss-fight.component';
|
||||
|
||||
describe('BossFightComponent', () => {
|
||||
let component: BossFightComponent;
|
||||
let fixture: ComponentFixture<BossFightComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ BossFightComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BossFightComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { Subject, Observable } from 'rxjs';
|
||||
import { first, takeUntil } from 'rxjs/operators';
|
||||
import { MD2Service } from '../../../services/MD2/md2.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { MD2Icon, MobDlgType, MobInfo } from '../massive-darkness2.model';
|
||||
import { MD2ComponentBase } from '../MD2Base';
|
||||
import { SpawnMobDlgComponent } from '../mobs/spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-boss-fight',
|
||||
templateUrl: './boss-fight.component.html',
|
||||
styleUrls: ['./boss-fight.component.scss']
|
||||
})
|
||||
export class BossFightComponent extends MD2ComponentBase {
|
||||
MobDlgType = MobDlgType;
|
||||
MD2Icon = MD2Icon;
|
||||
public get boss() {
|
||||
return this.md2Service.info.boss;
|
||||
}
|
||||
|
||||
|
||||
constructor(
|
||||
private msgBoxService: MsgBoxService,
|
||||
private dlgService: NbDialogService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
this.md2Service.heroAttackingSubject.pipe(takeUntil(this.destroy$)).subscribe(result => {
|
||||
if (this.md2Service.info.isBossFight) {
|
||||
this.attack(this.boss);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
activate() {
|
||||
this.md2Service.activateBoss();
|
||||
}
|
||||
WIN() {
|
||||
this.msgBoxService.show('Win', { text: 'You Win the Boss Fight', icon: ADIcon.INFO });
|
||||
this.md2Service.info.isBossFight = false;
|
||||
this.md2Service.info.boss = undefined;
|
||||
this.md2Service.heros.forEach(h => h.uiBossFight = false);
|
||||
this.md2Service.broadcastGameInfo();
|
||||
}
|
||||
attack(mob: MobInfo) {
|
||||
|
||||
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `Attack ${this.boss.name}`, mode: MobDlgType.BeenAttacked, mob: mob } })
|
||||
.onClose.pipe(first()).subscribe(mobResult => {
|
||||
if (mobResult) {
|
||||
let attackDamage = mobResult.uiWounds;
|
||||
if (attackDamage) {
|
||||
this.boss.unitRemainHp -= attackDamage;
|
||||
if (this.boss.unitRemainHp <= 0) {
|
||||
this.WIN();
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
<img src="{{imgUrl('DoorEvents/Cover.png')}}" width="40px"> Door Event
|
||||
<!-- <button nbButton hero status="warning" size="small" (click)="initMobDecks()" class="float-right">Reset
|
||||
Mobs</button> -->
|
||||
<button nbButton hero status="info" size="small" (click)="drawDoorCard()" class="float-right mr-2">Draw</button>
|
||||
<!-- <button nbButton hero status="warning" size="tiny" (click)="resetTreasureBag()"
|
||||
class="float-right">Reset</button>
|
||||
<button nbButton hero status="primary" size="tiny" (click)="addTreasure(TreasureType.Epic)"
|
||||
class="float-right mr-1">Add
|
||||
Epic</button>
|
||||
<button nbButton hero status="info" size="tiny" (click)="addTreasure(TreasureType.Rare)"
|
||||
class="float-right mr-1">Add
|
||||
Rare</button> -->
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<!-- <b>Content of the bag</b>
|
||||
<hr class="my-1"> -->
|
||||
<div class="row">
|
||||
<!-- this.mobs <div class="col" *ngFor="let treasure of treasureBag.drawingItems">
|
||||
<img src="{{treasure.imageUrl}}" width="40px"> X {{treasure.unitAmount}}
|
||||
</div> -->
|
||||
<div class="col col-md-3 mb-4" *ngFor="let door of this.drawDoorEvents">
|
||||
|
||||
<img class="g-width-95x" src="{{door.imageUrl}}" (click)="showMobImage(mob)" /><br>
|
||||
<label class="badge badge-info">{{door.name}}</label>
|
||||
<button nbButton hero status="primary" size="small" class="float-right"
|
||||
(click)="discard(door)">Discard</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="container">
|
||||
<img id="clip" src="{{imgUrl('Mobs/CoreGame/CoreGameMob.jpg')}}" />
|
||||
</div> -->
|
||||
<!-- <button nbButton hero fullWidth="" status="primary" class="mt-3" (click)="drawTreasure()">I feel
|
||||
LUCKY!!!!!</button> -->
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DoorEventsComponent } from './door-events.component';
|
||||
|
||||
describe('DoorEventsComponent', () => {
|
||||
let component: DoorEventsComponent;
|
||||
let fixture: ComponentFixture<DoorEventsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ DoorEventsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DoorEventsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { FileService } from '../../../services/file.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { ADButtons } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { DrawingBag, DrawingItem } from '../massive-darkness2.model';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { MD2Service } from '../../../services/MD2/md2.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { MD2Base, MD2ComponentBase } from '../MD2Base';
|
||||
import { SignalRMessage } from '../../../services/signal-r.service';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-door-events',
|
||||
templateUrl: './door-events.component.html',
|
||||
styleUrls: ['./door-events.component.scss']
|
||||
})
|
||||
export class DoorEventsComponent extends MD2ComponentBase implements OnInit {
|
||||
|
||||
constructor(
|
||||
private msgBoxService: MsgBoxService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
drawDoorEvents: DrawingItem[];
|
||||
treasureBag: DrawingBag<DrawingItem> = new DrawingBag<DrawingItem>();
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initDoorEvents();
|
||||
|
||||
this.md2Service.signalRService.ReceivedSignalRMessageSubject.subscribe(result => this.handleSignalRCallback(result));
|
||||
}
|
||||
handleSignalRCallback(result: SignalRMessage): void {
|
||||
if (result.actionName == 'openDoor') {
|
||||
this.drawDoorCard();
|
||||
}
|
||||
}
|
||||
|
||||
initDoorEvents() {
|
||||
this.drawDoorEvents = [];
|
||||
this.treasureBag = new DrawingBag<DrawingItem>();
|
||||
for (let i = 1; i <= 30; i++) {
|
||||
this.treasureBag.AddItem(new DrawingItem(i.toString(), '', this.imgUrl(`DoorEvents/DoorEvent-${i}.png`), 1));
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
drawDoorCard() {
|
||||
if (this.treasureBag.bagIsEmpty()) {
|
||||
this.treasureBag.RestoreRemoveItems();
|
||||
}
|
||||
let door = this.treasureBag.DrawAndRemove()[0];
|
||||
|
||||
this.msgBoxService.show('', { text: `<img src="${door.imageUrl}" class="g-height-70vh g-max-width-80vw">`, buttons: ADButtons.YesNo, confirmButtonText: 'Keep It', cancelButtonText: 'Discard' })
|
||||
.pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
door.name = this.md2Service.heroFullName(this.md2Service.currentActivateHero);
|
||||
this.drawDoorEvents.push(door);
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
});
|
||||
}
|
||||
discard(door: DrawingItem) {
|
||||
this.drawDoorEvents.splice(this.drawDoorEvents.indexOf(door), 1);
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ObjectUtils } from "../../../utilities/object-utils";
|
||||
import { IDrawingItem, MobInfo, TreasureItem } from "../massive-darkness2.model";
|
||||
|
||||
export class MD2Clone {
|
||||
|
||||
public static CloneDrawingItem(obj: IDrawingItem) {
|
||||
let type = obj.constructor.name;
|
||||
let cloneObj = null;
|
||||
switch (type) {
|
||||
case "TreasureItem":
|
||||
//let copy = structuredClone(obj);
|
||||
return new TreasureItem(obj['type'], 1);
|
||||
break;
|
||||
case "MobInfo":
|
||||
return new MobInfo(obj);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return ObjectUtils.CloneValue(obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
import { Subject } from "rxjs";
|
||||
import { first, map } from "rxjs/operators";
|
||||
import { environment } from "../../../../../environments/environment";
|
||||
import { ADButtons, ADIcon } from "../../../../ui/alert-dlg/alert-dlg.model";
|
||||
import { MD2Logic } from "../../massive-darkness2.logic";
|
||||
import { AttackInfo, AttackTarget, IMobFactory, MD2Icon, MobInfo, MobType, TreasureItem, TreasureType } from "../../massive-darkness2.model";
|
||||
import { MobSkillType } from "../../massive-darkness2.model.boss";
|
||||
import { MD2DiceSet, MD2MobSkill } from "../../massive-darkness2.db.model";
|
||||
|
||||
const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/Mobs${(id ? `${encodeURI(id)}` : '')}` }
|
||||
|
||||
export abstract class CoreGameRMFactory implements IMobFactory {
|
||||
abstract mobName: string;
|
||||
abstract generate(level: number): MobInfo
|
||||
protected mob: MobInfo;
|
||||
protected loadLevelInfo(mobName: string, level: number) {
|
||||
let levelInfo = CORE_GAME_MOB_LEVEL.find(m => m.name == mobName && level >= m.level);
|
||||
|
||||
this.mob = new MobInfo({
|
||||
type: MobType.RoamingMonster,
|
||||
name: mobName, hpPerHero: levelInfo.hp, level: level, rewardTokens: levelInfo.rewardTokens,
|
||||
attackInfos: levelInfo.attackInfos,
|
||||
defenseInfo: levelInfo.defenseInfo,
|
||||
actionSubject: new Subject<string>()
|
||||
});
|
||||
this.mob.leaderImgUrl = MD2_IMG_URL(`/CoreGame/RoamingMonsters/${this.mob.name}/Stand.png`);
|
||||
//this.mob.minionImgUrl = MD2_IMG_URL(`CoreGame/Mobs/${encodeURI(this.mob.name)}/Minion.png`);
|
||||
|
||||
|
||||
if (level < 3) {
|
||||
this.mob.rewardTokens = 2;
|
||||
this.mob.fixedCarriedTreasure = [new TreasureItem(TreasureType.Rare)];
|
||||
} else if (level < 5) {
|
||||
this.mob.rewardTokens = 2;
|
||||
this.mob.fixedCarriedTreasure = [new TreasureItem(TreasureType.Epic)];
|
||||
} else {
|
||||
this.mob.rewardTokens = 0;
|
||||
this.mob.fixedCarriedTreasure = [new TreasureItem(TreasureType.Epic, 3)];
|
||||
}
|
||||
|
||||
}
|
||||
iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
if (icon == MD2Icon.Fire) {
|
||||
cssClass += ' g-color-google-plus ';
|
||||
}
|
||||
if (icon == MD2Icon.Frost || icon == MD2Icon.Mana) {
|
||||
cssClass += ' g-color-aqua ';
|
||||
}
|
||||
|
||||
if (icon < MD2Icon.RedDice) {
|
||||
return `<span class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
|
||||
|
||||
}
|
||||
else {
|
||||
return `<span class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
export class RMUndeadQueenFactory extends CoreGameRMFactory {
|
||||
mobName: string = 'Ytheria, Undead Queen';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Ytheria, Undead Queen', level);
|
||||
|
||||
this.mob.activateFunction = (mob, msgBoxService, heros) => {
|
||||
let actionResult = '';
|
||||
|
||||
msgBoxService.show('Is There more than 1 Hero in LoS of Undead Queen?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
mob.actions = 0;
|
||||
mob.actionSubject.next(
|
||||
`Undead Queen attacks each Hero in LoS(resolve each attack separately).`
|
||||
);
|
||||
} else {
|
||||
msgBoxService.show('Is There any Hero in LoS of Undead Queen?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
if (level < 3) {
|
||||
actionResult = `Undead Queen +1 ${this.iconHtml(MD2Icon.YellowDice)} when attack`;
|
||||
} else if (level < 5) {
|
||||
actionResult = `Undead Queen +1 ${this.iconHtml(MD2Icon.YellowDice)} 1 ${this.iconHtml(MD2Icon.OrangeDice)} when attack`;
|
||||
} else {
|
||||
actionResult = `Undead Queen +2 ${this.iconHtml(MD2Icon.OrangeDice)} when attack`;
|
||||
}
|
||||
mob.actions = 0;
|
||||
mob.actionSubject.next(
|
||||
actionResult
|
||||
);
|
||||
} else {
|
||||
|
||||
mob.actions = 2;
|
||||
mob.actionSubject.next(
|
||||
`Undead Queen Gains 2 Actions`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.mob.skills = [
|
||||
{
|
||||
description: `Add 1 Minion to each Mob in the Dungeon, if possible.`,
|
||||
type: MobSkillType.Attack,
|
||||
|
||||
skillRoll: 1
|
||||
} as MD2MobSkill];
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
export class RMAndraFactory extends CoreGameRMFactory {
|
||||
mobName: string = 'Andra';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Andra', level);
|
||||
let damage = 2;
|
||||
|
||||
if (level < 3) {
|
||||
damage = 1;
|
||||
}
|
||||
|
||||
this.mob.activateFunction = (mob, msgBoxService, heros) => {
|
||||
let actionResult = '';
|
||||
|
||||
mob.actions = 0;
|
||||
msgBoxService.show('Is Andra in the Dungeon?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
mob.actions = 0;
|
||||
|
||||
msgBoxService.show('Is Any Hero in the LoS of Andra?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
|
||||
mob.actionSubject.next(
|
||||
`Andra attack the Hero with the lowest HP in LoS.<br>then Put Andra to out side of Dungeon.(Hero is not reachable but not dead)`
|
||||
);
|
||||
} else {
|
||||
mob.actions = 0;
|
||||
mob.actionSubject.next(
|
||||
`Put Andra to out side of Dungeon.(Hero is not reachable but not dead)`
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let beenAttackHero = MD2Logic.getTargetHerosByFilter(heros, AttackTarget.LeastHp, true)[0];
|
||||
mob.actionSubject.next(
|
||||
`Place Andra in the same zone of ${MD2Logic.heroFullName(beenAttackHero)} and attack that Hero.`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.mob.skills = [
|
||||
{
|
||||
description: `Deal ${damage} wound to another Hero with the lowest HP in LoS`,
|
||||
type: MobSkillType.Combat,
|
||||
skillRoll: 1
|
||||
} as MD2MobSkill];
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
export class RMTheGhoulFactory extends CoreGameRMFactory {
|
||||
mobName: string = 'The Ghoul';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('The Ghoul', level);
|
||||
let health = 2;
|
||||
|
||||
if (level < 3) {
|
||||
health = 5;
|
||||
} else if (level < 5) {
|
||||
health = 8;
|
||||
} else {
|
||||
health = 10;
|
||||
}
|
||||
|
||||
this.mob.activateFunction = (mob, msgBoxService, heros) => {
|
||||
let actionResult = '';
|
||||
|
||||
mob.actions = 0;
|
||||
msgBoxService.show('Is there any <b>Mob with minion</b> in The Ghoul zone?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
mob.unitRemainHp += health;
|
||||
mob.actionSubject.next(
|
||||
`Kill 1 minion in The Ghoul zone(player choose), The Ghoul heals ${health}.`
|
||||
);
|
||||
} else {
|
||||
mob.actionSubject.next(
|
||||
`The Ghoul moves 3 Zones toward the closest Hero and attack him/her, if possible.`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.mob.skills = [
|
||||
{
|
||||
description: `Move the closest <b>Mob with minion</b> 1 Zone toward The Ghoul.`,
|
||||
type: MobSkillType.Combat,
|
||||
skillRoll: 1
|
||||
} as MD2MobSkill];
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
export class RMLyidanIncubusLordFactory extends CoreGameRMFactory {
|
||||
mobName: string = 'Lyidan, Incubus Lord';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Lyidan, Incubus Lord', level);
|
||||
this.mob.activateFunction = (mob, msgBoxService, heros) => {
|
||||
let actionResult = '';
|
||||
|
||||
mob.actions = 0;
|
||||
msgBoxService.show('Is Incubus Lord in a Light Zone?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
mob.unitRemainHp -= 3;
|
||||
mob.actionSubject.next(
|
||||
`The Incubus Lord got 3 Wounds, Move it to the closest Shadow Zone.`
|
||||
);
|
||||
} else {
|
||||
msgBoxService.show('Is there a Hero up to 3 Zones away(regardless of LoS) from The Incubus Lord?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
mob.actionSubject.next(
|
||||
`Place The Incubus Lord in the zone of furthest Hero up to 3 Zones away.<br>` +
|
||||
`Add 1 ${this.iconHtml(MD2Icon.Fire)} to that Hero and attack him/her.`
|
||||
);
|
||||
} else {
|
||||
mob.actions = 2;
|
||||
mob.actionSubject.next(
|
||||
`The Incubus Lord 2 Actions`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.mob.skills = [
|
||||
{
|
||||
description: `After combat, resolve all ${this.iconHtml(MD2Icon.Fire)} on the defending Hero(once per combat).`,
|
||||
type: MobSkillType.Attack,
|
||||
skillRoll: 1
|
||||
} as MD2MobSkill];
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class RMBalrogFactory extends CoreGameRMFactory {
|
||||
mobName: string = 'Balrog';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Balrog', level);
|
||||
this.mob.extraRule = `When Balrog is in the Dungeon, ${this.iconHtml(MD2Icon.Fire)} on Heros can't be removed, Heros still suffer its effects when activating.`
|
||||
this.mob.activateFunction = (mob, msgBoxService, heros) => {
|
||||
let actionResult = '';
|
||||
|
||||
mob.actions = 0;
|
||||
let noFireHeros = heros.filter(h => h.fireToken == 0);
|
||||
if (noFireHeros.length == 0) {
|
||||
|
||||
mob.actions = 3;
|
||||
mob.actionSubject.next(
|
||||
`The Balrog gains 3 Actions`
|
||||
);
|
||||
|
||||
} else {
|
||||
let fireTokens = Math.round(Math.random() * 2) + 1;
|
||||
mob.actionSubject.next(
|
||||
`Balrog ,moves 2 Zones toward to ${MD2Logic.getTargetHerosHtml(noFireHeros)} and Each Hero in ${this.iconHtml(MD2Icon.Magic)} range takes ${fireTokens} ${this.iconHtml(MD2Icon.Fire)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.mob.skills = [
|
||||
{
|
||||
description: `The Hero takes 1 ${this.iconHtml(MD2Icon.Fire)}`,
|
||||
type: MobSkillType.Combat,
|
||||
skillRoll: 1
|
||||
} as MD2MobSkill];
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
export const CoreGameRMFactories = [
|
||||
new RMUndeadQueenFactory(),
|
||||
new RMAndraFactory(),
|
||||
new RMTheGhoulFactory(),
|
||||
new RMLyidanIncubusLordFactory(),
|
||||
new RMBalrogFactory()
|
||||
];
|
||||
@@ -0,0 +1,40 @@
|
||||
<nb-card status="info" size="large">
|
||||
<nb-card-header>
|
||||
<img src="{{md2Service.imgUrl('HeroIcon.png')}}" width="40px">
|
||||
<span class="ml-2 g-font-size-17">Initialize Game</span>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="form-group">
|
||||
<label class="label">Select Game Bundles:</label>
|
||||
<div class="form-group" *ngFor="let bundle of bundleOptions">
|
||||
<nb-checkbox [checked]="isBundleEnabled(bundle.value)" (checkedChange)="toggleBundle(bundle.value)">
|
||||
{{ bundle.label }}
|
||||
</nb-checkbox>
|
||||
</div>
|
||||
<small class="form-text text-muted" *ngIf="enabledBundles.length === 0">
|
||||
At least one bundle must be selected.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<nb-checkbox [(checked)]="enableMobSpecialRule">
|
||||
Enable Mob Special Rules
|
||||
</nb-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<nb-checkbox [(checked)]="enableHeroBetrayal">
|
||||
Enable Hero Betrayal
|
||||
</nb-checkbox>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
<nb-card-footer>
|
||||
<button class="float-right" nbButton hero status="warning" size="small" (click)="cancel()">
|
||||
Cancel
|
||||
</button>
|
||||
<button class="float-right mr-2" nbButton hero status="primary" size="small"
|
||||
[disabled]="enabledBundles.length === 0" (click)="submit()">
|
||||
Initialize
|
||||
</button>
|
||||
</nb-card-footer>
|
||||
</nb-card>
|
||||
@@ -0,0 +1,12 @@
|
||||
nb-card {
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
nb-checkbox {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { NbDialogRef } from '@nebular/theme';
|
||||
import { GameBundle } from '../massive-darkness2.db.model';
|
||||
import { MD2Service } from '../../../services/MD2/md2.service';
|
||||
import { StringUtils } from '../../../utilities/string-utils';
|
||||
|
||||
export interface GameInitConfig {
|
||||
enabledBundles: GameBundle[];
|
||||
enableMobSpecialRule: boolean;
|
||||
enableHeroBetrayal: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-game-init-dlg',
|
||||
templateUrl: './game-init-dlg.component.html',
|
||||
styleUrls: ['./game-init-dlg.component.scss']
|
||||
})
|
||||
export class GameInitDlgComponent implements OnInit {
|
||||
GameBundle = GameBundle;
|
||||
|
||||
@Input() initialConfig: GameInitConfig;
|
||||
|
||||
enabledBundles: GameBundle[] = [GameBundle.CoreGame];
|
||||
enableMobSpecialRule: boolean = false;
|
||||
enableHeroBetrayal: boolean = false;
|
||||
|
||||
bundleOptions = [];
|
||||
|
||||
constructor(
|
||||
private dlgRef: NbDialogRef<GameInitDlgComponent>,
|
||||
public md2Service: MD2Service
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
//For each GameBundle, create a new option
|
||||
this.bundleOptions = Object.values(GameBundle).filter(b => !isNaN(Number(b))).map(b => ({
|
||||
value: b as GameBundle, label: StringUtils.camelToTitle(GameBundle[b as GameBundle] as string)
|
||||
}));
|
||||
// Initialize from context if provided
|
||||
if (this.initialConfig) {
|
||||
this.enabledBundles = [...this.initialConfig.enabledBundles];
|
||||
this.enableMobSpecialRule = this.initialConfig.enableMobSpecialRule;
|
||||
this.enableHeroBetrayal = this.initialConfig.enableHeroBetrayal;
|
||||
}
|
||||
}
|
||||
|
||||
toggleBundle(bundle: GameBundle): void {
|
||||
const index = this.enabledBundles.indexOf(bundle);
|
||||
if (index > -1) {
|
||||
this.enabledBundles.splice(index, 1);
|
||||
} else {
|
||||
this.enabledBundles.push(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
isBundleEnabled(bundle: GameBundle): boolean {
|
||||
return this.enabledBundles.includes(bundle);
|
||||
}
|
||||
|
||||
submit(): void {
|
||||
const config: GameInitConfig = {
|
||||
enabledBundles: this.enabledBundles,
|
||||
enableMobSpecialRule: this.enableMobSpecialRule,
|
||||
enableHeroBetrayal: this.enableHeroBetrayal
|
||||
};
|
||||
this.dlgRef.close(config);
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.dlgRef.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
<!-- Hero Selection Screen - Initial -->
|
||||
<nb-card *ngIf="!hero && !isSelectingHero" class="hero-selection-card">
|
||||
<nb-card-body class="hero-selection-body">
|
||||
<div class="hero-selection-content">
|
||||
<h2 class="hero-selection-title">Choose Your Hero</h2>
|
||||
<p class="hero-selection-subtitle">Begin your epic adventure</p>
|
||||
<button nbButton hero status="primary" size="large" class="hero-selection-btn" (click)="initHero()">
|
||||
<nb-icon icon="star-outline" class="mr-2"></nb-icon>
|
||||
Select Hero
|
||||
</button>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
||||
<!-- Hero Selection Panel -->
|
||||
<div *ngIf="!hero && isSelectingHero && currentSelectingHero" class="hero-selection-panel">
|
||||
<div class="row no-gutters hero-selection-row">
|
||||
<div class="col-12 hero-selection-left">
|
||||
<div class="hero-selection-card-wrapper">
|
||||
<div class="hero-selection-header">
|
||||
<div class="hero-selection-title-bar">
|
||||
<h3 class="hero-selection-hero-name">{{currentSelectingHero.name}}</h3>
|
||||
<span class="hero-selection-class">{{HeroClass[selectedHeroClass]}}</span>
|
||||
<span class="hero-selection-counter">({{currentHeroIndex + 1}} / {{heros.length}})</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-selection-content-area">
|
||||
<div class="row no-gutters h-100">
|
||||
<div class="col-6 hero-select-image-col">
|
||||
<div class="hero-select-image-wrapper"
|
||||
[style.background-image]="'url(' + imgUrl('/Mobs/BG.png') + ')'">
|
||||
<img src="{{imgUrl('Heros/'+className+'.png')}}" class="hero-select-image"
|
||||
alt="{{currentSelectingHero.name}}">
|
||||
<!-- HP and Mana Bars -->
|
||||
<div class="hero-select-stats-overlay">
|
||||
<div class="stat-bar-overlay hp-bar-overlay">
|
||||
<div class="stat-bar-label-overlay">
|
||||
<md2-icon [icon]="MD2Icon.HP_Color" size="sm"></md2-icon>
|
||||
<span class="stat-value-overlay">{{currentSelectingHero.hpMaximum}}</span>
|
||||
</div>
|
||||
<div class="stat-progress-bar-overlay">
|
||||
<div class="stat-progress-fill-overlay hp-fill-overlay full-stat"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-bar-overlay mp-bar-overlay">
|
||||
<div class="stat-bar-label-overlay">
|
||||
<md2-icon [icon]="MD2Icon.Mana_Color" size="sm"></md2-icon>
|
||||
<span class="stat-value-overlay">{{currentSelectingHero.mpMaximum}}</span>
|
||||
</div>
|
||||
<div class="stat-progress-bar-overlay">
|
||||
<div class="stat-progress-fill-overlay mp-fill-overlay full-stat"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 hero-select-skills-col">
|
||||
<div class="hero-select-skills">
|
||||
<div class="skills-title">Abilities</div>
|
||||
<div class="skill-content" [innerHTML]="currentSelectingHero.skillHtml"></div>
|
||||
<div class="skills-title shadow-skills-title">Shadow Abilities</div>
|
||||
<div class="skill-content shadow-skill-content"
|
||||
[innerHTML]="currentSelectingHero.shadowSkillHtml"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-selection-actions">
|
||||
<button nbButton hero status="basic" class="nav-hero-btn" (click)="previousHero()">
|
||||
<nb-icon icon="chevron-back-outline" class="mr-1"></nb-icon>
|
||||
Previous
|
||||
</button>
|
||||
<button nbButton hero status="primary" class="select-hero-btn" (click)="selectCurrentHero()">
|
||||
<nb-icon icon="checkmark-circle-outline" class="mr-2"></nb-icon>
|
||||
It's Me!
|
||||
</button>
|
||||
<button nbButton hero status="basic" class="nav-hero-btn" (click)="nextHero()">
|
||||
Next
|
||||
<nb-icon icon="chevron-forward-outline" class="ml-1"></nb-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="hero">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-12 col-sm-7">
|
||||
<div class="tp-wrapper mb-2">
|
||||
<div class="tp-box" [@flipState]="flip">
|
||||
<div class="tp-box__side tp-box__front ">
|
||||
|
||||
<div class="hero-card-content">
|
||||
<div class="row no-gutters h-100">
|
||||
<div class="col-6 hero-image-col">
|
||||
<div class="hero-image-wrapper"
|
||||
[style.background-image]="'url(' + imgUrl('/Mobs/BG.png') + ')'">
|
||||
<img src="{{imgUrl('Heros/'+className+'.png')}}" class="hero-image"
|
||||
(click)="toggleFlip()" alt="{{hero.name}}">
|
||||
<!-- HP and Mana Bars -->
|
||||
<div class="hero-stats-overlay">
|
||||
<div class="stat-bar-overlay hp-bar-overlay">
|
||||
<div class="stat-bar-label-overlay">
|
||||
<md2-icon [icon]="MD2Icon.HP_Color" size="sm"></md2-icon>
|
||||
<span
|
||||
class="stat-value-overlay">{{hero.hp}}/{{hero.hpMaximum}}</span>
|
||||
</div>
|
||||
<div class="stat-progress-bar-overlay">
|
||||
<div class="stat-progress-fill-overlay hp-fill-overlay"
|
||||
[style.width.%]="(hero.hp / hero.hpMaximum) * 100"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-bar-overlay mp-bar-overlay">
|
||||
<div class="stat-bar-label-overlay">
|
||||
<md2-icon [icon]="MD2Icon.Mana_Color" size="sm"></md2-icon>
|
||||
<span
|
||||
class="stat-value-overlay">{{hero.mp}}/{{hero.mpMaximum}}</span>
|
||||
</div>
|
||||
<div class="stat-progress-bar-overlay">
|
||||
<div class="stat-progress-fill-overlay mp-fill-overlay"
|
||||
[style.width.%]="(hero.mp / hero.mpMaximum) * 100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 hero-skills-col">
|
||||
<div class="hero-skills">
|
||||
<div class="skills-title" (click)="showSkills('abilities')">Abilities</div>
|
||||
<div class="skill-content" [innerHTML]="hero.skillHtml"></div>
|
||||
<div class="skills-title shadow-skills-title" (click)="showSkills('shadow')">
|
||||
Shadow Abilities</div>
|
||||
<div class="skill-content shadow-skill-content"
|
||||
[innerHTML]="hero.shadowSkillHtml"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <img class="MD2HeroCard " src="{{imgUrl('Heros/'+className+'.jpg')}}" (click)="toggleFlip()"> -->
|
||||
|
||||
<!-- Action Buttons (Desktop/Landscape) -->
|
||||
<div class="hero-actions d-block">
|
||||
<div class="action-buttons-group" *ngIf="hero.uiActivating && hero.remainActions > 0">
|
||||
<button nbButton hero class="action-btn" status="info" (click)="moveAction()"
|
||||
*ngIf="!showMoveAction">
|
||||
<nb-icon icon="arrow-forward-outline" class="mr-1"></nb-icon>
|
||||
Move
|
||||
</button>
|
||||
<button nbButton hero class="action-btn" status="info" (click)="moveActionEnd()"
|
||||
*ngIf="showMoveAction">
|
||||
<nb-icon icon="checkmark-outline" class="mr-1"></nb-icon>
|
||||
End Move
|
||||
</button>
|
||||
<button nbButton hero class="action-btn" status="danger"
|
||||
(click)="action('attackAction')" *ngIf="!showMoveAction && allowAttack">
|
||||
<nb-icon icon="flash-outline" class="mr-1"></nb-icon>
|
||||
Attack!
|
||||
</button>
|
||||
<button nbButton hero class="action-btn" status="info" (click)="action('tradeAction')"
|
||||
*ngIf="!showMoveAction">
|
||||
<nb-icon icon="swap-outline" class="mr-1"></nb-icon>
|
||||
Trade
|
||||
</button>
|
||||
<button nbButton hero class="action-btn" status="success"
|
||||
(click)="action('recoveryAction')" *ngIf="!showMoveAction">
|
||||
<nb-icon icon="heart-outline" class="mr-1"></nb-icon>
|
||||
Recovery
|
||||
</button>
|
||||
</div>
|
||||
<button nbButton hero class="action-btn special-action-btn" status="info" fullWidth
|
||||
(click)="openDoor()" *ngIf="showMoveAction">
|
||||
<nb-icon icon="grid-outline" class="mr-1"></nb-icon>
|
||||
Open Door
|
||||
</button>
|
||||
<button nbButton hero fullWidth status="info" class="start-activation-btn"
|
||||
*ngIf="allowStartAction" (click)="startActivation()">
|
||||
<nb-icon icon="play-circle-outline" class="mr-2"></nb-icon>
|
||||
Start Activation
|
||||
</button>
|
||||
<button nbButton hero fullWidth status="warning" class="end-activation-btn"
|
||||
*ngIf="hero.uiActivating" (click)="endActivation()">
|
||||
<nb-icon icon="stop-circle-outline" class="mr-2"></nb-icon>
|
||||
End Activation
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tp-box__side tp-box__back">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-6">
|
||||
<img class="MD2HeroCard " src="{{imgUrl('Heros/Guide/'+className+'.jpg')}}"
|
||||
(click)="toggleFlip()">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<img class="MD2HeroCard " src="{{imgUrl('Sets/Shadowbane/'+className+'.png')}}"
|
||||
(click)="toggleFlip()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="g-max-height-80vh mb-2">
|
||||
<img class="MD2HeroCard" src="{{hero.imgUrl}}">
|
||||
<div class="MD2HeroCard">
|
||||
<span class="MD2text MD2Name">{{hero.name}}</span>
|
||||
<span class="MD2text MD2Hp">{{hero.hpMaximum}}</span>
|
||||
<span class="MD2text MD2Mp">{{hero.mpMaximum}}</span>
|
||||
</div>
|
||||
<img class="MD2HeroCard" src="{{hero.imgUrl}}">
|
||||
<img class="MD2HeroCard HpMpBar" src="{{imgUrl('/Heros/Template/Border.png')}}">
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-5">
|
||||
<nb-card>
|
||||
<nb-card-body>
|
||||
<div class="row no-gutters">
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="heroHP" [(ngModel)]="hero.hp" [maximum]="hero.hpMaximum" minimum="0"
|
||||
title="{{imgHtml('HpIcon.png','g-height-25')}}" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
||||
|
||||
<adj-number-input name="heroLevel" [(ngModel)]="hero.level" minimum="1" maximum="5"
|
||||
title="Level" (blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="heroMana" [(ngModel)]="hero.mp" [maximum]="hero.mpMaximum"
|
||||
minimum="0" title="{{imgHtml('HeroIcon.png','g-height-25')}}" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="heroExp" [(ngModel)]="hero.exp" minimum="0" title="Exp"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="heroFire" [(ngModel)]="hero.fireToken" minimum="0"
|
||||
title="{{iconHtml(MD2Icon.Fire,'g-color-google-plus mr-1 g-font-size-18')}}Fire Token"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="heroFrozen" [(ngModel)]="hero.frozenToken" minimum="0"
|
||||
title="{{iconHtml(MD2Icon.Frost,'g-color-aqua mr-1 g-font-size-18')}}Frozen Token"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="remainActions" [(ngModel)]="hero.remainActions" minimum="0"
|
||||
title="Remain Actions" (blur)="heroUpdateDebounceTimer.resetTimer()" hideIncreaseBtn
|
||||
*ngIf="hero.uiActivating">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-6" *ngIf="hero.class==HeroClass.Berserker">
|
||||
<adj-number-input name="heroRage" [(ngModel)]="hero.rage" minimum="0" maximum="7"
|
||||
title="{{iconHtml(MD2Icon.Rage,'g-color-google-plus mr-1 g-font-size-18')}}Rage"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-6" *ngIf="hero.uiShowExtraToken">
|
||||
<adj-number-input name="heroExtraToken" [(ngModel)]="hero.extraToken" minimum="0"
|
||||
title="{{hero.uiExtraTokenHtml}} {{hero.uiExtraTokenName}}"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-6" *ngIf="hero.uiShowExtraToken2">
|
||||
<adj-number-input name="heroExtraToken2" [(ngModel)]="hero.extraToken2" minimum="0"
|
||||
title="{{hero.uiExtraTokenHtml2}} {{hero.uiExtraTokenName2}}"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div *ngIf="md2Service.info.isBossFight"></div>
|
||||
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- <nb-flip-card *ngIf="hero">
|
||||
<nb-card-front>
|
||||
<nb-card>
|
||||
|
||||
<nb-card-body>
|
||||
|
||||
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</nb-card-front>
|
||||
<nb-card-back>
|
||||
<nb-card>
|
||||
<nb-card-body>
|
||||
Back card text
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</nb-card-back>
|
||||
</nb-flip-card> -->
|
||||
@@ -0,0 +1,815 @@
|
||||
// Hero Selection Screen
|
||||
.hero-selection-card {
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-selection-body {
|
||||
padding: 3rem 2rem;
|
||||
text-align: center;
|
||||
background: linear-gradient(135deg, rgba(102, 126, 234, 0.9) 0%, rgba(118, 75, 162, 0.9) 100%);
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-content {
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hero-selection-title {
|
||||
color: white;
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.75rem;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-subtitle {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-btn {
|
||||
padding: 0.75rem 2rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
border-radius: 50px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.6rem 1.5rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Hero Selection Panel
|
||||
.hero-selection-panel {
|
||||
height: 85vh;
|
||||
max-height: 85vh;
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media (orientation: landscape) {
|
||||
height: 85vh;
|
||||
max-height: 85vh;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) {
|
||||
height: 85vh;
|
||||
max-height: 85vh;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-height: 667px) {
|
||||
height: 85vh;
|
||||
max-height: 85vh;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
height: 85vh;
|
||||
max-height: 85vh;
|
||||
padding: 0.15rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-row {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hero-selection-left {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-card-wrapper {
|
||||
height: 100%;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-header {
|
||||
padding: 0.75rem 1rem;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
|
||||
flex-shrink: 0;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-title-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 767px) {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-hero-name {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 767px) {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-class {
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.9;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 500;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 767px) {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-counter {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.8;
|
||||
margin-left: auto;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 767px) {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-content-area {
|
||||
flex: 1;
|
||||
padding: 0.75rem;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.4rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 767px) {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-select-image-col,
|
||||
.hero-select-skills-col {
|
||||
padding: 0.4rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.2rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 767px) {
|
||||
padding: 0.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-select-image-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background: #f8f9fa;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hero-select-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hero-select-stats-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.5) 70%, transparent 100%);
|
||||
border-radius: 0 0 8px 8px;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-select-skills {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem;
|
||||
overflow-y: auto;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
min-height: 0;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.4rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 767px) {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-selection-actions {
|
||||
padding: 0.75rem;
|
||||
border-top: 2px solid #e9ecef;
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.5rem;
|
||||
gap: 0.35rem;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 767px) {
|
||||
padding: 0.5rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-hero-btn {
|
||||
flex: 1;
|
||||
min-height: 40px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s;
|
||||
padding: 0.5rem;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
min-height: 32px;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.35rem 0.5rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 767px) {
|
||||
min-height: 38px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.select-hero-btn {
|
||||
flex: 2;
|
||||
min-height: 40px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||
transition: all 0.3s;
|
||||
padding: 0.5rem;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.5);
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
min-height: 32px;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.35rem 0.5rem;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 767px) {
|
||||
min-height: 38px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
.MD2Hp {
|
||||
font-size: xx-large;
|
||||
position: fixed;
|
||||
z-index: 92;
|
||||
color: white;
|
||||
bottom: 23%;
|
||||
left: 46%;
|
||||
}
|
||||
.MD2Mp {
|
||||
font-size: xx-large;
|
||||
position: fixed;
|
||||
z-index: 92;
|
||||
color: white;
|
||||
bottom: 22%;
|
||||
left: 7%;
|
||||
}
|
||||
|
||||
.MD2Name {
|
||||
font-size: xx-large;
|
||||
position: fixed;
|
||||
z-index: 92;
|
||||
color: #2e2e30;
|
||||
bottom: 20%;
|
||||
left: 23%;
|
||||
}
|
||||
.MD2HeroCard {
|
||||
//position: absolute;
|
||||
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
&.HpMpBar {
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
.tp-wrapper {
|
||||
-webkit-perspective: 800px;
|
||||
perspective: 800px;
|
||||
height: 40vh; // Default for portrait
|
||||
|
||||
@media (orientation: landscape) {
|
||||
height: 85vh;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-height: 667px) {
|
||||
height: 50vh;
|
||||
}
|
||||
}
|
||||
|
||||
.tp-box {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-transform-style: preserve-3d;
|
||||
transform-style: preserve-3d;
|
||||
-webkit-transform: transform 1s;
|
||||
-ms-transform: transform 1s;
|
||||
transform: transform 1s;
|
||||
}
|
||||
|
||||
.tp-box__side {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Hero Card Content Section
|
||||
.hero-card-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding: 0.5rem;
|
||||
overflow: hidden;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-image-col {
|
||||
padding: 0.25rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.15rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-image-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background: #f8f9fa;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hero-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
}
|
||||
|
||||
// HP and Mana Bars Overlay
|
||||
.hero-stats-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0.5rem;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.5) 70%, transparent 100%);
|
||||
border-radius: 0 0 8px 8px;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-bar-overlay {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-bar-label-overlay {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.25rem;
|
||||
gap: 0.5rem;
|
||||
|
||||
md2-icon {
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.stat-value-overlay {
|
||||
color: white;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
margin-bottom: 0.15rem;
|
||||
gap: 0.3rem;
|
||||
|
||||
md2-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.stat-value-overlay {
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stat-progress-bar-overlay {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
height: 16px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-progress-fill-overlay {
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
transition: width 0.5s ease-out;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
animation: shimmer 2s infinite;
|
||||
}
|
||||
|
||||
&.full-stat {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.hp-fill-overlay {
|
||||
background: linear-gradient(90deg, #ff6b6b, #ee5a6f);
|
||||
box-shadow: 0 0 8px rgba(238, 90, 111, 0.6);
|
||||
}
|
||||
|
||||
.mp-fill-overlay {
|
||||
background: linear-gradient(90deg, #4ecdc4, #44a08d);
|
||||
box-shadow: 0 0 8px rgba(68, 160, 141, 0.6);
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.hero-skills-col {
|
||||
padding: 0.25rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.15rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-skills {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem;
|
||||
overflow-y: auto;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.skills-title {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
color: #667eea;
|
||||
margin-bottom: 0.5rem;
|
||||
padding-bottom: 0.25rem;
|
||||
border-bottom: 2px solid #667eea;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
font-size: 0.75rem;
|
||||
margin-bottom: 0.3rem;
|
||||
padding-bottom: 0.15rem;
|
||||
}
|
||||
}
|
||||
|
||||
.shadow-skills-title {
|
||||
margin-top: 1rem;
|
||||
color: #764ba2;
|
||||
border-bottom-color: #764ba2;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.skill-content {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
font-size: 0.7rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
.shadow-skill-content {
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
// Action Buttons Section
|
||||
.hero-actions {
|
||||
padding: 0.75rem;
|
||||
border-top: 2px solid #e9ecef;
|
||||
margin-top: auto;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.5rem;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
gap: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
min-width: 100px;
|
||||
min-height: 40px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
min-width: 70px;
|
||||
min-height: 36px;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.35rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.special-action-btn {
|
||||
margin-bottom: 0.5rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
min-height: 40px;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
margin-bottom: 0.35rem;
|
||||
min-height: 32px;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.35rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.start-activation-btn {
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
padding: 0.75rem;
|
||||
min-height: 44px;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.5rem;
|
||||
min-height: 36px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.end-activation-btn {
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
padding: 0.75rem;
|
||||
min-height: 44px;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0 2px 8px rgba(255, 193, 7, 0.3);
|
||||
margin-top: 0.5rem;
|
||||
background: linear-gradient(135deg, #ffc107 0%, #ff9800 100%);
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.4);
|
||||
}
|
||||
|
||||
@media (max-height: 450px) and (orientation: landscape) {
|
||||
padding: 0.5rem;
|
||||
min-height: 36px;
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
.tp-box__front {
|
||||
-webkit-transform: rotateY(0deg);
|
||||
-ms-transform: rotateY(0deg);
|
||||
transform: rotateY(0deg);
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.tp-box__back {
|
||||
-webkit-transform: rotateY(-180deg);
|
||||
-ms-transform: rotateY(-180deg);
|
||||
transform: rotateY(-180deg);
|
||||
}
|
||||
::ng-deep .skill-content .MD2Icon {
|
||||
font-size: 30px;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HeroDashboardComponent } from './hero-dashboard.component';
|
||||
|
||||
describe('HeroDashboardComponent', () => {
|
||||
let component: HeroDashboardComponent;
|
||||
let fixture: ComponentFixture<HeroDashboardComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ HeroDashboardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HeroDashboardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,307 @@
|
||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { DropDownOption } from '../../../entity/dropDownOption';
|
||||
import { GameRoomService } from '../../../services/game-room.service';
|
||||
import { MD2Service } from '../../../services/MD2/md2.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { ADButtonColor, ADButtons } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { ArrayUtils } from '../../../utilities/array-utils';
|
||||
import { StringUtils } from '../../../utilities/string-utils';
|
||||
import { DebounceTimer } from '../../../utilities/timer-utils';
|
||||
import { HeroClass, MD2HeroInfo, MD2HeroProfile, MD2Icon } from '../massive-darkness2.model';
|
||||
import { MD2Base } from '../MD2Base';
|
||||
import { MD2HeroProfileService } from '../service/massive-darkness2.service';
|
||||
import { SignalRService } from '../../../services/signal-r.service';
|
||||
import { NbToastrService } from '@nebular/theme';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-hero-dashboard',
|
||||
templateUrl: './hero-dashboard.component.html',
|
||||
styleUrls: ['./hero-dashboard.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [
|
||||
trigger('flipState', [
|
||||
state('active', style({
|
||||
transform: 'rotateY(179deg)'
|
||||
})),
|
||||
state('inactive', style({
|
||||
transform: 'rotateY(0)'
|
||||
})),
|
||||
transition('active => inactive', animate('500ms ease-out')),
|
||||
transition('inactive => active', animate('500ms ease-in'))
|
||||
])
|
||||
]
|
||||
})
|
||||
export class HeroDashboardComponent extends MD2Base implements OnInit {
|
||||
MD2Icon = MD2Icon;
|
||||
heroAction(hero: MD2HeroInfo, action: string) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
showMoveAction: boolean = false;
|
||||
HeroClass = HeroClass;
|
||||
refreshUI() {
|
||||
console.log('HeroDashboard RefreshUI');
|
||||
|
||||
}
|
||||
|
||||
heroUpdateDebounceTimer = new DebounceTimer(1000, () => {
|
||||
this.broadcastHeroInfo();
|
||||
})
|
||||
|
||||
classOptions: DropDownOption[] = [
|
||||
new DropDownOption(HeroClass.Berserker, 'Berserker'),
|
||||
new DropDownOption(HeroClass.Paladin, 'Paladin'),
|
||||
new DropDownOption(HeroClass.Ranger, 'Ranger'),
|
||||
new DropDownOption(HeroClass.Rogue, 'Rogue'),
|
||||
new DropDownOption(HeroClass.Wizard, 'Wizard'),
|
||||
new DropDownOption(HeroClass.Shaman, 'Shaman'),
|
||||
new DropDownOption(HeroClass.Druid, 'Druid'),
|
||||
new DropDownOption(HeroClass.Necromancer, 'Necromancer'),
|
||||
new DropDownOption(HeroClass.Monk, 'Monk'),
|
||||
new DropDownOption(HeroClass.Thinker, 'Thinker'),
|
||||
new DropDownOption(HeroClass.Bard, 'Bard'),
|
||||
];
|
||||
heros = [] as MD2HeroInfo[];
|
||||
heroProfiles: MD2HeroProfile[] = [];
|
||||
currentHeroIndex: number = 0;
|
||||
isSelectingHero: boolean = false;
|
||||
selectedHeroClass: HeroClass;
|
||||
|
||||
public get hero() {
|
||||
return this.md2Service.playerHero;
|
||||
}
|
||||
|
||||
public get className() {
|
||||
if (this.md2Service.playerHero) {
|
||||
return HeroClass[this.md2Service.playerHero.class];
|
||||
}
|
||||
if (this.selectedHeroClass) {
|
||||
return HeroClass[this.selectedHeroClass];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
public get currentSelectingHero(): MD2HeroInfo {
|
||||
return this.heros[this.currentHeroIndex];
|
||||
}
|
||||
|
||||
constructor(
|
||||
private gameRoomService: GameRoomService,
|
||||
public md2Service: MD2Service,
|
||||
private heroProfileService: MD2HeroProfileService,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
private msgBoxService: MsgBoxService,
|
||||
private signalRService: SignalRService,
|
||||
private toastrService: NbToastrService
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
this.isHeroDashboard = true;
|
||||
}
|
||||
|
||||
public get allowAttack(): boolean {
|
||||
return this.hero.uiBossFight || this.md2Service.mobs?.length > 0 || this.md2Service.roamingMonsters?.length > 0;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
this.gameRoomService.gameRoomId = this.roomId;
|
||||
this.gameRoomService.joinGameRoom(this.roomId);
|
||||
//this.fetchGameInfo();
|
||||
this.signalRService.signalRMessageConnSubject.subscribe(state => {
|
||||
//fetchGameInfo is called in MD2Base.handleSignalRCallback sendJoinInfo message
|
||||
// if (state.status == 'connected') {
|
||||
// this.fetchGameInfo();
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
override signalRInitialized() {
|
||||
// this.gameRoomService.joinGameRoom(this.roomId);
|
||||
// if (this.md2Service.initialized == false) {
|
||||
// this.gameRoomService.createGameRoom('MD2');
|
||||
// this.md2Service.initialized = true;
|
||||
// }
|
||||
}
|
||||
initHero() {
|
||||
if (!this.md2Service.heros.some(h => h.playerInfo.signalRClientId == this.stateService.loginUserService.userAccess.signalRConnectionId)) {
|
||||
|
||||
this.msgBoxService.showInputbox('Select Hero Class', '', { dropDownOptions: this.classOptions, inputType: 'dropdown' })
|
||||
.pipe(first()).subscribe(heroClass => {
|
||||
if (heroClass != null) {
|
||||
// switch (heroClass) {
|
||||
// case HeroClass.Berserker: break;
|
||||
// case HeroClass.Wizard:
|
||||
// this.heros = this.wizards;
|
||||
// break;
|
||||
// case HeroClass.Rogue: break;
|
||||
// case HeroClass.Ranger: break;
|
||||
// case HeroClass.Shaman: break;
|
||||
// case HeroClass.Paladin: break;
|
||||
// default: break;
|
||||
// }
|
||||
// this.showHeroList(heroClass, 0);
|
||||
this.initClassHeroList(heroClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
initClassHeroList(heroClass: HeroClass) {
|
||||
this.heros = [];
|
||||
this.selectedHeroClass = heroClass;
|
||||
this.heroProfileService.getAll().pipe(first()).subscribe(result => {
|
||||
|
||||
this.heroProfiles = result.filter(h => h.heroClass == heroClass);
|
||||
for (let i = 0; i < this.heroProfiles.length; i++) {
|
||||
const heroProfile = this.heroProfiles[i];
|
||||
const heroInfo = new MD2HeroInfo({
|
||||
name: heroProfile.title,
|
||||
mpMaximum: heroProfile.mana,
|
||||
hpMaximum: heroProfile.hp,
|
||||
hp: heroProfile.hp,
|
||||
mp: heroProfile.mana,
|
||||
skillHtml: heroProfile.skillHtml,
|
||||
shadowSkillHtml: heroProfile.shadowSkillHtml.replace("<p>", '<p>' + this.iconHtml(MD2Icon.Shadow) + ' : '),
|
||||
class: heroClass
|
||||
});
|
||||
heroInfo.imgUrl = this.imgUrl('Heros/' + HeroClass[heroClass] + '.jpg');
|
||||
this.heros.push(heroInfo);
|
||||
}
|
||||
this.heros = ArrayUtils.Shuffle(this.heros);//.sort((a, b) => StringUtils.compareSemVer(a.name, b.name));
|
||||
this.currentHeroIndex = 0;
|
||||
this.isSelectingHero = true;
|
||||
this.detectChanges();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
selectCurrentHero() {
|
||||
if (this.currentSelectingHero) {
|
||||
this.md2Service.playerJoin(this.currentSelectingHero);
|
||||
this.md2Service.broadcastMyHeroInfo();
|
||||
this.isSelectingHero = false;
|
||||
this.detectChanges();
|
||||
}
|
||||
}
|
||||
showSkills(type: string) {
|
||||
if (type == 'abilities') {
|
||||
this.msgBoxService.show('Abilities', { text: this.currentSelectingHero.skillHtml });
|
||||
} else {
|
||||
this.msgBoxService.show('Shadow Abilities', { text: this.currentSelectingHero.shadowSkillHtml });
|
||||
}
|
||||
}
|
||||
nextHero() {
|
||||
this.currentHeroIndex++;
|
||||
if (this.currentHeroIndex >= this.heros.length) {
|
||||
this.currentHeroIndex = 0;
|
||||
}
|
||||
this.detectChanges();
|
||||
}
|
||||
|
||||
previousHero() {
|
||||
this.currentHeroIndex--;
|
||||
if (this.currentHeroIndex < 0) {
|
||||
this.currentHeroIndex = this.heros.length - 1;
|
||||
}
|
||||
this.detectChanges();
|
||||
}
|
||||
|
||||
fetchGameInfo() {
|
||||
this.md2Service.broadcastFetchGameInfo();
|
||||
}
|
||||
|
||||
broadcastHeroInfo() {
|
||||
this.md2Service.broadcastMyHeroInfo();
|
||||
this.heroUpdateDebounceTimer.clearOut();
|
||||
}
|
||||
increaseRage() {
|
||||
if (this.hero.rage < 7) {
|
||||
this.hero.rage++;
|
||||
}
|
||||
}
|
||||
openDoor() {
|
||||
this.md2Service.broadcastHeroAction('openDoor');
|
||||
//this.showMoveAction = false;
|
||||
this.detectChanges();
|
||||
}
|
||||
moveAction() {
|
||||
this.showMoveAction = true;
|
||||
this.detectChanges();
|
||||
}
|
||||
moveActionEnd() {
|
||||
this.showMoveAction = false;
|
||||
this.reduceAction();
|
||||
this.detectChanges();
|
||||
}
|
||||
action(action: string) {
|
||||
this.showMoveAction = false;
|
||||
switch (action) {
|
||||
case 'recoveryAction':
|
||||
this.msgBoxService.show('Recovery', { text: 'takes the Recover action may gain up to 2 Health or Mana in any combination (either 2 Health, 2 Mana, or 1 of each).' });
|
||||
break;
|
||||
case 'attackAction':
|
||||
this.msgBoxService.show('Attacking', { text: 'Please process attacking action in Dashboard.' });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.md2Service.broadcastHeroAction(action);
|
||||
this.reduceAction();
|
||||
}
|
||||
reduceAction() {
|
||||
this.hero.remainActions -= 1;
|
||||
this.detectChanges();
|
||||
this.broadcastHeroInfo();
|
||||
}
|
||||
flip: string = 'inactive';
|
||||
|
||||
toggleFlip() {
|
||||
this.flip = (this.flip == 'inactive') ? 'active' : 'inactive';
|
||||
}
|
||||
|
||||
get allowStartAction() {
|
||||
return !this.md2Service.heros.some(h => h.uiActivating) && !this.hero.uiActivating && this.hero.remainActions > 0;
|
||||
}
|
||||
startActivation() {
|
||||
this.hero.uiActivating = true;
|
||||
//this.hero.remainActions = 3;
|
||||
if (this.hero.fireToken > 0) {
|
||||
this.msgBoxService.show(`You Are On ${this.iconHtml(MD2Icon.Fire)}!`, {
|
||||
text: `Roll ${this.iconHtml(MD2Icon.YellowDice)} ${this.hero.fireToken} times.`
|
||||
});
|
||||
}
|
||||
if (this.hero.frozenToken > 0) {
|
||||
let loseActions = Math.min(this.hero.frozenToken, this.hero.remainActions);
|
||||
this.hero.remainActions -= loseActions;
|
||||
this.hero.frozenToken -= loseActions;
|
||||
this.msgBoxService.show(`It's So Cold ${this.iconHtml(MD2Icon.Frost)}!`, {
|
||||
text: `Lose ${loseActions} actions.`
|
||||
});
|
||||
}
|
||||
if (this.hero.remainActions == 0) {
|
||||
this.hero.uiActivating = false;
|
||||
}
|
||||
this.broadcastHeroInfo();
|
||||
}
|
||||
endActivation() {
|
||||
if (this.hero.remainActions > 0) {
|
||||
this.msgBoxService.show('Are you sure?', { text: `End Activation will lose ${this.hero.remainActions} remaining actions.`, buttons: ADButtons.YesNo }).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
this.hero.remainActions = 0;
|
||||
this.endActivation();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.hero.uiActivating = false;
|
||||
this.broadcastHeroInfo();
|
||||
this.detectChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user