Add widget management functionality: Implement getAvailableWidgets method in dashboardService to retrieve widget types, and create corresponding API endpoint in dashboardRouter. Update LoggedInView to allow users to select and add widgets dynamically, enhancing dashboard customization options.

This commit is contained in:
Torsten Schulz (local)
2026-01-29 16:57:12 +01:00
parent 8d2db95540
commit c09159d6ce
8 changed files with 180 additions and 15 deletions

View File

@@ -13,9 +13,22 @@
Dashboard bearbeiten
</button>
<template v-else>
<button type="button" class="btn-add" @click="addWidget">
+ Widget hinzufügen
</button>
<div class="widget-add-row">
<select
v-model="selectedWidgetTypeId"
class="widget-type-select"
@change="onSelectWidgetType"
>
<option value="">+ Widget hinzufügen </option>
<option
v-for="wt in widgetTypeOptions"
:key="wt.id"
:value="wt.id"
>
{{ wt.label }}
</option>
</select>
</div>
<button type="button" class="btn-done" @click="doneEditing">
Fertig
</button>
@@ -106,9 +119,17 @@ function generateId() {
export default {
name: 'HomeLoggedInView',
components: { DashboardWidget },
computed: {
widgetTypeOptions() {
if (this.availableWidgets.length > 0) return this.availableWidgets;
return [{ id: 'default', label: 'Termine', endpoint: '/api/termine' }];
}
},
data() {
return {
widgets: [],
availableWidgets: [],
selectedWidgetTypeId: '',
loading: true,
loadError: null,
editMode: false,
@@ -118,12 +139,21 @@ export default {
},
mounted() {
this.loadConfig();
this.loadAvailableWidgets();
},
methods: {
...mapActions(['logout']),
handleLogout() {
this.logout();
},
async loadAvailableWidgets() {
try {
const { data } = await apiClient.get('/api/dashboard/widgets');
this.availableWidgets = Array.isArray(data) ? data : [];
} catch (e) {
this.availableWidgets = [];
}
},
async loadConfig() {
this.loading = true;
this.loadError = null;
@@ -150,13 +180,19 @@ export default {
console.error('Dashboard speichern fehlgeschlagen:', e);
}
},
addWidget() {
this.widgets.push({
id: generateId(),
title: 'Neues Widget',
endpoint: '/api/termine'
});
this.saveConfig();
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.selectedWidgetTypeId = '';
},
removeWidget(index) {
this.widgets.splice(index, 1);
@@ -250,14 +286,23 @@ export default {
}
.btn-edit:hover,
.btn-add:hover,
.btn-done:hover {
background: #f1f3f5;
}
.btn-add {
border-color: #0d6efd;
color: #0d6efd;
.widget-add-row {
display: flex;
align-items: center;
}
.widget-type-select {
padding: 8px 12px;
border-radius: 6px;
border: 1px solid #ced4da;
background: #fff;
color: #495057;
font-size: 0.9rem;
min-width: 180px;
}
.btn-done {