feat: push+email notifications, recipe notes, fork/clone, pantry-aware lists, GDPR export
Five S-sized items from HANDOFF.md's new-features backlog, all wiring up previously-orphaned infra: - createNotification now sends web push + email for every notification type (follow/comment/reply/reaction/rating/mention), not just comments - Personal recipe notes: private per-user notes on any viewable recipe (recipeNotes table had zero API/UI before this) - Recipe fork/clone: deep-copies a viewable recipe into your own library as a private draft, linked via recipeVariations, respects tier quota - Pantry-aware shopping lists: meal-plan-generated lists now subtract on-hand pantry quantities (ingredientId match, falling back to normalized name match) and flag partial/ambiguous matches instead of guessing - GDPR data export: downloadable JSON of a user's own content and activity across every relevant table, secrets/internal tables excluded New migrations 0025 (unique index for recipe-notes upsert) and 0026 (shopping_list_items.in_pantry) generated, left unapplied like 0023/0024. Verified with typecheck, lint, and a full local `docker build`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
CREATE UNIQUE INDEX "recipe_notes_recipe_user_idx" ON "recipe_notes" USING btree ("recipe_id","user_id");
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "shopping_list_items" ADD COLUMN "in_pantry" boolean DEFAULT false NOT NULL;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -176,6 +176,20 @@
|
||||
"when": 1783623700772,
|
||||
"tag": "0024_moaning_roughhouse",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 25,
|
||||
"version": "7",
|
||||
"when": 1783661174093,
|
||||
"tag": "0025_wonderful_micromacro",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 26,
|
||||
"version": "7",
|
||||
"when": 1783661460616,
|
||||
"tag": "0026_sharp_rictor",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -72,6 +72,7 @@ export const shoppingListItems = pgTable("shopping_list_items", {
|
||||
unit: text("unit"),
|
||||
aisle: text("aisle"),
|
||||
checked: boolean("checked").notNull().default(false),
|
||||
inPantry: boolean("in_pantry").notNull().default(false),
|
||||
});
|
||||
|
||||
export const shoppingListMembers = pgTable("shopping_list_members", {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
jsonb,
|
||||
pgEnum,
|
||||
index,
|
||||
uniqueIndex,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
import { users } from "./users";
|
||||
@@ -101,7 +102,9 @@ export const recipeNotes = pgTable("recipe_notes", {
|
||||
content: text("content").notNull(),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
});
|
||||
}, (t) => [
|
||||
uniqueIndex("recipe_notes_recipe_user_idx").on(t.recipeId, t.userId),
|
||||
]);
|
||||
|
||||
export const recipeVariations = pgTable("recipe_variations", {
|
||||
id: text("id").primaryKey(),
|
||||
|
||||
Reference in New Issue
Block a user