Update maintenance Component for Weaponskills
This commit is contained in:
@@ -21,8 +21,11 @@ func RegisterRoutes(r *gin.RouterGroup) {
|
||||
maintGrp.DELETE("/skills/:id", DeleteMDSkill)
|
||||
|
||||
maintGrp.GET("/weaponskills", GetMDWeaponSkills)
|
||||
maintGrp.GET("/weaponskills-enhanced", GetEnhancedMDWeaponSkills) // New enhanced endpoint
|
||||
maintGrp.GET("/weaponskills/:id", GetMDWeaponSkill)
|
||||
maintGrp.GET("/weaponskills-enhanced/:id", GetEnhancedMDWeaponSkill) // New enhanced endpoint
|
||||
maintGrp.PUT("/weaponskills/:id", UpdateMDWeaponSkill)
|
||||
maintGrp.PUT("/weaponskills-enhanced/:id", UpdateEnhancedMDWeaponSkill) // New enhanced endpoint
|
||||
maintGrp.POST("/weaponskills", AddWeaponSkill)
|
||||
maintGrp.DELETE("/weaponskills/:id", DeleteMDWeaponSkill)
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package gsmaster
|
||||
|
||||
import (
|
||||
"bamort/database"
|
||||
"bamort/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GetEnhancedMDWeaponSkills returns weapon skills with their source information
|
||||
func GetEnhancedMDWeaponSkills(c *gin.Context) {
|
||||
type EnhancedWeaponSkill struct {
|
||||
models.WeaponSkill
|
||||
SourceCode string `json:"source_code,omitempty"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
WeaponSkills []EnhancedWeaponSkill `json:"weaponskills"`
|
||||
Sources []models.Source `json:"sources"`
|
||||
}
|
||||
|
||||
var weaponSkills []models.WeaponSkill
|
||||
if err := database.DB.Find(&weaponSkills).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve weapon skills"})
|
||||
return
|
||||
}
|
||||
|
||||
var sources []models.Source
|
||||
if err := database.DB.Find(&sources).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve sources"})
|
||||
return
|
||||
}
|
||||
|
||||
// Build source code map
|
||||
sourceMap := make(map[uint]string)
|
||||
for _, source := range sources {
|
||||
sourceMap[source.ID] = source.Code
|
||||
}
|
||||
|
||||
// Enhance weapon skills with source code
|
||||
enhancedWeaponSkills := make([]EnhancedWeaponSkill, len(weaponSkills))
|
||||
for i, ws := range weaponSkills {
|
||||
enhancedWeaponSkills[i] = EnhancedWeaponSkill{
|
||||
WeaponSkill: ws,
|
||||
SourceCode: sourceMap[ws.SourceID],
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, Response{
|
||||
WeaponSkills: enhancedWeaponSkills,
|
||||
Sources: sources,
|
||||
})
|
||||
}
|
||||
|
||||
// GetEnhancedMDWeaponSkill returns a single weapon skill with source information
|
||||
func GetEnhancedMDWeaponSkill(c *gin.Context) {
|
||||
type EnhancedWeaponSkill struct {
|
||||
models.WeaponSkill
|
||||
SourceCode string `json:"source_code,omitempty"`
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var weaponSkill models.WeaponSkill
|
||||
if err := database.DB.First(&weaponSkill, uint(id)).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Weapon skill not found"})
|
||||
return
|
||||
}
|
||||
|
||||
var source models.Source
|
||||
var sourceCode string
|
||||
if weaponSkill.SourceID > 0 {
|
||||
if err := database.DB.First(&source, weaponSkill.SourceID).Error; err == nil {
|
||||
sourceCode = source.Code
|
||||
}
|
||||
}
|
||||
|
||||
enhanced := EnhancedWeaponSkill{
|
||||
WeaponSkill: weaponSkill,
|
||||
SourceCode: sourceCode,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, enhanced)
|
||||
}
|
||||
|
||||
// UpdateEnhancedMDWeaponSkill updates a weapon skill
|
||||
func UpdateEnhancedMDWeaponSkill(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var weaponSkill models.WeaponSkill
|
||||
if err := database.DB.First(&weaponSkill, uint(id)).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Weapon skill not found"})
|
||||
return
|
||||
}
|
||||
|
||||
// Bind the request to weapon skill
|
||||
if err := c.ShouldBindJSON(&weaponSkill); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure ID stays the same
|
||||
weaponSkill.ID = uint(id)
|
||||
|
||||
// Update the weapon skill
|
||||
if err := database.DB.Save(&weaponSkill).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update weapon skill"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, weaponSkill)
|
||||
}
|
||||
@@ -13,6 +13,32 @@
|
||||
|
||||
<div class="cd-view">
|
||||
<div class="cd-list">
|
||||
<!-- Filter Row -->
|
||||
<div class="filter-row">
|
||||
<div class="filter-item">
|
||||
<label>{{ $t('weaponskill.category') }}:</label>
|
||||
<select v-model="filterCategory">
|
||||
<option value="">{{ $t('all') || 'All' }}</option>
|
||||
<option v-for="cat in availableCategories" :key="cat" :value="cat">{{ cat }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>{{ $t('weaponskill.bonusskill') }}:</label>
|
||||
<select v-model="filterBonuseigenschaft">
|
||||
<option value="">{{ $t('all') || 'All' }}</option>
|
||||
<option v-for="bonus in availableBonuseigenschaften" :key="bonus" :value="bonus">{{ bonus }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>{{ $t('weaponskill.quelle') }}:</label>
|
||||
<select v-model="filterQuelle">
|
||||
<option value="">{{ $t('all') || 'All' }}</option>
|
||||
<option v-for="quelle in availableQuellen" :key="quelle" :value="quelle">{{ quelle }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button @click="clearFilters" class="btn-clear-filters">{{ $t('clearFilters') || 'Clear Filters' }}</button>
|
||||
</div>
|
||||
|
||||
<div class="tables-container">
|
||||
<table class="cd-table">
|
||||
<thead>
|
||||
@@ -21,8 +47,6 @@
|
||||
<th class="cd-table-header">{{ $t('weaponskill.category') }}<button @click="sortBy('category')">-{{ sortField === 'category' ? (sortAsc ? '↑' : '↓') : '' }}</button></th>
|
||||
<th class="cd-table-header">{{ $t('weaponskill.name') }} <button @click="sortBy('name')">-{{ sortField === 'name' ? (sortAsc ? '↑' : '↓') : '' }}</button></th>
|
||||
<th class="cd-table-header">{{ $t('weaponskill.initialwert') }}</th>
|
||||
<th class="cd-table-header">{{ $t('weaponskill.improvable') }}</th>
|
||||
<th class="cd-table-header">{{ $t('weaponskill.innateskill') }}</th>
|
||||
<th class="cd-table-header">{{ $t('weaponskill.description') }}</th>
|
||||
<th class="cd-table-header">{{ $t('weaponskill.bonusskill') }}</th>
|
||||
<th class="cd-table-header">{{ $t('weaponskill.quelle') }}</th>
|
||||
@@ -37,36 +61,87 @@
|
||||
<td>{{ dtaItem.category|| '-' }}</td>
|
||||
<td>{{ dtaItem.name || '-' }}</td>
|
||||
<td>{{ dtaItem.initialwert || '0' }}</td>
|
||||
<td>{{ dtaItem.improvable || '0' }}</td>
|
||||
<td>{{ dtaItem.innateskill || '0' }}</td>
|
||||
<td>{{ dtaItem.beschreibung || '-' }}</td>
|
||||
<td>{{ dtaItem.bonuseigenschaft || '-' }}</td>
|
||||
<td>{{ dtaItem.quelle || '-' }}</td>
|
||||
<td>{{ formatQuelle(dtaItem) }}</td>
|
||||
<td>{{ dtaItem.system || 'midgard' }}</td>
|
||||
<td>
|
||||
<button @click="startEdit(index)">Edit</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Edit Mode -->
|
||||
<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['skillcategories']"
|
||||
:key="category"
|
||||
:value="category">
|
||||
{{ category }}
|
||||
</option>
|
||||
</select></td>
|
||||
<td><input v-model="editedItem.name"/></td>
|
||||
<td><input v-model.number="editedItem.initialwert" type="number" style="width:40px;"/></td>
|
||||
<td><input type="checkbox" :checked="true" v-model="editedItem.improvable" style="width:50px;"/></td>
|
||||
<td><input type="checkbox" :checked="true" v-model="editedItem.innateskill" style="width:50px;"/></td>
|
||||
<td><input v-model="editedItem.beschreibung" /></td>
|
||||
<td><input v-model="editedItem.bonuseigenschaft" style="width:80px;" ></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><input v-model="editedItem.id" style="width:20px;" disabled /></td>
|
||||
<td colspan="7">
|
||||
<!-- Expanded edit form -->
|
||||
<div class="edit-form">
|
||||
<div class="edit-row">
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('weaponskill.name') }}:</label>
|
||||
<input v-model="editedItem.name" />
|
||||
</div>
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('weaponskill.category') }}:</label>
|
||||
<select v-model="editedItem.category" style="width:120px;">
|
||||
<option v-for="category in mdata['skillcategories']" :key="category" :value="category">
|
||||
{{ category }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('weaponskill.initialwert') }}:</label>
|
||||
<input v-model.number="editedItem.initialwert" type="number" style="width:60px;" />
|
||||
</div>
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('weaponskill.bonusskill') }}:</label>
|
||||
<select v-model="editedItem.bonuseigenschaft" style="width:80px;">
|
||||
<option value="">-</option>
|
||||
<option value="St">St</option>
|
||||
<option value="Gs">Gs</option>
|
||||
<option value="Gw">Gw</option>
|
||||
<option value="Ko">Ko</option>
|
||||
<option value="In">In</option>
|
||||
<option value="Zt">Zt</option>
|
||||
<option value="Au">Au</option>
|
||||
<option value="pA">pA</option>
|
||||
<option value="Wk">Wk</option>
|
||||
<option value="B">B</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-row">
|
||||
<div class="edit-field full-width">
|
||||
<label>{{ $t('weaponskill.description') }}:</label>
|
||||
<input v-model="editedItem.beschreibung" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-row">
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('weaponskill.quelle') }}:</label>
|
||||
<select v-model="editedItem.sourceCode" style="width:100px;">
|
||||
<option value="">-</option>
|
||||
<option v-for="source in availableSources" :key="source.code" :value="source.code">
|
||||
{{ source.code }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('weaponskill.page') || 'Page' }}:</label>
|
||||
<input v-model.number="editedItem.page_number" type="number" style="width:60px;" />
|
||||
</div>
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('weaponskill.system') }}:</label>
|
||||
<input v-model="editedItem.system" style="width:100px;" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-actions">
|
||||
<button @click="saveEdit(index)" class="btn-save">Save</button>
|
||||
<button @click="cancelEdit" class="btn-cancel">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
@@ -141,80 +216,171 @@ export default {
|
||||
sortField: 'name',
|
||||
sortAsc: true,
|
||||
editingIndex: -1,
|
||||
editedItem: null
|
||||
editedItem: null,
|
||||
filterCategory: '',
|
||||
filterBonuseigenschaft: '',
|
||||
filterQuelle: '',
|
||||
enhancedWeaponSkills: [],
|
||||
availableSources: []
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.loadEnhancedWeaponSkills()
|
||||
},
|
||||
computed: {
|
||||
filteredAndSortedWaeponSkills() {
|
||||
if (!this.mdata?.weaponskills) return [];
|
||||
|
||||
return [...this.mdata.weaponskills]
|
||||
.filter(skill => {
|
||||
const searchLower = this.searchTerm.toLowerCase();
|
||||
return !this.searchTerm ||
|
||||
skill.name?.toLowerCase().includes(searchLower) ||
|
||||
skill.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);
|
||||
});
|
||||
availableCategories() {
|
||||
const categories = new Set()
|
||||
this.enhancedWeaponSkills.forEach(ws => {
|
||||
if (ws.category) categories.add(ws.category)
|
||||
})
|
||||
return Array.from(categories).sort()
|
||||
},
|
||||
sortedWaeponSkills() {
|
||||
return [...this.mdata.weaponskills].sort((a, b) => {
|
||||
const aValue = (a[this.sortField] || '').toLowerCase();
|
||||
const bValue = (b[this.sortField] || '').toLowerCase();
|
||||
return this.sortAsc
|
||||
? aValue.localeCompare(bValue)
|
||||
: bValue.localeCompare(aValue);
|
||||
});
|
||||
availableBonuseigenschaften() {
|
||||
const bonuses = new Set()
|
||||
this.enhancedWeaponSkills.forEach(ws => {
|
||||
if (ws.bonuseigenschaft) bonuses.add(ws.bonuseigenschaft)
|
||||
})
|
||||
return Array.from(bonuses).sort()
|
||||
},
|
||||
availableQuellen() {
|
||||
const quellen = new Set()
|
||||
this.enhancedWeaponSkills.forEach(ws => {
|
||||
if (ws.source_id && this.availableSources.length > 0) {
|
||||
const source = this.availableSources.find(s => s.id === ws.source_id)
|
||||
if (source) {
|
||||
quellen.add(source.code)
|
||||
}
|
||||
}
|
||||
})
|
||||
return Array.from(quellen).sort()
|
||||
},
|
||||
filteredAndSortedWaeponSkills() {
|
||||
let filtered = [...this.enhancedWeaponSkills]
|
||||
|
||||
// Apply search filter
|
||||
if (this.searchTerm) {
|
||||
const searchLower = this.searchTerm.toLowerCase()
|
||||
filtered = filtered.filter(ws =>
|
||||
ws.name?.toLowerCase().includes(searchLower) ||
|
||||
ws.category?.toLowerCase().includes(searchLower)
|
||||
)
|
||||
}
|
||||
|
||||
// Apply category filter
|
||||
if (this.filterCategory) {
|
||||
filtered = filtered.filter(ws => ws.category === this.filterCategory)
|
||||
}
|
||||
|
||||
// Apply bonuseigenschaft filter
|
||||
if (this.filterBonuseigenschaft) {
|
||||
filtered = filtered.filter(ws => ws.bonuseigenschaft === this.filterBonuseigenschaft)
|
||||
}
|
||||
|
||||
// Apply Quelle filter (only by source code, ignoring page number)
|
||||
if (this.filterQuelle) {
|
||||
filtered = filtered.filter(ws => {
|
||||
if (ws.source_id && this.availableSources.length > 0) {
|
||||
const source = this.availableSources.find(s => s.id === ws.source_id)
|
||||
return source && source.code === this.filterQuelle
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// Apply sorting
|
||||
filtered.sort((a, b) => {
|
||||
const aValue = (a[this.sortField] || '').toString().toLowerCase()
|
||||
const bValue = (b[this.sortField] || '').toString().toLowerCase()
|
||||
return this.sortAsc ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue)
|
||||
})
|
||||
|
||||
return filtered
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
startEdit(index) {
|
||||
this.editingIndex = index;
|
||||
this.editedItem = { ...this.filteredAndSortedWaeponSkills[index] };
|
||||
async loadEnhancedWeaponSkills() {
|
||||
try {
|
||||
const response = await API.get('/api/maintenance/weaponskills-enhanced')
|
||||
this.enhancedWeaponSkills = response.data.weaponskills || []
|
||||
this.availableSources = response.data.sources || []
|
||||
} catch (error) {
|
||||
console.error('Failed to load enhanced weapon skills:', error)
|
||||
}
|
||||
},
|
||||
saveEdit(index) {
|
||||
this.handleWeaponSkillUpdate({ index, weaponSkill: this.editedItem });
|
||||
this.editingIndex = -1;
|
||||
this.editedItem = null;
|
||||
startEdit(index) {
|
||||
const weaponSkill = this.filteredAndSortedWaeponSkills[index]
|
||||
this.editedItem = {
|
||||
...weaponSkill,
|
||||
sourceCode: this.getSourceCode(weaponSkill.source_id)
|
||||
}
|
||||
this.editingIndex = index
|
||||
},
|
||||
async saveEdit(index) {
|
||||
try {
|
||||
// Find source ID from code
|
||||
const source = this.availableSources.find(s => s.code === this.editedItem.sourceCode)
|
||||
|
||||
const updateData = {
|
||||
...this.editedItem,
|
||||
source_id: source ? source.id : null,
|
||||
page_number: this.editedItem.page_number || 0
|
||||
}
|
||||
|
||||
const response = await API.put(
|
||||
`/api/maintenance/weaponskills-enhanced/${this.editedItem.id}`,
|
||||
updateData
|
||||
)
|
||||
|
||||
// Update the weapon skill in the list using splice for proper reactivity
|
||||
const wsIndex = this.enhancedWeaponSkills.findIndex(ws => ws.id === this.editedItem.id)
|
||||
if (wsIndex !== -1) {
|
||||
this.enhancedWeaponSkills.splice(wsIndex, 1, response.data)
|
||||
}
|
||||
|
||||
this.editingIndex = -1
|
||||
this.editedItem = null
|
||||
} catch (error) {
|
||||
console.error('Failed to save weapon skill:', error)
|
||||
alert('Failed to save weapon skill: ' + (error.response?.data?.error || error.message))
|
||||
}
|
||||
},
|
||||
cancelEdit() {
|
||||
this.editingIndex = -1;
|
||||
this.editedItem = null;
|
||||
this.editingIndex = -1
|
||||
this.editedItem = null
|
||||
},
|
||||
sortBy(field) {
|
||||
if (this.sortField === field) {
|
||||
this.sortAsc = !this.sortAsc;
|
||||
this.sortAsc = !this.sortAsc
|
||||
} else {
|
||||
this.sortField = field;
|
||||
this.sortAsc = true;
|
||||
this.sortField = field
|
||||
this.sortAsc = true
|
||||
}
|
||||
},
|
||||
async handleWeaponSkillUpdate({ index, weaponSkill }) {
|
||||
try {
|
||||
const response = await API.put(
|
||||
`/api/maintenance/weaponskills/${weaponSkill.id}`, weaponSkill,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
formatQuelle(weaponSkill) {
|
||||
if (weaponSkill.source_id && this.availableSources.length > 0) {
|
||||
const source = this.availableSources.find(s => s.id === weaponSkill.source_id)
|
||||
if (source) {
|
||||
if (weaponSkill.page_number) {
|
||||
return `${source.code}:${weaponSkill.page_number}`
|
||||
} else {
|
||||
// No page number - show code and quelle if available
|
||||
const quelle = weaponSkill.quelle ? ` (${weaponSkill.quelle})` : ''
|
||||
return `${source.code}${quelle}`
|
||||
}
|
||||
)
|
||||
if (response.status !== 200) throw new Error('Update failed');
|
||||
const updatedSkill = response.data;
|
||||
// Update the weaponskills in mdata
|
||||
this.mdata.weaponskills = this.mdata.weaponskills.map(s =>
|
||||
s.id === updatedSkill.id ? updatedSkill : s
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to update weapon skill:', error);
|
||||
}
|
||||
}
|
||||
return weaponSkill.quelle || '-'
|
||||
},
|
||||
getSourceCode(sourceId) {
|
||||
if (!sourceId || !this.availableSources.length) return ''
|
||||
const source = this.availableSources.find(s => s.id === sourceId)
|
||||
return source ? source.code : ''
|
||||
},
|
||||
clearFilters() {
|
||||
this.searchTerm = ''
|
||||
this.filterCategory = ''
|
||||
this.filterBonuseigenschaft = ''
|
||||
this.filterQuelle = ''
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user