feat: garden care reminder bodies, task sync, upload cleanup
- care reminders now fire with plant-specific body ("Time to water Pothos")
via title/body columns on the reminders table (migration 0016)
- bi-directional task sync: checking off a garden-linked task list item
creates a care log; logging care from garden marks the linked task done
(list_items.metadata stores gardenPlantId + gardenCareType linkage)
- upload error response no longer leaks debug detail; error message
corrected from "5 MB" to "100 MB"
This commit is contained in:
@@ -1,4 +1,13 @@
|
||||
import { index, integer, pgTable, text, timestamp, uuid, boolean } from "drizzle-orm/pg-core";
|
||||
import {
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
uuid,
|
||||
boolean,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { households, users } from "../_core/schema";
|
||||
|
||||
export const lists = pgTable(
|
||||
@@ -33,6 +42,7 @@ export const listItems = pgTable(
|
||||
dueAt: timestamp("due_at", { withTimezone: true }),
|
||||
assigneeId: uuid("assignee_id").references(() => users.id, { onDelete: "set null" }),
|
||||
position: integer("position").notNull().default(0),
|
||||
metadata: jsonb("metadata").$type<Record<string, unknown>>(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ import { z } from "zod";
|
||||
import { db } from "@/lib/db";
|
||||
import { getCurrentSession } from "@/lib/session";
|
||||
import { logActivity } from "@/modules/_core/activity";
|
||||
import { fireItemToggleHooks } from "@/modules/_core/registry";
|
||||
import { listItems, lists } from "../schema";
|
||||
import { getOrCreateDefaultList } from "./defaults";
|
||||
import { canAccessList, getList } from "./queries";
|
||||
@@ -23,6 +24,7 @@ const itemInput = z.object({
|
||||
notes: z.string().trim().max(2000).nullable().optional(),
|
||||
dueAt: z.coerce.date().nullable().optional(),
|
||||
assigneeId: z.string().uuid().nullable().optional(),
|
||||
metadata: z.record(z.string(), z.unknown()).nullable().optional(),
|
||||
});
|
||||
|
||||
const updateItemInput = z.object({
|
||||
@@ -103,6 +105,7 @@ export async function addItem(input: z.input<typeof itemInput>) {
|
||||
notes: parsed.notes || null,
|
||||
dueAt: parsed.dueAt ?? null,
|
||||
assigneeId: parsed.assigneeId ?? null,
|
||||
metadata: parsed.metadata ?? null,
|
||||
position: (positionRow?.maxPosition ?? -1) + 1,
|
||||
})
|
||||
.returning();
|
||||
@@ -133,7 +136,7 @@ export async function addItemToDefaultList(input: { type: string; text: string }
|
||||
|
||||
export async function toggleItem(input: { id: string; done?: boolean }) {
|
||||
const parsed = z.object({ id: z.string().uuid(), done: z.boolean().optional() }).parse(input);
|
||||
const { household } = await getCurrentSession();
|
||||
const { household, user } = await getCurrentSession();
|
||||
const existing = await getAuthorizedItem(parsed.id, household.id);
|
||||
const done = parsed.done ?? !existing.done;
|
||||
|
||||
@@ -148,6 +151,16 @@ export async function toggleItem(input: { id: string; done?: boolean }) {
|
||||
action: "toggle",
|
||||
payload: { done, text: existing.text },
|
||||
});
|
||||
|
||||
if (done && existing.metadata) {
|
||||
await fireItemToggleHooks({
|
||||
itemId: parsed.id,
|
||||
metadata: existing.metadata as Record<string, unknown>,
|
||||
done,
|
||||
userId: user.id,
|
||||
});
|
||||
}
|
||||
|
||||
revalidatePath(`/lists/${existing.listId}`);
|
||||
await notifyListChanged(existing.listId);
|
||||
return getList(existing.listId);
|
||||
@@ -229,6 +242,7 @@ async function getAuthorizedItem(itemId: string, householdId: string) {
|
||||
listId: listItems.listId,
|
||||
done: listItems.done,
|
||||
text: listItems.text,
|
||||
metadata: listItems.metadata,
|
||||
})
|
||||
.from(listItems)
|
||||
.innerJoin(lists, eq(listItems.listId, lists.id))
|
||||
|
||||
Reference in New Issue
Block a user