New apps Added
This commit is contained in:
@@ -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 0–100 % 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 };
|
||||
}
|
||||
Reference in New Issue
Block a user