Implement activity log (task 22)

- activity_log table in _core/schema.ts with household+created_at index;
  migration 0008_activity_log.sql applied
- logActivity() in _core/activity.ts reads current session and inserts a row
- ActivityLogEntry type + renderActivity?(entry): string added to
  EntityTypeRegistration so modules declare human-readable labels without
  any if/else branches in the widget
- core.activity widget replaced with a real async server component that
  queries the last 20 rows and renders via the registry
- logActivity wired into every create/update/delete in calendar, lists,
  and notes server actions; getAuthorizedItem also returns text so
  toggle/delete can include item text in the payload
- pnpm typecheck, lint, build, and all 4 E2E specs pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ginnoir
2026-05-06 13:57:03 -05:00
co-authored by Claude Sonnet 4.6
parent 1133fa8aac
commit 28475e483d
13 changed files with 182 additions and 11 deletions
+18
View File
@@ -54,6 +54,13 @@ const manifest: ModuleManifest = {
share: { canShare: true, defaultCapabilities: ["read", "write"] },
search: { search: searchLists },
resolveUrl: (id) => `/lists/${id}`,
renderActivity: (entry) => {
const name = entry.payload?.name as string | undefined;
if (entry.action === "create") return `Created list${name ? ` "${name}"` : ""}`;
if (entry.action === "archive") return `Archived list${name ? ` "${name}"` : ""}`;
if (entry.action === "delete") return "Deleted list";
return `Updated list${name ? ` "${name}"` : ""}`;
},
},
{
type: "lists.item",
@@ -61,6 +68,17 @@ const manifest: ModuleManifest = {
share: { canShare: false },
search: { search: searchItems },
resolveUrl: (id) => `/lists/items/${id}`,
renderActivity: (entry) => {
const text = entry.payload?.text as string | undefined;
if (entry.action === "create") return `Added${text ? ` "${text}"` : " item"}`;
if (entry.action === "delete") return `Removed${text ? ` "${text}"` : " item"}`;
if (entry.action === "toggle") {
const done = entry.payload?.done as boolean | undefined;
const label = text ? ` "${text}"` : " item";
return done ? `Checked off${label}` : `Unchecked${label}`;
}
return `Updated${text ? ` "${text}"` : " item"}`;
},
},
],
dashboardWidgets: [