Fix: CardDAV sync broke when todo items didn't have a creation date. Also fixed todos without a due date not being displays and wrong calculation of warning badge states.
This commit is contained in:
@@ -23,46 +23,69 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
// Define a type for the key-value pairs
|
||||
type GroupedTodos = Array<{ key: string; todos: Todo[] }>;
|
||||
|
||||
const groupedTodos = computed(() => {
|
||||
const groups: Record<string, Todo[]> = {};
|
||||
|
||||
if (todos.value) {
|
||||
for (let todo of todos.value) {
|
||||
if (!todo.dueDate) continue
|
||||
if (todo.dueDate) {
|
||||
let dueDate = new Date(todo.dueDate)
|
||||
|
||||
let dueDate = new Date(todo.dueDate)
|
||||
|
||||
// today
|
||||
if (isToday(dueDate)) {
|
||||
if (!groups['today']) groups['today'] = []
|
||||
groups['today'].push(todo)
|
||||
}
|
||||
// tomorrow
|
||||
else if (daysFromNow(dueDate) === 1) {
|
||||
if (!groups['tomorrow']) groups['tomorrow'] = []
|
||||
groups['tomorrow'].push(todo)
|
||||
}
|
||||
// overdue
|
||||
else if (daysFromNow(dueDate) < 0) {
|
||||
if (!groups['overdue']) groups['overdue'] = []
|
||||
groups['overdue'].push(todo)
|
||||
}
|
||||
// by month
|
||||
else {
|
||||
let month = dueDate.toLocaleDateString('de-DE', { month: 'long' })
|
||||
if (!groups[month]) groups[month] = []
|
||||
groups[month].push(todo)
|
||||
// today
|
||||
if (isToday(dueDate)) {
|
||||
if (!groups['today']) groups['today'] = []
|
||||
groups['today'].push(todo)
|
||||
}
|
||||
// tomorrow
|
||||
else if (daysFromNow(dueDate) === 1) {
|
||||
if (!groups['tomorrow']) groups['tomorrow'] = []
|
||||
groups['tomorrow'].push(todo)
|
||||
}
|
||||
// overdue
|
||||
else if (daysFromNow(dueDate) < 0) {
|
||||
if (!groups['overdue']) groups['overdue'] = []
|
||||
groups['overdue'].push(todo)
|
||||
}
|
||||
// by month
|
||||
else {
|
||||
let month = dueDate.toLocaleDateString('de-DE', { month: 'long' })
|
||||
if (!groups[month]) groups[month] = []
|
||||
groups[month].push(todo)
|
||||
}
|
||||
} else {
|
||||
if (!groups['noDueDate']) groups['noDueDate'] = []
|
||||
groups['noDueDate'].push(todo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return groups
|
||||
// Convert the groupedTodos object to an array of key-value pairs
|
||||
const entries = Object.entries(groups);
|
||||
|
||||
// Sort the entries so that 'noDueDate' is always last
|
||||
entries.sort((a, b) => {
|
||||
if (a[0] === 'noDueDate') return 1;
|
||||
if (b[0] === 'noDueDate') return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Convert the sorted array to an array of key-value pairs
|
||||
const sortedGroupedTodos: GroupedTodos = entries.map(([key, todos]) => ({
|
||||
key,
|
||||
todos,
|
||||
}));
|
||||
|
||||
return sortedGroupedTodos;
|
||||
})
|
||||
|
||||
const groupNameForKey = (key: string) => {
|
||||
if (key === 'today') return 'Heute'
|
||||
if (key === 'tomorrow') return 'Morgen'
|
||||
if (key === 'overdue') return 'Überfällig'
|
||||
if (key === 'noDueDate') return 'Ohne Fälligkeitsdatum'
|
||||
return key
|
||||
}
|
||||
|
||||
@@ -105,20 +128,20 @@ const shouldDisplay = (todo: Todo) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="todos" v-for="(todos, groupKey) in groupedTodos" :key="groupKey">
|
||||
<div v-if="!todosEmpty(todos)">
|
||||
<div v-if="todos" v-for="group in groupedTodos">
|
||||
<div v-if="!todosEmpty(group.todos)">
|
||||
<!-- Group header -->
|
||||
<h3 class="mt-4 mb-2 text-sm text-muted-foreground" :class="{
|
||||
'text-destructive! font-bold': groupKey === 'overdue',
|
||||
'text-warning! font-bold': groupKey === 'today'
|
||||
'text-destructive! font-bold': group.key === 'overdue',
|
||||
'text-warning! font-bold': group.key === 'today'
|
||||
}">
|
||||
{{ groupNameForKey(groupKey) }}
|
||||
{{ groupNameForKey(group.key) }}
|
||||
</h3>
|
||||
|
||||
<hr>
|
||||
|
||||
<ul>
|
||||
<li v-for="todo in todos" class="flex gap-3 items-baseline py-2.5 pr-1 transition-all"
|
||||
<li v-for="todo in group.todos" class="flex gap-3 items-baseline py-2.5 pr-1 transition-all"
|
||||
:class="{ 'scale-y-0 h-0 py-0! my-0 origin-top': !shouldDisplay(todo) }">
|
||||
|
||||
<!-- Check mark -->
|
||||
@@ -148,11 +171,11 @@ const shouldDisplay = (todo: Todo) => {
|
||||
<div class="text-xs text-muted-foreground flex gap-3 items-center mt-1">
|
||||
<Badge v-if="props.showTodoable && todoBadge(todo.title)" variant="outline">{{
|
||||
todoBadge(todo.title)
|
||||
}}
|
||||
}}
|
||||
</Badge>
|
||||
<span v-if="todo.dueDate" :class="{
|
||||
'text-destructive! font-bold': groupKey === 'overdue',
|
||||
'text-warning! font-bold': groupKey === 'today'
|
||||
'text-destructive! font-bold': group.key === 'overdue',
|
||||
'text-warning! font-bold': group.key === 'today'
|
||||
}">
|
||||
{{ toDuration(todo.dueDate) }}</span>
|
||||
<Repeat v-if="todo.recurring" stroke-width="2" :size="14" />
|
||||
|
||||
@@ -22,7 +22,6 @@ import TodoService from '@/services/TodoService'
|
||||
import NumberInput from '@/components/ui/crm-number-input/NumberInput.vue';
|
||||
import { alertStore } from '@/stores/alertStore'
|
||||
import PipelineService from '@/services/PipelineService'
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
interface Props {
|
||||
pipeline: PipelineLane[]
|
||||
@@ -144,6 +143,12 @@ const badgeVariant = (date: string | null): "default" | "secondary" | "destructi
|
||||
return "secondary"
|
||||
}
|
||||
|
||||
const itemsNextTodoDueDate = (item: PipelineItem): string | null => {
|
||||
if (!item.todos || item.todos.length === 0) return null
|
||||
const next = item.todos.find(t => t.status.toLowerCase() !== 'completed')
|
||||
return next?.dueDate || null
|
||||
}
|
||||
|
||||
const editItem = async (item: PipelineItem) => {
|
||||
// Load todos lazily
|
||||
if (item.id !== 0 && (item.todos === undefined || item.todos.length === 0)) {
|
||||
@@ -287,7 +292,7 @@ const saveItem = (item: PipelineItem | undefined) => {
|
||||
</Badge>
|
||||
|
||||
<Badge v-if="item.todos && item.todos.length > 0"
|
||||
:variant="badgeVariant(item.todos[item.todos.length - 1]?.dueDate || null)">
|
||||
:variant="badgeVariant(itemsNextTodoDueDate(item))">
|
||||
<SquareCheckBig /> {{item.todos.filter(todo => todo.status.toLowerCase() !==
|
||||
'completed').length}}
|
||||
</Badge>
|
||||
@@ -351,7 +356,7 @@ const saveItem = (item: PipelineItem | undefined) => {
|
||||
<template v-slot:sidebar>
|
||||
<NumberInput label="Erwarteter Umsatz" :modelValue="selectedItem?.expectedRevenue as number" suffix=" €"
|
||||
@update:model-value="console.log" />
|
||||
<Todos v-if="selectedItem" title="Aufgaben" :modelValue="selectedItem.todos" :show-completed="false" />
|
||||
<Todos v-if="selectedItem" :modelValue="selectedItem.todos" :show-completed="false" />
|
||||
</template>
|
||||
|
||||
</EditorDialog>
|
||||
|
||||
Reference in New Issue
Block a user