made efficent handler for spells

added routes for spells maintanance
updated model for spells with more fields, default values
moved update handlers inside spell and skill components
This commit is contained in:
2025-01-18 20:59:54 +01:00
parent 82690bfe67
commit 3c8e415359
9 changed files with 464 additions and 184 deletions
+6 -27
View File
@@ -7,7 +7,6 @@
<component
:is="currentView"
:mdata="mdata"
@update-skill="handleSkillUpdate"
/>
</div>
<!-- Submenu -->
@@ -31,8 +30,8 @@
<script>
import API from '../utils/api'
import SkillView from "./maintenance/SkillView.vue"; // Component for character history
/*import WeaponSkillView from "./maintenance/WeaponSkillView.vue"; // Component for character equipment
import SpellView from "./maintenance/SpellView.vue"; // Component for character history
/*import WeaponSkillView from "./maintenance/WeaponSkillView.vue"; // Component for character equipment
import EquipmentView from "./maintenance/EquipmentView.vue"; // Component for character equipment
import WeaponView from "./maintenance/WeaponView.vue"; // Component for character history
*/
@@ -43,8 +42,8 @@ export default {
//props: ["id"], // Receive the route parameter as a prop
components: {
SkillView,
/*WeaponSkillView,
SpellView,
/*WeaponSkillView,
WeaponView,
EquipmentView,*/
},
@@ -54,7 +53,8 @@ export default {
skills: [],
skillcategories: [],
weaponskills: [],
spells: []
spells: [],
spellcategories: [],
},
loading: true,
/*
@@ -67,8 +67,8 @@ export default {
lastView: "SkillView",
menus: [
{ id: 0, name: "skill", component: "SkillView" },
/*{ id: 1, name: "weaponskill", component: "WeaponSkillView" },
{ id: 2, name: "spell", component: "SpellView" },
/*{ id: 1, name: "weaponskill", component: "WeaponSkillView" },
{ id: 3, name: "equipment", component: "EquipmentView" },
{ id: 1, name: "weapon", component: "WeaponView" },
*/
@@ -99,28 +99,7 @@ export default {
changeView(view) {
this.lastView = this.currentView;
this.currentView = view;
},
async handleSkillUpdate({ index, skill }) {
try {
const response = await API.put(
`/api/maintenance/skills/${skill.id}`, skill,
{
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}` ,
'Content-Type': 'application/json'
}
}
)
if (!response.statusText== "OK") throw new Error('Update failed');
const updatedSkill = response.data;
// Update the skill in mdata
this.mdata.skills = this.mdata.skills.map(s =>
s.id === updatedSkill.id ? updatedSkill : s
);
} catch (error) {
console.error('Failed to update skill:', error);
}
}
}
},
};
</script>
@@ -122,6 +122,7 @@
<script>
import API from '../../utils/api'
export default {
name: "SkillView",
props: {
@@ -178,7 +179,8 @@ export default {
this.editedItem = { ...this.filteredAndSortedSkills[index] };
},
saveEdit(index) {
this.$emit('update-skill', { index, skill: this.editedItem });
//this.$emit('update-skill', { index, skill: this.editedItem });
this.handleSkillUpdate({ index, skill: this.editedItem });
this.editingIndex = -1;
this.editedItem = null;
},
@@ -193,6 +195,27 @@ export default {
this.sortField = field;
this.sortAsc = true;
}
},
async handleSkillUpdate({ index, skill }) {
try {
const response = await API.put(
`/api/maintenance/skills/${skill.id}`, skill,
{
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}` ,
'Content-Type': 'application/json'
}
}
)
if (!response.statusText== "OK") throw new Error('Update failed');
const updatedSkill = response.data;
// Update the skill in mdata
this.mdata.skills = this.mdata.skills.map(s =>
s.id === updatedSkill.id ? updatedSkill : s
);
} catch (error) {
console.error('Failed to update skill:', error);
}
}
}
};
+179 -31
View File
@@ -1,43 +1,85 @@
<template>
<div class="header-section">
<h2>{{ $t('maintenance') }}</h2>
<!-- Add search input -->
<div class="search-box">
<input
type="text"
v-model="searchTerm"
:placeholder="`${$t('search')} ${$t('Spell')}...`"
/>
</div>
</div>
<div class="cd-view">
<div class="cd-list">
<div class="tables-container">
<h2 style="line-height: 1.5; margin-top: 5px;"><!-- {{ character.name }}'s -->Fertigkeiten</h2>
<table class="cd-table">
<thead>
<tr>
<th class="cd-table-header" width="60%">{{ $t('skill.name') }}</th>
<th class="cd-table-header" width="35">{{ $t('skill.value') }}</th>
<th class="cd-table-header" width="35">{{ $t('skill.bonus') }}</th>
<th class="cd-table-header" width="35">{{ $t('skill.pp') }}</th>
<!-- <th class="cd-table-header">{{ $t('skill.description') }}</th>-->
<th class="cd-table-header" width="30%">{{ $t('skill.note') }}</th>
<th class="cd-table-header">{{ $t('spell.id') }}</th>
<th class="cd-table-header">{{ $t('spell.category') }}<button @click="sortBy('category')">-{{ sortField === 'category' ? (sortAsc ? '' : '') : '' }}</button></th>
<th class="cd-table-header">{{ $t('spell.name') }} <button @click="sortBy('name')">-{{ sortField === 'name' ? (sortAsc ? '' : '') : '' }}</button></th>
<th class="cd-table-header">{{ $t('spell.level') }}</th>
<th class="cd-table-header">{{ $t('spell.apverbrauch') }}</th>
<th class="cd-table-header">{{ $t('spell.zauberdauer') }}</th>
<th class="cd-table-header">{{ $t('spell.reichweite') }}</th>
<th class="cd-table-header">{{ $t('spell.wirkungsziel') }}</th>
<th class="cd-table-header">{{ $t('spell.wirkungsbereich') }}</th>
<th class="cd-table-header">{{ $t('spell.wirkungsdauer') }}</th>
<th class="cd-table-header">{{ $t('spell.ursprung') }}</th>
<th class="cd-table-header">{{ $t('spell.description') }}</th>
<th class="cd-table-header">{{ $t('spell.quelle') }}</th>
<th class="cd-table-header">{{ $t('spell.system') }}</th>
<th class="cd-table-header"> </th>
</tr>
</thead>
<tbody>
<template v-for="skills,categorie in character.categorizedskills">
<tr>
<td colspan="6">{{ categorie || '-' }}</td>
</tr>
<template v-for="skill in skills">
<tr>
<td>{{ skill.name || '-' }}</td>
<td>{{ skill.fertigkeitswert || '-' }}</td>
<td>{{ skill.bonus || '0' }}</td>
<td>{{ skill.pp || '0' }}</td>
<!-- <td>{{ skill.beschreibung || '-' }}</td>-->
<td>{{ skill.bemerkung || '-' }}</td>
<template v-for="(dtaItem, index) in filteredAndSortedSpells" :key="dtaItem.id">
<tr v-if="editingIndex !== index">
<td>{{ dtaItem.id || '' }}</td>
<td>{{ dtaItem.category|| '-' }}</td>
<td>{{ dtaItem.name || '-' }}</td>
<td>{{ dtaItem.level || '0' }}</td>
<td>{{ dtaItem.ap || '0' }}</td>
<td>{{ dtaItem.zauberdauer || '-' }}</td>
<td>{{ dtaItem.reichweite || '0' }}</td>
<td>{{ dtaItem.wirkungsziel || '-' }}</td>
<td>{{ dtaItem.wirkungsbereich || '-' }}</td>
<td>{{ dtaItem.wirkungsdauer || '-' }}</td>
<td>{{ dtaItem.ursprung || '-' }}</td>
<td>{{ dtaItem.beschreibung || '-' }}</td>
<td>{{ dtaItem.quelle || '-' }}</td>
<td>{{ dtaItem.system || 'midgard' }}</td>
<td>
<button @click="startEdit(index)">Edit</button>
</td>
</tr>
</template>
</template>
<template v-for="skill in character.waffenfertigkeiten">
<tr>
<td>{{ skill.name || '-' }}</td>
<td>{{ skill.fertigkeitswert || '-' }}</td>
<td>{{ skill.bonus || '0' }}</td>
<td>{{ skill.pp || '0' }}</td>
<!-- <td>{{ skill.beschreibung || '-' }}</td> -->
<td>{{ skill.bemerkung || '-' }}</td>
<tr v-else>
<td><input v-model="editedItem.id" style="width:20px;"/></td>
<td><select v-model="editedItem.category" style="width:80px;">
<option v-for="category in mdata['spellcategories']"
:key="category"
:value="category">
{{ category }}
</option>
</select></td>
<td><input v-model="editedItem.name"/></td>
<td><input v-model.number="editedItem.level" type="number" style="width:40px;"/></td>
<td><input v-model="editedItem.ap" style="width:40px;"/></td>
<td><input v-model="editedItem.zauberdauer" /></td>
<td><input v-model="editedItem.reichweite" style="width:40px;"/></td>
<td><input v-model="editedItem.wirkungsziel" /></td>
<td><input v-model="editedItem.wirkungsbereich" /></td>
<td><input v-model="editedItem.wirkungsdauer" /></td>
<td><input v-model="editedItem.ursprung" /></td>
<td><input v-model="editedItem.beschreibung" /></td>
<td><input v-model="editedItem.quelle" style="width:80px;"/></td>
<td><input v-model="editedItem.system" style="width:80px;"/></td>
<td>
<button @click="saveEdit(index)">Save</button>
<button @click="cancelEdit">Cancel</button>
</td>
</tr>
</template>
</tbody>
@@ -47,7 +89,25 @@
</div> <!--- end character -datasheet-->
</template>
<!-- <style scoped> -->
<style>
.header-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.3rem;
height: fit-content;
padding: 0.5rem;
}
.search-box {
margin-bottom: 1rem;
}
.search-box input {
padding: 0.2rem;
width: 200px;
border: 1px solid #ddd;
border-radius: 4px;
}
.tables-container {
display: flex;
gap: 1rem;
@@ -74,13 +134,101 @@
<script>
import API from '../../utils/api'
export default {
name: "SpellView",
props: {
character: {
mdata: {
type: Object,
required: true
required: true,
default: () => ({
spells: [],
spellcategories: []
})
}
},
data() {
return {
searchTerm: '',
sortField: 'name',
sortAsc: true,
editingIndex: -1,
editedItem: null
}
},
computed: {
filteredAndSortedSpells() {
if (!this.mdata?.spells) return [];
return [...this.mdata.spells]
.filter(spell => {
const searchLower = this.searchTerm.toLowerCase();
return !this.searchTerm ||
spell.name?.toLowerCase().includes(searchLower) ||
spell.category?.toLowerCase().includes(searchLower);
})
.sort((a, b) => {
const aValue = (a[this.sortField] || '').toLowerCase();
const bValue = (b[this.sortField] || '').toLowerCase();
return this.sortAsc
? aValue.localeCompare(bValue)
: bValue.localeCompare(aValue);
});
},
sortedSpells() {
return [...this.mdata.spells].sort((a, b) => {
const aValue = (a[this.sortField] || '').toLowerCase();
const bValue = (b[this.sortField] || '').toLowerCase();
return this.sortAsc
? aValue.localeCompare(bValue)
: bValue.localeCompare(aValue);
});
}
},
methods: {
startEdit(index) {
this.editingIndex = index;
this.editedItem = { ...this.filteredAndSortedSpells[index] };
},
saveEdit(index) {
//this.$emit('update-spell', { index, spell: this.editedItem });
this.handleSpellUpdate( { index, spell: this.editedItem });
this.editingIndex = -1;
this.editedItem = null;
},
cancelEdit() {
this.editingIndex = -1;
this.editedItem = null;
},
sortBy(field) {
if (this.sortField === field) {
this.sortAsc = !this.sortAsc;
} else {
this.sortField = field;
this.sortAsc = true;
}
},
async handleSpellUpdate({ index, spell }) {
try {
const response = await API.put(
`/api/maintenance/spells/${spell.id}`, spell,
{
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}` ,
'Content-Type': 'application/json'
}
}
)
if (!response.statusText== "OK") throw new Error('Update failed');
const updatedSkill = response.data;
// Update the spell in mdata
this.mdata.spells = this.mdata.spells.map(s =>
s.id === updatedSkill.id ? updatedSkill : s
);
} catch (error) {
console.error('Failed to update spell:', error);
}
}
}
};
</script>
+14 -2
View File
@@ -58,12 +58,24 @@ export default {
initialwert:'Startwert',
},
spell:{
id:'ID',
category:'Kategorie',
name:'Name',
description:'Beschreibung',
bonus:'Bonus',
pp:'AP',
level:'Stufe',
art:'Art', //GestenZauber, GedankenZauber, WortZauber
apverbrauch:'AP',
zauberdauer:'Zauberdauer',
reichweite:'Reichweite',
wirkungsziel:'Wirkungsziel',
wirkungsbereich:'Wirkungsbereich',
wirkungsdauer:'Wirkungsdauer',
ursprung:'Ursprung',
quelle:'Quelle',
system:'System',
},
Spell:'Zauber',
weapon:{
name:'Name',
description:'Beschreibung',