diff --git a/backend/app/routers/dashboard.py b/backend/app/routers/dashboard.py
index 3e51f42..10470b9 100644
--- a/backend/app/routers/dashboard.py
+++ b/backend/app/routers/dashboard.py
@@ -69,22 +69,23 @@ async def get_dashboard(
)
total_incomplete_todos = total_incomplete_result.scalar()
- # Next starred event (soonest future starred event)
+ # Starred events (upcoming, ordered by date)
now = datetime.now()
starred_query = select(CalendarEvent).where(
CalendarEvent.is_starred == True,
CalendarEvent.start_datetime > now
- ).order_by(CalendarEvent.start_datetime.asc()).limit(1)
+ ).order_by(CalendarEvent.start_datetime.asc()).limit(5)
starred_result = await db.execute(starred_query)
- next_starred = starred_result.scalar_one_or_none()
+ starred_events = starred_result.scalars().all()
- next_starred_event_data = None
- if next_starred:
- next_starred_event_data = {
- "id": next_starred.id,
- "title": next_starred.title,
- "start_datetime": next_starred.start_datetime
+ starred_events_data = [
+ {
+ "id": e.id,
+ "title": e.title,
+ "start_datetime": e.start_datetime
}
+ for e in starred_events
+ ]
return {
"todays_events": [
@@ -122,7 +123,7 @@ async def get_dashboard(
"by_status": projects_by_status
},
"total_incomplete_todos": total_incomplete_todos,
- "next_starred_event": next_starred_event_data
+ "starred_events": starred_events_data
}
diff --git a/frontend/src/components/dashboard/CalendarWidget.tsx b/frontend/src/components/dashboard/CalendarWidget.tsx
index 01776dc..688a202 100644
--- a/frontend/src/components/dashboard/CalendarWidget.tsx
+++ b/frontend/src/components/dashboard/CalendarWidget.tsx
@@ -37,13 +37,13 @@ export default function CalendarWidget({ events }: CalendarWidgetProps) {
{events.map((event) => (
-
+
{event.all_day
? 'All day'
: `${format(new Date(event.start_datetime), 'h:mm a')} โ ${format(new Date(event.end_datetime), 'h:mm a')}`}
diff --git a/frontend/src/components/dashboard/CountdownWidget.tsx b/frontend/src/components/dashboard/CountdownWidget.tsx
index 426414c..dc3f892 100644
--- a/frontend/src/components/dashboard/CountdownWidget.tsx
+++ b/frontend/src/components/dashboard/CountdownWidget.tsx
@@ -2,31 +2,33 @@ import { differenceInCalendarDays } from 'date-fns';
import { Star } from 'lucide-react';
interface CountdownWidgetProps {
- event: {
+ events: Array<{
id: number;
title: string;
start_datetime: string;
- };
+ }>;
}
-export default function CountdownWidget({ event }: CountdownWidgetProps) {
- const daysUntil = differenceInCalendarDays(new Date(event.start_datetime), new Date());
- if (daysUntil < 0) return null;
-
- const label = daysUntil === 0
- ? 'Today'
- : daysUntil === 1
- ? '1 day'
- : `${daysUntil} days`;
+export default function CountdownWidget({ events }: CountdownWidgetProps) {
+ const visible = events.filter((e) => differenceInCalendarDays(new Date(e.start_datetime), new Date()) >= 0);
+ if (visible.length === 0) return null;
return (
-
-
-
- {label}
- {daysUntil > 0 ? ' until ' : ' โ '}
- {event.title}
-
+
+ {visible.map((event) => {
+ const days = differenceInCalendarDays(new Date(event.start_datetime), new Date());
+ const label = days === 0 ? 'Today' : days === 1 ? '1 day' : `${days} days`;
+ return (
+
+
+
+ {label}
+ {days > 0 ? ' until ' : ' โ '}
+ {event.title}
+
+
+ );
+ })}
);
}
diff --git a/frontend/src/components/dashboard/DashboardPage.tsx b/frontend/src/components/dashboard/DashboardPage.tsx
index 34ff68c..c38eda3 100644
--- a/frontend/src/components/dashboard/DashboardPage.tsx
+++ b/frontend/src/components/dashboard/DashboardPage.tsx
@@ -197,8 +197,8 @@ export default function DashboardPage() {
{/* Right: Countdown + Today's events + todos stacked */}
- {data.next_starred_event && (
-
+ {data.starred_events.length > 0 && (
+
)}
@@ -217,19 +217,17 @@ export default function DashboardPage() {
-
+
{data.active_reminders.map((reminder) => (
-
-
-
{reminder.title}
-
- {format(new Date(reminder.remind_at), 'MMM d, yyyy h:mm a')}
-
-
+
+
{reminder.title}
+
+ {format(new Date(reminder.remind_at), 'MMM d, h:mm a')}
+
))}
diff --git a/frontend/src/components/dashboard/StatsWidget.tsx b/frontend/src/components/dashboard/StatsWidget.tsx
index 37bc1d1..1c87030 100644
--- a/frontend/src/components/dashboard/StatsWidget.tsx
+++ b/frontend/src/components/dashboard/StatsWidget.tsx
@@ -7,7 +7,7 @@ interface StatsWidgetProps {
by_status: Record
;
};
totalIncompleteTodos: number;
- weatherData?: { temp: number; description: string } | null;
+ weatherData?: { temp: number; description: string; city?: string } | null;
}
export default function StatsWidget({ projectStats, totalIncompleteTodos, weatherData }: StatsWidgetProps) {
@@ -73,6 +73,11 @@ export default function StatsWidget({ projectStats, totalIncompleteTodos, weathe
{weatherData.description}
+ {weatherData.city && (
+
+ {weatherData.city}
+
+ )}
>
) : (
<>
diff --git a/frontend/src/components/dashboard/TodoWidget.tsx b/frontend/src/components/dashboard/TodoWidget.tsx
index d9123ae..b81b29c 100644
--- a/frontend/src/components/dashboard/TodoWidget.tsx
+++ b/frontend/src/components/dashboard/TodoWidget.tsx
@@ -1,5 +1,5 @@
import { format, isPast, endOfDay } from 'date-fns';
-import { Calendar, CheckCircle2 } from 'lucide-react';
+import { CheckCircle2 } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { cn } from '@/lib/utils';
@@ -22,6 +22,12 @@ const priorityColors: Record = {
high: 'bg-red-500/10 text-red-400 border-red-500/20',
};
+const dotColors: Record = {
+ high: 'bg-red-400',
+ medium: 'bg-yellow-400',
+ low: 'bg-green-400',
+};
+
export default function TodoWidget({ todos }: TodoWidgetProps) {
return (
@@ -39,33 +45,24 @@ export default function TodoWidget({ todos }: TodoWidgetProps) {
All caught up.
) : (
-
+
{todos.slice(0, 5).map((todo) => {
const isOverdue = isPast(endOfDay(new Date(todo.due_date)));
return (
-
-
-
{todo.title}
-
-
-
- {format(new Date(todo.due_date), 'MMM d')}
- {isOverdue && overdue}
-
-
-
-
+
+ {todo.title}
+
+ {format(new Date(todo.due_date), 'MMM d')}
+ {isOverdue && ' overdue'}
+
+
{todo.priority}
diff --git a/frontend/src/components/dashboard/UpcomingWidget.tsx b/frontend/src/components/dashboard/UpcomingWidget.tsx
index bbc2d2b..4eb0b2b 100644
--- a/frontend/src/components/dashboard/UpcomingWidget.tsx
+++ b/frontend/src/components/dashboard/UpcomingWidget.tsx
@@ -10,25 +10,10 @@ interface UpcomingWidgetProps {
days?: number;
}
-const typeConfig: Record
= {
- todo: {
- icon: CheckSquare,
- color: 'text-blue-400',
- borderColor: 'border-l-blue-400',
- label: 'Todo',
- },
- event: {
- icon: Calendar,
- color: 'text-purple-400',
- borderColor: 'border-l-purple-400',
- label: 'Event',
- },
- reminder: {
- icon: Bell,
- color: 'text-orange-400',
- borderColor: 'border-l-orange-400',
- label: 'Reminder',
- },
+const typeConfig: Record = {
+ todo: { icon: CheckSquare, color: 'text-blue-400', label: 'TODO' },
+ event: { icon: Calendar, color: 'text-purple-400', label: 'EVENT' },
+ reminder: { icon: Bell, color: 'text-orange-400', label: 'REMINDER' },
};
export default function UpcomingWidget({ items, days = 7 }: UpcomingWidgetProps) {
@@ -52,37 +37,28 @@ export default function UpcomingWidget({ items, days = 7 }: UpcomingWidgetProps)
) : (
-
+
{items.map((item, index) => {
const config = typeConfig[item.type] || typeConfig.todo;
const Icon = config.icon;
return (
-
-
-
{item.title}
-
-
- {item.datetime
- ? format(new Date(item.datetime), 'MMM d ยท h:mm a')
- : format(new Date(item.date), 'MMM d')}
-
-
- {config.label}
-
-
-
+
+
{item.title}
+
+ {item.datetime
+ ? format(new Date(item.datetime), 'MMM d, h:mm a')
+ : format(new Date(item.date), 'MMM d')}
+
+
+ {config.label}
+
{item.priority && (
;
};
total_incomplete_todos: number;
- next_starred_event: {
+ starred_events: Array<{
id: number;
title: string;
start_datetime: string;
- } | null;
+ }>;
}
export interface WeatherData {