"use client"; import { useState, useEffect, useMemo } from "react"; import { Plus, Check, Pill, Video, CheckCircle, Clock, X, ChevronDown, Droplets, Heart, } from "lucide-react"; import { todayISO, FREQUENCY_LABELS, APPOINTMENT_TYPE_META, type Medication, type Appointment, type MedicationLog, } from "@/lib/health-store"; import { t, type SupportedLanguage } from "@/lib/i18n"; interface ScheduleViewProps { medications: Medication[]; medicationLogs: MedicationLog[]; appointments: Appointment[]; onMarkMedTaken: (medId: string, date: string, time: string) => void; isMedTaken: (medId: string, date: string, time: string) => boolean; onEditAppointment: (id: string, patch: Partial) => void; onNavigate: (view: string) => void; language: SupportedLanguage; } interface TimelineEvent { id: string; time: string; // "HH:MM" title: string; subtitle: string; type: "medication" | "appointment" | "task" | "habit"; done: boolean; onAction?: () => void; actionLabel?: string; } const HOURS = Array.from({ length: 14 }, (_, i) => i + 7); // 7 AM to 8 PM function formatHour(h: number): string { if (h === 0 || h === 12) return `${h === 0 ? 12 : 12} ${h < 12 ? "AM" : "PM"}`; return `${h > 12 ? h - 12 : h} ${h >= 12 ? "PM" : "AM"}`; } function timeToMinutes(t: string): number { const [h, m] = t.split(":").map(Number); return (h || 0) * 60 + (m || 0); } export function ScheduleView({ medications, medicationLogs, appointments, onMarkMedTaken, isMedTaken, onEditAppointment, onNavigate, language, }: ScheduleViewProps) { const today = todayISO(); const [now, setNow] = useState(new Date()); // Update "now" every minute for the live indicator. useEffect(() => { const id = setInterval(() => setNow(new Date()), 60000); return () => clearInterval(id); }, []); const nowMinutes = now.getHours() * 60 + now.getMinutes(); const nowLabel = now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); // Build the timeline events from medications + appointments. const events: TimelineEvent[] = useMemo(() => { const items: TimelineEvent[] = []; // Active medications → one event per scheduled time. for (const med of medications.filter((m) => m.active)) { for (const time of med.times) { const done = isMedTaken(med.id, today, time); items.push({ id: `med-${med.id}-${time}`, time, title: `${med.name} (${med.dose})`, subtitle: `${time} · ${t("nav_medications", language)}`, type: "medication", done, onAction: done ? undefined : () => onMarkMedTaken(med.id, today, time), actionLabel: done ? undefined : t("appt_mark_done", language), }); } } // Today's appointments. for (const appt of appointments.filter((a) => a.date === today)) { const done = appt.status === "completed"; const meta = APPOINTMENT_TYPE_META[appt.type]; items.push({ id: `appt-${appt.id}`, time: appt.time, title: appt.title, subtitle: `${appt.time} · ${meta.label}${appt.doctor ? ` · ${appt.doctor}` : ""}`, type: "appointment", done, onAction: done ? undefined : () => onEditAppointment(appt.id, { status: "completed" }), actionLabel: done ? undefined : t("appt_mark_done", language), }); } return items.sort((a, b) => timeToMinutes(a.time) - timeToMinutes(b.time)); }, [medications, appointments, today, isMedTaken, onMarkMedTaken, onEditAppointment, language]); // Position of the "Now" indicator as a percentage of the timeline. const timelineStart = HOURS[0] * 60; const timelineEnd = (HOURS[HOURS.length - 1] + 1) * 60; const nowPercent = Math.max( 0, Math.min(100, ((nowMinutes - timelineStart) / (timelineEnd - timelineStart)) * 100), ); const dayOfWeek = now.toLocaleDateString(undefined, { weekday: "long", month: "long", day: "numeric" }); return (
{/* Header */}

{t("health_tracker", language)}

{dayOfWeek}

{/* Timeline */}
{/* Hour rows */} {HOURS.map((hour) => { const hourEvents = events.filter((e) => { const m = timeToMinutes(e.time); return m >= hour * 60 && m < (hour + 1) * 60; }); return (
{/* Hour label */}
{formatHour(hour)}
{/* Event area */}
{hourEvents.map((ev) => ( ))}
); })} {/* "Now" indicator line — only if within visible hours */} {nowMinutes >= timelineStart && nowMinutes <= timelineEnd && (
{nowLabel}
)}
{/* Empty state */} {events.length === 0 && (

No events today

Add medications or appointments to see them on your timeline

)}
); } function EventCard({ event }: { event: TimelineEvent }) { const typeStyles: Record = { medication: event.done ? "bg-success-500/8 border-success-500/30" : "bg-rose-50 dark:bg-rose-900/15 border-rose-200 dark:border-rose-700/40", appointment: "bg-purple-50 dark:bg-purple-900/15 border-purple-200 dark:border-purple-700/40", task: "bg-blue-50 dark:bg-blue-900/15 border-blue-200 dark:border-blue-700/40", habit: "bg-sky-50 dark:bg-sky-900/15 border-sky-200 dark:border-sky-700/40", }; const iconMap: Record = { medication: Pill, appointment: Video, task: CheckCircle, habit: Droplets, }; const Icon = iconMap[event.type] || Clock; return (
{event.title} {event.subtitle}
{event.done ? ( Done ) : event.onAction ? ( ) : null}
); }