Update maintenance Component for Skills
This commit is contained in:
@@ -3884,4 +3884,216 @@ a,
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* ===== Maintenance SkillView Styles ===== */
|
||||
|
||||
/* Filter Row */
|
||||
.filter-row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 1rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.filter-item label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.filter-item select {
|
||||
padding: 0.4rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
min-width: 120px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.btn-clear-filters {
|
||||
padding: 0.4rem 1rem;
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.btn-clear-filters:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
|
||||
/* Edit Form */
|
||||
.edit-form {
|
||||
padding: 1rem;
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.edit-row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.edit-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
flex: 1;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.edit-field.full-width {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
.edit-field label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.edit-field input,
|
||||
.edit-field select,
|
||||
.edit-field textarea {
|
||||
padding: 0.4rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.edit-field input[type="checkbox"] {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
/* Category Checkboxes */
|
||||
.category-checkboxes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
padding: 0.5rem;
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.category-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.category-checkbox input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.category-checkbox label {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Difficulty Selects */
|
||||
.difficulty-selects {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.difficulty-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.difficulty-select span {
|
||||
min-width: 100px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Edit Actions */
|
||||
.edit-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: flex-end;
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
padding: 0.5rem 1.5rem;
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-save:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
padding: 0.5rem 1.5rem;
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-cancel:hover {
|
||||
background-color: #c82333;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.filter-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-item select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.edit-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.edit-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.category-checkboxes {
|
||||
max-height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,128 +1,224 @@
|
||||
<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('Skill')}...`"
|
||||
/>
|
||||
</div>
|
||||
<div class="search-box">
|
||||
<input
|
||||
type="text"
|
||||
v-model="searchTerm"
|
||||
:placeholder="`${$t('search')} ${$t('Skill')}...`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cd-view">
|
||||
<div class="cd-list">
|
||||
<div class="tables-container">
|
||||
<table class="cd-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="cd-table-header">{{ $t('skill.id') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.category') }}<button @click="sortBy('category')">-{{ sortField === 'category' ? (sortAsc ? '↑' : '↓') : '' }}</button></th>
|
||||
<th class="cd-table-header">{{ $t('skill.name') }} <button @click="sortBy('name')">-{{ sortField === 'name' ? (sortAsc ? '↑' : '↓') : '' }}</button></th>
|
||||
<th class="cd-table-header">{{ $t('skill.initialwert') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.improvable') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.innateskill') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.description') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.bonusskill') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.quelle') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.system') }}</th>
|
||||
<th class="cd-table-header"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="(dtaItem, index) in filteredAndSortedSkills" :key="dtaItem.id">
|
||||
<tr v-if="editingIndex !== index">
|
||||
<td>{{ dtaItem.id || '' }}</td>
|
||||
<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>{{ dtaItem.system || 'midgard' }}</td>
|
||||
<td>
|
||||
<button @click="startEdit(index)">Edit</button>
|
||||
</td>
|
||||
</tr>
|
||||
<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>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- Filter Row -->
|
||||
<div class="filter-row">
|
||||
<div class="filter-item">
|
||||
<label>{{ $t('skill.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('skill.difficulty') }}:</label>
|
||||
<select v-model="filterDifficulty">
|
||||
<option value="">{{ $t('all') || 'All' }}</option>
|
||||
<option v-for="diff in availableDifficulties" :key="diff" :value="diff">{{ diff }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>{{ $t('skill.improvable') }}:</label>
|
||||
<select v-model="filterImprovable">
|
||||
<option value="">{{ $t('all') || 'All' }}</option>
|
||||
<option value="true">{{ $t('yes') || 'Yes' }}</option>
|
||||
<option value="false">{{ $t('no') || 'No' }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>{{ $t('skill.innateskill') }}:</label>
|
||||
<select v-model="filterInnateskill">
|
||||
<option value="">{{ $t('all') || 'All' }}</option>
|
||||
<option value="true">{{ $t('yes') || 'Yes' }}</option>
|
||||
<option value="false">{{ $t('no') || 'No' }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>{{ $t('skill.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>
|
||||
<button @click="clearFilters" class="btn-clear-filters">{{ $t('clearFilters') || 'Clear Filters' }}</button>
|
||||
</div>
|
||||
</div> <!--- end cd-list-->
|
||||
</div> <!--- end character -datasheet-->
|
||||
|
||||
<div class="tables-container">
|
||||
<table class="cd-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="cd-table-header">{{ $t('skill.id') }}</th>
|
||||
<th class="cd-table-header">
|
||||
{{ $t('skill.category') }}
|
||||
<button @click="sortBy('category')">{{ sortField === 'category' ? (sortAsc ? '↑' : '↓') : '-' }}</button>
|
||||
</th>
|
||||
<th class="cd-table-header">
|
||||
{{ $t('skill.name') }}
|
||||
<button @click="sortBy('name')">{{ sortField === 'name' ? (sortAsc ? '↑' : '↓') : '-' }}</button>
|
||||
</th>
|
||||
<th class="cd-table-header">{{ $t('skill.difficulty') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.initialwert') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.improvable') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.innateskill') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.description') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.bonusskill') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.quelle') }}</th>
|
||||
<th class="cd-table-header">{{ $t('skill.system') }}</th>
|
||||
<th class="cd-table-header"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="(dtaItem, index) in filteredAndSortedSkills" :key="dtaItem.id">
|
||||
<!-- Display Mode -->
|
||||
<tr v-if="editingIndex !== index">
|
||||
<td>{{ dtaItem.id || '' }}</td>
|
||||
<td>{{ formatCategories(dtaItem.categories) }}</td>
|
||||
<td>{{ dtaItem.name || '-' }}</td>
|
||||
<td>{{ formatDifficulties(dtaItem.difficulties) }}</td>
|
||||
<td>{{ dtaItem.initialwert || '0' }}</td>
|
||||
<td><input type="checkbox" :checked="dtaItem.improvable" disabled /></td>
|
||||
<td><input type="checkbox" :checked="dtaItem.innateskill" disabled /></td>
|
||||
<td>{{ dtaItem.beschreibung || '-' }}</td>
|
||||
<td>{{ dtaItem.bonuseigenschaft || '-' }}</td>
|
||||
<td>{{ formatQuelle(dtaItem) }}</td>
|
||||
<td>{{ dtaItem.game_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;" disabled /></td>
|
||||
<td colspan="11">
|
||||
<!-- Expanded edit form -->
|
||||
<div class="edit-form">
|
||||
<div class="edit-row">
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('skill.name') }}:</label>
|
||||
<input v-model="editedItem.name" />
|
||||
</div>
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('skill.initialwert') }}:</label>
|
||||
<input v-model.number="editedItem.initialwert" type="number" style="width:60px;" />
|
||||
</div>
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('skill.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">
|
||||
<label>{{ $t('skill.improvable') }}:</label>
|
||||
<input type="checkbox" v-model="editedItem.improvable" />
|
||||
</div>
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('skill.innateskill') }}:</label>
|
||||
<input type="checkbox" v-model="editedItem.innateskill" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-row">
|
||||
<div class="edit-field full-width">
|
||||
<label>{{ $t('skill.description') }}:</label>
|
||||
<input v-model="editedItem.beschreibung" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-row">
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('skill.quelle') }}:</label>
|
||||
<select v-model="editedItem.sourceCode" style="width:100px;">
|
||||
<option v-for="source in availableSources" :key="source.code" :value="source.code">
|
||||
{{ source.code }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="edit-field">
|
||||
<label>{{ $t('skill.page') || 'Page' }}:</label>
|
||||
<input v-model.number="editedItem.page_number" type="number" style="width:60px;" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-row">
|
||||
<div class="edit-field full-width">
|
||||
<label>{{ $t('skill.categories') || 'Categories' }}:</label>
|
||||
<div class="category-checkboxes">
|
||||
<div v-for="category in mdata.categories" :key="category.id" class="category-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
:value="category.id"
|
||||
v-model="editedItem.selectedCategories"
|
||||
@change="onCategoryToggle(category.id)"
|
||||
/>
|
||||
<label>{{ category.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-row">
|
||||
<div class="edit-field full-width">
|
||||
<label>{{ $t('skill.difficulties') || 'Difficulties' }}:</label>
|
||||
<div class="difficulty-selects">
|
||||
<div v-for="catId in editedItem.selectedCategories" :key="catId" class="difficulty-select">
|
||||
<span>{{ getCategoryName(catId) }}:</span>
|
||||
<select v-model="editedItem.categoryDifficulties[catId]" style="width:120px;">
|
||||
<option v-for="diff in mdata.difficulties" :key="diff.id" :value="diff.id">
|
||||
{{ diff.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-wrapper-left {
|
||||
flex: 6;
|
||||
min-width: 0; /* Prevent table from overflowing */
|
||||
}
|
||||
.table-wrapper-right {
|
||||
flex: 4;
|
||||
min-width: 0; /* Prevent table from overflowing */
|
||||
}
|
||||
|
||||
.cd-table {
|
||||
width: 100%;
|
||||
}
|
||||
.cd-table-header {
|
||||
background-color: #1da766;
|
||||
font-weight: bold;
|
||||
}
|
||||
/* All styles moved to main.css as per project conventions */
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
import API from '../../utils/api'
|
||||
|
||||
export default {
|
||||
name: "SkillView",
|
||||
props: {
|
||||
@@ -131,7 +227,9 @@ export default {
|
||||
required: true,
|
||||
default: () => ({
|
||||
skills: [],
|
||||
skillcategories: []
|
||||
categories: [],
|
||||
difficulties: [],
|
||||
sources: []
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -141,82 +239,247 @@ export default {
|
||||
sortField: 'name',
|
||||
sortAsc: true,
|
||||
editingIndex: -1,
|
||||
editedItem: null
|
||||
editedItem: null,
|
||||
filterCategory: '',
|
||||
filterDifficulty: '',
|
||||
filterImprovable: '',
|
||||
filterInnateskill: '',
|
||||
filterBonuseigenschaft: '',
|
||||
enhancedSkills: [],
|
||||
availableSources: []
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.loadEnhancedSkills()
|
||||
},
|
||||
computed: {
|
||||
filteredAndSortedSkills() {
|
||||
if (!this.mdata?.skills) return [];
|
||||
|
||||
return [...this.mdata.skills]
|
||||
.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.enhancedSkills.forEach(skill => {
|
||||
if (skill.categories) {
|
||||
skill.categories.forEach(cat => categories.add(cat.category_name))
|
||||
}
|
||||
})
|
||||
return Array.from(categories).sort()
|
||||
},
|
||||
sortedSkills() {
|
||||
return [...this.mdata.skills].sort((a, b) => {
|
||||
const aValue = (a[this.sortField] || '').toLowerCase();
|
||||
const bValue = (b[this.sortField] || '').toLowerCase();
|
||||
return this.sortAsc
|
||||
? aValue.localeCompare(bValue)
|
||||
: bValue.localeCompare(aValue);
|
||||
});
|
||||
availableDifficulties() {
|
||||
const difficulties = new Set()
|
||||
this.enhancedSkills.forEach(skill => {
|
||||
if (skill.difficulties) {
|
||||
skill.difficulties.forEach(diff => difficulties.add(diff))
|
||||
}
|
||||
})
|
||||
return Array.from(difficulties).sort()
|
||||
},
|
||||
availableBonuseigenschaften() {
|
||||
const bonuses = new Set()
|
||||
this.enhancedSkills.forEach(skill => {
|
||||
if (skill.bonuseigenschaft) {
|
||||
bonuses.add(skill.bonuseigenschaft)
|
||||
}
|
||||
})
|
||||
return Array.from(bonuses).sort()
|
||||
},
|
||||
filteredAndSortedSkills() {
|
||||
let filtered = [...this.enhancedSkills]
|
||||
|
||||
// Apply search filter
|
||||
if (this.searchTerm) {
|
||||
const searchLower = this.searchTerm.toLowerCase()
|
||||
filtered = filtered.filter(skill =>
|
||||
skill.name?.toLowerCase().includes(searchLower) ||
|
||||
this.formatCategories(skill.categories).toLowerCase().includes(searchLower)
|
||||
)
|
||||
}
|
||||
|
||||
// Apply category filter
|
||||
if (this.filterCategory) {
|
||||
filtered = filtered.filter(skill =>
|
||||
skill.categories && skill.categories.some(cat => cat.category_name === this.filterCategory)
|
||||
)
|
||||
}
|
||||
|
||||
// Apply difficulty filter
|
||||
if (this.filterDifficulty) {
|
||||
filtered = filtered.filter(skill =>
|
||||
skill.difficulties && skill.difficulties.includes(this.filterDifficulty)
|
||||
)
|
||||
}
|
||||
|
||||
// Apply improvable filter
|
||||
if (this.filterImprovable !== '') {
|
||||
const improvableValue = this.filterImprovable === 'true'
|
||||
filtered = filtered.filter(skill => skill.improvable === improvableValue)
|
||||
}
|
||||
|
||||
// Apply innateskill filter
|
||||
if (this.filterInnateskill !== '') {
|
||||
const innateskillValue = this.filterInnateskill === 'true'
|
||||
filtered = filtered.filter(skill => skill.innateskill === innateskillValue)
|
||||
}
|
||||
|
||||
// Apply bonuseigenschaft filter
|
||||
if (this.filterBonuseigenschaft) {
|
||||
filtered = filtered.filter(skill => skill.bonuseigenschaft === this.filterBonuseigenschaft)
|
||||
}
|
||||
|
||||
// Apply sorting
|
||||
filtered.sort((a, b) => {
|
||||
let aValue, bValue
|
||||
|
||||
if (this.sortField === 'category') {
|
||||
aValue = this.formatCategories(a.categories).toLowerCase()
|
||||
bValue = this.formatCategories(b.categories).toLowerCase()
|
||||
} else {
|
||||
aValue = (a[this.sortField] || '').toString().toLowerCase()
|
||||
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.filteredAndSortedSkills[index] };
|
||||
async loadEnhancedSkills() {
|
||||
try {
|
||||
const response = await API.get('/api/maintenance/skills-enhanced')
|
||||
this.enhancedSkills = response.data.skills || []
|
||||
this.availableSources = response.data.sources || []
|
||||
// Also update mdata for compatibility
|
||||
if (response.data.categories) {
|
||||
this.mdata.categories = response.data.categories
|
||||
}
|
||||
if (response.data.difficulties) {
|
||||
this.mdata.difficulties = response.data.difficulties
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load enhanced skills:', error)
|
||||
}
|
||||
},
|
||||
saveEdit(index) {
|
||||
//this.$emit('update-skill', { index, skill: this.editedItem });
|
||||
this.handleSkillUpdate({ index, skill: this.editedItem });
|
||||
this.editingIndex = -1;
|
||||
this.editedItem = null;
|
||||
formatCategories(categories) {
|
||||
if (!categories || categories.length === 0) return '-'
|
||||
return categories.map(cat => cat.category_name).join(', ')
|
||||
},
|
||||
formatDifficulties(difficulties) {
|
||||
if (!difficulties || difficulties.length === 0) return '-'
|
||||
return difficulties.join(', ')
|
||||
},
|
||||
formatQuelle(skill) {
|
||||
if (skill.source_id && this.availableSources.length > 0) {
|
||||
const source = this.availableSources.find(s => s.id === skill.source_id)
|
||||
if (source) {
|
||||
return skill.page_number ? `${source.code}:${skill.page_number}` : source.code
|
||||
}
|
||||
}
|
||||
return skill.quelle || '-'
|
||||
},
|
||||
getCategoryName(categoryId) {
|
||||
const category = this.mdata.categories.find(c => c.id === categoryId)
|
||||
return category ? category.name : `ID:${categoryId}`
|
||||
},
|
||||
startEdit(index) {
|
||||
const skill = this.filteredAndSortedSkills[index]
|
||||
|
||||
// Initialize edit object
|
||||
this.editedItem = {
|
||||
...skill,
|
||||
selectedCategories: skill.categories ? skill.categories.map(cat => cat.category_id) : [],
|
||||
categoryDifficulties: {},
|
||||
sourceCode: this.getSourceCode(skill.source_id),
|
||||
}
|
||||
|
||||
// Map category difficulties
|
||||
if (skill.categories) {
|
||||
skill.categories.forEach(cat => {
|
||||
this.editedItem.categoryDifficulties[cat.category_id] = cat.difficulty_id
|
||||
})
|
||||
}
|
||||
|
||||
this.editingIndex = index
|
||||
},
|
||||
getSourceCode(sourceId) {
|
||||
if (!sourceId || !this.availableSources.length) return ''
|
||||
const source = this.availableSources.find(s => s.id === sourceId)
|
||||
return source ? source.code : ''
|
||||
},
|
||||
onCategoryToggle(categoryId) {
|
||||
// If category was removed, also remove its difficulty setting
|
||||
if (!this.editedItem.selectedCategories.includes(categoryId)) {
|
||||
delete this.editedItem.categoryDifficulties[categoryId]
|
||||
} else {
|
||||
// Set a default difficulty if not already set
|
||||
if (!this.editedItem.categoryDifficulties[categoryId] && this.mdata.difficulties.length > 0) {
|
||||
// Find "normal" difficulty or use first one
|
||||
const normalDiff = this.mdata.difficulties.find(d => d.name.toLowerCase() === 'normal')
|
||||
this.editedItem.categoryDifficulties[categoryId] = normalDiff ? normalDiff.id : this.mdata.difficulties[0].id
|
||||
}
|
||||
}
|
||||
},
|
||||
async saveEdit(index) {
|
||||
try {
|
||||
// Find source ID from code
|
||||
const source = this.availableSources.find(s => s.code === this.editedItem.sourceCode)
|
||||
|
||||
// Build category_difficulties array
|
||||
const categoryDifficulties = this.editedItem.selectedCategories.map(catId => ({
|
||||
category_id: catId,
|
||||
difficulty_id: this.editedItem.categoryDifficulties[catId]
|
||||
}))
|
||||
|
||||
const updateData = {
|
||||
id: this.editedItem.id,
|
||||
name: this.editedItem.name,
|
||||
beschreibung: this.editedItem.beschreibung,
|
||||
game_system: this.editedItem.game_system || 'midgard',
|
||||
initialwert: this.editedItem.initialwert,
|
||||
bonuseigenschaft: this.editedItem.bonuseigenschaft,
|
||||
improvable: this.editedItem.improvable,
|
||||
innateskill: this.editedItem.innateskill,
|
||||
source_id: source ? source.id : null,
|
||||
page_number: this.editedItem.page_number || 0,
|
||||
category_difficulties: categoryDifficulties
|
||||
}
|
||||
|
||||
const response = await API.put(
|
||||
`/api/maintenance/skills-enhanced/${this.editedItem.id}`,
|
||||
updateData
|
||||
)
|
||||
|
||||
// Update the skill in the list using splice for proper reactivity
|
||||
const skillIndex = this.enhancedSkills.findIndex(s => s.id === this.editedItem.id)
|
||||
if (skillIndex !== -1) {
|
||||
this.enhancedSkills.splice(skillIndex, 1, response.data)
|
||||
}
|
||||
|
||||
this.editingIndex = -1
|
||||
this.editedItem = null
|
||||
} catch (error) {
|
||||
console.error('Failed to update skill:', error)
|
||||
alert('Failed to update 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 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);
|
||||
}
|
||||
clearFilters() {
|
||||
this.searchTerm = ''
|
||||
this.filterCategory = ''
|
||||
this.filterDifficulty = ''
|
||||
this.filterImprovable = ''
|
||||
this.filterInnateskill = ''
|
||||
this.filterBonuseigenschaft = ''
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user