Refactor dashboard widget management: Update dashboardService to handle user-specific widget configurations with create and update logic. Enhance LoggedInView to support adding the same widget type and display error messages for save operations. Ensure effective endpoint handling for widgets and improve UI interactions.
This commit is contained in:
@@ -28,6 +28,14 @@
|
||||
{{ wt.label }}
|
||||
</option>
|
||||
</select>
|
||||
<button
|
||||
v-if="selectedWidgetTypeId"
|
||||
type="button"
|
||||
class="btn-add-again"
|
||||
@click="addSameWidgetType"
|
||||
>
|
||||
Nochmal hinzufügen
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="btn-done" @click="doneEditing">
|
||||
Fertig
|
||||
@@ -42,6 +50,12 @@
|
||||
>
|
||||
{{ loadError }}
|
||||
</div>
|
||||
<div
|
||||
v-if="saveError"
|
||||
class="dashboard-message dashboard-error"
|
||||
>
|
||||
{{ saveError }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="dashboard-grid"
|
||||
@@ -60,7 +74,7 @@
|
||||
v-if="!editMode"
|
||||
:widget-id="w.id"
|
||||
:title="w.title"
|
||||
:endpoint="w.endpoint"
|
||||
:endpoint="effectiveEndpoint(w)"
|
||||
:request-counter="widgetRequestCounter(index)"
|
||||
@drag-start="() => (draggedIndex = index)"
|
||||
@drag-end="() => (draggedIndex = null)"
|
||||
@@ -73,12 +87,6 @@
|
||||
placeholder="Titel"
|
||||
class="widget-edit-input"
|
||||
/>
|
||||
<input
|
||||
v-model="w.endpoint"
|
||||
type="text"
|
||||
placeholder="Endpoint (z. B. /api/termine)"
|
||||
class="widget-edit-input widget-edit-endpoint"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
@@ -129,6 +137,7 @@ export default {
|
||||
selectedWidgetTypeId: '',
|
||||
loading: true,
|
||||
loadError: null,
|
||||
saveError: null,
|
||||
editMode: false,
|
||||
draggedIndex: null,
|
||||
dragOverIndex: null
|
||||
@@ -139,13 +148,19 @@ export default {
|
||||
this.loadAvailableWidgets();
|
||||
},
|
||||
methods: {
|
||||
/** Endpoint aus Widget-Typ (anhand gespeichertem endpoint gematcht), sonst w.endpoint. */
|
||||
effectiveEndpoint(w) {
|
||||
if (!w?.endpoint) return '';
|
||||
const t = this.availableWidgets.find(wt => wt.endpoint === w.endpoint);
|
||||
return t ? t.endpoint : w.endpoint;
|
||||
},
|
||||
/** Counter für EP: wievieltes Widget mit gleichem Endpoint (0, 1, 2, …), damit z. B. News nicht doppelt. */
|
||||
widgetRequestCounter(index) {
|
||||
const endpoint = this.widgets[index]?.endpoint;
|
||||
if (endpoint == null) return undefined;
|
||||
const endpoint = this.effectiveEndpoint(this.widgets[index]);
|
||||
if (!endpoint) return undefined;
|
||||
let count = 0;
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (this.widgets[i].endpoint === endpoint) count++;
|
||||
if (this.effectiveEndpoint(this.widgets[i]) === endpoint) count++;
|
||||
}
|
||||
return count;
|
||||
},
|
||||
@@ -177,33 +192,52 @@ export default {
|
||||
}
|
||||
},
|
||||
async saveConfig() {
|
||||
this.saveError = null;
|
||||
try {
|
||||
await apiClient.put('/api/dashboard/config', { widgets: this.widgets });
|
||||
const payload = this.widgets.map(w => ({
|
||||
id: w.id,
|
||||
title: w.title,
|
||||
endpoint: this.effectiveEndpoint(w)
|
||||
}));
|
||||
await apiClient.put('/api/dashboard/config', { widgets: payload });
|
||||
} catch (e) {
|
||||
console.error('Dashboard speichern fehlgeschlagen:', e);
|
||||
this.saveError = e.response?.data?.error || e.message || 'Dashboard konnte nicht gespeichert werden.';
|
||||
}
|
||||
},
|
||||
onSelectWidgetType() {
|
||||
addWidgetFromType(wt) {
|
||||
this.widgets.push({
|
||||
id: generateId(),
|
||||
title: wt.label,
|
||||
endpoint: wt.endpoint
|
||||
});
|
||||
},
|
||||
async onSelectWidgetType() {
|
||||
const id = this.selectedWidgetTypeId;
|
||||
if (!id) return;
|
||||
const wt = this.widgetTypeOptions.find(w => String(w.id) === String(id));
|
||||
if (wt) {
|
||||
this.widgets.push({
|
||||
id: generateId(),
|
||||
title: wt.label,
|
||||
endpoint: wt.endpoint
|
||||
});
|
||||
this.saveConfig();
|
||||
this.addWidgetFromType(wt);
|
||||
await this.saveConfig();
|
||||
}
|
||||
this.selectedWidgetTypeId = '';
|
||||
},
|
||||
removeWidget(index) {
|
||||
this.widgets.splice(index, 1);
|
||||
this.saveConfig();
|
||||
async addSameWidgetType() {
|
||||
const id = this.selectedWidgetTypeId;
|
||||
if (!id) return;
|
||||
const wt = this.widgetTypeOptions.find(w => String(w.id) === String(id));
|
||||
if (wt) {
|
||||
this.addWidgetFromType(wt);
|
||||
await this.saveConfig();
|
||||
}
|
||||
},
|
||||
doneEditing() {
|
||||
async removeWidget(index) {
|
||||
this.widgets.splice(index, 1);
|
||||
await this.saveConfig();
|
||||
},
|
||||
async doneEditing() {
|
||||
this.editMode = false;
|
||||
this.saveConfig();
|
||||
await this.saveConfig();
|
||||
},
|
||||
setDropTarget(index) {
|
||||
this.dragOverIndex = index;
|
||||
@@ -214,7 +248,7 @@ export default {
|
||||
onGridDragover() {
|
||||
this.dragOverIndex = this.widgets.length;
|
||||
},
|
||||
onGridDrop() {
|
||||
async onGridDrop() {
|
||||
if (this.draggedIndex == null) return;
|
||||
const from = this.draggedIndex;
|
||||
const to = this.widgets.length;
|
||||
@@ -227,9 +261,9 @@ export default {
|
||||
this.widgets.splice(to, 0, item);
|
||||
this.draggedIndex = null;
|
||||
this.dragOverIndex = null;
|
||||
this.saveConfig();
|
||||
await this.saveConfig();
|
||||
},
|
||||
onDrop(toIndex) {
|
||||
async onDrop(toIndex) {
|
||||
if (this.draggedIndex == null) return;
|
||||
const from = this.draggedIndex;
|
||||
const to = toIndex;
|
||||
@@ -242,7 +276,7 @@ export default {
|
||||
this.widgets.splice(to, 0, item);
|
||||
this.draggedIndex = null;
|
||||
this.dragOverIndex = null;
|
||||
this.saveConfig();
|
||||
await this.saveConfig();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -297,6 +331,22 @@ export default {
|
||||
.widget-add-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn-add-again {
|
||||
padding: 8px 14px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--color-text-secondary);
|
||||
background: #fff;
|
||||
color: var(--color-text-primary);
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-add-again:hover {
|
||||
background: var(--color-primary-orange-light);
|
||||
border-color: var(--color-primary-orange);
|
||||
}
|
||||
|
||||
.widget-type-select {
|
||||
@@ -361,11 +411,6 @@ export default {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.widget-edit-endpoint {
|
||||
font-family: monospace;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.btn-remove {
|
||||
align-self: flex-start;
|
||||
padding: 6px 12px;
|
||||
|
||||
Reference in New Issue
Block a user