Skill editieren un neu erstellen
This commit is contained in:
@@ -12,7 +12,8 @@ func RegisterRoutes(r *gin.RouterGroup) {
|
||||
{
|
||||
maintGrp.GET("", GetMasterData)
|
||||
maintGrp.GET("/skills", GetMDSkills)
|
||||
maintGrp.GET("/skills-enhanced", GetEnhancedMDSkills) // New enhanced endpoint
|
||||
maintGrp.GET("/skills-enhanced", GetEnhancedMDSkills) // New enhanced endpoint
|
||||
maintGrp.POST("/skills-enhanced", CreateEnhancedMDSkill) // Create new skill
|
||||
maintGrp.GET("/skills/:id", GetMDSkill)
|
||||
maintGrp.GET("/skills-enhanced/:id", GetEnhancedMDSkill) // New enhanced endpoint
|
||||
maintGrp.PUT("/skills/:id", UpdateMDSkill)
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package gsmaster
|
||||
|
||||
import (
|
||||
"bamort/database"
|
||||
"bamort/models"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateSkillWithCategories(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
database.SetupTestDB()
|
||||
|
||||
// Create test dependencies
|
||||
source := getOrCreateSource("TSTCRT", "TestCreate")
|
||||
category := getOrCreateCategory("Alltag", source.ID)
|
||||
difficulty := getOrCreateDifficulty("normal")
|
||||
|
||||
// Prepare create request
|
||||
req := SkillUpdateRequest{
|
||||
Skill: models.Skill{
|
||||
Name: "Neue Fertigkeit",
|
||||
GameSystem: "midgard",
|
||||
Beschreibung: "Test Fertigkeit",
|
||||
Initialwert: 5,
|
||||
BasisWert: 0,
|
||||
Bonuseigenschaft: "In",
|
||||
Improvable: true,
|
||||
InnateSkill: false,
|
||||
SourceID: source.ID,
|
||||
PageNumber: 42,
|
||||
},
|
||||
CategoryDifficulties: []CategoryDifficultyPair{
|
||||
{
|
||||
CategoryID: category.ID,
|
||||
DifficultyID: difficulty.ID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Test creating new skill
|
||||
skillID, err := CreateSkillWithCategories(req)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateSkillWithCategories failed: %v", err)
|
||||
}
|
||||
|
||||
if skillID == 0 {
|
||||
t.Fatalf("Expected non-zero skill ID, got 0")
|
||||
}
|
||||
|
||||
// Verify skill was created
|
||||
var skill models.Skill
|
||||
if err := database.DB.First(&skill, skillID).Error; err != nil {
|
||||
t.Fatalf("Failed to retrieve created skill: %v", err)
|
||||
}
|
||||
|
||||
if skill.Name != "Neue Fertigkeit" {
|
||||
t.Errorf("Expected name 'Neue Fertigkeit', got '%s'", skill.Name)
|
||||
}
|
||||
|
||||
if skill.Initialwert != 5 {
|
||||
t.Errorf("Expected initialwert 5, got %d", skill.Initialwert)
|
||||
}
|
||||
|
||||
if skill.BasisWert != 0 {
|
||||
t.Errorf("Expected basiswert 0, got %d", skill.BasisWert)
|
||||
}
|
||||
|
||||
// Verify category-difficulty relationship
|
||||
var scd models.SkillCategoryDifficulty
|
||||
if err := database.DB.Where("skill_id = ?", skillID).First(&scd).Error; err != nil {
|
||||
t.Fatalf("Failed to retrieve skill category difficulty: %v", err)
|
||||
}
|
||||
|
||||
if scd.SkillCategoryID != category.ID {
|
||||
t.Errorf("Expected category ID %d, got %d", category.ID, scd.SkillCategoryID)
|
||||
}
|
||||
|
||||
if scd.SkillDifficultyID != difficulty.ID {
|
||||
t.Errorf("Expected difficulty ID %d, got %d", difficulty.ID, scd.SkillDifficultyID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSkillWithMultipleCategories(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
database.SetupTestDB()
|
||||
|
||||
// Create test dependencies
|
||||
source := getOrCreateSource("TSTMLT", "TestMultiple")
|
||||
category1 := getOrCreateCategory("Körper", source.ID)
|
||||
category2 := getOrCreateCategory("Geist", source.ID)
|
||||
difficulty1 := getOrCreateDifficulty("leicht")
|
||||
difficulty2 := getOrCreateDifficulty("schwer")
|
||||
|
||||
// Prepare create request with multiple categories
|
||||
req := SkillUpdateRequest{
|
||||
Skill: models.Skill{
|
||||
Name: "Multi-Kategorie Fertigkeit",
|
||||
GameSystem: "midgard",
|
||||
Initialwert: 10,
|
||||
Improvable: true,
|
||||
SourceID: source.ID,
|
||||
},
|
||||
CategoryDifficulties: []CategoryDifficultyPair{
|
||||
{
|
||||
CategoryID: category1.ID,
|
||||
DifficultyID: difficulty1.ID,
|
||||
},
|
||||
{
|
||||
CategoryID: category2.ID,
|
||||
DifficultyID: difficulty2.ID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Test creating skill with multiple categories
|
||||
skillID, err := CreateSkillWithCategories(req)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateSkillWithCategories failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify both category-difficulty relationships exist
|
||||
var scds []models.SkillCategoryDifficulty
|
||||
if err := database.DB.Where("skill_id = ?", skillID).Find(&scds).Error; err != nil {
|
||||
t.Fatalf("Failed to retrieve skill category difficulties: %v", err)
|
||||
}
|
||||
|
||||
if len(scds) != 2 {
|
||||
t.Fatalf("Expected 2 category-difficulty relationships, got %d", len(scds))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSkillValidation(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
database.SetupTestDB()
|
||||
|
||||
// Test creating skill without name
|
||||
req := SkillUpdateRequest{
|
||||
Skill: models.Skill{
|
||||
GameSystem: "midgard",
|
||||
Initialwert: 5,
|
||||
},
|
||||
CategoryDifficulties: []CategoryDifficultyPair{},
|
||||
}
|
||||
|
||||
_, err := CreateSkillWithCategories(req)
|
||||
if err == nil {
|
||||
t.Error("Expected error when creating skill without name, got nil")
|
||||
}
|
||||
}
|
||||
@@ -95,12 +95,77 @@ type CategoryDifficultyPair struct {
|
||||
LearnCost int `json:"learn_cost,omitempty"`
|
||||
}
|
||||
|
||||
// CreateSkillWithCategories creates a new skill with category-difficulty relationships
|
||||
func CreateSkillWithCategories(req SkillUpdateRequest) (uint, error) {
|
||||
// Validate required fields
|
||||
if req.Skill.Name == "" {
|
||||
return 0, fmt.Errorf("skill name is required")
|
||||
}
|
||||
|
||||
var skillID uint
|
||||
|
||||
// Start transaction
|
||||
err := database.DB.Transaction(func(tx *gorm.DB) error {
|
||||
// Create skill
|
||||
if err := tx.Create(&req.Skill).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
skillID = req.Skill.ID
|
||||
|
||||
// Create category-difficulty relationships
|
||||
for _, cd := range req.CategoryDifficulties {
|
||||
// Get category and difficulty names for denormalized fields
|
||||
var category models.SkillCategory
|
||||
if err := tx.First(&category, cd.CategoryID).Error; err != nil {
|
||||
return fmt.Errorf("category not found: %w", err)
|
||||
}
|
||||
|
||||
var difficulty models.SkillDifficulty
|
||||
if err := tx.First(&difficulty, cd.DifficultyID).Error; err != nil {
|
||||
return fmt.Errorf("difficulty not found: %w", err)
|
||||
}
|
||||
|
||||
learnCost := cd.LearnCost
|
||||
if learnCost == 0 {
|
||||
// Use default based on difficulty
|
||||
learnCost = getDefaultLearnCost(difficulty.Name)
|
||||
}
|
||||
|
||||
scd := models.SkillCategoryDifficulty{
|
||||
SkillID: skillID,
|
||||
SkillCategoryID: cd.CategoryID,
|
||||
SkillDifficultyID: cd.DifficultyID,
|
||||
LearnCost: learnCost,
|
||||
SCategory: category.Name,
|
||||
SDifficulty: difficulty.Name,
|
||||
}
|
||||
|
||||
if err := tx.Create(&scd).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return skillID, nil
|
||||
}
|
||||
|
||||
// UpdateSkillWithCategories updates a skill and its category-difficulty relationships
|
||||
func UpdateSkillWithCategories(skillID uint, req SkillUpdateRequest) error {
|
||||
// Start transaction
|
||||
return database.DB.Transaction(func(tx *gorm.DB) error {
|
||||
// Update skill basic info
|
||||
if err := tx.Model(&models.Skill{}).Where("id = ?", skillID).Updates(req.Skill).Error; err != nil {
|
||||
// Update skill basic info - use Select to explicitly include boolean fields
|
||||
// This ensures false values are also updated (GORM skips zero values by default in Updates)
|
||||
if err := tx.Model(&models.Skill{}).Where("id = ?", skillID).
|
||||
Select("name", "beschreibung", "game_system", "initialwert", "basis_wert",
|
||||
"bonuseigenschaft", "improvable", "innate_skill", "source_id", "page_number").
|
||||
Updates(req.Skill).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -240,3 +305,28 @@ func UpdateEnhancedMDSkill(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, skill)
|
||||
}
|
||||
|
||||
// CreateEnhancedMDSkill creates a new skill with categories
|
||||
func CreateEnhancedMDSkill(c *gin.Context) {
|
||||
var req SkillUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
respondWithError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Create the skill
|
||||
skillID, err := CreateSkillWithCategories(req)
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to create skill: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Return created skill
|
||||
skill, err := GetSkillWithCategories(skillID)
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to retrieve created skill")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, skill)
|
||||
}
|
||||
|
||||
@@ -292,6 +292,73 @@ func TestUpdateSkillWithCategories(t *testing.T) {
|
||||
t.Error("Expected to find 'Alltag/normal' category after update")
|
||||
}
|
||||
}
|
||||
func TestUpdateSkillBooleanFields(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
database.SetupTestDB()
|
||||
|
||||
// Create test data with improvable=true and innateskill=false
|
||||
source := getOrCreateSource("TSTBOOL", "TestBoolean")
|
||||
skill := models.Skill{
|
||||
Name: "TestBooleanSkill",
|
||||
GameSystem: "midgard",
|
||||
Initialwert: 5,
|
||||
Improvable: true,
|
||||
InnateSkill: false,
|
||||
SourceID: source.ID,
|
||||
}
|
||||
database.DB.Create(&skill)
|
||||
|
||||
category := getOrCreateCategory("Alltag", source.ID)
|
||||
difficulty := getOrCreateDifficulty("normal")
|
||||
|
||||
scd := models.SkillCategoryDifficulty{
|
||||
SkillID: skill.ID,
|
||||
SkillCategoryID: category.ID,
|
||||
SkillDifficultyID: difficulty.ID,
|
||||
LearnCost: 10,
|
||||
SCategory: category.Name,
|
||||
SDifficulty: difficulty.Name,
|
||||
}
|
||||
database.DB.Create(&scd)
|
||||
|
||||
// Update to set improvable=false and innateskill=true
|
||||
updateReq := SkillUpdateRequest{
|
||||
Skill: models.Skill{
|
||||
ID: skill.ID,
|
||||
Name: "TestBooleanSkill",
|
||||
GameSystem: "midgard",
|
||||
Initialwert: 5,
|
||||
Improvable: false, // Change to false
|
||||
InnateSkill: true, // Change to true
|
||||
SourceID: source.ID,
|
||||
},
|
||||
CategoryDifficulties: []CategoryDifficultyPair{
|
||||
{
|
||||
CategoryID: category.ID,
|
||||
DifficultyID: difficulty.ID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := UpdateSkillWithCategories(skill.ID, updateReq)
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateSkillWithCategories failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify boolean fields were updated correctly
|
||||
var updatedSkill models.Skill
|
||||
if err := database.DB.First(&updatedSkill, skill.ID).Error; err != nil {
|
||||
t.Fatalf("Failed to retrieve updated skill: %v", err)
|
||||
}
|
||||
|
||||
if updatedSkill.Improvable != false {
|
||||
t.Errorf("Expected improvable to be false, got %v", updatedSkill.Improvable)
|
||||
}
|
||||
|
||||
if updatedSkill.InnateSkill != true {
|
||||
t.Errorf("Expected innateskill to be true, got %v", updatedSkill.InnateSkill)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDefaultLearnCost(t *testing.T) {
|
||||
tests := []struct {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
v-model="searchTerm"
|
||||
:placeholder="`${$t('search')} ${$t('Skill')}...`"
|
||||
/>
|
||||
<button @click="startCreate" class="btn-primary">{{ $t('newSkill') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -80,6 +81,117 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Create New Skill Row -->
|
||||
<tr v-if="creatingNew">
|
||||
<td>New</td>
|
||||
<td colspan="11">
|
||||
<!-- Create 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.basiswert') }}:</label>
|
||||
<input v-model.number="editedItem.basiswert" 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.difficulty') || 'Difficulty' }}:</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="saveCreate" class="btn-save">{{ $t('createSkill') }}</button>
|
||||
<button @click="cancelCreate" class="btn-cancel">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<template v-for="(dtaItem, index) in filteredAndSortedSkills" :key="dtaItem.id">
|
||||
<!-- Display Mode -->
|
||||
<tr v-if="editingIndex !== index">
|
||||
@@ -189,7 +301,7 @@
|
||||
|
||||
<div class="edit-row">
|
||||
<div class="edit-field full-width">
|
||||
<label>{{ $t('skill.difficulties') || 'Difficulties' }}:</label>
|
||||
<label>{{ $t('skill.difficulty') || 'Difficulty' }}:</label>
|
||||
<div class="difficulty-selects">
|
||||
<div v-for="catId in editedItem.selectedCategories" :key="catId" class="difficulty-select">
|
||||
<span>{{ getCategoryName(catId) }}:</span>
|
||||
@@ -246,6 +358,7 @@ export default {
|
||||
sortAsc: true,
|
||||
editingIndex: -1,
|
||||
editedItem: null,
|
||||
creatingNew: false,
|
||||
filterCategory: '',
|
||||
filterDifficulty: '',
|
||||
filterImprovable: '',
|
||||
@@ -492,6 +605,76 @@ export default {
|
||||
this.filterImprovable = ''
|
||||
this.filterInnateskill = ''
|
||||
this.filterBonuseigenschaft = ''
|
||||
},
|
||||
startCreate() {
|
||||
// Initialize new skill object with defaults
|
||||
this.editedItem = {
|
||||
name: '',
|
||||
beschreibung: '',
|
||||
game_system: 'midgard',
|
||||
initialwert: 5,
|
||||
basiswert: 0,
|
||||
bonuseigenschaft: '',
|
||||
improvable: true,
|
||||
innateskill: false,
|
||||
sourceCode: this.availableSources.length > 0 ? this.availableSources[0].code : '',
|
||||
page_number: 0,
|
||||
selectedCategories: [],
|
||||
categoryDifficulties: {}
|
||||
}
|
||||
this.creatingNew = true
|
||||
},
|
||||
async saveCreate() {
|
||||
try {
|
||||
// Validate required fields
|
||||
if (!this.editedItem.name) {
|
||||
alert('Skill name is required')
|
||||
return
|
||||
}
|
||||
|
||||
// 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 createData = {
|
||||
name: this.editedItem.name,
|
||||
beschreibung: this.editedItem.beschreibung,
|
||||
game_system: this.editedItem.game_system || 'midgard',
|
||||
initialwert: this.editedItem.initialwert,
|
||||
basiswert: this.editedItem.basiswert || 0,
|
||||
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.post(
|
||||
'/api/maintenance/skills-enhanced',
|
||||
createData
|
||||
)
|
||||
|
||||
// Add the new skill to the list
|
||||
this.enhancedSkills.push(response.data)
|
||||
|
||||
// Hide the create dialog
|
||||
this.creatingNew = false
|
||||
this.editedItem = null
|
||||
} catch (error) {
|
||||
console.error('Failed to create skill:', error)
|
||||
alert('Failed to create skill: ' + (error.response?.data?.error || error.message))
|
||||
// Don't close dialog on error so user can fix the issue
|
||||
}
|
||||
},
|
||||
cancelCreate() {
|
||||
this.creatingNew = false
|
||||
this.editedItem = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,8 @@ export default {
|
||||
initialwert:'Startwert',
|
||||
basiswert:'Basiswert (ungelernt)',
|
||||
difficulty:'Schwierigkeit',
|
||||
page:'Seite',
|
||||
categories:'Kategorien',
|
||||
},
|
||||
spell:{
|
||||
id:'ID',
|
||||
@@ -233,6 +235,8 @@ export default {
|
||||
},
|
||||
search:'Suche',
|
||||
Skill:'Fertigkeit',
|
||||
newSkill:'Neue Fertigkeit',
|
||||
createSkill:'Fertigkeit erstellen',
|
||||
common: {
|
||||
loading: 'Laden...',
|
||||
cancel: 'Abbrechen',
|
||||
|
||||
@@ -105,6 +105,8 @@ export default {
|
||||
category:'Category',
|
||||
initialwert:'Initial value',
|
||||
basiswert:'Base value (untrained)',
|
||||
page:'Page',
|
||||
categories:'Categories',
|
||||
},
|
||||
spell:{
|
||||
id:'ID',
|
||||
@@ -125,7 +127,7 @@ export default {
|
||||
quelle:'Source',
|
||||
import: 'Import',
|
||||
selectCsv: 'select CSV',
|
||||
system: 'System'
|
||||
system: 'System',
|
||||
},
|
||||
spells: {
|
||||
learn: {
|
||||
@@ -229,6 +231,8 @@ export default {
|
||||
},
|
||||
search:'Suche',
|
||||
Skill:'Fertigkeit',
|
||||
newSkill:'New Skill',
|
||||
createSkill:'Create Skill',
|
||||
common: {
|
||||
loading: 'Laden...',
|
||||
cancel: 'Abbrechen',
|
||||
|
||||
Reference in New Issue
Block a user