diff --git a/synculous-client/src/app/dashboard/routines/page.tsx b/synculous-client/src/app/dashboard/routines/page.tsx index 4628265..ebedab5 100644 --- a/synculous-client/src/app/dashboard/routines/page.tsx +++ b/synculous-client/src/app/dashboard/routines/page.tsx @@ -2,7 +2,7 @@ import { useEffect, useState, useRef, useMemo } from 'react'; import { useRouter } from 'next/navigation'; -import api from '@/lib/api'; +import api, { type Task } from '@/lib/api'; import { PlusIcon, PlayIcon, ClockIcon, CheckIcon } from '@/components/ui/Icons'; import Link from 'next/link'; @@ -208,6 +208,7 @@ export default function RoutinesPage() { const [allRoutines, setAllRoutines] = useState([]); const [allSchedules, setAllSchedules] = useState([]); const [todayMeds, setTodayMeds] = useState([]); + const [allTasks, setAllTasks] = useState([]); const [isLoading, setIsLoading] = useState(true); const [selectedDate, setSelectedDate] = useState(() => new Date()); const [nowMinutes, setNowMinutes] = useState(() => { @@ -232,6 +233,14 @@ export default function RoutinesPage() { .filter((s) => s.days.includes(dayKey)) .sort((a, b) => timeToMinutes(a.time) - timeToMinutes(b.time)); + const tasksForDay = allTasks.filter((t) => { + if (t.status === 'cancelled') return false; + const d = new Date(t.scheduled_datetime); + return d.getFullYear() === selectedDate.getFullYear() && + d.getMonth() === selectedDate.getMonth() && + d.getDate() === selectedDate.getDate(); + }); + const scheduledRoutineIds = new Set(allSchedules.map((s) => s.routine_id)); const unscheduledRoutines = allRoutines.filter( (r) => !scheduledRoutineIds.has(r.id) @@ -299,6 +308,10 @@ export default function RoutinesPage() { const allEventMins = [ ...scheduledForDay.map((e) => timeToMinutes(e.time)), ...groupedMedEntries.map((e) => timeToMinutes(e.time)), + ...tasksForDay.map((t) => { + const d = new Date(t.scheduled_datetime); + return d.getHours() * 60 + d.getMinutes(); + }), ]; const eventStartHour = allEventMins.length > 0 ? Math.floor(Math.min(...allEventMins) / 60) : DEFAULT_START_HOUR; const eventEndHour = allEventMins.length > 0 ? Math.ceil(Math.max(...allEventMins) / 60) : DEFAULT_END_HOUR; @@ -325,9 +338,18 @@ export default function RoutinesPage() { endMin: timeToMinutes(g.time) + durationMin, }; }), + ...tasksForDay.map((t) => { + const d = new Date(t.scheduled_datetime); + const startMin = d.getHours() * 60 + d.getMinutes(); + return { + id: `t-${t.id}`, + startMin, + endMin: startMin + (48 / HOUR_HEIGHT) * 60, + }; + }), ]; return computeLanes(items); - }, [scheduledForDay, groupedMedEntries]); + }, [scheduledForDay, groupedMedEntries, tasksForDay]); // ── Handlers ────────────────────────────────────────────────── const handleTakeMed = async (medicationId: string, scheduledTime: string) => { @@ -416,11 +438,13 @@ export default function RoutinesPage() { api.routines.list(), api.routines.listAllSchedules(), api.medications.getToday().catch(() => []), + api.tasks.list('all').catch(() => []), ]) - .then(([routines, schedules, meds]) => { + .then(([routines, schedules, meds, tasks]) => { setAllRoutines(routines); setAllSchedules(schedules); setTodayMeds(meds); + setAllTasks(tasks); }) .catch(() => {}) .finally(() => setIsLoading(false)); @@ -776,8 +800,56 @@ export default function RoutinesPage() { ); })} + {/* Task cards */} + {tasksForDay.map((task) => { + const d = new Date(task.scheduled_datetime); + const startMin = d.getHours() * 60 + d.getMinutes(); + const topPx = minutesToTop(startMin, displayStartHour); + const isPast = task.status === 'completed'; + const layout = timelineLayout.get(`t-${task.id}`) ?? { + lane: 0, + totalLanes: 1, + }; + const timeStr = `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`; + + return ( +
router.push('/dashboard/tasks')} + style={{ + top: `${topPx}px`, + height: '48px', + ...laneStyle(layout.lane, layout.totalLanes), + }} + className={`absolute rounded-xl px-3 py-2 text-left shadow-sm border transition-all overflow-hidden cursor-pointer ${ + isPast + ? 'bg-green-50 dark:bg-green-900/30 border-green-200 dark:border-green-800 opacity-75' + : 'bg-purple-50 dark:bg-purple-900/30 border-purple-200 dark:border-purple-800' + }`} + > +
+ 📋 +
+

+ {task.title} +

+

+ {formatTime(timeStr)} + {task.description && ` · ${task.description}`} +

+
+ {isPast && ( + + + + )} +
+
+ ); + })} + {/* Empty day */} - {scheduledForDay.length === 0 && medEntries.length === 0 && ( + {scheduledForDay.length === 0 && medEntries.length === 0 && tasksForDay.length === 0 && (

No routines or medications for this day