moved some basic value lists to database
This commit is contained in:
@@ -3307,3 +3307,128 @@ func getStandBonusPoints(stand string) map[string]int {
|
|||||||
return make(map[string]int)
|
return make(map[string]int)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDatasheetOptions returns all available options for datasheet select boxes
|
||||||
|
func GetDatasheetOptions(c *gin.Context) {
|
||||||
|
logger.Debug("GetDatasheetOptions aufgerufen")
|
||||||
|
|
||||||
|
characterID := c.Param("id")
|
||||||
|
|
||||||
|
// Load character to get their weapon skills
|
||||||
|
var character models.Char
|
||||||
|
err := character.FirstID(characterID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("GetDatasheetOptions: Charakter nicht gefunden - ID: %s, Error: %s", characterID, err.Error())
|
||||||
|
respondWithError(c, http.StatusNotFound, "Character not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all available weapons from database
|
||||||
|
var allWeapons []models.Weapon
|
||||||
|
if err := database.DB.Find(&allWeapons).Error; err != nil {
|
||||||
|
logger.Error("GetDatasheetOptions: Fehler beim Laden der Waffen: %s", err.Error())
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to load weapons")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter weapons based on character's weapon skills
|
||||||
|
characterWeaponSkills := make(map[string]bool)
|
||||||
|
for _, skill := range character.Waffenfertigkeiten {
|
||||||
|
characterWeaponSkills[skill.Name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
availableWeapons := []string{}
|
||||||
|
for _, weapon := range allWeapons {
|
||||||
|
if characterWeaponSkills[weapon.SkillRequired] {
|
||||||
|
availableWeapons = append(availableWeapons, weapon.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load misc lookup data from database
|
||||||
|
genders, err := gsmaster.GetMiscLookupByKey("gender")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("GetDatasheetOptions: Fehler beim Laden der Geschlechter: %s", err.Error())
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to load genders")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
races, err := gsmaster.GetMiscLookupByKey("races")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("GetDatasheetOptions: Fehler beim Laden der Rassen: %s", err.Error())
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to load races")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
origins, err := gsmaster.GetMiscLookupByKey("origins")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("GetDatasheetOptions: Fehler beim Laden der Herkünfte: %s", err.Error())
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to load origins")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
socialClasses, err := gsmaster.GetMiscLookupByKey("social_classes")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("GetDatasheetOptions: Fehler beim Laden der Stände: %s", err.Error())
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to load social classes")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
faiths, err := gsmaster.GetMiscLookupByKey("faiths")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("GetDatasheetOptions: Fehler beim Laden der Glaubensrichtungen: %s", err.Error())
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to load faiths")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
handedness, err := gsmaster.GetMiscLookupByKey("handedness")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("GetDatasheetOptions: Fehler beim Laden der Händigkeiten: %s", err.Error())
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to load handedness")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to string arrays
|
||||||
|
genderValues := make([]string, len(genders))
|
||||||
|
for i, g := range genders {
|
||||||
|
genderValues[i] = g.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
raceValues := make([]string, len(races))
|
||||||
|
for i, r := range races {
|
||||||
|
raceValues[i] = r.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
originValues := make([]string, len(origins))
|
||||||
|
for i, o := range origins {
|
||||||
|
originValues[i] = o.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
socialClassValues := make([]string, len(socialClasses))
|
||||||
|
for i, sc := range socialClasses {
|
||||||
|
socialClassValues[i] = sc.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
faithValues := make([]string, len(faiths))
|
||||||
|
for i, f := range faiths {
|
||||||
|
faithValues[i] = f.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
handednessValues := make([]string, len(handedness))
|
||||||
|
for i, h := range handedness {
|
||||||
|
handednessValues[i] = h.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return all options
|
||||||
|
options := gin.H{
|
||||||
|
"gender": genderValues,
|
||||||
|
"races": raceValues,
|
||||||
|
"origins": originValues,
|
||||||
|
"social_classes": socialClassValues,
|
||||||
|
"faiths": faithValues,
|
||||||
|
"handedness": handednessValues,
|
||||||
|
"specializations": availableWeapons,
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug("GetDatasheetOptions: Erfolgreich geladen - %d verfügbare Waffen", len(availableWeapons))
|
||||||
|
c.JSON(http.StatusOK, options)
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
@@ -975,3 +976,142 @@ func TestListCharacters(t *testing.T) {
|
|||||||
assert.Equal(t, 0, len(response.SelfOwned), "Should return empty list for userID 0")
|
assert.Equal(t, 0, len(response.SelfOwned), "Should return empty list for userID 0")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetDatasheetOptions(t *testing.T) {
|
||||||
|
// Setup test environment
|
||||||
|
original := os.Getenv("ENVIRONMENT")
|
||||||
|
os.Setenv("ENVIRONMENT", "test")
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if original != "" {
|
||||||
|
os.Setenv("ENVIRONMENT", original)
|
||||||
|
} else {
|
||||||
|
os.Unsetenv("ENVIRONMENT")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Setup test database
|
||||||
|
database.SetupTestDB(true, true)
|
||||||
|
defer database.ResetTestDB()
|
||||||
|
|
||||||
|
err := models.MigrateStructure()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Populate misc lookup data
|
||||||
|
err = models.PopulateMiscLookupData()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Create test character with weapon skill
|
||||||
|
testChar := &models.Char{
|
||||||
|
BamortBase: models.BamortBase{
|
||||||
|
Name: "Test Character",
|
||||||
|
},
|
||||||
|
Typ: "Krieger",
|
||||||
|
Rasse: "Mensch",
|
||||||
|
Waffenfertigkeiten: []models.SkWaffenfertigkeit{
|
||||||
|
{
|
||||||
|
SkFertigkeit: models.SkFertigkeit{
|
||||||
|
BamortCharTrait: models.BamortCharTrait{
|
||||||
|
BamortBase: models.BamortBase{
|
||||||
|
Name: "Langschwert",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = testChar.Create()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotZero(t, testChar.ID, "Character ID should be set after Create")
|
||||||
|
|
||||||
|
// Setup Gin context
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(w)
|
||||||
|
// Use string conversion of actual character ID
|
||||||
|
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", testChar.ID)}}
|
||||||
|
|
||||||
|
// Call the handler
|
||||||
|
GetDatasheetOptions(c)
|
||||||
|
|
||||||
|
// Assert response
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify all expected keys exist
|
||||||
|
assert.Contains(t, response, "gender")
|
||||||
|
assert.Contains(t, response, "races")
|
||||||
|
assert.Contains(t, response, "origins")
|
||||||
|
assert.Contains(t, response, "social_classes")
|
||||||
|
assert.Contains(t, response, "faiths")
|
||||||
|
assert.Contains(t, response, "handedness")
|
||||||
|
assert.Contains(t, response, "specializations")
|
||||||
|
|
||||||
|
// Verify data from database
|
||||||
|
genders := response["gender"].([]interface{})
|
||||||
|
assert.Equal(t, 3, len(genders))
|
||||||
|
assert.Contains(t, genders, "divers")
|
||||||
|
assert.Contains(t, genders, "männlich")
|
||||||
|
assert.Contains(t, genders, "weiblich")
|
||||||
|
|
||||||
|
races := response["races"].([]interface{})
|
||||||
|
assert.Equal(t, 5, len(races))
|
||||||
|
assert.Contains(t, races, "Elf")
|
||||||
|
assert.Contains(t, races, "Mensch")
|
||||||
|
|
||||||
|
origins := response["origins"].([]interface{})
|
||||||
|
assert.Equal(t, 15, len(origins))
|
||||||
|
assert.Contains(t, origins, "Albai")
|
||||||
|
|
||||||
|
socialClasses := response["social_classes"].([]interface{})
|
||||||
|
assert.Equal(t, 3, len(socialClasses))
|
||||||
|
assert.Contains(t, socialClasses, "Adel")
|
||||||
|
assert.Contains(t, socialClasses, "Mittelschicht")
|
||||||
|
|
||||||
|
faiths := response["faiths"].([]interface{})
|
||||||
|
assert.Equal(t, 5, len(faiths))
|
||||||
|
assert.Contains(t, faiths, "Druide")
|
||||||
|
assert.Contains(t, faiths, "Keine")
|
||||||
|
|
||||||
|
handedness := response["handedness"].([]interface{})
|
||||||
|
assert.Equal(t, 3, len(handedness))
|
||||||
|
assert.Contains(t, handedness, "beidhändig")
|
||||||
|
assert.Contains(t, handedness, "links")
|
||||||
|
assert.Contains(t, handedness, "rechts")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDatasheetOptions_CharacterNotFound(t *testing.T) {
|
||||||
|
// Setup test environment
|
||||||
|
original := os.Getenv("ENVIRONMENT")
|
||||||
|
os.Setenv("ENVIRONMENT", "test")
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if original != "" {
|
||||||
|
os.Setenv("ENVIRONMENT", original)
|
||||||
|
} else {
|
||||||
|
os.Unsetenv("ENVIRONMENT")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Setup test database
|
||||||
|
database.SetupTestDB(true, true)
|
||||||
|
defer database.ResetTestDB()
|
||||||
|
|
||||||
|
err := models.MigrateStructure()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Setup Gin context with non-existent character ID
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(w)
|
||||||
|
c.Params = gin.Params{{Key: "id", Value: "99999"}}
|
||||||
|
|
||||||
|
// Call the handler
|
||||||
|
GetDatasheetOptions(c)
|
||||||
|
|
||||||
|
// Assert error response
|
||||||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ func RegisterRoutes(r *gin.RouterGroup) {
|
|||||||
charGrp.PUT("/:id", UpdateCharacter)
|
charGrp.PUT("/:id", UpdateCharacter)
|
||||||
charGrp.DELETE("/:id", DeleteCharacter)
|
charGrp.DELETE("/:id", DeleteCharacter)
|
||||||
charGrp.PUT("/:id/image", UpdateCharacterImage)
|
charGrp.PUT("/:id/image", UpdateCharacterImage)
|
||||||
|
charGrp.GET("/:id/datasheet-options", GetDatasheetOptions)
|
||||||
|
|
||||||
// Erfahrung und Vermögen
|
// Erfahrung und Vermögen
|
||||||
charGrp.GET("/:id/experience-wealth", GetCharacterExperienceAndWealth) // NewSystem
|
charGrp.GET("/:id/experience-wealth", GetCharacterExperienceAndWealth) // NewSystem
|
||||||
|
|||||||
@@ -59,6 +59,16 @@ func main() {
|
|||||||
database.ConnectDatabase()
|
database.ConnectDatabase()
|
||||||
logger.Info("Datenbankverbindung erfolgreich")
|
logger.Info("Datenbankverbindung erfolgreich")
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Populate initial misc lookup data
|
||||||
|
logger.Debug("Initialisiere Misc-Lookup-Daten...")
|
||||||
|
if err := models.PopulateMiscLookupData(); err != nil {
|
||||||
|
logger.Warn("Fehler beim Initialisieren der Misc-Lookup-Daten: %s", err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Info("Misc-Lookup-Daten erfolgreich initialisiert")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Initialize PDF templates
|
// Initialize PDF templates
|
||||||
logger.Debug("Initialisiere PDF-Templates...")
|
logger.Debug("Initialisiere PDF-Templates...")
|
||||||
if err := pdfrender.InitializeTemplates("/app/default_templates", cfg.TemplatesDir); err != nil {
|
if err := pdfrender.InitializeTemplates("/app/default_templates", cfg.TemplatesDir); err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package gsmaster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bamort/database"
|
||||||
|
"bamort/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetMiscLookupByKey retrieves all values for a given key
|
||||||
|
func GetMiscLookupByKey(key string) ([]models.MiscLookup, error) {
|
||||||
|
var items []models.MiscLookup
|
||||||
|
err := database.DB.Where("`key` = ?", key).Order("value ASC").Find(&items).Error
|
||||||
|
return items, err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// PopulateMiscLookupData populates initial misc lookup data if table is empty
|
||||||
|
func PopulateMiscLookupData() error {
|
||||||
|
// Check if data already exists
|
||||||
|
var count int64
|
||||||
|
if err := database.DB.Model(&models.MiscLookup{}).Count(&count).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
return nil // Data already exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define initial data
|
||||||
|
initialData := []struct {
|
||||||
|
key string
|
||||||
|
values []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
key: "gender",
|
||||||
|
values: []string{"männlich", "weiblich", "divers"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "races",
|
||||||
|
values: []string{"Mensch", "Elf", "Zwerg", "Gnom", "Halbling"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "origins",
|
||||||
|
values: []string{
|
||||||
|
"Albai", "Aran", "Chryseia", "Clanngadarn", "Erainn",
|
||||||
|
"Eschar", "Fuardain", "Ikengabecken", "KanThaiPan", "Küstenstaaten",
|
||||||
|
"Moravod", "Nahuatlan", "Rawindra", "Twyneddin", "Valian",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "social_classes",
|
||||||
|
values: []string{"Volk", "Mittelschicht", "Adel"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "faiths",
|
||||||
|
values: []string{"Keine", "Nathir", "Deis Albai", "Mahal", "Druide"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "handedness",
|
||||||
|
values: []string{"rechts", "links", "beidhändig"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert data
|
||||||
|
for _, item := range initialData {
|
||||||
|
for _, value := range item.values {
|
||||||
|
misc := models.MiscLookup{
|
||||||
|
Key: item.key,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
if err := database.DB.Create(&misc).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package gsmaster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bamort/database"
|
||||||
|
"bamort/models"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMiscLookup_TableName(t *testing.T) {
|
||||||
|
misc := models.MiscLookup{}
|
||||||
|
assert.Equal(t, "gsm_misc", misc.TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMiscLookup_CreateAndRetrieve(t *testing.T) {
|
||||||
|
database.SetupTestDB()
|
||||||
|
|
||||||
|
// Create test data
|
||||||
|
testData := []models.MiscLookup{
|
||||||
|
{Key: "gender", Value: "männlich"},
|
||||||
|
{Key: "gender", Value: "weiblich"},
|
||||||
|
{Key: "races", Value: "Mensch"},
|
||||||
|
{Key: "races", Value: "Elf"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert test data
|
||||||
|
for _, item := range testData {
|
||||||
|
err := database.DB.Create(&item).Error
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve by key
|
||||||
|
genders, err := GetMiscLookupByKey("gender")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, genders, 2)
|
||||||
|
assert.Equal(t, "männlich", genders[0].Value)
|
||||||
|
assert.Equal(t, "weiblich", genders[1].Value)
|
||||||
|
|
||||||
|
races, err := GetMiscLookupByKey("races")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, races, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMiscLookupByKey_NotFound(t *testing.T) {
|
||||||
|
database.SetupTestDB()
|
||||||
|
|
||||||
|
items, err := GetMiscLookupByKey("nonexistent")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Empty(t, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMiscLookup_WithSourceInfo(t *testing.T) {
|
||||||
|
database.SetupTestDB()
|
||||||
|
|
||||||
|
misc := models.MiscLookup{
|
||||||
|
Key: "test_key",
|
||||||
|
Value: "test_value",
|
||||||
|
SourceID: 1,
|
||||||
|
PageNumber: 42,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := database.DB.Create(&misc).Error
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotZero(t, misc.ID)
|
||||||
|
|
||||||
|
// Retrieve and verify
|
||||||
|
var retrieved models.MiscLookup
|
||||||
|
err = database.DB.First(&retrieved, misc.ID).Error
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "test_key", retrieved.Key)
|
||||||
|
assert.Equal(t, "test_value", retrieved.Value)
|
||||||
|
assert.Equal(t, uint(1), retrieved.SourceID)
|
||||||
|
assert.Equal(t, 42, retrieved.PageNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestPopulateMiscLookupData(t *testing.T) {
|
||||||
|
database.SetupTestDB()
|
||||||
|
|
||||||
|
// First population should succeed
|
||||||
|
err := PopulateMiscLookupData()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify all keys have data
|
||||||
|
expectedCounts := map[string]int{
|
||||||
|
"gender": 3,
|
||||||
|
"races": 5,
|
||||||
|
"origins": 15,
|
||||||
|
"social_classes": 3,
|
||||||
|
"faiths": 5,
|
||||||
|
"handedness": 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, expectedCount := range expectedCounts {
|
||||||
|
items, err := GetMiscLookupByKey(key)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, items, expectedCount, "Expected %d items for key %s", expectedCount, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify specific values
|
||||||
|
genders, _ := GetMiscLookupByKey("gender")
|
||||||
|
assert.Contains(t, []string{"männlich", "weiblich", "divers"}, genders[0].Value)
|
||||||
|
|
||||||
|
races, _ := GetMiscLookupByKey("races")
|
||||||
|
raceValues := make([]string, len(races))
|
||||||
|
for i, r := range races {
|
||||||
|
raceValues[i] = r.Value
|
||||||
|
}
|
||||||
|
assert.Contains(t, raceValues, "Mensch")
|
||||||
|
assert.Contains(t, raceValues, "Elf")
|
||||||
|
|
||||||
|
// Second population should not duplicate data
|
||||||
|
err = PopulateMiscLookupData()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var totalCount int64
|
||||||
|
err = database.DB.Model(&MiscLookup{}).Count(&totalCount).Error
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, int64(34), totalCount, "Should not duplicate data on second population")
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -61,6 +61,7 @@ func gsMasterMigrateStructure(db ...*gorm.DB) error {
|
|||||||
&Container{},
|
&Container{},
|
||||||
&Transportation{},
|
&Transportation{},
|
||||||
&Believe{},
|
&Believe{},
|
||||||
|
&MiscLookup{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -128,6 +128,15 @@ type Believe struct {
|
|||||||
PageNumber int `json:"page_number,omitempty"` // Seitenzahl im Quellenbuch
|
PageNumber int `json:"page_number,omitempty"` // Seitenzahl im Quellenbuch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MiscLookup represents miscellaneous lookup values like gender, race, origin, etc.
|
||||||
|
type MiscLookup struct {
|
||||||
|
ID uint `gorm:"primaryKey" json:"id"`
|
||||||
|
Key string `gorm:"column:key;type:varchar(50);index;not null" json:"key"`
|
||||||
|
Value string `gorm:"type:varchar(255);not null" json:"value"`
|
||||||
|
SourceID uint `json:"source_id,omitempty"`
|
||||||
|
PageNumber int `json:"page_number,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func (object *Skill) TableName() string {
|
func (object *Skill) TableName() string {
|
||||||
dbPrefix := "gsm"
|
dbPrefix := "gsm"
|
||||||
return dbPrefix + "_" + "skills"
|
return dbPrefix + "_" + "skills"
|
||||||
@@ -669,3 +678,8 @@ func GetBelievesByActiveSources(gameSystem string) ([]Believe, error) {
|
|||||||
Find(&believes).Error
|
Find(&believes).Error
|
||||||
return believes, err
|
return believes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName specifies the table name for MiscLookup
|
||||||
|
func (MiscLookup) TableName() string {
|
||||||
|
return "gsm_misc"
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,7 +49,9 @@
|
|||||||
@dblclick="startEditProp('gender', character.gender)"
|
@dblclick="startEditProp('gender', character.gender)"
|
||||||
class="editable-prop"
|
class="editable-prop"
|
||||||
>{{ character.gender || 'x' }}</span>
|
>{{ character.gender || 'x' }}</span>
|
||||||
<input v-else v-model="editPropValue" @blur="saveProp('gender')" @keyup.enter="saveProp('gender')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input" />
|
<select v-else v-model="editPropValue" @blur="saveProp('gender')" @keyup.enter="saveProp('gender')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input">
|
||||||
|
<option v-for="option in getSelectOptions('gender')" :key="option" :value="option">{{ option }}</option>
|
||||||
|
</select>
|
||||||
),
|
),
|
||||||
Grad:
|
Grad:
|
||||||
<span
|
<span
|
||||||
@@ -64,21 +66,27 @@
|
|||||||
@dblclick="startEditProp('rasse', character.rasse)"
|
@dblclick="startEditProp('rasse', character.rasse)"
|
||||||
class="editable-prop"
|
class="editable-prop"
|
||||||
>{{ character.rasse || 'x' }}</span>
|
>{{ character.rasse || 'x' }}</span>
|
||||||
<input v-else v-model="editPropValue" @blur="saveProp('rasse')" @keyup.enter="saveProp('rasse')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input" />,
|
<select v-else v-model="editPropValue" @blur="saveProp('rasse')" @keyup.enter="saveProp('rasse')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input">
|
||||||
|
<option v-for="option in getSelectOptions('rasse')" :key="option" :value="option">{{ option }}</option>
|
||||||
|
</select>,
|
||||||
Heimat:
|
Heimat:
|
||||||
<span
|
<span
|
||||||
v-if="editingProp !== 'origin'"
|
v-if="editingProp !== 'origin'"
|
||||||
@dblclick="startEditProp('origin', character.origin)"
|
@dblclick="startEditProp('origin', character.origin)"
|
||||||
class="editable-prop"
|
class="editable-prop"
|
||||||
>{{ character.origin || '-' }}</span>
|
>{{ character.origin || '-' }}</span>
|
||||||
<input v-else v-model="editPropValue" @blur="saveProp('origin')" @keyup.enter="saveProp('origin')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input" />,
|
<select v-else v-model="editPropValue" @blur="saveProp('origin')" @keyup.enter="saveProp('origin')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input">
|
||||||
|
<option v-for="option in getSelectOptions('origin')" :key="option" :value="option">{{ option }}</option>
|
||||||
|
</select>,
|
||||||
Stand:
|
Stand:
|
||||||
<span
|
<span
|
||||||
v-if="editingProp !== 'social_class'"
|
v-if="editingProp !== 'social_class'"
|
||||||
@dblclick="startEditProp('social_class', character.social_class)"
|
@dblclick="startEditProp('social_class', character.social_class)"
|
||||||
class="editable-prop"
|
class="editable-prop"
|
||||||
>{{ character.social_class || '-' }}</span>
|
>{{ character.social_class || '-' }}</span>
|
||||||
<input v-else v-model="editPropValue" @blur="saveProp('social_class')" @keyup.enter="saveProp('social_class')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input" />.
|
<select v-else v-model="editPropValue" @blur="saveProp('social_class')" @keyup.enter="saveProp('social_class')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input">
|
||||||
|
<option v-for="option in getSelectOptions('social_class')" :key="option" :value="option">{{ option }}</option>
|
||||||
|
</select>.
|
||||||
</p>
|
</p>
|
||||||
<p v-if="character.rasse==='Zwerg'">
|
<p v-if="character.rasse==='Zwerg'">
|
||||||
Hort für Grad {{ character.grad || 'x' }}: 125 GS, für nächsten Grad: 250 GS.
|
Hort für Grad {{ character.grad || 'x' }}: 125 GS, für nächsten Grad: 250 GS.
|
||||||
@@ -90,7 +98,10 @@
|
|||||||
@dblclick="startEditProp('spezialisierung', character.spezialisierung)"
|
@dblclick="startEditProp('spezialisierung', character.spezialisierung)"
|
||||||
class="editable-prop"
|
class="editable-prop"
|
||||||
>{{ character.spezialisierung || '-' }}</span>
|
>{{ character.spezialisierung || '-' }}</span>
|
||||||
<input v-else v-model="editPropValue" @blur="saveProp('spezialisierung')" @keyup.enter="saveProp('spezialisierung')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input" />.
|
<select v-else v-model="editPropValue" @blur="saveProp('spezialisierung')" @keyup.enter="saveProp('spezialisierung')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input" style="width: 300px;">
|
||||||
|
<option value="">-</option>
|
||||||
|
<option v-for="option in getSelectOptions('spezialisierung')" :key="option" :value="option">{{ option }}</option>
|
||||||
|
</select>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Alter:
|
Alter:
|
||||||
@@ -105,7 +116,9 @@
|
|||||||
<span v-else-if="character.hand=='links'">Linkshänder</span>
|
<span v-else-if="character.hand=='links'">Linkshänder</span>
|
||||||
<span v-else>Beidhändig</span>
|
<span v-else>Beidhändig</span>
|
||||||
</strong>
|
</strong>
|
||||||
<input v-else v-model="editPropValue" @blur="saveProp('hand')" @keyup.enter="saveProp('hand')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input" />,
|
<select v-else v-model="editPropValue" @blur="saveProp('hand')" @keyup.enter="saveProp('hand')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input">
|
||||||
|
<option v-for="option in getSelectOptions('hand')" :key="option" :value="option">{{ option }}</option>
|
||||||
|
</select>,
|
||||||
Größe:
|
Größe:
|
||||||
<span
|
<span
|
||||||
v-if="editingProp !== 'groesse'"
|
v-if="editingProp !== 'groesse'"
|
||||||
@@ -154,7 +167,9 @@
|
|||||||
@dblclick="startEditProp('glaube', character.glaube)"
|
@dblclick="startEditProp('glaube', character.glaube)"
|
||||||
class="editable-prop"
|
class="editable-prop"
|
||||||
>{{ character.glaube || '-' }}</span>
|
>{{ character.glaube || '-' }}</span>
|
||||||
<input v-else v-model="editPropValue" @blur="saveProp('glaube')" @keyup.enter="saveProp('glaube')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input" />
|
<select v-else v-model="editPropValue" @blur="saveProp('glaube')" @keyup.enter="saveProp('glaube')" @keyup.esc="cancelEditProp" ref="propInput" class="prop-input">
|
||||||
|
<option v-for="option in getSelectOptions('glaube')" :key="option" :value="option">{{ option }}</option>
|
||||||
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Merkmale:</strong>
|
<strong>Merkmale:</strong>
|
||||||
@@ -208,6 +223,7 @@ export default {
|
|||||||
editingProp: null,
|
editingProp: null,
|
||||||
editPropValue: '',
|
editPropValue: '',
|
||||||
editPropType: 'text',
|
editPropType: 'text',
|
||||||
|
datasheetOptions: null,
|
||||||
characterStats: [
|
characterStats: [
|
||||||
{ label: 'stats.strength', path: 'eigenschaften.6.value' },
|
{ label: 'stats.strength', path: 'eigenschaften.6.value' },
|
||||||
{ label: 'stats.dexterity', path: 'eigenschaften.1.value' },
|
{ label: 'stats.dexterity', path: 'eigenschaften.1.value' },
|
||||||
@@ -228,6 +244,17 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async loadDatasheetOptions() {
|
||||||
|
if (this.datasheetOptions) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await API.get(`/api/characters/${this.character.id}/datasheet-options`)
|
||||||
|
this.datasheetOptions = response.data
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load datasheet options:', error)
|
||||||
|
alert('Fehler beim Laden der Auswahloptionen')
|
||||||
|
}
|
||||||
|
},
|
||||||
handleImageUpdate(newImage) {
|
handleImageUpdate(newImage) {
|
||||||
this.$emit('character-updated')
|
this.$emit('character-updated')
|
||||||
},
|
},
|
||||||
@@ -283,6 +310,13 @@ export default {
|
|||||||
this.editValue = ''
|
this.editValue = ''
|
||||||
},
|
},
|
||||||
startEditProp(prop, value, type = 'text') {
|
startEditProp(prop, value, type = 'text') {
|
||||||
|
// Load options if this is a select field
|
||||||
|
const selectFields = ['gender', 'rasse', 'origin', 'social_class', 'glaube', 'hand', 'spezialisierung']
|
||||||
|
if (selectFields.includes(prop)) {
|
||||||
|
this.loadDatasheetOptions()
|
||||||
|
type = 'select'
|
||||||
|
}
|
||||||
|
|
||||||
this.editingProp = prop
|
this.editingProp = prop
|
||||||
this.editPropValue = value || ''
|
this.editPropValue = value || ''
|
||||||
this.editPropType = type
|
this.editPropType = type
|
||||||
@@ -291,7 +325,9 @@ export default {
|
|||||||
const input = Array.isArray(this.$refs.propInput) ? this.$refs.propInput[0] : this.$refs.propInput
|
const input = Array.isArray(this.$refs.propInput) ? this.$refs.propInput[0] : this.$refs.propInput
|
||||||
if (input) {
|
if (input) {
|
||||||
input.focus()
|
input.focus()
|
||||||
input.select()
|
if (type !== 'select') {
|
||||||
|
input.select()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -331,6 +367,21 @@ export default {
|
|||||||
this.cancelEditProp()
|
this.cancelEditProp()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getSelectOptions(prop) {
|
||||||
|
if (!this.datasheetOptions) return []
|
||||||
|
|
||||||
|
const optionMap = {
|
||||||
|
'gender': 'gender',
|
||||||
|
'rasse': 'races',
|
||||||
|
'origin': 'origins',
|
||||||
|
'social_class': 'social_classes',
|
||||||
|
'glaube': 'faiths',
|
||||||
|
'hand': 'handedness',
|
||||||
|
'spezialisierung': 'specializations'
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.datasheetOptions[optionMap[prop]] || []
|
||||||
|
},
|
||||||
cancelEditProp() {
|
cancelEditProp() {
|
||||||
this.editingProp = null
|
this.editingProp = null
|
||||||
this.editPropValue = ''
|
this.editPropValue = ''
|
||||||
|
|||||||
Reference in New Issue
Block a user