This commit is contained in:
2026-02-16 00:19:31 -06:00
parent 6a372aae03
commit bb6c9c3aa9
2 changed files with 32 additions and 8 deletions

View File

@@ -112,6 +112,14 @@ function timeToMinutes(t: string): number {
return h * 60 + m; return h * 60 + m;
} }
function timeToMinutesLocal(t: string, offsetMinutes: number): number {
const mins = timeToMinutes(t);
const localMins = mins - offsetMinutes;
if (localMins < 0) return localMins + 24 * 60;
if (localMins >= 24 * 60) return localMins - 24 * 60;
return localMins;
}
function minutesToTop(minutes: number, startHour: number): number { function minutesToTop(minutes: number, startHour: number): number {
return ((minutes - startHour * 60) / 60) * HOUR_HEIGHT; return ((minutes - startHour * 60) / 60) * HOUR_HEIGHT;
} }
@@ -127,6 +135,17 @@ function formatTime(t: string): string {
return `${dh}:${String(m).padStart(2, '0')} ${period}`; return `${dh}:${String(m).padStart(2, '0')} ${period}`;
} }
function formatTimeLocal(t: string, offsetMinutes: number): string {
const mins = timeToMinutes(t) - offsetMinutes;
let h = Math.floor(mins / 60) % 24;
if (h < 0) h += 24;
const m = mins % 60;
if (m < 0) m += 60;
const period = h >= 12 ? 'PM' : 'AM';
const dh = h > 12 ? h - 12 : h === 0 ? 12 : h;
return `${dh}:${String(m).padStart(2, '0')} ${period}`;
}
function addMinutesToTime(t: string, mins: number): string { function addMinutesToTime(t: string, mins: number): string {
const [h, m] = t.split(':').map(Number); const [h, m] = t.split(':').map(Number);
const total = h * 60 + m + mins; const total = h * 60 + m + mins;
@@ -210,6 +229,7 @@ export default function RoutinesPage() {
const [todayMeds, setTodayMeds] = useState<TodaysMedication[]>([]); const [todayMeds, setTodayMeds] = useState<TodaysMedication[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [selectedDate, setSelectedDate] = useState(() => new Date()); const [selectedDate, setSelectedDate] = useState(() => new Date());
const [timezoneOffset, setTimezoneOffset] = useState(0);
const [nowMinutes, setNowMinutes] = useState(() => { const [nowMinutes, setNowMinutes] = useState(() => {
const n = new Date(); const n = new Date();
return n.getHours() * 60 + n.getMinutes(); return n.getHours() * 60 + n.getMinutes();
@@ -230,7 +250,7 @@ export default function RoutinesPage() {
const scheduledForDay = allSchedules const scheduledForDay = allSchedules
.filter((s) => s.days.includes(dayKey)) .filter((s) => s.days.includes(dayKey))
.sort((a, b) => timeToMinutes(a.time) - timeToMinutes(b.time)); .sort((a, b) => timeToMinutesLocal(a.time, timezoneOffset) - timeToMinutesLocal(b.time, timezoneOffset));
const scheduledRoutineIds = new Set(allSchedules.map((s) => s.routine_id)); const scheduledRoutineIds = new Set(allSchedules.map((s) => s.routine_id));
const unscheduledRoutines = allRoutines.filter( const unscheduledRoutines = allRoutines.filter(
@@ -298,8 +318,8 @@ export default function RoutinesPage() {
// ── Dynamic time window ─────────────────────────────────────── // ── Dynamic time window ───────────────────────────────────────
const allEventMins = [ const allEventMins = [
nowMinutes, nowMinutes,
...scheduledForDay.map((e) => timeToMinutes(e.time)), ...scheduledForDay.map((e) => timeToMinutesLocal(e.time, timezoneOffset)),
...groupedMedEntries.map((e) => timeToMinutes(e.time)), ...groupedMedEntries.map((e) => timeToMinutesLocal(e.time, timezoneOffset)),
]; ];
const displayStartHour = const displayStartHour =
allEventMins.length > 0 allEventMins.length > 0
@@ -420,11 +440,13 @@ export default function RoutinesPage() {
api.routines.list(), api.routines.list(),
api.routines.listAllSchedules(), api.routines.listAllSchedules(),
api.medications.getToday().catch(() => []), api.medications.getToday().catch(() => []),
api.preferences.get().catch(() => ({ timezone_offset: 0 })),
]) ])
.then(([routines, schedules, meds]) => { .then(([routines, schedules, meds, prefs]) => {
setAllRoutines(routines); setAllRoutines(routines);
setAllSchedules(schedules); setAllSchedules(schedules);
setTodayMeds(meds); setTodayMeds(meds);
setTimezoneOffset((prefs as any).timezone_offset || 0);
}) })
.catch(() => {}) .catch(() => {})
.finally(() => setIsLoading(false)); .finally(() => setIsLoading(false));
@@ -618,7 +640,7 @@ export default function RoutinesPage() {
{/* Routine cards */} {/* Routine cards */}
{scheduledForDay.map((entry) => { {scheduledForDay.map((entry) => {
const startMin = timeToMinutes(entry.time); const startMin = timeToMinutesLocal(entry.time, timezoneOffset);
const topPx = minutesToTop(startMin, displayStartHour); const topPx = minutesToTop(startMin, displayStartHour);
const heightPx = durationToHeight(entry.total_duration_minutes); const heightPx = durationToHeight(entry.total_duration_minutes);
const endMin = startMin + entry.total_duration_minutes; const endMin = startMin + entry.total_duration_minutes;
@@ -656,16 +678,17 @@ export default function RoutinesPage() {
{entry.routine_name} {entry.routine_name}
</p> </p>
<p className="text-xs text-gray-500 dark:text-gray-400 truncate"> <p className="text-xs text-gray-500 dark:text-gray-400 truncate">
{formatTime(entry.time)} {formatTimeLocal(entry.time, timezoneOffset)}
{entry.total_duration_minutes > 0 && ( {entry.total_duration_minutes > 0 && (
<> <>
{' '} {' '}
-{' '} -{' '}
{formatTime( {formatTimeLocal(
addMinutesToTime( addMinutesToTime(
entry.time, entry.time,
entry.total_duration_minutes entry.total_duration_minutes
) ),
timezoneOffset
)} )}
</> </>
)} )}

View File

@@ -627,6 +627,7 @@ export const api = {
haptic_enabled: boolean; haptic_enabled: boolean;
show_launch_screen: boolean; show_launch_screen: boolean;
celebration_style: string; celebration_style: string;
timezone_offset: number;
}>('/api/preferences', { method: 'GET' }); }>('/api/preferences', { method: 'GET' });
}, },