updated rewards assignment and reward progress

This commit is contained in:
JCEEE
2026-08-31 16:44:37 +01:00
parent cda2cbfcab
commit 3bf78d777c
10 changed files with 549 additions and 209 deletions
+9
View File
@@ -229,6 +229,15 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
select("period", ["schedule", "daily", "weekly", "monthly"]),
select("status", ["active", "completed"], true),
bool("isPocketMoney"),
// Standalone/one-off reward progress window. `period` stays empty for
// these (they're measured once, not per-cycle). `completeBy` bounds how
// far progress counts: `unlimited` (cumulative since startDate),
// `week` (end of current week), or `custom` (a specific completeByDate).
// `startDate` is when tracking begins — without it "unlimited" would
// count points earned before the reward existed.
select("completeBy", ["unlimited", "week", "custom"]),
text("startDate"),
text("completeByDate"),
], {
createRule: RULE_PARENT_WRITE,
updateRule: RULE_PARENT_SCOPED,
+37
View File
@@ -104,6 +104,43 @@ export function periodEnd(period: string, start: string): string {
return start;
}
// Compute the completion-counting window for a bonus. Recurring period-based
// bonuses keep their per-cycle window (periodStart..periodEnd). Standalone /
// one-off rewards (no `period`) are scoped by `completeBy` + `startDate`:
// - `completeBy === "week"` → current week [weekStart, weekEnd]
// - `completeBy === "custom"` → [startDate, completeByDate]
// - otherwise (`unlimited`/empty) → [startDate, ∞] (cumulative)
// `from`/`to` of "" mean unbounded on that side (legacy configs with no dates
// count everything since records began, i.e. the admin-dashboard behaviour).
export function bonusWindow(
cfg: { period?: string; completeBy?: string; startDate?: string; completeByDate?: string },
payday: number,
tz: string,
): { from: string; to: string } {
if (cfg.period) {
const start = periodStart(cfg.period, payday, tz);
return { from: start, to: periodEnd(cfg.period, start) };
}
const from = cfg.startDate || "";
let to = "";
if (cfg.completeBy === "week") to = periodEnd("weekly", weekStart(payday, tz));
else if (cfg.completeBy === "custom") to = cfg.completeByDate || "";
return { from, to };
}
// True if a completion record's date falls within the window (dates may be
// YYYY-MM-DD or ISO with a time portion).
export function completionInWindow(
c: { date?: string },
win: { from: string; to: string },
): boolean {
const day = (c.date || "").slice(0, 10);
if (!day) return false;
if (win.from && day < win.from) return false;
if (win.to && day > win.to) return false;
return true;
}
// The completion window a chore of a given frequency is "done for" in the
// current period. Weekly chores are done once per week ([weekStart, +7)),
// daily chores once per day ([today, +1)). Half-open [from, to).