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",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 },
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
@ -3,4 +3,6 @@
|
||||
<router-outlet></router-outlet>
|
||||
</ngx-plain-layout>
|
||||
|
||||
<div kendoDialogContainer></div>
|
||||
|
||||
<!-- 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 { 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 { }
|
||||
|
||||
@ -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