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 f74846f7e..4c25a32f9 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 @@ -30,6 +30,7 @@ import RecentContactsWidget from '$lib/modules/core/widgets/RecentContactsWidget import ActiveTimerWidget from '$lib/modules/core/widgets/ActiveTimerWidget.svelte'; 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 DayTimelineWidget from './widgets/DayTimelineWidget.svelte'; import ActivityFeedWidget from './widgets/ActivityFeedWidget.svelte'; @@ -56,4 +57,5 @@ export const widgetComponents: Record = { 'plant-watering': PlantWateringWidget, 'day-timeline': DayTimelineWidget, 'activity-feed': ActivityFeedWidget, + cycles: CyclesWidget, }; 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 a96a0d7f4..3573105e7 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 @@ -144,6 +144,12 @@ "title": "Aktivität", "description": "Letzte Änderungen über alle Module", "empty": "Noch keine Aktivität" + }, + "cycles": { + "title": "Zyklus", + "description": "Aktuelle Phase und Countdown zur nächsten Periode", + "empty": "Noch kein Zyklus erfasst.", + "open": "Öffnen" } } } 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 e1ead197e..7489e3a74 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 @@ -144,6 +144,12 @@ "title": "Activity", "description": "Recent changes across all modules", "empty": "No activity yet" + }, + "cycles": { + "title": "Cycle", + "description": "Current phase and countdown to next period", + "empty": "No cycle logged yet.", + "open": "Open" } } } 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 d62cec79b..7c6e63ef2 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 @@ -139,6 +139,12 @@ "title": "Mi Día", "description": "Línea temporal cronológica de todas las actividades", "empty": "Nada todavía hoy" + }, + "cycles": { + "title": "Ciclo", + "description": "Fase actual y cuenta regresiva hasta el próximo período", + "empty": "Ningún ciclo registrado.", + "open": "Abrir" } } } 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 c3a9f14a4..6ef7a9bb0 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 @@ -139,6 +139,12 @@ "title": "Ma Journée", "description": "Chronologie de toutes les activités de la journée", "empty": "Rien encore aujourd'hui" + }, + "cycles": { + "title": "Cycle", + "description": "Phase actuelle et compte à rebours jusqu'aux prochaines règles", + "empty": "Aucun cycle enregistré.", + "open": "Ouvrir" } } } 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 a62ef40c8..d8cf950bc 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 @@ -139,6 +139,12 @@ "title": "La Mia Giornata", "description": "Cronologia di tutte le attività della giornata", "empty": "Niente ancora oggi" + }, + "cycles": { + "title": "Ciclo", + "description": "Fase attuale e conto alla rovescia per il prossimo ciclo", + "empty": "Nessun ciclo registrato.", + "open": "Apri" } } } diff --git a/apps/mana/apps/web/src/lib/modules/core/widgets/CyclesWidget.svelte b/apps/mana/apps/web/src/lib/modules/core/widgets/CyclesWidget.svelte new file mode 100644 index 000000000..98a167936 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/core/widgets/CyclesWidget.svelte @@ -0,0 +1,134 @@ + + +
+
+

+ {$_('dashboard.widgets.cycles.title')} +

+ {#if phase !== 'unknown'} + + {$_(`cycles.phase.${phase}`)} + + {/if} +
+ + {#if loading} +
+ {:else if cycles.length === 0} +
+

+ {$_('dashboard.widgets.cycles.empty')} +

+ + {$_('dashboard.widgets.cycles.open')} + +
+ {:else} + +
+
+ {#if cycleDay} +

+ {$_('cycles.label.cycleDay')} + {cycleDay} +

+ {/if} + {#if daysUntil !== null} +

+ {#if daysUntil > 0} + {daysUntil} + {:else if daysUntil === 0} + {$_('cycles.label.today')} + {:else} + +{Math.abs(daysUntil)} + {/if} +

+

+ {#if daysUntil > 0} + {$_('cycles.label.daysUntilPeriod')} + {:else if daysUntil === 0} + {$_('cycles.label.predicted')} + {:else} + {$_('cycles.label.daysOverdue')} + {/if} +

+ {/if} +
+ {#if nextPeriod && daysUntil !== null && daysUntil >= 0} +
+

+ {$_('cycles.stats.nextPeriod')} +

+

{formatShortDate(nextPeriod)}

+
+ {/if} +
+
+ {/if} +
diff --git a/apps/mana/apps/web/src/lib/types/dashboard.test.ts b/apps/mana/apps/web/src/lib/types/dashboard.test.ts index 98a1b5e00..dbc1af3b3 100644 --- a/apps/mana/apps/web/src/lib/types/dashboard.test.ts +++ b/apps/mana/apps/web/src/lib/types/dashboard.test.ts @@ -55,6 +55,7 @@ describe('WIDGET_REGISTRY', () => { 'mana-auth', 'nutriphi', 'planta', + 'cycles', undefined, ]; for (const widget of WIDGET_REGISTRY) { diff --git a/apps/mana/apps/web/src/lib/types/dashboard.ts b/apps/mana/apps/web/src/lib/types/dashboard.ts index 978366a07..5b1d4f586 100644 --- a/apps/mana/apps/web/src/lib/types/dashboard.ts +++ b/apps/mana/apps/web/src/lib/types/dashboard.ts @@ -29,7 +29,8 @@ export type WidgetType = | 'nutrition-progress' // NutriPhi: today's calorie progress | 'plant-watering' // Planta: plants due for watering | 'day-timeline' // TimeBlocks: chronological day timeline - | 'activity-feed'; // TimeBlocks: recent activity across modules + | 'activity-feed' // TimeBlocks: recent activity across modules + | 'cycles'; // Cycles: current phase + days until next period /** * Widget size - maps to CSS Grid columns @@ -129,6 +130,7 @@ export interface WidgetMeta { | 'times' | 'nutriphi' | 'planta' + | 'cycles' | 'mana-auth'; } @@ -331,6 +333,15 @@ export const WIDGET_REGISTRY: WidgetMeta[] = [ defaultSize: 'medium', allowMultiple: false, }, + { + type: 'cycles', + nameKey: 'dashboard.widgets.cycles.title', + descriptionKey: 'dashboard.widgets.cycles.description', + icon: '🌸', + defaultSize: 'small', + allowMultiple: false, + requiredBackend: 'cycles', + }, ]; /**