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:
co-authored by
Claude Sonnet 4.6
parent
1133fa8aac
commit
28475e483d
@@ -5,6 +5,7 @@ import { revalidatePath } from "next/cache";
|
||||
import { z } from "zod";
|
||||
import { db } from "@/lib/db";
|
||||
import { getCurrentSession } from "@/lib/session";
|
||||
import { logActivity } from "@/modules/_core/activity";
|
||||
import { listItems, lists } from "../schema";
|
||||
import { getOrCreateDefaultList } from "./defaults";
|
||||
import { canAccessList, getList } from "./queries";
|
||||
@@ -46,6 +47,7 @@ export async function createList(input: z.input<typeof listInput>) {
|
||||
.returning();
|
||||
|
||||
if (!list) throw new Error("List was not created");
|
||||
await logActivity({ entityType: "lists.list", entityId: list.id, action: "create", payload: { name: list.name } });
|
||||
revalidatePath("/lists");
|
||||
return list;
|
||||
}
|
||||
@@ -55,6 +57,7 @@ export async function renameList(input: { id: string; name: string }) {
|
||||
const { household } = await getCurrentSession();
|
||||
await assertCanAccessList(parsed.id, household.id);
|
||||
await db.update(lists).set({ name: parsed.name }).where(eq(lists.id, parsed.id));
|
||||
await logActivity({ entityType: "lists.list", entityId: parsed.id, action: "update", payload: { name: parsed.name } });
|
||||
revalidatePath("/lists");
|
||||
revalidatePath(`/lists/${parsed.id}`);
|
||||
await notifyListChanged(parsed.id);
|
||||
@@ -65,6 +68,7 @@ export async function archiveList(input: { id: string }) {
|
||||
const { household } = await getCurrentSession();
|
||||
await assertCanAccessList(parsed.id, household.id);
|
||||
await db.update(lists).set({ archived: true }).where(eq(lists.id, parsed.id));
|
||||
await logActivity({ entityType: "lists.list", entityId: parsed.id, action: "archive" });
|
||||
revalidatePath("/lists");
|
||||
revalidatePath(`/lists/${parsed.id}`);
|
||||
await notifyListChanged(parsed.id);
|
||||
@@ -94,6 +98,7 @@ export async function addItem(input: z.input<typeof itemInput>) {
|
||||
.returning();
|
||||
|
||||
if (!item) throw new Error("List item was not created");
|
||||
await logActivity({ entityType: "lists.item", entityId: item.id, action: "create", payload: { text: item.text } });
|
||||
revalidatePath(`/lists/${parsed.listId}`);
|
||||
await notifyListChanged(parsed.listId);
|
||||
return getList(parsed.listId);
|
||||
@@ -122,6 +127,7 @@ export async function toggleItem(input: { id: string; done?: boolean }) {
|
||||
.set({ done, updatedAt: new Date() })
|
||||
.where(eq(listItems.id, parsed.id));
|
||||
|
||||
await logActivity({ entityType: "lists.item", entityId: parsed.id, action: "toggle", payload: { done, text: existing.text } });
|
||||
revalidatePath(`/lists/${existing.listId}`);
|
||||
await notifyListChanged(existing.listId);
|
||||
return getList(existing.listId);
|
||||
@@ -144,6 +150,7 @@ export async function updateItem(input: z.input<typeof updateItemInput>) {
|
||||
})
|
||||
.where(eq(listItems.id, parsed.id));
|
||||
|
||||
await logActivity({ entityType: "lists.item", entityId: parsed.id, action: "update", payload: { text: parsed.text ?? existing.text } });
|
||||
revalidatePath(`/lists/${existing.listId}`);
|
||||
await notifyListChanged(existing.listId);
|
||||
return getList(existing.listId);
|
||||
@@ -153,6 +160,7 @@ export async function deleteItem(input: { id: string }) {
|
||||
const parsed = z.object({ id: z.string().uuid() }).parse(input);
|
||||
const { household } = await getCurrentSession();
|
||||
const existing = await getAuthorizedItem(parsed.id, household.id);
|
||||
await logActivity({ entityType: "lists.item", entityId: parsed.id, action: "delete", payload: { text: existing.text } });
|
||||
await db.delete(listItems).where(eq(listItems.id, parsed.id));
|
||||
revalidatePath(`/lists/${existing.listId}`);
|
||||
await notifyListChanged(existing.listId);
|
||||
@@ -190,6 +198,7 @@ async function getAuthorizedItem(itemId: string, householdId: string) {
|
||||
id: listItems.id,
|
||||
listId: listItems.listId,
|
||||
done: listItems.done,
|
||||
text: listItems.text,
|
||||
})
|
||||
.from(listItems)
|
||||
.innerJoin(lists, eq(listItems.listId, lists.id))
|
||||
|
||||
Reference in New Issue
Block a user