From e579e292cc3ea02690b4d11ca46966e43396e4af Mon Sep 17 00:00:00 2001 From: Till JS Date: Thu, 9 Apr 2026 15:54:57 +0200 Subject: [PATCH] feat(mana/web/news): workbench ListView + dashboard widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Surfaces News in two extra entry points beyond the dedicated /news route. The workbench ListView is a compact ranked-feed view designed for the AppPage carousel slot — it boots the same feed-cache poll, runs the same scoreArticle pipeline, but renders smaller cards and skips the onboarding wizard (un-onboarded users get a CTA pointing them at /news instead). The NewsUnreadWidget shows the top three ranked unread articles on the dashboard, sharing the exact same engine inputs so the ordering matches the main feed. WidgetType + WIDGET_REGISTRY get the new 'news-unread' entry, and dashboard.widgets.news_unread is added to all five locale files. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../components/dashboard/widget-registry.ts | 2 + .../src/lib/i18n/locales/dashboard/de.json | 4 + .../src/lib/i18n/locales/dashboard/en.json | 4 + .../src/lib/i18n/locales/dashboard/es.json | 4 + .../src/lib/i18n/locales/dashboard/fr.json | 4 + .../src/lib/i18n/locales/dashboard/it.json | 4 + .../web/src/lib/modules/news/ListView.svelte | 360 ++++++++++++++++++ .../news/widgets/NewsUnreadWidget.svelte | 129 +++++++ apps/mana/apps/web/src/lib/types/dashboard.ts | 11 +- 9 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 apps/mana/apps/web/src/lib/modules/news/ListView.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/news/widgets/NewsUnreadWidget.svelte diff --git a/apps/mana/apps/web/src/lib/components/dashboard/widget-registry.ts b/apps/mana/apps/web/src/lib/components/dashboard/widget-registry.ts index 4c25a32f9..a80a0b8a2 100644 --- a/apps/mana/apps/web/src/lib/components/dashboard/widget-registry.ts +++ b/apps/mana/apps/web/src/lib/components/dashboard/widget-registry.ts @@ -31,6 +31,7 @@ import ActiveTimerWidget from '$lib/modules/core/widgets/ActiveTimerWidget.svelt import NutritionProgressWidget from '$lib/modules/core/widgets/NutritionProgressWidget.svelte'; import PlantWateringWidget from '$lib/modules/core/widgets/PlantWateringWidget.svelte'; import CyclesWidget from '$lib/modules/core/widgets/CyclesWidget.svelte'; +import NewsUnreadWidget from '$lib/modules/news/widgets/NewsUnreadWidget.svelte'; import DayTimelineWidget from './widgets/DayTimelineWidget.svelte'; import ActivityFeedWidget from './widgets/ActivityFeedWidget.svelte'; @@ -58,4 +59,5 @@ export const widgetComponents: Record = { 'day-timeline': DayTimelineWidget, 'activity-feed': ActivityFeedWidget, cycles: CyclesWidget, + 'news-unread': NewsUnreadWidget, }; diff --git a/apps/mana/apps/web/src/lib/i18n/locales/dashboard/de.json b/apps/mana/apps/web/src/lib/i18n/locales/dashboard/de.json index 3573105e7..2d5b4b673 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/dashboard/de.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/dashboard/de.json @@ -150,6 +150,10 @@ "description": "Aktuelle Phase und Countdown zur nächsten Periode", "empty": "Noch kein Zyklus erfasst.", "open": "Öffnen" + }, + "news_unread": { + "title": "News", + "description": "Top-Artikel aus deinem kuratierten Feed" } } } diff --git a/apps/mana/apps/web/src/lib/i18n/locales/dashboard/en.json b/apps/mana/apps/web/src/lib/i18n/locales/dashboard/en.json index 7489e3a74..14bcd9bfd 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/dashboard/en.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/dashboard/en.json @@ -150,6 +150,10 @@ "description": "Current phase and countdown to next period", "empty": "No cycle logged yet.", "open": "Open" + }, + "news_unread": { + "title": "News", + "description": "Top articles from your curated feed" } } } diff --git a/apps/mana/apps/web/src/lib/i18n/locales/dashboard/es.json b/apps/mana/apps/web/src/lib/i18n/locales/dashboard/es.json index 7c6e63ef2..e1ad3765b 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/dashboard/es.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/dashboard/es.json @@ -145,6 +145,10 @@ "description": "Fase actual y cuenta regresiva hasta el próximo período", "empty": "Ningún ciclo registrado.", "open": "Abrir" + }, + "news_unread": { + "title": "Noticias", + "description": "Artículos destacados de tu feed curado" } } } diff --git a/apps/mana/apps/web/src/lib/i18n/locales/dashboard/fr.json b/apps/mana/apps/web/src/lib/i18n/locales/dashboard/fr.json index 6ef7a9bb0..5a2c055c2 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/dashboard/fr.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/dashboard/fr.json @@ -145,6 +145,10 @@ "description": "Phase actuelle et compte à rebours jusqu'aux prochaines règles", "empty": "Aucun cycle enregistré.", "open": "Ouvrir" + }, + "news_unread": { + "title": "Actualités", + "description": "Articles phares de ton fil personnalisé" } } } diff --git a/apps/mana/apps/web/src/lib/i18n/locales/dashboard/it.json b/apps/mana/apps/web/src/lib/i18n/locales/dashboard/it.json index d8cf950bc..2ca19bc21 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/dashboard/it.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/dashboard/it.json @@ -145,6 +145,10 @@ "description": "Fase attuale e conto alla rovescia per il prossimo ciclo", "empty": "Nessun ciclo registrato.", "open": "Apri" + }, + "news_unread": { + "title": "Notizie", + "description": "Articoli in evidenza dal tuo feed curato" } } } diff --git a/apps/mana/apps/web/src/lib/modules/news/ListView.svelte b/apps/mana/apps/web/src/lib/modules/news/ListView.svelte new file mode 100644 index 000000000..d86ef08df --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/news/ListView.svelte @@ -0,0 +1,360 @@ + + + +
+ {#if !prefs.onboardingCompleted} +
+

News Hub einrichten

+

+ Wähle Themen, Sprachen und Quellen — danach erscheinen hier deine Artikel. +

+ Jetzt einrichten +
+ {:else} +
+
+ {ranked.length} Artikel + {#if feedCacheStore.lastError} + · Fehler + {/if} +
+
+ + 📑 + +
+
+ + {#if ranked.length === 0} +
+ {#if pool.length === 0} +

Lade Artikel…

+ {:else} +

Keine neuen Artikel.

+ + {/if} +
+ {:else} +
    + {#each ranked.slice(0, 30) as { article } (article.id)} +
  • + {#if article.imageUrl} + + {/if} +
    +
    + {article.siteName} + · + {formatRelativeTime(article.publishedAt)} +
    + +
    + + + +
    +
    +
  • + {/each} +
+ {/if} + {/if} +
+ + diff --git a/apps/mana/apps/web/src/lib/modules/news/widgets/NewsUnreadWidget.svelte b/apps/mana/apps/web/src/lib/modules/news/widgets/NewsUnreadWidget.svelte new file mode 100644 index 000000000..799074725 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/news/widgets/NewsUnreadWidget.svelte @@ -0,0 +1,129 @@ + + +
+
+

+ + News +

+ Alle → +
+ + {#if loading} +
+ {#each Array(3) as _} +
+ {/each} +
+ {:else if !onboardingDone} +
+

Richte deinen Newsfeed ein.

+ + Jetzt starten + +
+ {:else if topThree.length === 0} +
+

Keine neuen Artikel.

+ + Feed öffnen + +
+ {:else} + + {/if} +
diff --git a/apps/mana/apps/web/src/lib/types/dashboard.ts b/apps/mana/apps/web/src/lib/types/dashboard.ts index 5b1d4f586..dc365401a 100644 --- a/apps/mana/apps/web/src/lib/types/dashboard.ts +++ b/apps/mana/apps/web/src/lib/types/dashboard.ts @@ -30,7 +30,8 @@ export type WidgetType = | 'plant-watering' // Planta: plants due for watering | 'day-timeline' // TimeBlocks: chronological day timeline | 'activity-feed' // TimeBlocks: recent activity across modules - | 'cycles'; // Cycles: current phase + days until next period + | 'cycles' // Cycles: current phase + days until next period + | 'news-unread'; // News: latest unread curated articles /** * Widget size - maps to CSS Grid columns @@ -342,6 +343,14 @@ export const WIDGET_REGISTRY: WidgetMeta[] = [ allowMultiple: false, requiredBackend: 'cycles', }, + { + type: 'news-unread', + nameKey: 'dashboard.widgets.news_unread.title', + descriptionKey: 'dashboard.widgets.news_unread.description', + icon: '📰', + defaultSize: 'small', + allowMultiple: false, + }, ]; /**