diff --git a/backend/character/image_handler.go b/backend/character/image_handler.go new file mode 100644 index 0000000..b88b6da --- /dev/null +++ b/backend/character/image_handler.go @@ -0,0 +1,45 @@ +package character + +import ( + "bamort/database" + "bamort/logger" + "bamort/models" + "net/http" + + "github.com/gin-gonic/gin" +) + +type ImageUpdateRequest struct { + Image string `json:"image" binding:"required"` +} + +func UpdateCharacterImage(c *gin.Context) { + id := c.Param("id") + logger.Debug("UpdateCharacterImage called for character ID: %s", id) + + var character models.Char + err := character.FirstID(id) + if err != nil { + logger.Error("Character not found: %s", err.Error()) + respondWithError(c, http.StatusNotFound, "Character not found") + return + } + + var request ImageUpdateRequest + if err := c.ShouldBindJSON(&request); err != nil { + logger.Error("Invalid request data: %s", err.Error()) + respondWithError(c, http.StatusBadRequest, "Invalid request data") + return + } + + character.Image = request.Image + + if err := database.DB.Save(&character).Error; err != nil { + logger.Error("Failed to update character image: %s", err.Error()) + respondWithError(c, http.StatusInternalServerError, "Failed to update character image") + return + } + + logger.Info("Character image updated successfully for ID: %s", id) + c.JSON(http.StatusOK, gin.H{"message": "Image updated successfully", "image": character.Image}) +} diff --git a/backend/character/image_handler_test.go b/backend/character/image_handler_test.go new file mode 100644 index 0000000..61d3fb7 --- /dev/null +++ b/backend/character/image_handler_test.go @@ -0,0 +1,99 @@ +package character + +import ( + "bamort/database" + "bamort/models" + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +func TestUpdateCharacterImage(t *testing.T) { + setupTestEnvironment(t) + database.SetupTestDB() + + router := gin.Default() + protected := router.Group("/api") + protected.Use(func(c *gin.Context) { + c.Set("userID", uint(1)) + c.Next() + }) + RegisterRoutes(protected) + + // Get existing character + var char models.Char + err := char.FirstID("18") + assert.NoError(t, err, "Test character 18 should exist") + + // Prepare image data + imageData := "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" + + requestBody := map[string]string{ + "image": imageData, + } + jsonData, _ := json.Marshal(requestBody) + + // Update character image + req, _ := http.NewRequest("PUT", "/api/characters/18/image", bytes.NewBuffer(jsonData)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + assert.Equal(t, http.StatusOK, w.Code, "Should successfully update image") + + // Verify image was saved + var updatedChar models.Char + err = updatedChar.FirstID("18") + assert.NoError(t, err) + assert.Equal(t, imageData, updatedChar.Image, "Image should be updated in database") +} + +func TestUpdateCharacterImageInvalidID(t *testing.T) { + setupTestEnvironment(t) + database.SetupTestDB() + + router := gin.Default() + protected := router.Group("/api") + protected.Use(func(c *gin.Context) { + c.Set("userID", uint(1)) + c.Next() + }) + RegisterRoutes(protected) + + requestBody := map[string]string{ + "image": "data:image/png;base64,test", + } + jsonData, _ := json.Marshal(requestBody) + + req, _ := http.NewRequest("PUT", "/api/characters/99999/image", bytes.NewBuffer(jsonData)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + assert.Equal(t, http.StatusNotFound, w.Code, "Should return 404 for non-existent character") +} + +func TestUpdateCharacterImageInvalidData(t *testing.T) { + setupTestEnvironment(t) + database.SetupTestDB() + + router := gin.Default() + protected := router.Group("/api") + protected.Use(func(c *gin.Context) { + c.Set("userID", uint(1)) + c.Next() + }) + RegisterRoutes(protected) + + req, _ := http.NewRequest("PUT", "/api/characters/18/image", bytes.NewBuffer([]byte("invalid json"))) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + assert.Equal(t, http.StatusBadRequest, w.Code, "Should return 400 for invalid JSON") +} diff --git a/backend/character/routes.go b/backend/character/routes.go index 2894ba2..825f814 100644 --- a/backend/character/routes.go +++ b/backend/character/routes.go @@ -11,6 +11,7 @@ func RegisterRoutes(r *gin.RouterGroup) { charGrp.GET("/:id", GetCharacter) charGrp.PUT("/:id", UpdateCharacter) charGrp.DELETE("/:id", DeleteCharacter) + charGrp.PUT("/:id/image", UpdateCharacterImage) // Erfahrung und Vermögen charGrp.GET("/:id/experience-wealth", GetCharacterExperienceAndWealth) // NewSystem diff --git a/frontend/src/components/DatasheetView.vue b/frontend/src/components/DatasheetView.vue index 12794d8..590b875 100644 --- a/frontend/src/components/DatasheetView.vue +++ b/frontend/src/components/DatasheetView.vue @@ -4,6 +4,10 @@
Character Image +
@@ -81,6 +85,16 @@ margin-top: 0; /* Kein zusätzlicher oberer Margin */ } +.character-image { + position: relative; +} + +.character-image .image-upload-container { + position: absolute; + bottom: 10px; + right: 10px; +} + .character-info { margin-top: 20px; } @@ -88,8 +102,13 @@ diff --git a/frontend/src/locales/de b/frontend/src/locales/de index d9f4ee6..9f2bbaf 100644 --- a/frontend/src/locales/de +++ b/frontend/src/locales/de @@ -470,5 +470,17 @@ export default { passwordUpdateSuccess: 'Passwort erfolgreich aktualisiert', passwordUpdateError: 'Fehler beim Aktualisieren des Passworts', currentPasswordIncorrect: 'Das aktuelle Passwort ist falsch' - } + }, + character: { + uploadImage: 'Bild hochladen', + selectImage: 'Bild auswählen', + cropRect: 'Rechteckig', + cropRound: 'Rund', + preview: 'Vorschau', + changeImage: 'Bild ändern', + saveImage: 'Bild speichern', + imageUploadSuccess: 'Bild erfolgreich hochgeladen', + imageUploadError: 'Fehler beim Hochladen des Bildes' + }, + uploading: 'Hochladen' } \ No newline at end of file diff --git a/frontend/src/locales/en b/frontend/src/locales/en index ece0745..cd09a23 100644 --- a/frontend/src/locales/en +++ b/frontend/src/locales/en @@ -468,5 +468,17 @@ export default { passwordUpdateSuccess: 'Password updated successfully', passwordUpdateError: 'Failed to update password', currentPasswordIncorrect: 'Current password is incorrect' - } + }, + character: { + uploadImage: 'Upload Image', + selectImage: 'Select Image', + cropRect: 'Rectangular', + cropRound: 'Round', + preview: 'Preview', + changeImage: 'Change Image', + saveImage: 'Save Image', + imageUploadSuccess: 'Image uploaded successfully', + imageUploadError: 'Failed to upload image' + }, + uploading: 'Uploading' } \ No newline at end of file