WIP
This commit is contained in:
parent
719108fd6a
commit
ed3c116d13
5639
package-lock.json
generated
5639
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,8 +13,8 @@
|
|||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"conventional-changelog": "conventional-changelog",
|
"conventional-changelog": "conventional-changelog",
|
||||||
"start": "ng serve --host=127.0.0.1",
|
"start": "ng serve --host=127.0.0.1",
|
||||||
"build": "ng build --output-path \\\\ArkNAS\\docker\\nginx-proxy\\data\\ChurchAngular --configuration production",
|
"build": "ng build",
|
||||||
"build:prod": "npm run build -- --configuration production --aot",
|
"build:prod": "ng build --output-path \\\\ArkNAS\\docker\\nginx-proxy\\data\\ChurchAngular --configuration production --aot",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"test:coverage": "rimraf coverage && npm run test -- --code-coverage",
|
"test:coverage": "rimraf coverage && npm run test -- --code-coverage",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
@ -47,7 +47,10 @@
|
|||||||
"@nebular/theme": "13.0.0",
|
"@nebular/theme": "13.0.0",
|
||||||
"@progress/kendo-angular-buttons": "^20.1.1",
|
"@progress/kendo-angular-buttons": "^20.1.1",
|
||||||
"@progress/kendo-angular-dialog": "^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-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-angular-toolbar": "^20.1.1",
|
||||||
"@progress/kendo-licensing": "^1.7.1",
|
"@progress/kendo-licensing": "^1.7.1",
|
||||||
"@progress/kendo-svg-icons": "^4.5.0",
|
"@progress/kendo-svg-icons": "^4.5.0",
|
||||||
@ -116,4 +119,4 @@
|
|||||||
"ts-node": "3.2.2",
|
"ts-node": "3.2.2",
|
||||||
"typescript": "~5.4.4"
|
"typescript": "~5.4.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,6 +5,7 @@ import { AvalonComponent } from './avalon/avalon.component';
|
|||||||
import { GamesComponent } from './games.component';
|
import { GamesComponent } from './games.component';
|
||||||
import { HeroDashboardComponent } from './massive-darkness2/hero-dashboard/hero-dashboard.component';
|
import { HeroDashboardComponent } from './massive-darkness2/hero-dashboard/hero-dashboard.component';
|
||||||
import { MassiveDarkness2Component } from './massive-darkness2/massive-darkness2.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 {
|
export class GameRoomMenuConfig {
|
||||||
public static HostMenu: NbMenuItem[] = [
|
public static HostMenu: NbMenuItem[] = [
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ const routes: Routes = [
|
|||||||
{ path: 'avalonHost', component: AvalonComponent },
|
{ path: 'avalonHost', component: AvalonComponent },
|
||||||
{ path: 'MD2', component: MassiveDarkness2Component },
|
{ path: 'MD2', component: MassiveDarkness2Component },
|
||||||
{ path: 'MD2_Hero/:roomId', component: HeroDashboardComponent },
|
{ path: 'MD2_Hero/:roomId', component: HeroDashboardComponent },
|
||||||
|
{ path: 'MD2MobInfo', component: MD2MobInfoMaintenanceComponent },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -3,4 +3,6 @@
|
|||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</ngx-plain-layout>
|
</ngx-plain-layout>
|
||||||
|
|
||||||
|
<div kendoDialogContainer></div>
|
||||||
|
|
||||||
<!-- ngx-plain-layout ngx-one-column-layout-->
|
<!-- ngx-plain-layout ngx-one-column-layout-->
|
||||||
@ -39,6 +39,14 @@ import { MD2IconPickerDlgComponent } from './massive-darkness2/md2-html-editor/m
|
|||||||
import { EditorModule as KendoEditorModule } from '@progress/kendo-angular-editor';
|
import { EditorModule as KendoEditorModule } from '@progress/kendo-angular-editor';
|
||||||
import { ToolBarModule } from '@progress/kendo-angular-toolbar';
|
import { ToolBarModule } from '@progress/kendo-angular-toolbar';
|
||||||
import { ButtonsModule } from '@progress/kendo-angular-buttons';
|
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({
|
@NgModule({
|
||||||
@ -68,12 +76,16 @@ import { ButtonsModule } from '@progress/kendo-angular-buttons';
|
|||||||
MobCombatInfoComponent,
|
MobCombatInfoComponent,
|
||||||
MobStandInfoComponent,
|
MobStandInfoComponent,
|
||||||
MD2HtmlEditorComponent,
|
MD2HtmlEditorComponent,
|
||||||
MD2IconPickerDlgComponent
|
MD2IconPickerDlgComponent,
|
||||||
|
MD2MobInfoMaintenanceComponent,
|
||||||
|
MD2MobInfoEditorComponent,
|
||||||
|
MD2MobInfoDetailComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
GamesRoutingModule,
|
GamesRoutingModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
AdminRoutingModule,
|
AdminRoutingModule,
|
||||||
ThemeModule,
|
ThemeModule,
|
||||||
NbMenuModule,
|
NbMenuModule,
|
||||||
@ -103,7 +115,11 @@ import { ButtonsModule } from '@progress/kendo-angular-buttons';
|
|||||||
EditorModule,
|
EditorModule,
|
||||||
KendoEditorModule,
|
KendoEditorModule,
|
||||||
ToolBarModule,
|
ToolBarModule,
|
||||||
ButtonsModule
|
ButtonsModule,
|
||||||
|
GridModule,
|
||||||
|
DialogModule,
|
||||||
|
InputsModule,
|
||||||
|
DropDownsModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class GamesModule { }
|
export class GamesModule { }
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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>
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
kendo-grid {
|
||||||
|
height: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
@ -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] || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user