feat(detail): let an event's goal be points, entered in the sheet
Some events want a number of points rather than a number of days. The goal control gains Days and Points. Switching period keeps the measure and the number, and the sheet keeps this period's number editable in place. A target is capped where the schema reads it back, so a huge number can't silently become no goal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
595ae55f4a
commit
d0b295c02f
5 files changed
+196
-13
No files matched your search
@@ -785,6 +785,8 @@ describe("the derived-boundary note", () => {
|
||||
dailyDays: [],
|
||||
goal: undefined,
|
||||
hasStoredGoal: false,
|
||||
amounts: {},
|
||||
onSetAmount: noop,
|
||||
onDaily: noop,
|
||||
onGoal: noop,
|
||||
onToggleDay: noop,
|
||||
@@ -872,6 +874,24 @@ describe("the derived-boundary note", () => {
|
||||
expect(sheet("done")).not.toContain("Mark event done");
|
||||
});
|
||||
|
||||
test("a points goal shows this week's number, editable in place", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<GameMetaProvider value={(id) => metaFor(id, GAMES)}>
|
||||
<EventDetail
|
||||
{...detailProps}
|
||||
daily
|
||||
goal={{ period: "week", measure: "amount", target: 2100, weekStartsOn: 1 }}
|
||||
hasStoredGoal
|
||||
amounts={{ "2026-08-24": { value: 1900, at: AT } }}
|
||||
row={{ event: PARSED, clock: clockFor(PARSED, detailProps.region, NOW) }}
|
||||
/>
|
||||
</GameMetaProvider>,
|
||||
);
|
||||
expect(html).toContain("1,900 of 2,100 this week · 200 to go");
|
||||
expect(html).toContain('value="1900"');
|
||||
expect(html).toContain("/ 2,100");
|
||||
});
|
||||
|
||||
test("'not daily' warns it removes a goal even when this build cannot read it", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<GameMetaProvider value={(id) => metaFor(id, GAMES)}>
|
||||
|
||||
+35
-1
@@ -17,7 +17,12 @@ import {
|
||||
} from "../src/client/components/Dailies.tsx";
|
||||
import type { GoalStatus } from "../src/shared/goal.ts";
|
||||
import { Welcome } from "../src/client/components/Welcome.tsx";
|
||||
import { chooseGoal, GoalControl, parseDraft } from "../src/client/components/GoalControl.tsx";
|
||||
import {
|
||||
chooseGoal,
|
||||
chooseMeasure,
|
||||
GoalControl,
|
||||
parseDraft,
|
||||
} from "../src/client/components/GoalControl.tsx";
|
||||
import { AmountEditor, parseAmount } from "../src/client/components/AmountEditor.tsx";
|
||||
import type { Task } from "../src/shared/task.ts";
|
||||
import { DailyChecklist } from "../src/client/components/DailyChecklist.tsx";
|
||||
@@ -1423,3 +1428,32 @@ describe("AmountEditor", () => {
|
||||
expect(parseAmount("-5")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("GoalControl with points", () => {
|
||||
const points = { period: "week", measure: "amount", target: 2100, weekStartsOn: 3 } as const;
|
||||
|
||||
test("switching period keeps the measure and the number", () => {
|
||||
expect(chooseGoal("event", points, 1, 84)).toEqual({
|
||||
period: "event", measure: "amount", target: 2100,
|
||||
});
|
||||
expect(chooseGoal("week", { period: "event", measure: "amount", target: 50 }, 2, 84)).toEqual({
|
||||
period: "week", measure: "amount", target: 50, weekStartsOn: 2,
|
||||
});
|
||||
});
|
||||
|
||||
test("switching measure keeps the period", () => {
|
||||
const days = { period: "week", measure: "days", required: 5, weekStartsOn: 3 } as const;
|
||||
expect(chooseMeasure("amount", days, 84)).toEqual({ ...points, target: 100 });
|
||||
expect(chooseMeasure("days", points, 84)).toEqual({ ...days, required: 5 });
|
||||
expect(chooseMeasure("days", days, 84)).toBeNull();
|
||||
});
|
||||
|
||||
test("a points goal shows its target and the measure toggle", () => {
|
||||
const html = render(
|
||||
<GoalControl goal={points} defaultWeekStartsOn={1} maxDays={84} onChange={() => {}} />,
|
||||
);
|
||||
expect(html).toMatch(/aria-pressed="true"[^>]*>Points</);
|
||||
expect(html).toContain('value="2100"');
|
||||
expect(html).toContain("points each week, weeks start");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user