From 4779a6e4afc5881a04f2cbe44283effa415e0cdd Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Fri, 30 Jan 2026 07:31:38 +0100 Subject: [PATCH] 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. --- backend/services/dashboardService.js | 11 ++- backend/services/newsService.js | 25 +++--- backend/utils/syncDatabase.js | 17 ++++ frontend/src/views/home/LoggedInView.vue | 109 ++++++++++++++++------- 4 files changed, 114 insertions(+), 48 deletions(-) diff --git a/backend/services/dashboardService.js b/backend/services/dashboardService.js index 267bdd4..b3277ec 100644 --- a/backend/services/dashboardService.js +++ b/backend/services/dashboardService.js @@ -45,10 +45,13 @@ class DashboardService extends BaseService { title: String(w?.title ?? ''), endpoint: String(w?.endpoint ?? '') })).filter(w => w.id && (w.title || w.endpoint)); - await UserDashboard.upsert({ - userId: user.id, - config: { widgets: sanitized } - }, { conflictFields: ['userId'] }); + const payload = { widgets: sanitized }; + const existing = await UserDashboard.findOne({ where: { userId: user.id } }); + if (existing) { + await existing.update({ config: payload }); + } else { + await UserDashboard.create({ userId: user.id, config: payload }); + } return { widgets: sanitized }; } } diff --git a/backend/services/newsService.js b/backend/services/newsService.js index 2cc1353..36ad4f6 100644 --- a/backend/services/newsService.js +++ b/backend/services/newsService.js @@ -38,33 +38,34 @@ async function fetchNewsPage({ counter, language = 'de', category = 'top', nextP } /** - * Liefert die Seite für das „counter.“-te Widget (0 = erste Seite, 1 = zweite, …). - * Ruft die API ggf. mehrfach auf (nextPage), damit jede Widget-Instanz andere Artikel bekommt. + * Liefert den N-ten Artikel (counter = 0, 1, 2, …) für das N-te News-Widget. + * Lädt ggf. mehrere Seiten, bis genug Artikel vorhanden sind; jede Widget-Instanz bekommt einen anderen Artikel. * * @param {object} options - * @param {number} options.counter + * @param {number} options.counter - Index des Artikels (0 = erster, 1 = zweiter, …) * @param {string} [options.language] * @param {string} [options.category] * @returns {Promise<{ results: Array, nextPage: string|null }>} */ async function getNews({ counter = 0, language = 'de', category = 'top' }) { + const neededIndex = Math.max(0, counter); + const collected = []; let nextPageToken = null; - let lastResult = { results: [], nextPage: null }; - for (let i = 0; i <= counter; i++) { - lastResult = await fetchNewsPage({ - counter: i, + while (collected.length <= neededIndex) { + const page = await fetchNewsPage({ language, category, nextPageToken: nextPageToken || undefined }); - nextPageToken = lastResult.nextPage; - if (!nextPageToken && i < counter) { - break; - } + const items = page.results ?? []; + collected.push(...items); + nextPageToken = page.nextPage ?? null; + if (items.length === 0 || !nextPageToken) break; } - return lastResult; + const single = collected[neededIndex] ? [collected[neededIndex]] : []; + return { results: single, nextPage: null }; } export default { getNews }; diff --git a/backend/utils/syncDatabase.js b/backend/utils/syncDatabase.js index 66c0780..3a3dfd3 100644 --- a/backend/utils/syncDatabase.js +++ b/backend/utils/syncDatabase.js @@ -108,6 +108,23 @@ const syncDatabase = async () => { console.warn('⚠️ Konnte type.widget_type nicht anlegen:', e?.message || e); } + // Dashboard: Benutzer-Konfiguration (Widgets pro User) + console.log("Ensuring user_dashboard table exists..."); + try { + await sequelize.query(` + CREATE TABLE IF NOT EXISTS community.user_dashboard ( + user_id INTEGER NOT NULL PRIMARY KEY, + config JSONB NOT NULL DEFAULT '{"widgets":[]}'::jsonb, + CONSTRAINT user_dashboard_user_fk + FOREIGN KEY (user_id) + REFERENCES community."user"(id) + ON DELETE CASCADE + ); + `); + } catch (e) { + console.warn('⚠️ Konnte community.user_dashboard nicht anlegen:', e?.message || e); + } + // Vokabeltrainer: Tabellen sicherstellen (auch ohne manuell ausgeführte Migrations) // Hintergrund: In Produktion sind Schema-Updates deaktiviert, und Migrations werden nicht automatisch ausgeführt. // Damit API/Menu nicht mit "relation does not exist" (42P01) scheitert, legen wir die Tabellen idempotent an. diff --git a/frontend/src/views/home/LoggedInView.vue b/frontend/src/views/home/LoggedInView.vue index 152f06e..5898d4a 100644 --- a/frontend/src/views/home/LoggedInView.vue +++ b/frontend/src/views/home/LoggedInView.vue @@ -28,6 +28,14 @@ {{ wt.label }} +