fix(tasks): keep a task whole through import and edit, and fix two counts

Review found that import and edit rebuilt a task from this build's parse.
That stripped any field it didn't know, and import dropped a newer
version's task entirely. Both now keep the record as stored and validate
only to display it.

An event whose goal is points can't be ticked, so it no longer counts as
waiting today: it counts with its week. The month-day field reset
itself to 1 when emptied; it now keeps a draft.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-13 13:54:29 +02:00
1 parent 54d8424d1e
commit 5a4c746a44
7 files changed
+110 -28

No files matched your search

+10 -1
View File
@@ -10,7 +10,7 @@ import {
strandedNotice,
} from "../src/client/components/CustomForms.tsx";
import { eventCaption, YourOwn } from "../src/client/components/YourOwn.tsx";
import { TaskForm, taskDraftFrom } from "../src/client/components/TaskForm.tsx";
import { parseMonthDay, TaskForm, taskDraftFrom } from "../src/client/components/TaskForm.tsx";
import { EventRow } from "../src/client/components/EventRow.tsx";
import { EventDetail } from "../src/client/components/EventDetail.tsx";
import { AUTHOR, Colophon, REPO_URL, RESET_CLOCK_CREDIT } from "../src/client/components/Colophon.tsx";
@@ -1311,3 +1311,12 @@ describe("the reader's weekly and monthly tasks in settings", () => {
});
});
});
describe("review fixes: the month-day field", () => {
test("an emptied field is mid-edit, not day 1", () => {
expect(parseMonthDay("")).toBeNull();
expect(parseMonthDay("5")).toBe(5);
expect(parseMonthDay("40")).toBe(31);
expect(parseMonthDay("0")).toBe(1);
});
});
+26 -1
View File
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test";
import { mergeAmounts, readableAmounts } from "../src/client/state/useAmounts.ts";
import { mergeTasks, visibleTasks } from "../src/client/state/useTasks.ts";
import { editedTask, mergeTasks, visibleTasks } from "../src/client/state/useTasks.ts";
/**
* The two stores weekly and monthly tasks add. Both are the only copy of what
@@ -91,3 +91,28 @@ describe("amounts", () => {
expect(merged).toEqual({});
});
});
describe("review fixes: a task's data outlives this build", () => {
test("merge keeps fields this build does not know", () => {
const withExtra = { ...task(A, "Weekly", "2026-10-05T00:00:00.000Z"), notes: "keep me" };
expect(mergeTasks({}, { [A]: withExtra })[A]).toEqual(withExtra);
});
test("a newer version's task in a backup reaches a fresh device", () => {
expect(mergeTasks({}, { [FUTURE.id]: FUTURE })[FUTURE.id]).toEqual(FUTURE);
});
test("but junk without a task id or a timestamp still stays out", () => {
expect(mergeTasks({}, { x: { id: "myevent:abcdef1234", updatedAt: "2026" } })).toEqual({});
expect(mergeTasks({}, { x: { id: "mytask:abcdef1234" } })).toEqual({});
});
test("editing keeps fields this build does not know", () => {
const stored = { ...task(A, "Weekly", "2026-10-05T00:00:00.000Z"), notes: "keep me" };
const next = editedTask(stored, {
game: "zzz", title: "Renamed", period: { unit: "week", startsOn: 1 },
measure: { kind: "days", required: 1 },
}, "2026-10-06T00:00:00.000Z");
expect(next).toMatchObject({ title: "Renamed", notes: "keep me", updatedAt: "2026-10-06T00:00:00.000Z" });
});
});
+24
View File
@@ -1457,3 +1457,27 @@ describe("GoalControl with points", () => {
expect(html).toContain("points each week, weeks start");
});
});
describe("review fixes: an event's points goal is not a daily", () => {
test("it is counted with the week, not as waiting today", () => {
const NOW = Date.parse("2026-10-15T12:00:00.000Z");
const ev = {
id: "p1", game: "zzz", title: "Points event", type: "other", summary: null,
startsAt: "2026-10-01T00:00:00.000Z", startPrecision: "day", endsAt: null,
endPrecision: "unknown", regionScoped: false, regionEnds: null,
sourceUrl: "https://example.test",
} as never;
const week: GoalStatus = {
scope: "week", measure: "amount", periodStart: "2026-10-12", required: 2100, count: 100,
met: false, needed: 2000, open: 4, shortfall: false,
};
const total: GoalStatus = { ...week, scope: "event", periodStart: "2026-10-01" };
const tally = (goal: GoalStatus) => {
const [group] = dailyGroups(["zzz"], [ev], NOW, "europe", (id) => metaFor(id, {}),
(e) => Date.parse(e.startsAt), false, () => goal);
return dailyTally(group!.items, () => []);
};
expect(tally(week)).toMatchObject({ total: 0, week: { total: 1, complete: 0 } });
expect(tally(total)).toMatchObject({ total: 0, week: { total: 0 }, month: { total: 0 } });
});
});