diff --git a/components/deadlines/DeadlineCard.tsx b/components/deadlines/DeadlineCard.tsx index 18bea351..844f264e 100644 --- a/components/deadlines/DeadlineCard.tsx +++ b/components/deadlines/DeadlineCard.tsx @@ -8,9 +8,10 @@ import { isDeadlineOverdue, DEADLINE_TYPE_LABELS, PRIORITY_LABELS, - PRIORITY_COLORS, + parseDate, + startOfDay, } from '@/lib/calendar/utils' -import { CheckCircle, Circle, Clock, AlertTriangle, Trash2, Edit2 } from 'lucide-react' +import { Check, Circle, Trash2, Pencil } from 'lucide-react' interface DeadlineCardProps { deadline: Deadline @@ -20,6 +21,25 @@ interface DeadlineCardProps { compact?: boolean } +function getRelativeDate(dateStr: string): string { + const today = startOfDay(new Date()) + const target = parseDate(dateStr) + const diffMs = target.getTime() - today.getTime() + const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24)) + + if (diffDays === 0) return 'Idag' + if (diffDays === 1) return 'Imorgon' + if (diffDays === -1) return 'Igår' + if (diffDays < -1) return `${Math.abs(diffDays)}d sedan` + if (diffDays <= 14) return `Om ${diffDays}d` + return '' +} + +const SWEDISH_MONTHS_SHORT = [ + 'jan', 'feb', 'mar', 'apr', 'maj', 'jun', + 'jul', 'aug', 'sep', 'okt', 'nov', 'dec', +] + export function DeadlineCard({ deadline, onToggle, @@ -29,56 +49,48 @@ export function DeadlineCard({ }: DeadlineCardProps) { const overdue = isDeadlineOverdue(deadline) const completed = deadline.is_completed - const priorityStyle = PRIORITY_COLORS[deadline.priority] - - const formatDueDate = () => { - const date = new Date(deadline.due_date) - const formatted = date.toLocaleDateString('sv-SE', { - day: 'numeric', - month: 'short', - }) - if (deadline.due_time) { - return `${formatted} kl. ${deadline.due_time.slice(0, 5)}` - } - return formatted - } + const relativeDate = getRelativeDate(deadline.due_date) + const dueDate = parseDate(deadline.due_date) + const dayNum = dueDate.getDate() + const monthStr = SWEDISH_MONTHS_SHORT[dueDate.getMonth()] if (compact) { return (
- -
-

- {deadline.title} -

-
- {deadline.priority !== 'normal' && !completed && ( -
+
) @@ -87,89 +99,151 @@ export function DeadlineCard({ return (
-
- +
+ {/* Date block — calendar page motif */} +
+ + {dayNum} + + + {monthStr} + +
-
-
-
-

+ {/* Top: title + hover actions */} +

+
+ {/* Toggle */} + + +
+

+ {deadline.title} +

+ + {/* Meta line */} +
+ {deadline.due_time && ( + + kl. {deadline.due_time.slice(0, 5)} + + )} + {deadline.due_time && (deadline.customer || (!completed && relativeDate)) && ( + · + )} + {!completed && relativeDate && ( + + {relativeDate} + + )} + {relativeDate && deadline.customer && !completed && ( + · + )} + {deadline.customer && ( + + {deadline.customer.name} + + )} +
+
-
- - {DEADLINE_TYPE_LABELS[deadline.deadline_type]} - - {!completed && deadline.priority !== 'normal' && ( + {/* Right column: badges + actions */} +
+ {/* Badges */} +
- {PRIORITY_LABELS[deadline.priority]} - - )} - {overdue && !completed && ( - - Förfallen + {DEADLINE_TYPE_LABELS[deadline.deadline_type]} + {!completed && deadline.priority !== 'normal' && ( + + {PRIORITY_LABELS[deadline.priority]} + + )} + {overdue && !completed && ( + + Förfallen + + )} +
+ + {/* Actions */} + {(onEdit || onDelete) && !completed && ( +
+
+ {onEdit && ( + + )} + {onDelete && ( + + )} +
)}
- {deadline.notes && ( -

+ {/* Notes */} + {deadline.notes && !completed && ( +

{deadline.notes}

)}
- - {(onEdit || onDelete) && ( -
- {onEdit && ( - - )} - {onDelete && ( - - )} -
- )}
) } diff --git a/components/deadlines/DeadlineList.tsx b/components/deadlines/DeadlineList.tsx index 9e9d5180..3a6fb95b 100644 --- a/components/deadlines/DeadlineList.tsx +++ b/components/deadlines/DeadlineList.tsx @@ -111,23 +111,29 @@ export function DeadlineList({ {/* Deadline groups */} {filteredDeadlines.length === 0 ? ( -
- -

Inga deadlines

-

+

+ +

Inga deadlines

+

{statusFilter !== 'all' || typeFilter !== 'all' ? 'Inga deadlines matchar dina filter' : 'Skapa din första deadline för att komma igång'}

) : ( -
+
{/* Overdue */} {groupedDeadlines.overdue.length > 0 && ( -
-

- Förfallna ({groupedDeadlines.overdue.length}) -

+
+
+

+ Förfallna +

+ + {groupedDeadlines.overdue.length} + +
+
{groupedDeadlines.overdue.map((deadline) => ( ))}
-
+
)} {/* Today */} {groupedDeadlines.today.length > 0 && ( -
-

- Idag ({groupedDeadlines.today.length}) -

+
+
+

+ Idag +

+ + {groupedDeadlines.today.length} + +
+
{groupedDeadlines.today.map((deadline) => ( ))}
-
+
)} {/* Upcoming */} {groupedDeadlines.upcoming.length > 0 && ( -
-

- Kommande ({groupedDeadlines.upcoming.length}) -

+
+
+

+ Kommande +

+ + {groupedDeadlines.upcoming.length} + +
+
{groupedDeadlines.upcoming.map((deadline) => ( ))}
-
+
)} {/* Completed */} {groupedDeadlines.completed.length > 0 && ( -
-

- Klara ({groupedDeadlines.completed.length}) -

+
+
+

+ Klara +

+ + {groupedDeadlines.completed.length} + +
+
{groupedDeadlines.completed.map((deadline) => ( ))}
-
+
)}
)} diff --git a/lib/api/schemas.ts b/lib/api/schemas.ts index 8644e682..7fb62a09 100644 --- a/lib/api/schemas.ts +++ b/lib/api/schemas.ts @@ -450,16 +450,16 @@ export const EvaluateMappingRulesSchema = z.union([ export const CreateDeadlineSchema = z.object({ title: z.string().min(1, 'Title is required'), due_date: isoDate, - due_time: timeString.optional(), + due_time: timeString.nullish(), deadline_type: DeadlineTypeSchema, - priority: DeadlinePrioritySchema.optional(), - customer_id: uuid.optional(), - notes: z.string().optional(), - tax_deadline_type: TaxDeadlineTypeSchema.optional(), - tax_period: z.string().optional(), + priority: DeadlinePrioritySchema.nullish(), + customer_id: uuid.nullish(), + notes: z.string().nullish(), + tax_deadline_type: TaxDeadlineTypeSchema.nullish(), + tax_period: z.string().nullish(), source: DeadlineSourceSchema.optional(), - linked_report_type: z.string().optional(), - linked_report_period: z.record(z.string(), z.unknown()).optional(), + linked_report_type: z.string().nullish(), + linked_report_period: z.record(z.string(), z.unknown()).nullish(), }) // ============================================================ diff --git a/lib/calendar/utils.ts b/lib/calendar/utils.ts index a0eb93be..8ca9d1d4 100644 --- a/lib/calendar/utils.ts +++ b/lib/calendar/utils.ts @@ -242,9 +242,9 @@ export const PRIORITY_LABELS: Record = { // Priority colors for styling export const PRIORITY_COLORS: Record = { - critical: { bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200' }, - important: { bg: 'bg-orange-50', text: 'text-orange-700', border: 'border-orange-200' }, - normal: { bg: 'bg-gray-50', text: 'text-gray-700', border: 'border-gray-200' } + critical: { bg: 'bg-red-50 dark:bg-red-950/40', text: 'text-red-700 dark:text-red-400', border: 'border-red-200 dark:border-red-800/50' }, + important: { bg: 'bg-orange-50 dark:bg-orange-950/40', text: 'text-orange-700 dark:text-orange-400', border: 'border-orange-200 dark:border-orange-800/50' }, + normal: { bg: 'bg-gray-50 dark:bg-gray-800/40', text: 'text-gray-700 dark:text-gray-300', border: 'border-gray-200 dark:border-gray-700/50' } } // ============================================================