From f4187512ba0316cf499ea329351437ede7f604aa Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 25 Sep 2025 19:35:13 +0200 Subject: [PATCH] =?UTF-8?q?Erweitert=20die=20Funktionalit=C3=A4t=20zur=20E?= =?UTF-8?q?rstellung=20und=20Aktualisierung=20von=20vordefinierten=20Aktiv?= =?UTF-8?q?it=C3=A4ten,=20indem=20das=20Feld=20f=C3=BCr=20Zeichnungsdaten?= =?UTF-8?q?=20in=20den=20entsprechenden=20Controllern,=20Modellen=20und=20?= =?UTF-8?q?Services=20hinzugef=C3=BCgt=20wird.=20Aktualisiert=20die=20Benu?= =?UTF-8?q?tzeroberfl=C3=A4che=20in=20CourtDrawingTool.vue=20und=20Predefi?= =?UTF-8?q?nedActivities.vue,=20um=20die=20Handhabung=20von=20Zeichnungsda?= =?UTF-8?q?ten=20zu=20verbessern=20und=20die=20Logik=20f=C3=BCr=20das=20La?= =?UTF-8?q?den=20und=20Speichern=20von=20Zeichnungen=20zu=20optimieren.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../predefinedActivityController.js | 8 ++-- backend/models/PredefinedActivity.js | 5 +++ backend/services/predefinedActivityService.js | 2 + frontend/src/components/CourtDrawingTool.vue | 24 +++++++--- frontend/src/views/PredefinedActivities.vue | 44 +++++++++++-------- 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/backend/controllers/predefinedActivityController.js b/backend/controllers/predefinedActivityController.js index 102a2f1..1320a7a 100644 --- a/backend/controllers/predefinedActivityController.js +++ b/backend/controllers/predefinedActivityController.js @@ -5,8 +5,8 @@ import fs from 'fs'; export const createPredefinedActivity = async (req, res) => { try { - const { name, code, description, durationText, duration, imageLink } = req.body; - const predefinedActivity = await predefinedActivityService.createPredefinedActivity({ name, code, description, durationText, duration, imageLink }); + const { name, code, description, durationText, duration, imageLink, drawingData } = req.body; + const predefinedActivity = await predefinedActivityService.createPredefinedActivity({ name, code, description, durationText, duration, imageLink, drawingData }); res.status(201).json(predefinedActivity); } catch (error) { console.error('[createPredefinedActivity] - Error:', error); @@ -42,8 +42,8 @@ export const getPredefinedActivityById = async (req, res) => { export const updatePredefinedActivity = async (req, res) => { try { const { id } = req.params; - const { name, code, description, durationText, duration, imageLink } = req.body; - const updatedActivity = await predefinedActivityService.updatePredefinedActivity(id, { name, code, description, durationText, duration, imageLink }); + const { name, code, description, durationText, duration, imageLink, drawingData } = req.body; + const updatedActivity = await predefinedActivityService.updatePredefinedActivity(id, { name, code, description, durationText, duration, imageLink, drawingData }); res.status(200).json(updatedActivity); } catch (error) { console.error('[updatePredefinedActivity] - Error:', error); diff --git a/backend/models/PredefinedActivity.js b/backend/models/PredefinedActivity.js index 749c6a5..63c7d8d 100644 --- a/backend/models/PredefinedActivity.js +++ b/backend/models/PredefinedActivity.js @@ -19,6 +19,11 @@ const PredefinedActivity = sequelize.define('PredefinedActivity', { type: DataTypes.TEXT, allowNull: true, }, + drawingData: { + type: DataTypes.TEXT, + allowNull: true, + comment: 'JSON string with metadata for Court Drawing Tool' + }, durationText: { type: DataTypes.STRING, allowNull: true, diff --git a/backend/services/predefinedActivityService.js b/backend/services/predefinedActivityService.js index 8152d3a..2dd9cdd 100644 --- a/backend/services/predefinedActivityService.js +++ b/backend/services/predefinedActivityService.js @@ -15,6 +15,7 @@ class PredefinedActivityService { durationText: data.durationText, duration: data.duration, imageLink: data.imageLink, + drawingData: data.drawingData ? JSON.stringify(data.drawingData) : null, }); } @@ -32,6 +33,7 @@ class PredefinedActivityService { durationText: data.durationText, duration: data.duration, imageLink: data.imageLink, + drawingData: data.drawingData ? JSON.stringify(data.drawingData) : null, }); } diff --git a/frontend/src/components/CourtDrawingTool.vue b/frontend/src/components/CourtDrawingTool.vue index 065ecc9..0ec848f 100644 --- a/frontend/src/components/CourtDrawingTool.vue +++ b/frontend/src/components/CourtDrawingTool.vue @@ -176,7 +176,6 @@
-
@@ -196,6 +195,10 @@ export default { drawingData: { // Added for loading saved drawing data type: Object, default: null + }, + allowImageUpload: { // Neu: steuert, ob das Tool ein Bild erzeugt/hochlädt + type: Boolean, + default: true } }, data() { @@ -1092,7 +1095,7 @@ export default { } }, - async saveDrawing() { + async saveDrawing() { console.log('CourtDrawingTool: saveDrawing called'); try { @@ -1116,7 +1119,10 @@ export default { }; console.log('CourtDrawingTool: drawingData created:', drawingData); - + // Immer Metadaten nach oben geben + this.$emit('update-drawing-data', drawingData); + + if (this.allowImageUpload) { // Konvertiere DataURL zu Blob für Upload const response = await fetch(dataURL); const blob = await response.blob(); @@ -1132,9 +1138,15 @@ export default { console.log('CourtDrawingTool: emitting upload-image event'); this.$emit('upload-image', file, drawingData); console.log('CourtDrawingTool: upload-image event emitted'); - - // Emittiere das File für Upload (Parent-Komponente macht den Upload) - console.log('CourtDrawingTool: File ready for upload'); + } else { + // Kein Bild-Upload mehr: gebe lediglich die Zeichnungsdaten an den Parent weiter, + // damit Felder (Kürzel/Name/Beschreibung) gefüllt werden können + this.$emit('update-fields', { + code: this.getFullCode ? this.getFullCode() : '', + name: this.getFullTitle ? this.getFullTitle() : '', + description: '' + }); + } } catch (error) { console.error('CourtDrawingTool: Error in saveDrawing:', error); } diff --git a/frontend/src/views/PredefinedActivities.vue b/frontend/src/views/PredefinedActivities.vue index 4ca2b5b..ae71ad1 100644 --- a/frontend/src/views/PredefinedActivities.vue +++ b/frontend/src/views/PredefinedActivities.vue @@ -81,8 +81,10 @@ v-model="editModel.drawingData" :activity-id="editModel.id" :drawing-data="editModel.drawingData" + :allow-image-upload="false" @save="onDrawingSave" @update-fields="onUpdateFields" + @update-drawing-data="onUpdateDrawingData" @upload-image="onDrawingImageUpload" @image-uploaded="onImageUploaded" /> @@ -150,6 +152,15 @@ export default { } }, methods: { + parseDrawingData(value) { + if (!value) return null; + if (typeof value === 'object') return value; + try { return JSON.parse(value); } catch (e) { return null; } + }, + normalizeActivity(activity, images) { + const drawingData = this.parseDrawingData(activity && activity.drawingData); + return { ...(activity || {}), drawingData }; + }, async reload() { const r = await apiClient.get('/predefined-activities'); this.activities = r.data || []; @@ -159,23 +170,12 @@ export default { const r = await apiClient.get(`/predefined-activities/${a.id}`); const { images, ...activity } = r.data; this.images = images || []; - - // Lade Zeichnungsdaten aus dem ersten Bild, falls vorhanden - let drawingData = null; - if (images && images.length > 0 && images[0].drawingData) { - try { - drawingData = JSON.parse(images[0].drawingData); - console.log('PredefinedActivities: Loaded drawingData:', drawingData); - } catch (error) { - console.error('PredefinedActivities: Error parsing drawingData:', error); - } - } else { - // Keine Bilder vorhanden - setze drawingData explizit auf null - console.log('PredefinedActivities: No images found, setting drawingData to null'); - drawingData = null; + // Server-Daten normalisieren und ggf. image-drawingData fallbacken + let model = this.normalizeActivity(activity, images); + if (!model.drawingData && images && images.length > 0 && images[0].drawingData) { + model.drawingData = this.parseDrawingData(images[0].drawingData); } - - this.editModel = { ...activity, drawingData }; + this.editModel = model; }, async reloadImages() { if (this.editModel && this.editModel.id) { @@ -226,11 +226,14 @@ export default { if (this.editModel.id) { const { id, ...payload } = this.editModel; + if (payload.drawingData && typeof payload.drawingData === 'object') { + payload.drawingData = payload.drawingData; + } const r = await apiClient.put(`/predefined-activities/${id}`, payload); - this.editModel = r.data; + this.editModel = this.normalizeActivity(r.data); } else { const r = await apiClient.post('/predefined-activities', this.editModel); - this.editModel = r.data; + this.editModel = this.normalizeActivity(r.data); } // Nach dem Speichern (sowohl CREATE als auch UPDATE): Bild hochladen falls vorhanden @@ -328,6 +331,11 @@ export default { this.editModel.description = fields.description; } }, + onUpdateDrawingData(data) { + if (this.editModel) { + this.editModel.drawingData = data; + } + }, async onDrawingImageUpload(file, drawingData) { console.log('onDrawingImageUpload called with file:', file);