Files
minabot/src/lib/dashboard.ts

61 lines
2.4 KiB
TypeScript

import type { HabitService } from "../habits/service";
import type { Schedule } from "../habits/contracts";
export type TodayHabit = ReturnType<HabitService["day"]>;
export type TodayResponse = {
date: string;
timezone: string;
habits: TodayHabit[];
due: number;
completed: number;
};
export async function habitRequest<T>(
path: string,
options?: RequestInit,
): Promise<T> {
const response = await fetch(`/api${path}`, options);
if (!response.ok) {
if (response.status === 401)
throw new Error("Your session has expired. Sign in again to continue.");
const body = await response.json().catch(() => null);
throw new Error(
(body?.error || "Could not reach your workspace. Please try again.") + (response.status >= 500 && body?.requestId ? ` Reference: ${body.requestId}` : ""),
);
}
return response.status === 204
? (undefined as T)
: (response.json() as Promise<T>);
}
export function scheduleLabel(schedule?: Schedule) {
if (!schedule || schedule.type === "daily") return "Every day";
if (schedule.type === "weekdays")
return schedule.days
.map((day) => ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][day])
.join(", ");
if (schedule.type === "interval") return `Every ${schedule.every} days`;
return `Every ${schedule.every === 1 ? "week" : `${schedule.every} weeks`} · ${["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][schedule.weekday]}`;
}
export function formatTrackingDate(date: string) {
return new Date(`${date}T12:00:00Z`).toLocaleDateString("en", {
weekday: "long",
month: "long",
day: "numeric",
timeZone: "UTC",
});
}
/** Preserve unchanged row references when a background refresh returns new JSON objects. */
export function reconcileToday(current: TodayResponse | null, next: TodayResponse): TodayResponse {
if (!current || current.date !== next.date || current.timezone !== next.timezone) return next;
const previous = new Map(current.habits.map(habit => [habit.habitId, habit]));
const habits = next.habits.map(habit => {
const old = previous.get(habit.habitId);
return old && JSON.stringify(old) === JSON.stringify(habit) ? old : habit;
});
if (current.due === next.due && current.completed === next.completed && habits.length === current.habits.length && habits.every((habit, index) => habit === current.habits[index])) return current;
return { ...next, habits };
}