From eede4a9708ad22ae2644daba58ddf496e9c23dff Mon Sep 17 00:00:00 2001 From: "Konrad M." Date: Tue, 21 Apr 2026 21:57:26 +0200 Subject: [PATCH] fix(dashboard): flatten header; replace greeting with date+time; split overdue/soon chips Header shows current date and time instead of user name + separate date line. urgentCount replaced by overdueCount (overdue tasks) and dueSoonCount (due today/soon), each with a distinct chip color. formatDueDate updated to accept due_time and return accurate overdue/soon states against the current moment. dashboard grid expands to 4 columns at 1280px instead of 1440px. --- public/pages/dashboard.js | 79 +++++++++++++++++++++++++++---------- public/styles/dashboard.css | 32 +++++++++++---- 2 files changed, 83 insertions(+), 28 deletions(-) diff --git a/public/pages/dashboard.js b/public/pages/dashboard.js index c8bd0b9..dbc22ae 100644 --- a/public/pages/dashboard.js +++ b/public/pages/dashboard.js @@ -66,20 +66,44 @@ function formatDateTime(isoString) { return `${dateStr}, ${timeStr}${suffix ? ' ' + suffix : ''}`.trim(); } -function formatDueDate(dateStr) { +function formatDueDate(dateStr, timeStr) { if (!dateStr) return null; - const due = new Date(dateStr); + + const dueDate = timeStr + ? new Date(`${dateStr}T${timeStr}`) + : new Date(`${dateStr}T23:59:59`); + + if (isNaN(dueDate)) return null; + const now = new Date(); - const diffMs = due - now; + const diffMs = dueDate - now; const diffH = diffMs / (1000 * 60 * 60); - if (diffMs < 0) return { text: t('dashboard.overdue'), overdue: true }; - if (diffH < 24) return { text: t('dashboard.dueSoon'), overdue: false }; - if (diffH < 48) return { text: t('dashboard.dueTomorrow'), overdue: false }; - return { - text: formatDate(due), - overdue: false, - }; + const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const dueDay = new Date(dueDate.getFullYear(), dueDate.getMonth(), dueDate.getDate()); + const calDayDiff = Math.round((dueDay - today) / (1000 * 60 * 60 * 24)); + + const fullLabel = timeStr + ? `${formatDate(dueDate)}, ${formatTime(dueDate)}` // beide aus i18n.js + : formatDate(dueDate); + + if (diffMs < 0) { + return { text: `${t('dashboard.overdue')} – ${fullLabel}`, overdue: true }; + } + + if (calDayDiff === 1 && dueDate.getHours() >= 22 && diffH < 24) { + return { text: `${t('dashboard.dueSoon')} – ${fullLabel}`, overdue: false, soon: true }; + } + + if (calDayDiff === 0) { + return { text: `${t('dashboard.dueToday')} – ${formatTime(dueDate)}`, overdue: false, soon: true }; + } + + if (calDayDiff === 1) { + return { text: `${t('dashboard.dueTomorrow')} – ${formatTime(dueDate)}`, overdue: false }; + } + + return { text: fullLabel, overdue: false }; } const PRIORITY_LABELS = () => ({ @@ -147,13 +171,18 @@ function skeletonWidget(lines = 3) { // -------------------------------------------------------- function renderGreeting(user, stats = {}) { - const { urgentCount = 0, todayEventCount = 0, todayMealTitle = null } = stats; + const { overdueCount = 0, dueSoonCount = 0, todayEventCount = 0, todayMealTitle = null } = stats; const statChips = []; - if (urgentCount > 0) + if (overdueCount > 0) statChips.push(` - ${urgentCount > 1 ? t('dashboard.urgentTasksChipPlural', { count: urgentCount }) : t('dashboard.urgentTasksChip', { count: urgentCount })} + ${overdueCount > 1 ? t('dashboard.overdueTasksChipPlural', { count: overdueCount }) : t('dashboard.overdueTasksChip', { count: overdueCount })} + `); + if (dueSoonCount > 0) + statChips.push(` + + ${dueSoonCount > 1 ? t('dashboard.urgentTasksChipPlural', { count: dueSoonCount }) : t('dashboard.urgentTasksChip', { count: dueSoonCount })} `); if (todayEventCount > 0) statChips.push(` @@ -166,12 +195,13 @@ function renderGreeting(user, stats = {}) { ${t('dashboard.todayMealChip', { title: esc(todayMealTitle) })} `); + let time = new Date().toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }); + return `
-
${greeting(user.display_name)}
-
${formatDate(new Date())}
+
${formatDate(new Date())} - ${time}
${statChips.length ? `
${statChips.join('')}
` : ''}