This commit is contained in:
Chris Chen 2025-11-03 21:43:24 -08:00
parent 719108fd6a
commit ed3c116d13
14 changed files with 6470 additions and 22 deletions

5639
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,8 +13,8 @@
"ng": "ng",
"conventional-changelog": "conventional-changelog",
"start": "ng serve --host=127.0.0.1",
"build": "ng build --output-path \\\\ArkNAS\\docker\\nginx-proxy\\data\\ChurchAngular --configuration production",
"build:prod": "npm run build -- --configuration production --aot",
"build": "ng build",
"build:prod": "ng build --output-path \\\\ArkNAS\\docker\\nginx-proxy\\data\\ChurchAngular --configuration production --aot",
"test": "ng test",
"test:coverage": "rimraf coverage && npm run test -- --code-coverage",
"lint": "ng lint",
@ -47,7 +47,10 @@
"@nebular/theme": "13.0.0",
"@progress/kendo-angular-buttons": "^20.1.1",
"@progress/kendo-angular-dialog": "^20.1.1",
"@progress/kendo-angular-dropdowns": "^20.1.1",
"@progress/kendo-angular-editor": "^20.1.1",
"@progress/kendo-angular-grid": "^20.1.1",
"@progress/kendo-angular-inputs": "^20.1.1",
"@progress/kendo-angular-toolbar": "^20.1.1",
"@progress/kendo-licensing": "^1.7.1",
"@progress/kendo-svg-icons": "^4.5.0",
@ -116,4 +119,4 @@
"ts-node": "3.2.2",
"typescript": "~5.4.4"
}
}
}

View File

@ -5,6 +5,7 @@ import { AvalonComponent } from './avalon/avalon.component';
import { GamesComponent } from './games.component';
import { HeroDashboardComponent } from './massive-darkness2/hero-dashboard/hero-dashboard.component';
import { MassiveDarkness2Component } from './massive-darkness2/massive-darkness2.component';
import { MD2MobInfoMaintenanceComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-info-maintenance.component';
export class GameRoomMenuConfig {
public static HostMenu: NbMenuItem[] = [
@ -47,6 +48,7 @@ const routes: Routes = [
{ path: 'avalonHost', component: AvalonComponent },
{ path: 'MD2', component: MassiveDarkness2Component },
{ path: 'MD2_Hero/:roomId', component: HeroDashboardComponent },
{ path: 'MD2MobInfo', component: MD2MobInfoMaintenanceComponent },
]
},

View File

@ -3,4 +3,6 @@
<router-outlet></router-outlet>
</ngx-plain-layout>
<div kendoDialogContainer></div>
<!-- ngx-plain-layout ngx-one-column-layout-->

View File

@ -39,6 +39,14 @@ import { MD2IconPickerDlgComponent } from './massive-darkness2/md2-html-editor/m
import { EditorModule as KendoEditorModule } from '@progress/kendo-angular-editor';
import { ToolBarModule } from '@progress/kendo-angular-toolbar';
import { ButtonsModule } from '@progress/kendo-angular-buttons';
import { GridModule } from '@progress/kendo-angular-grid';
import { DialogModule } from '@progress/kendo-angular-dialog';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
import { ReactiveFormsModule } from '@angular/forms';
import { MD2MobInfoMaintenanceComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-info-maintenance.component';
import { MD2MobInfoEditorComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-info-editor/md2-mob-info-editor.component';
import { MD2MobInfoDetailComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component';
@NgModule({
@ -68,12 +76,16 @@ import { ButtonsModule } from '@progress/kendo-angular-buttons';
MobCombatInfoComponent,
MobStandInfoComponent,
MD2HtmlEditorComponent,
MD2IconPickerDlgComponent
MD2IconPickerDlgComponent,
MD2MobInfoMaintenanceComponent,
MD2MobInfoEditorComponent,
MD2MobInfoDetailComponent
],
imports: [
CommonModule,
GamesRoutingModule,
FormsModule,
ReactiveFormsModule,
AdminRoutingModule,
ThemeModule,
NbMenuModule,
@ -103,7 +115,11 @@ import { ButtonsModule } from '@progress/kendo-angular-buttons';
EditorModule,
KendoEditorModule,
ToolBarModule,
ButtonsModule
ButtonsModule,
GridModule,
DialogModule,
InputsModule,
DropDownsModule
]
})
export class GamesModule { }

View File

@ -0,0 +1,148 @@
<div class="k-dialog-content detail-container">
<div class="mob-info-section">
<h3>{{ mobInfo?.name }}</h3>
<div class="info-grid">
<div class="info-item">
<label>Type:</label>
<span>{{ getMobTypeName(mobInfo?.type) }}</span>
</div>
<div class="info-item">
<label>Game Bundle:</label>
<span>{{ getGameBundleName(mobInfo?.from) }}</span>
</div>
<div class="info-item">
<label>Leader Image:</label>
<span>{{ mobInfo?.leaderImgUrl || 'N/A' }}</span>
</div>
<div class="info-item">
<label>Minion Image:</label>
<span>{{ mobInfo?.minionImgUrl || 'N/A' }}</span>
</div>
</div>
</div>
<div class="levels-section">
<h4>Mob Levels</h4>
<div class="levels-list">
<div
*ngFor="let levelInfo of mobLevelInfos; let i = index"
class="level-item"
[class.selected]="selectedLevelInfo?.id === levelInfo.id"
(click)="selectLevelInfo(levelInfo)">
<strong>Level {{ i + 1 }}</strong>
<div class="level-details">
<span>HP: {{ levelInfo.fixedHp }}</span>
<span>Actions: {{ levelInfo.actions }}</span>
<span>Reward Tokens: {{ levelInfo.rewardTokens }}</span>
</div>
</div>
</div>
</div>
<div class="skills-section" *ngIf="selectedLevelInfo">
<h4>Skills for Selected Level</h4>
<div class="skills-toolbar">
<button kendoButton (click)="addSkillHandler()" [primary]="true">
<span class="k-icon k-i-plus"></span> Add Skill
</button>
</div>
<kendo-grid
#skillsGrid
[data]="skillsData"
[loading]="isLoading"
[pageSize]="skillsState.take"
[skip]="skillsState.skip"
[sortable]="true"
[filterable]="true"
[pageable]="true"
[height]="400"
kendoGridTemplateEditing
(save)="saveSkillHandler($event)"
(remove)="removeSkillHandler($event)"
(dataStateChange)="skillsState = $event">
<kendo-grid-column field="name" title="Name" width="150">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<input kendoTextBox [(ngModel)]="dataItem.name" name="name" />
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="type" title="Type" width="120">
<ng-template kendoGridCellTemplate let-dataItem>
{{ getSkillTypeName(dataItem.type) }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-dropdownlist
[(ngModel)]="dataItem.type"
name="type"
[data]="skillTypes"
[valueField]="'value'"
[textField]="'text'">
</kendo-dropdownlist>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="skillTarget" title="Target" width="150">
<ng-template kendoGridCellTemplate let-dataItem>
{{ getSkillTargetName(dataItem.skillTarget) }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-dropdownlist
[(ngModel)]="dataItem.skillTarget"
name="skillTarget"
[data]="skillTargets"
[valueField]="'value'"
[textField]="'text'">
</kendo-dropdownlist>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="clawRoll" title="Claw Roll" width="100">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-numerictextbox
[(ngModel)]="dataItem.clawRoll"
name="clawRoll"
[min]="0">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="skillRoll" title="Skill Roll" width="100">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-numerictextbox
[(ngModel)]="dataItem.skillRoll"
name="skillRoll"
[min]="0">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="description" title="Description" width="300">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<textarea kendoTextArea
[(ngModel)]="dataItem.description"
name="description"
rows="3">
</textarea>
</ng-template>
</kendo-grid-column>
<kendo-grid-command-column title="Actions" width="200">
<ng-template kendoGridCellTemplate let-isNew="isNew" let-dataItem="dataItem" let-rowIndex="rowIndex">
<button kendoGridEditCommand [primary]="true">Edit</button>
<button kendoGridRemoveCommand>Remove</button>
</ng-template>
<ng-template kendoGridEditCommandTemplate let-isNew="isNew" let-rowIndex="rowIndex">
<button kendoGridSaveCommand>Save</button>
<button kendoGridCancelCommand>Cancel</button>
</ng-template>
</kendo-grid-command-column>
</kendo-grid>
</div>
</div>
<kendo-dialog-actions>
<button kendoButton [primary]="true" (click)="close()">Close</button>
</kendo-dialog-actions>

View File

@ -0,0 +1,89 @@
.k-dialog-content {
padding: 20px;
max-height: 80vh;
overflow-y: auto;
}
.detail-container {
display: flex;
flex-direction: column;
gap: 20px;
}
.mob-info-section {
border-bottom: 1px solid #e0e0e0;
padding-bottom: 15px;
h3 {
margin: 0 0 15px 0;
}
}
.info-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.info-item {
display: flex;
gap: 10px;
label {
font-weight: 500;
min-width: 120px;
}
}
.levels-section {
h4 {
margin: 0 0 10px 0;
}
}
.levels-list {
display: flex;
flex-direction: column;
gap: 10px;
max-height: 150px;
overflow-y: auto;
padding: 10px;
border: 1px solid #e0e0e0;
border-radius: 4px;
}
.level-item {
padding: 10px;
border: 1px solid #e0e0e0;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
&:hover {
background-color: #f5f5f5;
}
&.selected {
background-color: #e3f2fd;
border-color: #2196f3;
}
}
.level-details {
display: flex;
gap: 15px;
margin-top: 5px;
font-size: 12px;
color: #666;
}
.skills-section {
h4 {
margin: 0 0 10px 0;
}
}
.skills-toolbar {
margin-bottom: 10px;
}

View File

@ -0,0 +1,193 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { DialogRef, DialogContentBase } from '@progress/kendo-angular-dialog';
import { GridComponent, GridDataResult } from '@progress/kendo-angular-grid';
import { State } from '@progress/kendo-data-query';
import { first } from 'rxjs/operators';
import { MD2MobInfo, MD2MobLevelInfo, MD2MobSkill, MobSkillTarget, GameBundle } from '../../massive-darkness2.db.model';
import { MobType } from '../../massive-darkness2.model';
import { MobSkillType } from '../../massive-darkness2.model.boss';
import { MD2MobLevelInfoService, MD2MobSkillService } from '../../service/massive-darkness2.service';
import { MsgBoxService } from '../../../../services/msg-box.service';
@Component({
selector: 'ngx-md2-mob-info-detail',
templateUrl: './md2-mob-info-detail.component.html',
styleUrls: ['./md2-mob-info-detail.component.scss']
})
export class MD2MobInfoDetailComponent extends DialogContentBase implements OnInit {
@Input() public mobInfo: MD2MobInfo;
@ViewChild('skillsGrid') skillsGrid: GridComponent;
public mobLevelInfos: MD2MobLevelInfo[] = [];
public selectedLevelInfo: MD2MobLevelInfo | null = null;
public skillsData: GridDataResult = { data: [], total: 0 };
public skillsState: State = {
skip: 0,
take: 10,
sort: [],
filter: {
logic: 'and',
filters: []
}
};
public isLoading: boolean = false;
public skillTypes: Array<{ value: MobSkillType; text: string }> = [];
public skillTargets: Array<{ value: MobSkillTarget | null; text: string }> = [];
constructor(
public dialog: DialogRef,
private mobLevelInfoService: MD2MobLevelInfoService,
private mobSkillService: MD2MobSkillService,
private msgBoxService: MsgBoxService
) {
super(dialog);
this.initializeEnums();
}
ngOnInit(): void {
if (this.mobInfo) {
this.mobLevelInfos = this.mobInfo.mobLevelInfos || [];
if (this.mobLevelInfos.length > 0) {
this.selectLevelInfo(this.mobLevelInfos[0]);
}
}
}
private initializeEnums(): void {
// Initialize MobSkillType options
Object.keys(MobSkillType).filter(key => isNaN(Number(key))).forEach(key => {
this.skillTypes.push({
value: MobSkillType[key] as MobSkillType,
text: key
});
});
// Initialize MobSkillTarget options
this.skillTargets.push({ value: null, text: 'None' });
Object.keys(MobSkillTarget).filter(key => isNaN(Number(key))).forEach(key => {
this.skillTargets.push({
value: MobSkillTarget[key] as MobSkillTarget,
text: key
});
});
}
public selectLevelInfo(levelInfo: MD2MobLevelInfo): void {
this.selectedLevelInfo = levelInfo;
this.loadSkills(levelInfo);
}
public loadSkills(levelInfo: MD2MobLevelInfo): void {
if (levelInfo && levelInfo.skills) {
this.skillsData = {
data: levelInfo.skills,
total: levelInfo.skills.length
};
} else {
this.skillsData = { data: [], total: 0 };
}
}
public addSkillHandler(): void {
if (!this.selectedLevelInfo) return;
const newSkill: MD2MobSkill = {
id: this.generateId(),
mobLevelInfoId: this.selectedLevelInfo.id,
type: MobSkillType.Combat,
skillTarget: null,
clawRoll: 0,
skillRoll: 1,
name: '',
description: ''
};
if (!this.selectedLevelInfo.skills) {
this.selectedLevelInfo.skills = [];
}
this.selectedLevelInfo.skills.push(newSkill);
this.loadSkills(this.selectedLevelInfo);
}
public saveSkillHandler({ dataItem, isNew }: any): void {
if (isNew) {
dataItem.id = this.generateId();
dataItem.mobLevelInfoId = this.selectedLevelInfo?.id;
}
this.isLoading = true;
this.mobSkillService.createOrUpdate(dataItem).pipe(first()).subscribe(result => {
this.isLoading = false;
if (this.selectedLevelInfo) {
if (isNew) {
if (!this.selectedLevelInfo.skills) {
this.selectedLevelInfo.skills = [];
}
const index = this.selectedLevelInfo.skills.findIndex(s => s.id === dataItem.id);
if (index !== -1) {
this.selectedLevelInfo.skills[index] = result;
} else {
this.selectedLevelInfo.skills.push(result);
}
} else {
const index = this.selectedLevelInfo.skills.findIndex(s => s.id === result.id);
if (index !== -1) {
this.selectedLevelInfo.skills[index] = result;
}
}
this.loadSkills(this.selectedLevelInfo);
}
}, error => {
this.isLoading = false;
console.error('Error saving skill:', error);
});
}
public removeSkillHandler({ dataItem }: { dataItem: MD2MobSkill }): void {
if (!this.selectedLevelInfo) return;
this.msgBoxService.showConfirmDeleteBox().pipe(first()).subscribe(answer => {
if (answer === true) {
this.isLoading = true;
this.mobSkillService.delete(dataItem.id).pipe(first()).subscribe(result => {
if (this.selectedLevelInfo.skills) {
const index = this.selectedLevelInfo.skills.findIndex(s => s.id === dataItem.id);
if (index !== -1) {
this.selectedLevelInfo.skills.splice(index, 1);
}
this.loadSkills(this.selectedLevelInfo);
}
this.isLoading = false;
});
}
});
}
private generateId(): string {
return 'skill_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
public getSkillTypeName(type: MobSkillType): string {
return MobSkillType[type] || '';
}
public getSkillTargetName(target: MobSkillTarget | null): string {
if (target === null) return 'None';
return MobSkillTarget[target] || '';
}
public getMobTypeName(type: MobType): string {
return MobType[type] || '';
}
public getGameBundleName(bundle: GameBundle): string {
return GameBundle[bundle] || '';
}
public close(): void {
this.dialog.close(true);
}
}

View File

@ -0,0 +1,45 @@
<div class="k-dialog-content">
<form #form="ngForm" class="k-form">
<div class="k-form-field">
<label class="k-label">Name *</label>
<input kendoTextBox [(ngModel)]="model.name" name="name" required class="k-input"
placeholder="Enter mob name" />
<span class="k-form-error" *ngIf="form.controls['name']?.invalid && form.controls['name']?.touched">
Name is required
</span>
</div>
<div class="k-form-field">
<label class="k-label">Type *</label>
<kendo-dropdownlist [(ngModel)]="selectedMobType" name="type" [data]="mobTypes" [valueField]="'value'"
[textField]="'text'" [defaultItem]="{ value: null, text: 'Select type...' }">
</kendo-dropdownlist>
</div>
<div class="k-form-field">
<label class="k-label">Game Bundle *</label>
<kendo-dropdownlist [(ngModel)]="selectedGameBundle" name="from" [data]="gameBundles" [valueField]="'value'"
[textField]="'text'" [defaultItem]="{ value: null, text: 'Select bundle...' }">
</kendo-dropdownlist>
</div>
<div class="k-form-field">
<label class="k-label">Leader Image URL</label>
<input kendoTextBox [(ngModel)]="model.leaderImgUrl" name="leaderImgUrl" class="k-input"
placeholder="Enter leader image URL" />
</div>
<div class="k-form-field">
<label class="k-label">Minion Image URL</label>
<input kendoTextBox [(ngModel)]="model.minionImgUrl" name="minionImgUrl" class="k-input"
placeholder="Enter minion image URL" />
</div>
</form>
</div>
<kendo-dialog-actions>
<button kendoButton (click)="close()">Cancel</button>
<button kendoButton [primary]="true" (click)="save()" [disabled]="!isValid || processing">
{{ processing ? 'Saving...' : 'Save' }}
</button>
</kendo-dialog-actions>

View File

@ -0,0 +1,29 @@
.k-dialog-content {
padding: 20px;
}
.k-form {
display: flex;
flex-direction: column;
gap: 20px;
}
.k-form-field {
display: flex;
flex-direction: column;
gap: 5px;
}
.k-label {
font-weight: 500;
}
.k-input {
width: 100%;
}
.k-form-error {
color: red;
font-size: 12px;
}

View File

@ -0,0 +1,115 @@
import { Component, Input, OnInit, ViewChild, ChangeDetectorRef } from '@angular/core';
import { DialogRef, DialogContentBase } from '@progress/kendo-angular-dialog';
import { NgForm } from '@angular/forms';
import { first } from 'rxjs/operators';
import { MD2MobInfo, GameBundle } from '../../massive-darkness2.db.model';
import { MobType } from '../../massive-darkness2.model';
import { MD2MobInfoService } from '../../service/massive-darkness2.service';
@Component({
selector: 'ngx-md2-mob-info-editor',
templateUrl: './md2-mob-info-editor.component.html',
styleUrls: ['./md2-mob-info-editor.component.scss']
})
export class MD2MobInfoEditorComponent extends DialogContentBase implements OnInit {
@Input() public data: MD2MobInfo;
@Input() public isAdding: boolean = false;
@ViewChild('form') form: NgForm;
public model: MD2MobInfo;
public processing: boolean = false;
public mobTypes: Array<{ value: MobType; text: string }> = [];
public gameBundles: Array<{ value: GameBundle; text: string }> = [];
public selectedMobType: { value: MobType; text: string } | null = null;
public selectedGameBundle: { value: GameBundle; text: string } | null = null;
constructor(
public dialog: DialogRef,
private mobInfoService: MD2MobInfoService,
private cdr: ChangeDetectorRef
) {
super(dialog);
this.initializeEnums();
}
ngOnInit(): void {
this.initializeModel();
}
public initializeModel(): void {
const typeValue = this.data?.type !== undefined && this.data?.type !== null ? this.data.type : MobType.Mob;
const fromValue = this.data?.from !== undefined && this.data?.from !== null ? this.data.from : GameBundle.CoreGame;
this.model = {
id: this.data?.id || '',
name: this.data?.name || '',
type: typeValue,
from: fromValue,
leaderImgUrl: this.data?.leaderImgUrl || '',
minionImgUrl: this.data?.minionImgUrl || '',
mobLevelInfos: this.data?.mobLevelInfos || []
};
// Set selected objects for dropdowns
this.selectedMobType = this.mobTypes.find(t => t.value === typeValue) || this.mobTypes[0] || null;
this.selectedGameBundle = this.gameBundles.find(b => b.value === fromValue) || this.gameBundles[0] || null;
this.cdr.detectChanges();
}
private initializeEnums(): void {
// Initialize MobType options
Object.keys(MobType).filter(key => isNaN(Number(key))).forEach(key => {
this.mobTypes.push({
value: MobType[key] as MobType,
text: key
});
});
// Initialize GameBundle options
Object.keys(GameBundle).filter(key => isNaN(Number(key))).forEach(key => {
this.gameBundles.push({
value: GameBundle[key] as GameBundle,
text: key
});
});
}
public close(): void {
this.dialog.close();
}
public save(): void {
if (this.model.name && !this.processing) {
this.processing = true;
// Extract enum values from selected objects
const mobInfo: MD2MobInfo = {
...this.model,
type: this.selectedMobType?.value ?? MobType.Mob,
from: this.selectedGameBundle?.value ?? GameBundle.CoreGame,
mobLevelInfos: this.data?.mobLevelInfos || []
};
this.mobInfoService.createOrUpdate(mobInfo).pipe(first()).subscribe(result => {
this.processing = false;
this.dialog.close(result);
}, error => {
this.processing = false;
console.error('Error saving mob info:', error);
});
}
}
public get isValid(): boolean {
if (!this.model) {
return false;
}
const nameValid = this.model.name && this.model.name.trim().length > 0;
const typeValid = this.selectedMobType !== null && this.selectedMobType !== undefined;
const fromValid = this.selectedGameBundle !== null && this.selectedGameBundle !== undefined;
return nameValid && typeValid && fromValid;
}
}

View File

@ -0,0 +1,51 @@
<nb-card>
<nb-card-header>
<h4>MD2 Mob Info Maintenance</h4>
<div class="float-right">
<button kendoButton (click)="addHandler()" [primary]="true">
<span class="k-icon k-i-plus"></span> Add New
</button>
</div>
</nb-card-header>
<nb-card-body>
<kendo-grid #grid [data]="gridData" [loading]="isLoading" [pageSize]="gridState.take" [skip]="gridState.skip"
[sortable]="true" [filterable]="true" [pageable]="true" [selectable]="true"
(dataStateChange)="gridState = $event; loadData()">
<kendo-grid-toolbar>
<button kendoGridAddCommand>Add new</button>
</kendo-grid-toolbar>
<kendo-grid-column field="name" title="Name" width="200">
</kendo-grid-column>
<kendo-grid-column field="type" title="Type" width="150">
<ng-template kendoGridCellTemplate let-dataItem>
{{ getMobTypeName(dataItem.type) }}
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="from" title="Game Bundle" width="150">
<ng-template kendoGridCellTemplate let-dataItem>
{{ getGameBundleName(dataItem.from) }}
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="leaderImgUrl" title="Leader Image" width="200">
</kendo-grid-column>
<kendo-grid-column field="minionImgUrl" title="Minion Image" width="200">
</kendo-grid-column>
<kendo-grid-command-column title="Actions" width="250">
<ng-template kendoGridCellTemplate let-isNew="isNew" let-dataItem="dataItem" let-rowIndex="rowIndex">
<button kendoGridEditCommand [primary]="true">Edit</button>
<button kendoGridRemoveCommand>Remove</button>
<button kendoButton (click)="viewDetailHandler({ dataItem })" [look]="'flat'">
View Details
</button>
</ng-template>
</kendo-grid-command-column>
</kendo-grid>
</nb-card-body>
</nb-card>

View File

@ -0,0 +1,12 @@
:host {
display: block;
}
.float-right {
float: right;
}
kendo-grid {
height: 600px;
}

View File

@ -0,0 +1,138 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { GridComponent, GridDataResult } from '@progress/kendo-angular-grid';
import { State } from '@progress/kendo-data-query';
import { first } from 'rxjs/operators';
import { MD2MobInfo, GameBundle } from '../massive-darkness2.db.model';
import { MobType } from '../massive-darkness2.model';
import { MD2MobInfoService } from '../service/massive-darkness2.service';
import { MD2MobInfoDetailComponent } from './md2-mob-info-detail/md2-mob-info-detail.component';
import { MD2MobInfoEditorComponent } from './md2-mob-info-editor/md2-mob-info-editor.component';
import { DialogService } from '@progress/kendo-angular-dialog';
import { MsgBoxService } from '../../../services/msg-box.service';
@Component({
selector: 'ngx-md2-mob-info-maintenance',
templateUrl: './md2-mob-info-maintenance.component.html',
styleUrls: ['./md2-mob-info-maintenance.component.scss']
})
export class MD2MobInfoMaintenanceComponent implements OnInit {
@ViewChild('grid') grid: GridComponent;
public gridData: GridDataResult = { data: [], total: 0 };
public gridState: State = {
skip: 0,
take: 10,
sort: [],
filter: {
logic: 'and',
filters: []
}
};
public isLoading: boolean = false;
constructor(
private mobInfoService: MD2MobInfoService,
private dialogService: DialogService,
private msgBoxService: MsgBoxService
) {
}
ngOnInit(): void {
this.loadData();
}
public loadData(): void {
this.isLoading = true;
this.mobInfoService.getAll().pipe(first()).subscribe(result => {
this.gridData = {
data: result,
total: result.length
};
this.isLoading = false;
});
}
public addHandler(): void {
const editorData = {} as MD2MobInfo;
const dialogRef = this.dialogService.open({
title: 'Add New Mob Info',
content: MD2MobInfoEditorComponent,
width: 800,
height: 600
});
const editor = dialogRef.content.instance;
editor.isAdding = true;
editor.data = editorData;
// Force model re-initialization after data is set
setTimeout(() => {
editor.initializeModel();
}, 0);
dialogRef.result.subscribe(result => {
if (result) {
this.loadData();
}
});
}
public editHandler({ dataItem }: { dataItem: MD2MobInfo }): void {
const dialogRef = this.dialogService.open({
title: 'Edit Mob Info',
content: MD2MobInfoEditorComponent,
width: 800,
height: 600
});
const editor = dialogRef.content.instance;
editor.isAdding = false;
editor.data = JSON.parse(JSON.stringify(dataItem));
dialogRef.result.subscribe(result => {
if (result) {
this.loadData();
}
});
}
public removeHandler({ dataItem }: { dataItem: MD2MobInfo }): void {
this.msgBoxService.showConfirmDeleteBox().pipe(first()).subscribe(answer => {
if (answer === true) {
this.isLoading = true;
this.mobInfoService.delete(dataItem.id).pipe(first()).subscribe(result => {
this.loadData();
});
}
});
}
public viewDetailHandler({ dataItem }: { dataItem: MD2MobInfo }): void {
this.mobInfoService.getById(dataItem.id).pipe(first()).subscribe(mobInfo => {
const dialogRef = this.dialogService.open({
title: `Mob Info: ${mobInfo.name}`,
content: MD2MobInfoDetailComponent,
width: 1200,
height: 800
});
const detail = dialogRef.content.instance;
detail.mobInfo = mobInfo;
dialogRef.result.subscribe(result => {
if (result) {
this.loadData();
}
});
});
}
public getMobTypeName(type: MobType): string {
return MobType[type] || '';
}
public getGameBundleName(bundle: GameBundle): string {
return GameBundle[bundle] || '';
}
}