New apps Added

This commit is contained in:
2026-07-08 10:43:39 -04:00
parent 3b1f4bbd75
commit fefc2c8b5c
1114 changed files with 406637 additions and 154 deletions
@@ -0,0 +1,41 @@
/**
* Unit-aware interval math (issue #59) for the frontend's progress bars and
* calendar projection stepping. Uses an AVERAGE day-span per unit (below), an
* intentional approximation — the backend `helpers/dates.add_interval` is
* calendar-exact (Jan+1mo = 31 days), so display-side spans can differ by a day
* or two. Cosmetic only. Pure functions — unit-tested in interval.test.ts.
*/
/** Approximate (average) days per interval unit — mean Gregorian month / Julian
* year. NOT calendar-exact; see the module note above. */
export const UNIT_DAYS: Record<string, number> = {
days: 1,
weeks: 7,
months: 30.4368,
years: 365.25,
};
/** Approximate length of `intervalDays` of `unit` in days; 0 when no interval. */
export function intervalSpanDays(
intervalDays: number | null | undefined,
unit?: string | null,
): number {
if (!intervalDays || intervalDays <= 0) return 0;
return intervalDays * (UNIT_DAYS[unit || "days"] ?? 1);
}
/**
* Progress through the current cycle, unit-aware.
* Returns a clamped 0100 % plus `overflow` (raw % > 100, i.e. past due).
* Falls back to {0, false} when there is no usable interval/countdown.
*/
export function daysProgress(
intervalDays: number | null | undefined,
daysUntilDue: number | null | undefined,
unit?: string | null,
): { pct: number; overflow: boolean } {
const span = intervalSpanDays(intervalDays, unit);
if (span <= 0 || daysUntilDue == null) return { pct: 0, overflow: false };
const raw = ((span - daysUntilDue) / span) * 100;
return { pct: Math.max(0, Math.min(100, raw)), overflow: raw > 100 };
}