diff --git a/public/pages/tasks.js b/public/pages/tasks.js
index 47eb400..0cab2b0 100644
--- a/public/pages/tasks.js
+++ b/public/pages/tasks.js
@@ -24,6 +24,8 @@ const PRIORITIES = () => [
{ value: 'none', label: t('tasks.priorityNone'), color: 'var(--color-priority-none)' },
];
+const PRIO_ORDER = { urgent: 0, high: 1, medium: 2, low: 3, none: 4 };
+
const STATUSES = () => [
{ value: 'open', label: t('tasks.statusOpen') },
{ value: 'in_progress', label: t('tasks.statusInProgress') },
@@ -228,6 +230,28 @@ function renderTaskCard(task, opts = {}) {
`;
}
+// Effektive Fälligkeit: mit due_time wenn vorhanden, sonst 23:59:59 des Tages
+function effectiveDue(task) {
+ if (!task.due_date) return null;
+ return task.due_time
+ ? new Date(`${task.due_date}T${task.due_time}`)
+ : new Date(`${task.due_date}T23:59:59`);
+}
+
+// Einheitliche Sortierung: überfällig zuerst → Datum/Zeit ASC → Prio als Tiebreaker
+function sortTasks(a, b, now) {
+ const aDate = effectiveDue(a);
+ const bDate = effectiveDue(b);
+ const aOver = aDate && aDate < now ? 1 : 0;
+ const bOver = bDate && bDate < now ? 1 : 0;
+ if (bOver !== aOver) return bOver - aOver;
+ if (!aDate && !bDate) return (PRIO_ORDER[a.priority] ?? 4) - (PRIO_ORDER[b.priority] ?? 4);
+ if (!aDate) return 1;
+ if (!bDate) return -1;
+ if (aDate.getTime() !== bDate.getTime()) return aDate < bDate ? -1 : 1;
+ return (PRIO_ORDER[a.priority] ?? 4) - (PRIO_ORDER[b.priority] ?? 4);
+}
+
function renderTaskGroups(tasks, groupMode) {
if (!tasks.length) {
return `
@@ -240,16 +264,20 @@ function renderTaskGroups(tasks, groupMode) {
`;
}
- const groups = groupBy(tasks, groupMode);
+ const now = new Date();
const catLabelsMap = CATEGORY_LABELS();
- return groups.map(([name, groupTasks]) => `
+ const groups = groupBy(tasks, groupMode);
+ return groups.map(([name, groupTasks]) => {
+ const sorted = [...groupTasks].sort((a, b) => sortTasks(a, b, now));
+ return `
- ${groupTasks.map((t) => renderSwipeRow(t, renderTaskCard(t))).join('')}
-
`).join('');
+ ${sorted.map((t) => renderSwipeRow(t, renderTaskCard(t))).join('')}
+ `;
+ }).join('');
}
// --------------------------------------------------------
@@ -638,6 +666,11 @@ function renderKanban(container) {
else grouped['open'].push(t);
}
+ const now = new Date();
+ for (const col of cols) {
+ grouped[col.status].sort((a, b) => sortTasks(a, b, now));
+ }
+
listEl.innerHTML = `
${cols.map((col) => `
diff --git a/server/routes/dashboard.js b/server/routes/dashboard.js
index 8101c9a..7e36483 100644
--- a/server/routes/dashboard.js
+++ b/server/routes/dashboard.js
@@ -42,9 +42,12 @@ router.get('/', (req, res) => {
SELECT
ce.*,
u.display_name AS assigned_name,
- u.avatar_color AS assigned_color
+ u.avatar_color AS assigned_color,
+ ec.name AS cal_name,
+ ec.color AS cal_color
FROM calendar_events ce
- LEFT JOIN users u ON ce.assigned_to = u.id
+ LEFT JOIN users u ON ce.assigned_to = u.id
+ LEFT JOIN external_calendars ec ON ec.id = ce.calendar_ref_id
WHERE ce.start_datetime >= ?
ORDER BY ce.start_datetime ASC
LIMIT 5
@@ -54,27 +57,39 @@ router.get('/', (req, res) => {
result.upcomingEvents = [];
}
- // Offene Aufgaben: alle nicht-erledigten, sortiert nach Priorität und Fälligkeit
+ // Offene Aufgaben: in JS sortiert damit due_time korrekt gegen lokale Zeit geprüft wird
try {
- result.urgentTasks = d.prepare(`
- SELECT
- t.*,
- u.display_name AS assigned_name,
- u.avatar_color AS assigned_color
+ const allOpen = d.prepare(`
+ SELECT t.*, u.display_name AS assigned_name, u.avatar_color AS assigned_color
FROM tasks t
LEFT JOIN users u ON t.assigned_to = u.id
WHERE t.status != 'done'
- ORDER BY
- CASE t.priority
- WHEN 'urgent' THEN 0
- WHEN 'high' THEN 1
- WHEN 'medium' THEN 2
- WHEN 'low' THEN 3
- ELSE 4
- END,
- t.due_date ASC NULLS LAST
- LIMIT 5
`).all();
+
+ const PRIO = { urgent: 0, high: 1, medium: 2, low: 3, none: 4 };
+ const now = new Date();
+
+ function effectiveDue(task) {
+ if (!task.due_date) return null;
+ return task.due_time
+ ? new Date(`${task.due_date}T${task.due_time}`)
+ : new Date(`${task.due_date}T23:59:59`);
+ }
+
+ allOpen.sort((a, b) => {
+ const aDate = effectiveDue(a);
+ const bDate = effectiveDue(b);
+ const aOver = aDate && aDate < now ? 1 : 0;
+ const bOver = bDate && bDate < now ? 1 : 0;
+ if (bOver !== aOver) return bOver - aOver;
+ if (!aDate && !bDate) return (PRIO[a.priority] ?? 4) - (PRIO[b.priority] ?? 4);
+ if (!aDate) return 1;
+ if (!bDate) return -1;
+ if (aDate.getTime() !== bDate.getTime()) return aDate < bDate ? -1 : 1;
+ return (PRIO[a.priority] ?? 4) - (PRIO[b.priority] ?? 4);
+ });
+
+ result.urgentTasks = allOpen.slice(0, 5);
} catch (err) {
log.error('urgentTasks-Fehler:', err.message);
result.urgentTasks = [];