Compare commits
2 Commits
42e7ee39be
...
b46392bc41
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b46392bc41 | ||
|
|
6301d6008b |
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -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"
|
||||
],
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
"ng": "ng",
|
||||
"conventional-changelog": "conventional-changelog",
|
||||
"start": "ng serve",
|
||||
"build": "ng build --output-path \\\\ArkNAS\\docker\\nginx-proxy\\data\\ChurchAngular --prod",
|
||||
"build": "ng build --output-path \\\\ArkNAS\\docker\\nginx-proxy\\data\\ChurchAngular --configuration production",
|
||||
"build:prod": "npm run build -- --configuration production --aot",
|
||||
"test": "ng test",
|
||||
"test:coverage": "rimraf coverage && npm run test -- --code-coverage",
|
||||
|
||||
@ -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';
|
||||
|
||||
34
src/app/@theme/components/header/breakPoint.ts
Normal file
34
src/app/@theme/components/header/breakPoint.ts
Normal file
@ -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,
|
||||
},
|
||||
];
|
||||
@ -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() {
|
||||
|
||||
@ -23,4 +23,4 @@ import { Component } from '@angular/core';
|
||||
</nb-layout>
|
||||
`,
|
||||
})
|
||||
export class OneColumnLayoutComponent {}
|
||||
export class OneColumnLayoutComponent { }
|
||||
|
||||
@ -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,10 +6,7 @@ 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';
|
||||
@ -26,14 +23,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 +36,7 @@ import { WeekTaskEditorComponent } from './happiness-groups/week-task-editor/wee
|
||||
LogDetailComponent,
|
||||
LineMessagingAccountComponent,
|
||||
LineMessagingAccountEditorComponent,
|
||||
WeekTaskEditorComponent],
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
|
||||
@ -4,10 +4,10 @@ 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';
|
||||
|
||||
@ -3,10 +3,10 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
nb-card {
|
||||
max-height: 90vh;
|
||||
width: 700px;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 => {
|
||||
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,11 +73,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
|
||||
|
||||
93
src/app/cell-group/MyAppBase.ts
Normal file
93
src/app/cell-group/MyAppBase.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { basename } from "path";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import { ScreenBase } from "../ScreenBase";
|
||||
import { ICrudService } from "../services/crudServices/crud.service";
|
||||
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>
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
13
src/app/cell-group/finance/finance.component.html
Normal file
13
src/app/cell-group/finance/finance.component.html
Normal file
@ -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
src/app/cell-group/finance/finance.component.scss
Normal file
0
src/app/cell-group/finance/finance.component.scss
Normal file
25
src/app/cell-group/finance/finance.component.spec.ts
Normal file
25
src/app/cell-group/finance/finance.component.spec.ts
Normal file
@ -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();
|
||||
});
|
||||
});
|
||||
208
src/app/cell-group/finance/finance.component.ts
Normal file
208
src/app/cell-group/finance/finance.component.ts
Normal file
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
17
src/app/cell-group/join-domain/join-domain.component.html
Normal file
17
src/app/cell-group/join-domain/join-domain.component.html
Normal file
@ -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>
|
||||
25
src/app/cell-group/join-domain/join-domain.component.spec.ts
Normal file
25
src/app/cell-group/join-domain/join-domain.component.spec.ts
Normal file
@ -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();
|
||||
});
|
||||
});
|
||||
39
src/app/cell-group/join-domain/join-domain.component.ts
Normal file
39
src/app/cell-group/join-domain/join-domain.component.ts
Normal file
@ -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 {
|
||||
|
||||
@ -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('更新完成!', '個人資料');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,8 @@ export interface LoginTokenViewModel {
|
||||
avatarImage: string;
|
||||
role: Role;
|
||||
cellGroup: PastoralDomain;
|
||||
signalRSessionId;
|
||||
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;
|
||||
|
||||
7
src/app/entity/contribution.model.spec.ts
Normal file
7
src/app/entity/contribution.model.spec.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { Contribution } from './contribution.model';
|
||||
|
||||
describe('Contribution', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new Contribution()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
8
src/app/entity/contribution.model.ts
Normal file
8
src/app/entity/contribution.model.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export interface Contribution {
|
||||
groupId: string;
|
||||
id: string;
|
||||
contributor: string;
|
||||
amount: number;
|
||||
comment: string;
|
||||
time: Date;
|
||||
}
|
||||
7
src/app/entity/happiness-cost.model.spec.ts
Normal file
7
src/app/entity/happiness-cost.model.spec.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { HappinessCost } from './happiness-cost.model';
|
||||
|
||||
describe('HappinessCost', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new HappinessCost()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
7
src/app/entity/happiness-cost.model.ts
Normal file
7
src/app/entity/happiness-cost.model.ts
Normal file
@ -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;
|
||||
@ -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, '你確定沒有選錯腳色??')
|
||||
|
||||
@ -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,41 @@
|
||||
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';
|
||||
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 +45,8 @@ const routes: Routes = [
|
||||
[
|
||||
{ path: 'avalon', component: AvalonComponent },
|
||||
{ path: 'avalonHost', component: AvalonComponent },
|
||||
{ path: 'MD2', component: MassiveDarkness2Component },
|
||||
{ path: 'MD2_Hero/:roomId', component: HeroDashboardComponent },
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
<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>
|
||||
|
||||
<!-- 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('我是遊戲王');
|
||||
|
||||
17
src/app/games/games.model.ts
Normal file
17
src/app/games/games.model.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export interface IGamePlayer {
|
||||
id: string;
|
||||
name: string;
|
||||
gameRoomId: string;
|
||||
isPlayer: boolean;
|
||||
signalRClientId: string;
|
||||
tabId: string;
|
||||
}
|
||||
|
||||
export class GamePlayer implements IGamePlayer {
|
||||
id: string;
|
||||
name: string;
|
||||
gameRoomId: string;
|
||||
isPlayer: boolean;
|
||||
signalRClientId: string;
|
||||
tabId: string;
|
||||
}
|
||||
@ -4,7 +4,7 @@ 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 +16,22 @@ 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';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -28,7 +44,21 @@ 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
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -52,9 +82,14 @@ import { QuestTableComponent } from './avalon/quest-table/quest-table.component'
|
||||
NbToggleModule,
|
||||
NbUserModule,
|
||||
NbSpinnerModule,
|
||||
NbAccordionModule,
|
||||
NbBadgeModule,
|
||||
NbAlertModule,
|
||||
CurrencyInputModule,
|
||||
NbDialogModule.forRoot(),
|
||||
AlertDlgModule,
|
||||
QRCodeModule,
|
||||
DropDownListModule
|
||||
]
|
||||
})
|
||||
export class GamesModule { }
|
||||
|
||||
230
src/app/games/massive-darkness2/MD2Base.ts
Normal file
230
src/app/games/massive-darkness2/MD2Base.ts
Normal file
@ -0,0 +1,230 @@
|
||||
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.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";
|
||||
|
||||
@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.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 {
|
||||
switch (message.actionType) {
|
||||
case 'hero':
|
||||
let heroInfo = JSON.parse(message.parameters['hero']) as MD2HeroInfo;
|
||||
switch (message.actionName) {
|
||||
case 'join':
|
||||
this.md2Service.heros.push(heroInfo);
|
||||
break;
|
||||
case 'update':
|
||||
let exitingHero = this.md2Service.heros.find(h => h.playerInfo.signalRClientId == heroInfo.playerInfo.signalRClientId);
|
||||
if (exitingHero) {
|
||||
Object.keys(heroInfo).forEach(key => exitingHero[key] = heroInfo[key]);
|
||||
} else {
|
||||
this.md2Service.heros.push(heroInfo);
|
||||
}
|
||||
if (!this.isHeroDashboard) {
|
||||
if (this.gameInfo.roundPhase == RoundPhase.HeroPhase) {
|
||||
if (!this.md2Service.heros.some(h => h.remainActions > 0)) {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Object.assign(heroInfo, exitingHero);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.detectChanges();
|
||||
break;
|
||||
case 'GameRoom':
|
||||
switch (message.actionName) {
|
||||
case 'Leaving':
|
||||
this.md2Service.heros.splice(this.md2Service.heros.findIndex(h => h.playerInfo.signalRClientId == message.from.sessionId));
|
||||
this.detectChanges();
|
||||
break;
|
||||
case 'update':
|
||||
if (this.isHeroDashboard) {
|
||||
this.md2Service.info = new MD2GameInfo(JSON.parse(message.parameters['gameInfo']) as MD2GameInfo);
|
||||
this.detectChanges();
|
||||
}
|
||||
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.gameInfo.currentActivateHero = this.md2Service.heros.find(h => h.playerInfo.tabId == message.parameters['tabId']);
|
||||
switch (message.actionName) {
|
||||
case 'attackAction':
|
||||
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</b> with ${this.md2Service.heroFullName(this.gameInfo.currentActivateHero)} may freely trade and
|
||||
equip items!`,
|
||||
icon: ADIcon.INFO
|
||||
});
|
||||
break;
|
||||
default:
|
||||
//this.md2Service.roundPhase = Number.parseInt(message.parameters['phase']);
|
||||
break;
|
||||
}
|
||||
this.heroAction(this.gameInfo.currentActivateHero, message.actionName);
|
||||
this.detectChanges();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
<nb-card>
|
||||
|
||||
<nb-card-body>
|
||||
<div class="row form-group" style="
|
||||
height: 53vh;
|
||||
overflow: auto;
|
||||
">
|
||||
<div class="col-md-5">
|
||||
<img src="{{boss.standUrl}}" class="img-fluid">
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<label class="MD2text g-font-size-40 mt-5" [innerHtml]="bossAction.skillName">
|
||||
|
||||
</label>
|
||||
<label class="g-font-size-20 mt-3" [innerHtml]="bossAction.skillDescription">
|
||||
|
||||
</label>
|
||||
</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>
|
||||
@ -0,0 +1,4 @@
|
||||
nb-card {
|
||||
height: 70vh;
|
||||
width: 80vw;
|
||||
}
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,47 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogRef } from '@nebular/theme';
|
||||
import { MD2Service } from '../../../../services/md2.service';
|
||||
import { MsgBoxService } from '../../../../services/msg-box.service';
|
||||
import { StateService } from '../../../../services/state.service';
|
||||
import { MobDlgType, MD2Icon, MD2HeroInfo } 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';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-boss-activation',
|
||||
templateUrl: './boss-activation.component.html',
|
||||
styleUrls: ['./boss-activation.component.scss']
|
||||
})
|
||||
export class BossActivationComponent extends MD2ComponentBase implements OnInit {
|
||||
boss: IBossFight;
|
||||
bossAction: MobSkill;
|
||||
|
||||
MobDlgType = MobDlgType;
|
||||
mode: MobDlgType;
|
||||
|
||||
title: string;
|
||||
titleHtml: string;
|
||||
actionHtml: string;
|
||||
MD2Icon = MD2Icon;
|
||||
|
||||
beenAttackedHero = [] as MD2HeroInfo[];
|
||||
attackTarget: string;
|
||||
otherAttackTarget: string;
|
||||
constructor(
|
||||
private dlgRef: NbDialogRef<SpawnMobDlgComponent>,
|
||||
private msgBoxService: MsgBoxService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
close() {
|
||||
this.boss.standUrl
|
||||
this.dlgRef.close();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
{{boss.name}}
|
||||
<button nbButton hero status="primary" (click)="activate()">Action</button>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<img src="{{boss.standUrl}}" class="w-100 g-max-height-80vh">
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<md2-mob-detail-info [mob]="boss.info">
|
||||
</md2-mob-detail-info>
|
||||
|
||||
<adj-number-input name="mob{{boss.info.name}}" [(ngModel)]="boss.info.unitRemainHp" minimum="0"
|
||||
title="Boss HP" (hitMinimum)="WIN()">
|
||||
</adj-number-input>
|
||||
<button nbButton hero status="danger" size="small" (click)="attack(boss.info)">Attack It</button>
|
||||
|
||||
<label class="MD2Text mt-3" [innerHtml]="boss.combatInfo.skillName">
|
||||
</label>
|
||||
<label class="MD2Text" [innerHtml]="boss.combatInfo.skillDescription">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
@ -0,0 +1,4 @@
|
||||
nb-card {
|
||||
height: 80vh;
|
||||
//width: 80vw;
|
||||
}
|
||||
@ -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,63 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { Subject, Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { MD2Service } from '../../../services/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 {
|
||||
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
activate() {
|
||||
this.boss.activating();
|
||||
}
|
||||
WIN() {
|
||||
|
||||
}
|
||||
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.info.hp -= attackDamage;
|
||||
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.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.info.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,141 @@
|
||||
<nb-card *ngIf="!md2Service.playerHero">
|
||||
<nb-card-body>
|
||||
<button nbButton hero status="primary" fullWidth (click)="initHero()">Choose Hero</button>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
<div *ngIf="md2Service.playerHero">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-12 col-sm-7">
|
||||
<div class="tp-wrapper mb-2">
|
||||
<div class="tp-box g-height-300 g-height-350--sm g-height-500--md" (click)="toggleFlip()"
|
||||
[@flipState]="flip">
|
||||
<div class="tp-box__side tp-box__front ">
|
||||
|
||||
<img class="MD2HeroCard " src="{{md2Service.playerHero.imgUrl}}">
|
||||
</div>
|
||||
<div class="tp-box__side tp-box__back">
|
||||
|
||||
|
||||
<img class="MD2HeroCard " src="{{imgUrl('Heros/Guide/'+className+'.jpg')}}">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="g-max-height-80vh mb-2">
|
||||
<img class="MD2HeroCard" src="{{md2Service.playerHero.imgUrl}}">
|
||||
<div class="MD2HeroCard">
|
||||
<span class="MD2text MD2Name">{{md2Service.playerHero.name}}</span>
|
||||
<span class="MD2text MD2Hp">{{md2Service.playerHero.hpMaximum}}</span>
|
||||
<span class="MD2text MD2Mp">{{md2Service.playerHero.mpMaximum}}</span>
|
||||
</div>
|
||||
<img class="MD2HeroCard" src="{{md2Service.playerHero.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)]="md2Service.playerHero.hp"
|
||||
[maximum]="md2Service.playerHero.hpMaximum" minimum="0"
|
||||
title="{{iconHtml(MD2Icon.HP,'g-color-google-plus mr-1 g-font-size-18')}}HP" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()">
|
||||
</adj-number-input> -->
|
||||
|
||||
|
||||
<adj-number-input name="heroHP" [(ngModel)]="md2Service.playerHero.hp"
|
||||
[maximum]="md2Service.playerHero.hpMaximum" minimum="0"
|
||||
title="{{imgHtml('HpIcon.png','g-height-25 mr-1')}}HP" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroMana" [(ngModel)]="md2Service.playerHero.mp"
|
||||
[maximum]="md2Service.playerHero.mpMaximum" minimum="0"
|
||||
title="{{imgHtml('HeroIcon.png','g-height-25 mr-1')}}Mana" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
|
||||
<adj-number-input name="heroFire" [(ngModel)]="md2Service.playerHero.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>
|
||||
<adj-number-input name="heroFire" [(ngModel)]="md2Service.playerHero.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)]="md2Service.playerHero.remainActions"
|
||||
minimum="0" title="Remain Actions" (blur)="heroUpdateDebounceTimer.resetTimer()"
|
||||
hideIncreaseBtn>
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroLevel" [(ngModel)]="md2Service.playerHero.level" minimum="1"
|
||||
maximum="5" title="Level" (blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroExp" [(ngModel)]="md2Service.playerHero.exp" minimum="0"
|
||||
title="Exp" (blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroRage" [(ngModel)]="md2Service.playerHero.rage" minimum="0"
|
||||
maximum="7"
|
||||
title="{{iconHtml(MD2Icon.Rage,'g-color-google-plus mr-1 g-font-size-18')}}Rage"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()"
|
||||
*ngIf="md2Service.playerHero.class==HeroClass.Berserker">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroCorruption" [(ngModel)]="md2Service.playerHero.corruptionToken"
|
||||
minimum="0" title="{{imgHtml('Tokens/CorruptToken.png','g-height-18')}} Corruption"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()"
|
||||
*ngIf="md2Service.playerHero.corruptionToken>0">
|
||||
</adj-number-input>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div *ngIf="md2Service.info.isBossFight"></div>
|
||||
<div *ngIf="md2Service.playerHero.remainActions>0">
|
||||
<button nbButton hero class="mr-2" status="info" (click)="moveAction()"
|
||||
*ngIf="!showMoveAction">Move</button>
|
||||
<button nbButton hero class="mr-2" status="info" (click)="moveActionEnd()"
|
||||
*ngIf="showMoveAction">Move End</button>
|
||||
<button nbButton hero class="mr-2" status="danger" (click)="action('attackAction')"
|
||||
*ngIf="!showMoveAction&&allowAttack">Attack!</button>
|
||||
<button nbButton hero class="mr-2" status="info" (click)="action('tradeAction')"
|
||||
*ngIf="!showMoveAction">Trade</button>
|
||||
<button nbButton hero status="success" (click)="action('recoveryAction')"
|
||||
*ngIf="!showMoveAction">Recovery</button>
|
||||
|
||||
</div>
|
||||
|
||||
<button nbButton hero status="info" class="mt-2" (click)="openDoor()" *ngIf="showMoveAction">Open
|
||||
Door</button>
|
||||
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- <nb-flip-card *ngIf="md2Service.playerHero">
|
||||
<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,80 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
.tp-box {
|
||||
position: relative;
|
||||
//width: 200px;
|
||||
//height: 100px;
|
||||
//margin: 3rem auto;
|
||||
-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;
|
||||
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.tp-box__front {
|
||||
-webkit-transform: rotateY(0deg);
|
||||
-ms-transform: rotateY(0deg);
|
||||
transform: rotateY(0deg);
|
||||
}
|
||||
.tp-box__back {
|
||||
-webkit-transform: rotateY(-180deg);
|
||||
-ms-transform: rotateY(-180deg);
|
||||
transform: rotateY(-180deg);
|
||||
}
|
||||
@ -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,205 @@
|
||||
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.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 { StringUtils } from '../../../utilities/string-utils';
|
||||
import { DebounceTimer } from '../../../utilities/timer-utils';
|
||||
import { HeroClass, MD2HeroInfo } from '../massive-darkness2.model';
|
||||
import { MD2Base } from '../MD2Base';
|
||||
|
||||
@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 {
|
||||
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'),
|
||||
];
|
||||
heros = [] as MD2HeroInfo[];
|
||||
wizards: MD2HeroInfo[] = [
|
||||
new MD2HeroInfo({ name: 'Ajax', mpMaximum: 6, hpMaximum: 4, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Baldric', mpMaximum: 5, hpMaximum: 4, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Ego', mpMaximum: 5, hpMaximum: 6, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Elias', mpMaximum: 6, hpMaximum: 5, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Megan', mpMaximum: 5, hpMaximum: 5, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Moira', mpMaximum: 6, hpMaximum: 5, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Myriam', mpMaximum: 7, hpMaximum: 4, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Valdis', mpMaximum: 6, hpMaximum: 4, skillHtml: '', shadowSkillHtml: '' })
|
||||
]
|
||||
constructor(
|
||||
private gameRoomService: GameRoomService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
private msgBoxService: MsgBoxService,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
this.isHeroDashboard = true;
|
||||
}
|
||||
|
||||
public get allowAttack(): boolean {
|
||||
return (!!this.md2Service.mobs && this.md2Service.mobs.length > 0) || (!!this.md2Service.roamingMonsters && this.md2Service.roamingMonsters.length > 0);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
override signalRInitialized() {
|
||||
// this.gameRoomService.joinGameRoom(this.roomId);
|
||||
// if (this.md2Service.initialized == false) {
|
||||
// this.gameRoomService.createGameRoom('MD2');
|
||||
// this.md2Service.initialized = true;
|
||||
// }
|
||||
}
|
||||
initHero() {
|
||||
this.gameRoomService.gameRoomId = this.roomId;
|
||||
this.gameRoomService.joinGameRoom(this.roomId);
|
||||
if (!this.md2Service.heros.some(h => h.playerInfo.signalRClientId == this.stateService.loginUserService.userAccess.signalRSessionId)) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
className: string;
|
||||
initClassHeroList(heroClass: HeroClass) {
|
||||
this.heros = [];
|
||||
this.className = HeroClass[heroClass];
|
||||
this.fileList(`Heros/${this.className}`).pipe(first()).subscribe(fileNames => {
|
||||
for (let i = 0; i < fileNames.length; i++) {
|
||||
const heroNames = fileNames[i].split('.')[0].split('-');
|
||||
|
||||
this.heros.push(new MD2HeroInfo({
|
||||
name: heroNames[0].replace('/', ''),
|
||||
mpMaximum: Number.parseInt(heroNames[1]),
|
||||
hpMaximum: Number.parseInt(heroNames[2]),
|
||||
imgUrl: this.imgUrl(`Heros/${this.className}/${fileNames[i]}`),
|
||||
class: heroClass
|
||||
}))
|
||||
}
|
||||
this.heros = this.heros.sort((a, b) => StringUtils.compareSemVer(a.name, b.name));
|
||||
this.showHeroList(heroClass, 0);
|
||||
});
|
||||
}
|
||||
showHeroList(heroClass: HeroClass, index: number) {
|
||||
let className = HeroClass[heroClass];
|
||||
let heroInfo = this.heros[index];
|
||||
this.msgBoxService.show(`${className}(${index + 1}/${this.heros.length})`, {
|
||||
text: `<img src='${heroInfo.imgUrl}' class="g-width-50vw-md g-width-80vw">`,
|
||||
buttons: ADButtons.YesNo,
|
||||
cardWidthClass: '',
|
||||
confirmButtonText: 'It\'s Me!',
|
||||
cancelButtonText: 'Next',
|
||||
cancelButtonColor: ADButtonColor.INFO
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
this.md2Service.playerJoin(heroInfo);
|
||||
this.detectChanges();
|
||||
} else {
|
||||
index++;
|
||||
if (index == this.heros.length) index = 0;
|
||||
this.showHeroList(heroClass, index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
broadcastHeroInfo() {
|
||||
this.md2Service.broadcastMyHeroInfo();
|
||||
this.heroUpdateDebounceTimer.clearOut();
|
||||
}
|
||||
increaseRage() {
|
||||
if (this.md2Service.playerHero.rage < 7) {
|
||||
this.md2Service.playerHero.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;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.md2Service.broadcastHeroAction(action);
|
||||
this.reduceAction();
|
||||
}
|
||||
reduceAction() {
|
||||
this.md2Service.playerHero.remainActions -= 1;
|
||||
this.detectChanges();
|
||||
this.broadcastHeroInfo();
|
||||
}
|
||||
flip: string = 'inactive';
|
||||
|
||||
toggleFlip() {
|
||||
this.flip = (this.flip == 'inactive') ? 'active' : 'inactive';
|
||||
}
|
||||
}
|
||||
101
src/app/games/massive-darkness2/massive-darkness2.component.html
Normal file
101
src/app/games/massive-darkness2/massive-darkness2.component.html
Normal file
@ -0,0 +1,101 @@
|
||||
<div class="row">
|
||||
<!-- <div class="col-12 mb-1">
|
||||
<nb-accordion>
|
||||
<nb-accordion-item>
|
||||
<nb-accordion-item-header class="my-0">
|
||||
Global Config
|
||||
</nb-accordion-item-header>
|
||||
<nb-accordion-item-body>
|
||||
|
||||
</nb-accordion-item-body>
|
||||
</nb-accordion-item>
|
||||
</nb-accordion>
|
||||
</div> -->
|
||||
|
||||
<div class="col-12 col-md-5">
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
<img src="{{imgUrl('HeroIcon.png')}}" width="40px"> Game Info
|
||||
|
||||
<button nbButton hero status="info" size="small" (click)="showQrCode()"
|
||||
class="float-right">Invite</button>
|
||||
<button nbButton hero status="info" size="small" [disabled]="anyHeroRemainAction"
|
||||
(click)="md2Service.runNextPhase()" class="float-right mr-2">Next Phase</button>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
|
||||
<div class="row" *ngIf="md2Service.heros.length==0">
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="heroLevel" [(ngModel)]="md2Service.playerAmount" [maximum]="6"
|
||||
minimum="1" title="Hero Amount">
|
||||
</adj-number-input>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<adj-number-input name="heroLevel" [(ngModel)]="md2Service.highestPlayerLevel" [maximum]="5"
|
||||
minimum="1" title="Highest Hero Level">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" *ngIf="md2Service.heros.length>0">
|
||||
<div class="col-12 g-font-size-17" [innerHtml]="roundPhase"></div>
|
||||
|
||||
<!-- <div class="col-6">
|
||||
<label for='playerAmount' class='label'>Hero Amount ({{md2Service.playerAmount}})</label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label for='playerAmount' class='label'>Highest Hero Level
|
||||
({{md2Service.highestPlayerLevel}})</label>
|
||||
</div> -->
|
||||
<div class="col-12" *ngFor="let hero of md2Service.heros">
|
||||
<label class='label mr-1'>{{hero.playerInfo.name}} ({{heroClassName(hero)}} -
|
||||
{{hero.name}})</label>
|
||||
<span class="badge badge-primary mr-1">Lv.:{{hero.level}}</span>
|
||||
<span class="badge badge-primary mr-1">HP: {{hero.hp}}/{{hero.hpMaximum}}</span>
|
||||
<span class="badge badge-primary mr-1">Mana: {{hero.mp}}/{{hero.mpMaximum}}</span>
|
||||
<span class="badge badge-success mr-1">Exp: {{hero.exp}}</span>
|
||||
<span class="badge badge-danger mr-1" *ngIf="hero.fireToken">Fire:{{hero.fireToken}}</span>
|
||||
<span class="badge badge-info mr-1" *ngIf="hero.frozenToken">Frozen:{{hero.frozenToken}}</span>
|
||||
<span class="badge badge-light mr-1" *ngIf="hero.remainActions==0">Inactive</span>
|
||||
<span class="badge badge-success mr-1" *ngIf="hero.remainActions>0">Remain
|
||||
Actions: {{hero.remainActions}}</span>
|
||||
<!-- <span class="badge badge-success mr-1">{{hero.playerInfo.signalRClientId}}</span> -->
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<md2-treasure-bag></md2-treasure-bag>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-3">
|
||||
<div class="row">
|
||||
<div class="form-group col-12">
|
||||
|
||||
<button nbButton hero fullWidth status="primary" (click)="enterBossFight()">Enter Boss Fight</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-12 col-md-12" *ngIf="md2Service.info.isBossFight">
|
||||
<md2-boss-fight></md2-boss-fight>
|
||||
</div>
|
||||
<div class="col-12 col-md-12">
|
||||
<md2-mobs isRoamingMonster="true"></md2-mobs>
|
||||
</div>
|
||||
<div class="col-12 col-md-12">
|
||||
<md2-mobs></md2-mobs>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-12">
|
||||
<md2-door-events></md2-door-events>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MassiveDarkness2Component } from './massive-darkness2.component';
|
||||
|
||||
describe('MassiveDarkness2Component', () => {
|
||||
let component: MassiveDarkness2Component;
|
||||
let fixture: ComponentFixture<MassiveDarkness2Component>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MassiveDarkness2Component ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MassiveDarkness2Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
134
src/app/games/massive-darkness2/massive-darkness2.component.ts
Normal file
134
src/app/games/massive-darkness2/massive-darkness2.component.ts
Normal file
@ -0,0 +1,134 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { FileService } from '../../services/file.service';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { ArrayUtils } from '../../utilities/array-utils';
|
||||
import { ObjectUtils } from '../../utilities/object-utils';
|
||||
import { first, map, take, takeUntil } from 'rxjs/operators';
|
||||
import { TreasureType, DrawingBag, DrawingItem, HeroClass, MD2HeroInfo, RoundPhase, MobInfo, MobDlgType } from './massive-darkness2.model';
|
||||
import { MD2Service } from '../../services/md2.service';
|
||||
import { GameRoomService } from '../../services/game-room.service';
|
||||
import { MD2Base } from './MD2Base';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { QRCodeService } from '../../services/qrcode.service';
|
||||
import { StringUtils } from '../../utilities/string-utils';
|
||||
import { SpawnMobDlgComponent } from './mobs/spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
import { BossMicheal } from './massive-darkness2.model.boss';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-massive-darkness2',
|
||||
templateUrl: './massive-darkness2.component.html',
|
||||
styleUrls: ['./massive-darkness2.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MassiveDarkness2Component extends MD2Base implements OnInit {
|
||||
HeroClass: HeroClass
|
||||
constructor(
|
||||
private fileService: FileService,
|
||||
private msgBoxService: MsgBoxService,
|
||||
private qrCodeService: QRCodeService,
|
||||
public gameRoomService: GameRoomService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
this.md2Service.enemyPhaseSubject.pipe(takeUntil(this.destroy$)).subscribe(result => {
|
||||
this.showEnemyPhaseAction(0);
|
||||
});
|
||||
|
||||
}
|
||||
override signalRInitialized() {
|
||||
|
||||
}
|
||||
showQrCode() {
|
||||
if (this.md2Service.initialized == false) {
|
||||
this.gameRoomService.createGameRoom('MD2');
|
||||
this.md2Service.initialized = true;
|
||||
}
|
||||
let initUrl = `${window.location.origin}/games/MD2_Hero/${this.gameRoomService.gameRoomId}`;
|
||||
this.msgBoxService.show("Scan To Join", { text: `<img src='${this.qrCodeService.QRCodeUrl(initUrl, 5)}'><br><a href='${initUrl}' target='_blank'>Link</a>` });
|
||||
}
|
||||
refreshUI() {
|
||||
console.log('Dashboard RefreshUI');
|
||||
|
||||
}
|
||||
heroClassName(heroInfo: MD2HeroInfo) {
|
||||
return HeroClass[heroInfo.class];
|
||||
}
|
||||
|
||||
showEnemyPhaseAction(index: number) {
|
||||
let mob = new MobInfo(this.md2Service.enemyPhaseMobs[index]);
|
||||
let enemyInfo = `<img src="${mob.imageUrl}" class='g-height-70vh'><br>`;
|
||||
let extraRule = '';
|
||||
|
||||
// switch (Math.random() * 3) {
|
||||
|
||||
// case 1:
|
||||
|
||||
// break;
|
||||
// case 2:
|
||||
|
||||
// break;
|
||||
// case 3:
|
||||
|
||||
// break;
|
||||
|
||||
// case 0:
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
this.msgBoxService.dlgService.open(SpawnMobDlgComponent, { context: { title: `Enemy Phase(${(index + 1)}/${this.md2Service.enemyPhaseMobs.length})`, mode: MobDlgType.Activating, mob: mob } })
|
||||
.onClose.pipe(first()).subscribe(result => {
|
||||
index++;
|
||||
if (index < this.md2Service.enemyPhaseMobs.length) {
|
||||
this.showEnemyPhaseAction(index);
|
||||
} else {
|
||||
this.md2Service.runNextPhase();
|
||||
}
|
||||
});
|
||||
// return this.msgBoxService.show(`Enemy Phase(${(this.enemyPhaseMobs.indexOf(mob) + 1)}/${this.enemyPhaseMobs.length})`, {
|
||||
// text: enemyInfo,
|
||||
// confirmButtonText: 'Next', buttons: ADButtons.OK
|
||||
// }).pipe(first()).subscribe(result => {
|
||||
// if ((this.enemyPhaseMobs.indexOf(mob) + 1) < this.enemyPhaseMobs.length) {
|
||||
// this.showEnemyPhaseAction(this.enemyPhaseMobs[this.enemyPhaseMobs.indexOf(mob) + 1]);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
public get roundPhase(): string {
|
||||
switch (this.md2Service.info.roundPhase) {
|
||||
case RoundPhase.HeroPhase:
|
||||
return StringUtils.getHtmlBadge("Hero Action Phase", "primary")
|
||||
case RoundPhase.EnemyPhase:
|
||||
return StringUtils.getHtmlBadge("Enemy Action Phase", "danger")
|
||||
case RoundPhase.LevelUpPhase:
|
||||
return StringUtils.getHtmlBadge("Level Up Phase", "success")
|
||||
case RoundPhase.DarknessPhase:
|
||||
return StringUtils.getHtmlBadge("Darkness Phase", "dark")
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
public get anyHeroRemainAction(): boolean {
|
||||
return this.md2Service.heros.some(h => h.remainActions > 0);
|
||||
}
|
||||
|
||||
heroAction(hero: MD2HeroInfo, action: string) {
|
||||
|
||||
}
|
||||
enterBossFight() {
|
||||
this.msgBoxService.showInputbox('Boss Fight', 'Choose the boss').pipe(first()).subscribe(result => {
|
||||
this.md2Service.info.isBossFight = true;
|
||||
this.md2Service.info.boss = new BossMicheal(this.md2Service);
|
||||
this.detectChanges();
|
||||
});
|
||||
}
|
||||
}
|
||||
135
src/app/games/massive-darkness2/massive-darkness2.model.boss.ts
Normal file
135
src/app/games/massive-darkness2/massive-darkness2.model.boss.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import { Subject } from "rxjs"
|
||||
import { first } from "rxjs/operators"
|
||||
import { MD2Service } from "../../services/md2.service"
|
||||
import { StringUtils } from "../../utilities/string-utils"
|
||||
import { BossActivationComponent } from "./boss-fight/boss-activation/boss-activation.component"
|
||||
import { TreasureType, AttackInfo, DefenseInfo, AttackType, MD2Icon, MD2HeroInfo, AttackTarget, MobInfo } from "./massive-darkness2.model"
|
||||
import { RollingBlackDice } from "./massive-darkness2.model.dice"
|
||||
|
||||
|
||||
export interface IBossFight {
|
||||
name: string
|
||||
addTreasureToken: Subject<TreasureType>
|
||||
spawnMob: Subject<void>
|
||||
spawnRoamingMonster: Subject<void>
|
||||
rounds: number
|
||||
actions: number
|
||||
hpPerHero: number
|
||||
info: MobInfo
|
||||
actionBlackDice: number
|
||||
imgUrl: string
|
||||
standUrl: string
|
||||
combatInfo: MobSkill
|
||||
activating(): boolean
|
||||
prepareForBossFight(): void
|
||||
nextRound(): void
|
||||
}
|
||||
export class BossMicheal implements IBossFight {
|
||||
|
||||
constructor(private md2Service: MD2Service) {
|
||||
this.name = 'Michael - The Corrupted Archangel';
|
||||
this.imgUrl = md2Service.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg');
|
||||
this.standUrl = md2Service.imgUrl('/Boss/Michael.png');
|
||||
this.hpPerHero = 15;
|
||||
this.info = new MobInfo({
|
||||
isRoamingMonster: true,
|
||||
hp: this.md2Service.heros.length * this.hpPerHero,
|
||||
level: 10
|
||||
});
|
||||
this.info.defenseInfos = new DefenseInfo(5, 1);
|
||||
this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)];
|
||||
this.actions = 1;
|
||||
this.rounds = 0;
|
||||
this.actionBlackDice = 2;
|
||||
this.corruptionTokenHtml = this.md2Service.imgHtml('Tokens/CorruptToken.png');
|
||||
|
||||
this.combatInfo = new MobSkill(`Combat 1${this.md2Service.iconHtml(MD2Icon.EnemySkill)}`,
|
||||
`Deal 1 Wound for each ${this.corruptionTokenHtml} on the attacking or defending Hero. Discard the tokens afterwards(once per combat).`);;
|
||||
|
||||
}
|
||||
name: string
|
||||
addTreasureToken: Subject<TreasureType>
|
||||
spawnMob: Subject<void>
|
||||
spawnRoamingMonster: Subject<void>
|
||||
rounds: number
|
||||
actions: number
|
||||
hpPerHero: number
|
||||
info: MobInfo
|
||||
actionBlackDice: number
|
||||
imgUrl: string
|
||||
standUrl: string
|
||||
combatInfo: MobSkill
|
||||
corruptionTokenHtml: string
|
||||
activating(): boolean {
|
||||
|
||||
let actionResult = new RollingBlackDice().roll(this.actionBlackDice);
|
||||
let actionHtml = '';
|
||||
let beenAttackedHero = [] as MD2HeroInfo[];
|
||||
let bossAction: MobSkill;
|
||||
switch (actionResult.claws) {
|
||||
case 0:
|
||||
//Justice From Above
|
||||
beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.MostCorruption, true);
|
||||
bossAction = new MobSkill('Justice From Above',
|
||||
`Place Michael in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`, beenAttackedHero);
|
||||
|
||||
break;
|
||||
case 1:
|
||||
//Lance Dash
|
||||
beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true);
|
||||
bossAction = new MobSkill('Lance Dash',
|
||||
`Move Michael and Place 1 ${this.corruptionTokenHtml} in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`, beenAttackedHero);
|
||||
break;
|
||||
case 2:
|
||||
//Dark Blessing
|
||||
bossAction = new MobSkill('Dark Blessing',
|
||||
`Place Michael ion the central Zone and add 1 ${this.corruptionTokenHtml} to the Corruption Stone Zone with the least amount of ${this.corruptionTokenHtml}.<br>` +
|
||||
`Deal <b>${this.darkBlessingCorruptionAmt}</b> Wounds per ${this.corruptionTokenHtml} to all Heros in each Tiles <b>distributed as they wish</b>.`, beenAttackedHero);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.md2Service.dlgService.open(BossActivationComponent, { context: { boss: this, bossAction: bossAction } }).onClose
|
||||
.pipe(first()).subscribe(result => {
|
||||
|
||||
});
|
||||
return true;
|
||||
}
|
||||
prepareForBossFight(): void {
|
||||
|
||||
}
|
||||
darkBlessingCorruptionAmt: number = 1;
|
||||
nextRound(): void {
|
||||
this.rounds++;
|
||||
switch (this.rounds) {
|
||||
case 3:
|
||||
case 5:
|
||||
this.darkBlessingCorruptionAmt++;
|
||||
break;
|
||||
case 2:
|
||||
case 4:
|
||||
this.info.defenseInfos[0].black += 1;
|
||||
this.info.attackInfos[0].black += 1;
|
||||
break;
|
||||
// case 4:
|
||||
// this.defInfo.black += 2;
|
||||
// this.atkInfos[0].black += 2;
|
||||
// break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MobSkill {
|
||||
constructor(skillName: string, skillDescription: string, targetHeros: MD2HeroInfo[] = []) {
|
||||
this.skillName = skillName
|
||||
this.skillDescription = skillDescription
|
||||
this.targetHeros = targetHeros
|
||||
}
|
||||
skillName: string
|
||||
skillDescription: string
|
||||
targetHeros: MD2HeroInfo[]
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
export class RollingBlackDice {
|
||||
roll(times: number) {
|
||||
let wounds = 0;
|
||||
let claws = 0;
|
||||
//miss 33%
|
||||
//1 claw 33%
|
||||
//1 wound 17%
|
||||
//1 claw, 1 wound 17%
|
||||
|
||||
for (let i = 0; i < times; i++) {
|
||||
let result = Math.random() * 100;
|
||||
if (result <= 33) {
|
||||
} else if (result <= 67) {
|
||||
claws += 1;
|
||||
} else if (result <= 83) {
|
||||
wounds += 1;
|
||||
} else {
|
||||
claws += 1;
|
||||
wounds += 1;
|
||||
}
|
||||
}
|
||||
return { claws, wounds };
|
||||
}
|
||||
}
|
||||
447
src/app/games/massive-darkness2/massive-darkness2.model.ts
Normal file
447
src/app/games/massive-darkness2/massive-darkness2.model.ts
Normal file
@ -0,0 +1,447 @@
|
||||
import { Subject } from "rxjs";
|
||||
import { ObjectUtils } from "../../utilities/object-utils";
|
||||
import { GamePlayer } from "../games.model";
|
||||
import { MobSkill } from "./massive-darkness2.model.boss";
|
||||
|
||||
export enum MobDlgType {
|
||||
Spawn,
|
||||
Activating,
|
||||
BeenAttacked,
|
||||
PreView
|
||||
}
|
||||
export enum RoundPhase {
|
||||
HeroPhase,
|
||||
EnemyPhase,
|
||||
LevelUpPhase,
|
||||
DarknessPhase,
|
||||
BossActivation
|
||||
}
|
||||
export enum TreasureType {
|
||||
Common,
|
||||
Rare,
|
||||
Epic,
|
||||
Legendary
|
||||
}
|
||||
export enum HeroClass {
|
||||
Berserker,
|
||||
Wizard,
|
||||
Rogue,
|
||||
Ranger,
|
||||
Shaman,
|
||||
Paladin,
|
||||
}
|
||||
export enum MD2Icon {
|
||||
Attack,
|
||||
Defense,
|
||||
Mana,
|
||||
Shadow,
|
||||
EnemySkill,
|
||||
EnemyClaw,
|
||||
Reroll,
|
||||
Fire,
|
||||
Frost,
|
||||
OneHand,
|
||||
TwoHand,
|
||||
Helmet,
|
||||
Armor,
|
||||
Ring,
|
||||
Foot,
|
||||
Melee,
|
||||
Range,
|
||||
Magic,
|
||||
HP,
|
||||
MP,
|
||||
Dice,
|
||||
Arrow,
|
||||
ArrowBullseye,
|
||||
ArrowOverload,
|
||||
SoulToken,
|
||||
Rage,
|
||||
RedDice,
|
||||
BlueDice,
|
||||
YellowDice,
|
||||
OrangeDice
|
||||
}
|
||||
export enum AttackTarget {
|
||||
Random = 40,
|
||||
LowestHp = 50,
|
||||
HighestHp = 60,
|
||||
HighestMp = 70,
|
||||
LowestLevel = 80,
|
||||
MostCorruption = 200,
|
||||
LeastCorruption = 201
|
||||
}
|
||||
export enum AttackType {
|
||||
Melee = 15,
|
||||
Range = 16,
|
||||
Magic = 17
|
||||
}
|
||||
export class AttackInfo {
|
||||
constructor(
|
||||
type: MD2Icon,
|
||||
yellow: number = 0,
|
||||
orange: number = 0,
|
||||
red: number = 0,
|
||||
black: number = 0
|
||||
) {
|
||||
this.type = type
|
||||
this.orange = orange
|
||||
this.red = red
|
||||
this.yellow = yellow
|
||||
this.black = black
|
||||
}
|
||||
type: MD2Icon
|
||||
orange: number
|
||||
red: number
|
||||
yellow: number
|
||||
black: number
|
||||
attackSkill: MobSkill
|
||||
}
|
||||
export class DefenseInfo {
|
||||
constructor(blue: number, black: number = 0) {
|
||||
this.blue = blue
|
||||
this.black = black
|
||||
}
|
||||
blue: number
|
||||
black: number
|
||||
defenseSkill: MobSkill
|
||||
}
|
||||
export class MD2LevelUpReward {
|
||||
constructor(config: Partial<MD2LevelUpReward>) {
|
||||
Object.assign(this, config);
|
||||
|
||||
}
|
||||
level: number = 1;
|
||||
needExp: number = 0;
|
||||
currentExp = 0
|
||||
extraHp = 0
|
||||
extraMp = 0
|
||||
extraRareToken = 0
|
||||
extraEpicToken = 0
|
||||
}
|
||||
export class DrawingBag<T extends IDrawingItem> {
|
||||
constructor(drawingItems: IDrawingItem[] = []) {
|
||||
this.drawingItems = drawingItems;
|
||||
this.removedItems = [];
|
||||
}
|
||||
drawingItems: IDrawingItem[]
|
||||
removedItems: IDrawingItem[]
|
||||
public bagIsEmpty() {
|
||||
return this.drawingItems.reduce((sum, current) => sum + current.drawingWeight, 0) == 0;
|
||||
}
|
||||
|
||||
public Draw(amount: number): T[] {
|
||||
let drawItems: T[] = this.DrawAndRemove(amount);
|
||||
this.RestoreRemoveItems();
|
||||
return drawItems;
|
||||
}
|
||||
public DrawAndRemove(amount: number = 1, predicate: (value: T) => boolean = undefined): T[] {
|
||||
let drawItems: T[] = [];
|
||||
for (let i = 0; i < amount; i++) {
|
||||
if (!this.bagIsEmpty()) {
|
||||
let drawItem = null as T;
|
||||
let drawingPool = [] as T[];
|
||||
if (predicate) {
|
||||
drawingPool = this.drawingItems.filter(predicate) as T[];
|
||||
} else {
|
||||
drawingPool = this.drawingItems as T[];
|
||||
}
|
||||
let drawIndex = Math.random() * drawingPool.reduce((sum, current) => sum + current.drawingWeight, 0);
|
||||
|
||||
let drawCalc = 0;
|
||||
for (let i = 0; i < drawingPool.length; i++) {
|
||||
const item = drawingPool[i];
|
||||
drawCalc += item.drawingWeight;
|
||||
if (drawCalc >= drawIndex) {
|
||||
drawItem = ObjectUtils.CloneValue(item);
|
||||
drawItem.drawingWeight = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//ObjectUtils.CloneValue
|
||||
this.RemoveItem(drawItem);
|
||||
drawItems.push(drawItem);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return drawItems;
|
||||
}
|
||||
|
||||
public RestoreRemoveItems() {
|
||||
for (let i = 0; i < this.removedItems.length; i++) {
|
||||
const removedItem = this.removedItems[i];
|
||||
this.AddItem(removedItem);
|
||||
}
|
||||
this.removedItems = [];
|
||||
}
|
||||
public AddItem(item: IDrawingItem) {
|
||||
let existingItem = this.drawingItems.find(i => i.name == item.name);
|
||||
if (existingItem) {
|
||||
existingItem.drawingWeight += item.drawingWeight;
|
||||
} else {
|
||||
this.drawingItems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
public RemoveItem(item: IDrawingItem) {
|
||||
if (item) {
|
||||
let existingItem = this.drawingItems.find(i => i.name == item.name);
|
||||
if (existingItem) {
|
||||
existingItem.drawingWeight -= item.drawingWeight;
|
||||
|
||||
let removedItem = this.removedItems.find(i => i.name == item.name);
|
||||
if (removedItem) {
|
||||
removedItem.drawingWeight += item.drawingWeight;
|
||||
} else {
|
||||
this.removedItems.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public ClearAllItems() {
|
||||
|
||||
this.drawingItems = [];
|
||||
this.removedItems = [];
|
||||
}
|
||||
}
|
||||
|
||||
export interface IDrawingItem {
|
||||
imageUrl: string
|
||||
name: string
|
||||
description: string
|
||||
drawingWeight: number
|
||||
}
|
||||
export class DrawingItem implements IDrawingItem {
|
||||
constructor(
|
||||
name: string,
|
||||
description: string,
|
||||
imageUrl: string,
|
||||
drawingWeight: number = 1
|
||||
) {
|
||||
this.imageUrl = imageUrl
|
||||
this.name = name
|
||||
this.description = description
|
||||
this.drawingWeight = drawingWeight
|
||||
}
|
||||
imageUrl: string
|
||||
name: string
|
||||
description: string
|
||||
drawingWeight: number
|
||||
}
|
||||
|
||||
export class MobInfo implements IDrawingItem {
|
||||
constructor(
|
||||
config: Partial<MobInfo> = {}
|
||||
) {
|
||||
Object.assign(this, config);
|
||||
this.description = config.name;
|
||||
this.drawingWeight = 1;
|
||||
this.unitRemainHp = config.hp
|
||||
}
|
||||
imageUrl: string
|
||||
standUrl: string
|
||||
name: string
|
||||
description: string
|
||||
drawingWeight: number
|
||||
level: number;
|
||||
rewardTokens: number;
|
||||
hp: number;
|
||||
mobAmount: number;
|
||||
carriedTreasure: DrawingItem[];
|
||||
fixedCarriedTreasure: DrawingItem[];
|
||||
unitRemainHp: number;
|
||||
isRoamingMonster: boolean = false;
|
||||
attackInfos: AttackInfo[];
|
||||
defenseInfos: DefenseInfo;
|
||||
fireToken: number = 0;
|
||||
frozenToken: number = 0;
|
||||
corruptionToken: number = 0;
|
||||
uiWounds: number;
|
||||
uiFireTokens: number;
|
||||
uiFrozenTokens: number;
|
||||
uiCorruptionTokens: number;
|
||||
uiAttackedBy: string;
|
||||
|
||||
public get carriedTreasureHtml(): string {
|
||||
if (!this.carriedTreasure) return '';
|
||||
return this.carriedTreasure.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`)
|
||||
.concat(this.fixedCarriedTreasure?.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`)).join();
|
||||
}
|
||||
|
||||
public get totalHp(): number {
|
||||
return this.isRoamingMonster ? this.unitRemainHp : (this.mobAmount - 1) * this.hp + this.unitRemainHp;
|
||||
}
|
||||
|
||||
public get minionAmount(): number {
|
||||
return (this.mobAmount - 1);
|
||||
}
|
||||
public get leaderExp(): number {
|
||||
return this.isRoamingMonster ? 4 : 2;
|
||||
}
|
||||
|
||||
public get mobInfoHtml(): string {
|
||||
let html = `<img src="${this.imageUrl}" class="g-height-50vh">`
|
||||
+ `<br>Target Unit HP:${this.unitRemainHp}`;
|
||||
|
||||
if (this.isRoamingMonster) {
|
||||
html += `<br><label class="label">Alive Units:${this.mobAmount}`;
|
||||
} else {
|
||||
html += `<br>Total HP:${this.totalHp}`;
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
public getCssClass(): string {
|
||||
let levelString = '';
|
||||
if (this.level < 3) {
|
||||
levelString = '-lv1-2';
|
||||
} else if (this.level < 5) {
|
||||
|
||||
levelString = '-lv3-4';
|
||||
} else {
|
||||
levelString = '-lv5';
|
||||
}
|
||||
return `${this.name.replace(' ', '')}${levelString}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class MD2HeroInfo {
|
||||
constructor(
|
||||
config: Partial<MD2HeroInfo> = {}
|
||||
) {
|
||||
Object.assign(this, config);
|
||||
}
|
||||
class: HeroClass;
|
||||
name: string;
|
||||
hp: number;
|
||||
mp: number;
|
||||
ap: number;
|
||||
hpMaximum: number;
|
||||
mpMaximum: number;
|
||||
exp: number = 0;
|
||||
level: number = 1;
|
||||
fireToken: number = 0;
|
||||
frozenToken: number = 0;
|
||||
corruptionToken: number = 0;
|
||||
playerInfo: GamePlayer;
|
||||
imgUrl: string;
|
||||
skillHtml: string;
|
||||
shadowSkillHtml: string;
|
||||
remainActions: number = 3;
|
||||
rage: number = 0;
|
||||
|
||||
|
||||
public get heroFullName(): string {
|
||||
return `${this.playerInfo.name} (${HeroClass[this.class]} - ${this.name}`
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class MD2Rules {
|
||||
public static CoreGameLevelBoard = [
|
||||
new MD2LevelUpReward({ level: 2, needExp: 5, extraHp: 1, extraMp: 0, extraRareToken: 1 }),
|
||||
new MD2LevelUpReward({ level: 3, needExp: 10, extraHp: 1, extraMp: 1, extraEpicToken: 1 }),
|
||||
new MD2LevelUpReward({ level: 4, needExp: 12, extraHp: 2, extraMp: 2, extraEpicToken: 1 }),
|
||||
new MD2LevelUpReward({ level: 5, needExp: 18, extraHp: 2, extraMp: 2, extraEpicToken: 1 }),
|
||||
];
|
||||
public static checkCoreGameLevelup(currentLevel: number, currentExp: number): MD2LevelUpReward {
|
||||
let result = null as MD2LevelUpReward;
|
||||
let nextLevel = this.CoreGameLevelBoard.find(r => r.level > currentLevel && currentExp >= r.needExp);
|
||||
|
||||
if (nextLevel) {
|
||||
result = ObjectUtils.CloneValue(nextLevel) as MD2LevelUpReward;
|
||||
result.currentExp = currentExp - nextLevel.needExp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class MD2EnemyPhaseSpecialInfo {
|
||||
specialRule: MD2EnemyPhaseSpecialRule
|
||||
specialRules = [
|
||||
new MD2EnemyPhaseSpecialRule(30, '', ''),
|
||||
new MD2EnemyPhaseSpecialRule(44, 'Surprise Attack',
|
||||
'One Mob with line of sight to a hero changes its attack profile to ranged during its <b>SECOND</b> action.<br>' +
|
||||
'You have to apply this to a Mob that is not in the same zone as a hero, and that could not attack otherwise.<br>' +
|
||||
'If no Mob has line of sight to a hero, one Mob moves an additional zone instead.<br>'),
|
||||
new MD2EnemyPhaseSpecialRule(22, 'Fast Advance', 'Each Mob moves 1 additional zone in its first move action.<br>' +
|
||||
'If a Mob doesn’t move, it adds one additional black enemy die to it’s first attack action instead.<br>' +
|
||||
'(If it already has all six black enemy dice in its attack pool, it rerolls one blank result on a black die instead.)<br>'),
|
||||
new MD2EnemyPhaseSpecialRule(4, 'A Dark Portal Appears, Guiding Predators To Their Prey',
|
||||
'First, move one green portal token from the board to the zone with the hero who has the least amount of health tokens. (If there is no green portal token on the board, put one onto the board.)<br>' +
|
||||
'Then, move the Mob that is furthest away from any heroes into this new portal zone. In its following activation, this Mob only has one action.'),
|
||||
]
|
||||
}
|
||||
export class MD2EnemyPhaseSpecialRule implements IDrawingItem {
|
||||
constructor(drawingWeight: number, title: string, description: string) {
|
||||
this.drawingWeight = drawingWeight
|
||||
this.title = title
|
||||
this.description = description
|
||||
}
|
||||
imageUrl: string;
|
||||
name: string;
|
||||
drawingWeight: number;
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface IDarknessPhaseRule {
|
||||
addTreasureToken: Subject<TreasureType>
|
||||
spawnMob: Subject<void>
|
||||
spawnRoamingMonster: Subject<void>
|
||||
runDarknessPhase(): boolean
|
||||
}
|
||||
export class CoreGameDarknessPhaseRule implements IDarknessPhaseRule {
|
||||
round: number = 1;
|
||||
frontEndRound: number = 9;
|
||||
extraRound: number = 0;
|
||||
constructor() {
|
||||
}
|
||||
addTreasureToken = new Subject<TreasureType>();
|
||||
spawnMob = new Subject<void>();
|
||||
spawnRoamingMonster = new Subject<void>();
|
||||
runDarknessPhase() {
|
||||
if (this.round >= this.frontEndRound) {
|
||||
this.extraRound++;
|
||||
if (this.extraRound % 4 == 0) {
|
||||
this.spawnRoamingMonster.next();
|
||||
return false;
|
||||
} else if (this.extraRound % 2 == 0) {
|
||||
this.spawnMob.next();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this.round++;
|
||||
|
||||
switch (this.round) {
|
||||
case 3:
|
||||
case 9:
|
||||
this.spawnMob.next();
|
||||
return false;
|
||||
break;
|
||||
case 4:
|
||||
this.addTreasureToken.next(TreasureType.Rare);
|
||||
break;
|
||||
case 5:
|
||||
case 7:
|
||||
this.spawnRoamingMonster.next();
|
||||
return false;
|
||||
break;
|
||||
case 6:
|
||||
case 8:
|
||||
this.addTreasureToken.next(TreasureType.Epic);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1 @@
|
||||
<op-drop-down [(ngModel)]="playerTabId" [source]="heroOptions" (blur)="onBlur()"></op-drop-down>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MD2HeroSelectComponent } from './md2-hero-select.component';
|
||||
|
||||
describe('MD2HeroSelectComponent', () => {
|
||||
let component: MD2HeroSelectComponent;
|
||||
let fixture: ComponentFixture<MD2HeroSelectComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MD2HeroSelectComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MD2HeroSelectComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,105 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, Renderer2 } from '@angular/core';
|
||||
import { ControlValueAccessor, Validator, AbstractControl, ValidationErrors, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { DropDownOption } from '../../../entity/dropDownOption';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { ArrayUtils } from '../../../utilities/array-utils';
|
||||
import { HeroClass, MD2HeroInfo } from '../massive-darkness2.model';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-hero-select',
|
||||
templateUrl: './md2-hero-select.component.html',
|
||||
styleUrls: ['./md2-hero-select.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => MD2HeroSelectComponent),
|
||||
multi: true
|
||||
},
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MD2HeroSelectComponent implements ControlValueAccessor, Validator {
|
||||
|
||||
private _lastBlurValue: string;
|
||||
playerTabId: string;
|
||||
readonly: boolean = false;
|
||||
isRequired: boolean = false;
|
||||
heroOptions: DropDownOption[];
|
||||
@Input() id?= '';
|
||||
@Input() name = '';
|
||||
@Input() data: MD2HeroInfo;
|
||||
|
||||
@Output() focus = new EventEmitter();
|
||||
@Output() blur = new EventEmitter<MD2HeroInfo>();
|
||||
|
||||
@Input('readonly')
|
||||
public set input_readonly(value) {
|
||||
this.readonly = typeof value !== 'undefined' && value !== false;
|
||||
}
|
||||
@Input('isRequired')
|
||||
public set input_isRequired(value) {
|
||||
this.isRequired = typeof value !== 'undefined' && value !== false;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private elementRef: ElementRef,
|
||||
private renderer: Renderer2,
|
||||
private md2Service: MD2Service,
|
||||
private cdRef: ChangeDetectorRef
|
||||
) {
|
||||
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();
|
||||
}
|
||||
|
||||
validate(control: AbstractControl): ValidationErrors {
|
||||
// if (this.required && (this.value == null || this.value == 0)) {
|
||||
// return { 'currency': '' };
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
registerOnValidatorChange?(fn: () => void): void {
|
||||
}
|
||||
onChange = (value: number) => { };
|
||||
onTouched = () => { };
|
||||
writeValue(obj: MD2HeroInfo): void {
|
||||
this.data = obj;
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
this.readonly = isDisabled;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
this.heroOptions = ArrayUtils.ToDropDownOptions(this.md2Service.heros, h => h.playerInfo.tabId,
|
||||
hero => this.md2Service.heroFullName(hero));
|
||||
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.renderer.removeAttribute(this.elementRef.nativeElement, 'id')
|
||||
}
|
||||
onBlur() {
|
||||
if (this._lastBlurValue != this.playerTabId) {
|
||||
this._lastBlurValue = this.playerTabId;
|
||||
this.data = this.md2Service.heros.find(h => h.playerInfo.tabId == this.playerTabId);
|
||||
this.blur.emit(this.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<span class="MD2Icon {{icon}} {{iconClass}}"></span>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MD2IconComponent } from './md2-icon.component';
|
||||
|
||||
describe('MD2IconComponent', () => {
|
||||
let component: MD2IconComponent;
|
||||
let fixture: ComponentFixture<MD2IconComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MD2IconComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MD2IconComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,21 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MD2Icon } from '../massive-darkness2.model';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-icon',
|
||||
templateUrl: './md2-icon.component.html',
|
||||
styleUrls: ['./md2-icon.component.scss']
|
||||
})
|
||||
export class MD2IconComponent implements OnInit {
|
||||
|
||||
@Input() iconClass: string = '';
|
||||
|
||||
@Input("icon") icon: MD2Icon;
|
||||
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
<label class='label'>Weapon Info</label>
|
||||
<div class="g-brd-3 g-brd-bottom--dashed g-brd-gray-light-v2 mb-3 mt-2 row" *ngFor="let info of mob.attackInfos">
|
||||
<div class="col-md-4">
|
||||
<span class=" g-font-size-50" [innerHtml]="iconHtml(info.type)"></span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div *ngIf="info.yellow" class="g-height-45">
|
||||
<span class="MD2Icon Yellow dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.yellow}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="info.orange" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Orange dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.orange}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="info.red" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Red dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.red}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MobAttackInfoComponent } from './mob-attack-info.component';
|
||||
|
||||
describe('MobAttackInfoComponent', () => {
|
||||
let component: MobAttackInfoComponent;
|
||||
let fixture: ComponentFixture<MobAttackInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MobAttackInfoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MobAttackInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,29 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MD2Icon, MobInfo } from '../../../massive-darkness2.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-mob-attack-info',
|
||||
templateUrl: './mob-attack-info.component.html',
|
||||
styleUrls: ['./mob-attack-info.component.scss']
|
||||
})
|
||||
export class MobAttackInfoComponent implements OnInit {
|
||||
|
||||
MD2Icon = MD2Icon;
|
||||
private _mob: MobInfo;
|
||||
public get mob(): MobInfo {
|
||||
return this._mob;
|
||||
}
|
||||
|
||||
@Input() public set mob(v: MobInfo) {
|
||||
if (this._mob != v) {
|
||||
this._mob = v;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<p>mob-combat-info works!</p>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user