diff --git a/apps/admin/app/(authed)/page.tsx b/apps/admin/app/(authed)/page.tsx index c335e87..98535eb 100644 --- a/apps/admin/app/(authed)/page.tsx +++ b/apps/admin/app/(authed)/page.tsx @@ -373,12 +373,16 @@ export default function DashboardPage() { return { calories, protein, drinkMl, carbs, fat }; }, [day.drinks, day.foods]); - // Sportbonus des AUSGEWAEHLTEN Tages (nicht der ganzen Woche). Wird nur dann - // aufs Tagesziel addiert, wenn der Nutzer das in den Zielen aktiviert hat. - const dayActivityCalories = - weeklyBudget?.days.find((entry) => entry.date === selectedDate)?.activityCalories ?? 0; + // Tageswerte aus dem Wochenplan (enthaelt die per Wochenbudget verteilten Ziele + // und einen evtl. eingeplanten Mehrbedarf fuer diesen Tag). + const weekDay = weeklyBudget?.days.find((entry) => entry.date === selectedDate); + const dayActivityCalories = weekDay?.activityCalories ?? 0; const dailySportBonus = goals.sportAddsCalories ? dayActivityCalories : 0; - const dailyCalorieGoal = goals.calories + dailySportBonus; + // Basis-Tagesziel ist der verteilte Plan-Wert dieses Tages (z. B. 1.740 nach + // Umverteilung oder 2.800 an einem Blocker-Tag), sonst das allgemeine Tagesziel. + const baseDailyGoal = weekDay?.plannedCalories ?? goals.calories; + const dayExtraCalories = weekDay?.extraCalories ?? 0; + const dailyCalorieGoal = baseDailyGoal + dailySportBonus; const remainingCalories = Math.max(0, dailyCalorieGoal - totals.calories); const nutritionScore = Math.min(100, Math.round((progress(totals.protein, goals.protein) + progress(totals.drinkMl, goals.drinkMl)) / 2)); @@ -583,10 +587,14 @@ export default function DashboardPage() {
- Ziel: {goals.calories.toLocaleString("de-DE")} kcal + Ziel: {baseDailyGoal.toLocaleString("de-DE")} kcal {dailySportBonus > 0 ? ` + ${dailySportBonus.toLocaleString("de-DE")} Sport = ${dailyCalorieGoal.toLocaleString("de-DE")} kcal` : ""} + {dayExtraCalories > 0 ? " · höherer Tag eingeplant" : ""} + {baseDailyGoal < goals.calories && dayExtraCalories === 0 + ? " · Ausgleich für höheren Tag" + : ""}
diff --git a/apps/admin/app/(authed)/wochenbudget/page.tsx b/apps/admin/app/(authed)/wochenbudget/page.tsx index fb9cd87..120d617 100644 --- a/apps/admin/app/(authed)/wochenbudget/page.tsx +++ b/apps/admin/app/(authed)/wochenbudget/page.tsx @@ -1,7 +1,7 @@ "use client"; import { ArrowPathIcon, CalendarDaysIcon, FireIcon, SparklesIcon } from "@heroicons/react/24/outline"; -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { todayKey } from "@/lib/hellth-data"; import { trpc } from "@/lib/trpc"; @@ -15,48 +15,63 @@ function prognosis(balance: number) { return { label: "leicht drüber - flexibel ausgleichen", color: "text-amber-700 bg-amber-50" }; } +type ExtraDraft = { extraCalories: string; note: string }; + export default function WochenbudgetPage() { const utils = trpc.useUtils(); const [selectedDate, setSelectedDate] = useState(todayKey()); - const [extraDate, setExtraDate] = useState(todayKey()); - const [extraCalories, setExtraCalories] = useState("800"); const [saved, setSaved] = useState(false); + // Pro Tag eingeplanter Mehrbedarf ("Blocker"), bevor er verteilt wird. + const [drafts, setDrafts] = useState>({}); const summaryQuery = trpc.health.statsSummary.useQuery({ referenceDate: selectedDate }); const summary = summaryQuery.data?.week ?? null; const prediction = summary ? prognosis(summary.balance) : null; - const upsertPlanMutation = trpc.health.upsertWeeklyCaloriePlan.useMutation({ + + const setExtrasMutation = trpc.health.setWeeklyExtras.useMutation({ onSuccess: async () => { - await utils.health.statsSummary.invalidate({ referenceDate: selectedDate }); + // Ohne Key-Filter invalidieren, damit Dashboard + Statistiken mitziehen. + await utils.health.statsSummary.invalidate(); + await utils.health.overview.invalidate(); setSaved(true); window.setTimeout(() => setSaved(false), 1500); }, }); - const dayByDate = useMemo(() => new Map((summary?.days ?? []).map((day) => [day.date, day])), [summary?.days]); - - function updatePlan(date: string, plannedCalories: number, note?: string) { - upsertPlanMutation.mutate({ - date, - plannedCalories: Math.max(800, Math.min(6000, Math.round(plannedCalories))), - note: note ?? dayByDate.get(date)?.note ?? null, + // Entwurf aus den gespeicherten Tageswerten vorbefüllen, sobald die Woche geladen ist. + useEffect(() => { + if (!summary) return; + setDrafts((current) => { + const next: Record = {}; + for (const day of summary.days) { + next[day.date] = current[day.date] ?? { + extraCalories: day.extraCalories ? String(day.extraCalories) : "", + note: day.note ?? "", + }; + } + return next; }); + }, [summary]); + + const totalExtra = useMemo( + () => Object.values(drafts).reduce((sum, draft) => sum + (Number(draft.extraCalories) || 0), 0), + [drafts], + ); + + function updateDraft(date: string, patch: Partial) { + setDrafts((current) => ({ ...current, [date]: { ...current[date], ...patch } as ExtraDraft })); } - function applyRestaurantDistribution() { + function redistribute() { if (!summary) return; - const extra = Math.max(0, Number(extraCalories) || 0); - const days = summary.days; - const targetDay = days.find((day) => day.date === extraDate); - if (!targetDay) return; - - const otherDays = days.filter((day) => day.date !== extraDate); - const savingPerOtherDay = otherDays.length ? Math.ceil(extra / otherDays.length) : 0; - - updatePlan(extraDate, targetDay.plannedCalories + extra, targetDay.note ?? "Restaurant / höherer Tag"); - for (const day of otherDays) { - updatePlan(day.date, Math.max(1200, day.plannedCalories - savingPerOtherDay), day.note); - } + setExtrasMutation.mutate({ + weekStart: summary.weekStart, + days: summary.days.map((day) => ({ + date: day.date, + extraCalories: Math.max(0, Number(drafts[day.date]?.extraCalories) || 0), + note: drafts[day.date]?.note?.trim() || null, + })), + }); } if (!summary || !prediction) { @@ -77,7 +92,9 @@ export default function WochenbudgetPage() {

Flexible Planung

Wochenbudget

- Ein höherer Tag ist kein Problem. Hellth Hub verteilt den Ausgleich freundlich auf die Woche. + Trag pro Tag ein, wenn du mehr brauchst (z. B. Familienfeier +800). Mit + „kcal neu verteilen" hebt Hellth Hub diese Tage an und gleicht den Rest der Woche + gleichmäßig aus. Dein Wochenziel bleibt gleich.

setSelectedDate(event.target.value || todayKey())} className="rounded-full border border-gray-200 px-4 py-3 text-sm font-bold" /> @@ -98,7 +115,7 @@ export default function WochenbudgetPage() {

+{fmt(summary.activityCalories)}

-

Sportbonus

+

Sportbonus (Woche)

{prediction.label}
@@ -108,13 +125,34 @@ export default function WochenbudgetPage() {
-

Tagesverteilung

+
+
+

Tagesverteilung

+

+ {totalExtra > 0 + ? `${fmt(totalExtra)} kcal Mehrbedarf eingeplant – wird beim Verteilen auf die übrigen Tage ausgeglichen.` + : "Trag bei einzelnen Tagen einen Mehrbedarf ein und verteile anschließend."} +

+
+ +
+
{summary.days.map((day) => { const date = new Date(`${day.date}T12:00:00`); const over = day.eatenCalories > day.plannedCalories + day.activityCalories; + const draft = drafts[day.date] ?? { extraCalories: "", note: "" }; + const isBlocker = (Number(draft.extraCalories) || 0) > 0; return ( -
+

{date.toLocaleDateString("de-DE", { weekday: "short" })}

{date.toLocaleDateString("de-DE")}

@@ -126,14 +164,25 @@ export default function WochenbudgetPage() {

{fmt(day.eatenCalories)} / {fmt(day.plannedCalories)} kcal {day.activityCalories ? ` + ${fmt(day.activityCalories)} Sport` : ""} - {day.note ? ` - ${day.note}` : ""}

+ updatePlan(day.date, Number(event.target.value) || summary.dailyGoal, day.note)} - className="rounded-2xl border border-gray-200 px-4 py-3 text-sm font-bold" + value={draft.note} + onChange={(event) => updateDraft(day.date, { note: event.target.value })} + placeholder="Anlass" + className="rounded-2xl border border-gray-200 px-3 py-3 text-sm font-bold" />

= 0 ? "text-emerald-700" : "text-amber-700"}`}> {day.balance >= 0 ? "+" : ""}{fmt(day.balance)} kcal @@ -144,21 +193,6 @@ export default function WochenbudgetPage() {

-
-

Restaurantbesuch einplanen

-

- Beispiel: Samstag +800 kcal. Die App verteilt den Ausgleich auf die restlichen Tage. -

-
- setExtraDate(event.target.value)} className="rounded-2xl border border-gray-200 px-4 py-3 text-sm font-bold" /> - setExtraCalories(event.target.value)} placeholder="+ kcal" className="rounded-2xl border border-gray-200 px-4 py-3 text-sm font-bold" /> - -
-
- {saved ?
Gespeichert
: null}
); diff --git a/apps/admin/lib/hellth/stats.ts b/apps/admin/lib/hellth/stats.ts index 5c7ae96..42a7792 100644 --- a/apps/admin/lib/hellth/stats.ts +++ b/apps/admin/lib/hellth/stats.ts @@ -58,6 +58,7 @@ export function buildWeeklyBudgetSummary(data: HellthData, dateKey = todayKey()) return { date, plannedCalories, + extraCalories: planByDate.get(date)?.extraCalories ?? 0, eatenCalories, activityCalories, balance: plannedCalories + activityCalories - eatenCalories, diff --git a/apps/admin/lib/hellth/types.ts b/apps/admin/lib/hellth/types.ts index 45324b2..323a110 100644 --- a/apps/admin/lib/hellth/types.ts +++ b/apps/admin/lib/hellth/types.ts @@ -172,6 +172,7 @@ export type ActivityLog = { export type WeeklyCaloriePlan = { date: string; plannedCalories: number; + extraCalories?: number; note?: string; }; @@ -254,6 +255,7 @@ export type HellthData = { export type WeeklyBudgetDay = { date: string; plannedCalories: number; + extraCalories: number; eatenCalories: number; activityCalories: number; balance: number; diff --git a/packages/api/src/trpc/health.test.ts b/packages/api/src/trpc/health.test.ts index b960ee4..178613b 100644 --- a/packages/api/src/trpc/health.test.ts +++ b/packages/api/src/trpc/health.test.ts @@ -1,6 +1,7 @@ import assert from "node:assert/strict"; import test from "node:test"; import { appRouter } from "./index"; +import { distributeWeeklyExtras } from "./routers/health/_shared"; type Call = { op: string; payload?: unknown }; @@ -771,3 +772,85 @@ test("health.upsertProfile weist mealsPerDay groesser 8 ab", async () => { ); }); +test("distributeWeeklyExtras haelt das Wochenbudget konstant", async () => { + const daily = 2000; + // So +800 (Familienfeier), Mi +500 (Trinken), Rest ohne Extra. + const planned = distributeWeeklyExtras( + { + "2026-05-25": 0, + "2026-05-26": 0, + "2026-05-27": 500, + "2026-05-28": 0, + "2026-05-29": 0, + "2026-05-30": 0, + "2026-05-31": 800, + }, + daily, + ); + + // Blocker-Tage = Tagesziel + Extra + assert.equal(planned["2026-05-27"], 2500); + assert.equal(planned["2026-05-31"], 2800); + // 1300 Extra auf 5 Tage ohne Extra: ceil(1300/5)=260 Abzug pro Tag + assert.equal(planned["2026-05-25"], 1740); + + // Wochenbudget bleibt im Rahmen des Ausgleichs erhalten (kleine Ceil-Rundung erlaubt). + const total = Object.values(planned).reduce((sum, value) => sum + value, 0); + assert.ok(total <= daily * 7, `Summe ${total} darf das Wochenziel ${daily * 7} nicht ueberschreiten`); + assert.ok(total >= daily * 7 - 7, "Summe darf nur minimal (Rundung) unter dem Wochenziel liegen"); +}); + +test("distributeWeeklyExtras unterschreitet den Floor nicht", async () => { + // Sehr grosser Blocker an einem Tag, der die anderen rechnerisch unter 1200 druecken wuerde. + const planned = distributeWeeklyExtras( + { + "2026-05-25": 0, + "2026-05-26": 0, + "2026-05-31": 4000, + }, + 2000, + 1200, + ); + assert.equal(planned["2026-05-31"], 6000); + // 4000/2 = 2000 Abzug -> 2000-2000=0, aber Floor greift bei 1200 + assert.equal(planned["2026-05-25"], 1200); + assert.equal(planned["2026-05-26"], 1200); +}); + +test("health.setWeeklyExtras verteilt und speichert plannedCalories pro Tag", async () => { + const { db, calls } = createHealthDbMock(); + const caller = appRouter.createCaller(createContext({ db }) as never); + + const result = await caller.health.setWeeklyExtras({ + weekStart: "2026-05-25", + days: [ + { date: "2026-05-25", extraCalories: 0 }, + { date: "2026-05-26", extraCalories: 0 }, + { date: "2026-05-27", extraCalories: 0 }, + { date: "2026-05-28", extraCalories: 0 }, + { date: "2026-05-29", extraCalories: 0 }, + { date: "2026-05-30", extraCalories: 0 }, + { date: "2026-05-31", extraCalories: 700, note: "Familienfeier" }, + ], + }); + + assert.deepEqual(result, { ok: true }); + // Mock-Goals liefern calories=2000. Blocker-Tag bekommt 2700, andere 2000-ceil(700/6)=1883. + const insertCalls = calls.filter((call) => call.op === "tx.insert.values"); + const sunday = insertCalls.find((call) => (call.payload as { date?: string }).date === "2026-05-31")?.payload as { + plannedCalories?: number; + extraCalories?: number; + note?: string | null; + }; + assert.equal(sunday.plannedCalories, 2700); + assert.equal(sunday.extraCalories, 700); + assert.equal(sunday.note, "Familienfeier"); + + const monday = insertCalls.find((call) => (call.payload as { date?: string }).date === "2026-05-25")?.payload as { + plannedCalories?: number; + extraCalories?: number; + }; + assert.equal(monday.plannedCalories, 2000 - Math.ceil(700 / 6)); + assert.equal(monday.extraCalories, 0); +}); + diff --git a/packages/api/src/trpc/routers/health/_shared.ts b/packages/api/src/trpc/routers/health/_shared.ts index 7ceb34e..3d0d54f 100644 --- a/packages/api/src/trpc/routers/health/_shared.ts +++ b/packages/api/src/trpc/routers/health/_shared.ts @@ -249,6 +249,36 @@ export function numberString(value: number | string | null | undefined, fallback return String(value); } +/** + * Verteilt eingeplante Mehrkalorien ("Blocker", z. B. Familienfeier) gleichmaessig + * auf die uebrigen Wochentage, sodass das Wochenbudget (dailyGoal * 7) exakt erhalten + * bleibt. Tage mit Extra erhalten dailyGoal + extra; Tage ohne Extra werden anteilig + * reduziert, aber nie unter floor (Schutz vor zu niedrigen Tageszielen). + * + * Gibt fuer jeden uebergebenen Tag den errechneten plannedCalories-Wert zurueck. + */ +export function distributeWeeklyExtras( + extrasByDate: Record, + dailyGoal: number, + floor = 1200, +): Record { + const dates = Object.keys(extrasByDate); + const totalExtra = dates.reduce((sum, date) => sum + Math.max(0, extrasByDate[date] ?? 0), 0); + const daysWithoutExtra = dates.filter((date) => (extrasByDate[date] ?? 0) <= 0); + const reductionPerDay = daysWithoutExtra.length + ? Math.ceil(totalExtra / daysWithoutExtra.length) + : 0; + + const result: Record = {}; + for (const date of dates) { + const extra = Math.max(0, extrasByDate[date] ?? 0); + result[date] = extra > 0 + ? dailyGoal + extra + : Math.max(floor, dailyGoal - reductionPerDay); + } + return result; +} + export function startOfWeek(dateKey: string) { const date = new Date(`${dateKey}T12:00:00`); const day = date.getDay() === 0 ? 7 : date.getDay(); diff --git a/packages/api/src/trpc/routers/health/community.ts b/packages/api/src/trpc/routers/health/community.ts index 44b7832..5d17c0c 100644 --- a/packages/api/src/trpc/routers/health/community.ts +++ b/packages/api/src/trpc/routers/health/community.ts @@ -309,6 +309,7 @@ export const communityRouter = router({ return { date, plannedCalories, + extraCalories: weeklyPlanByDate.get(date)?.extraCalories ?? 0, eatenCalories, activityCalories, balance: plannedCalories + activityCalories - eatenCalories, diff --git a/packages/api/src/trpc/routers/health/tracking.ts b/packages/api/src/trpc/routers/health/tracking.ts index 73dad76..dc56d86 100644 --- a/packages/api/src/trpc/routers/health/tracking.ts +++ b/packages/api/src/trpc/routers/health/tracking.ts @@ -7,6 +7,7 @@ import { addDays, auditHealthMutation, dateKeySchema, + distributeWeeklyExtras, id, mealTypeSchema, numberString, @@ -308,6 +309,60 @@ export const trackingRouter = router({ return { ok: true }; }), + // Setzt fuer eine ganze Woche die eingeplanten Mehrkalorien ("Blocker") pro Tag und + // verteilt den Ausgleich serverseitig, sodass das Wochenbudget (dailyGoal * 7) erhalten + // bleibt. Ersetzt das fehleranfaellige Verteilen einzelner Tage im Frontend. + setWeeklyExtras: protectedProcedure + .input(z.object({ + weekStart: dateKeySchema, + days: z.array(z.object({ + date: dateKeySchema, + extraCalories: z.number().int().min(0).max(4000), + note: z.string().max(500).optional().nullable(), + })).min(1).max(7), + })) + .mutation(async ({ ctx, input }) => { + const goals = await ctx.db.query.healthGoals.findFirst({ + where: (row, { eq: rowEq }) => rowEq(row.userId, ctx.user.id), + }); + const dailyGoal = goals?.calories ?? 2000; + + const extrasByDate: Record = {}; + for (const day of input.days) extrasByDate[day.date] = day.extraCalories; + const noteByDate = new Map(input.days.map((day) => [day.date, day.note || null])); + const plannedByDate = distributeWeeklyExtras(extrasByDate, dailyGoal); + + await ctx.db.transaction(async (tx) => { + for (const day of input.days) { + await tx + .insert(schema.weeklyCaloriePlans) + .values({ + userId: ctx.user.id, + date: day.date, + plannedCalories: plannedByDate[day.date]!, + extraCalories: day.extraCalories, + note: noteByDate.get(day.date) ?? null, + updatedAt: new Date(), + }) + .onConflictDoUpdate({ + target: [schema.weeklyCaloriePlans.userId, schema.weeklyCaloriePlans.date], + set: { + plannedCalories: plannedByDate[day.date]!, + extraCalories: day.extraCalories, + note: noteByDate.get(day.date) ?? null, + updatedAt: new Date(), + }, + }); + } + }); + + auditHealthMutation(ctx, "weekly-calorie-plan.set-extras", { + weekStart: input.weekStart, + totalExtra: Object.values(extrasByDate).reduce((sum, value) => sum + value, 0), + }); + return { ok: true }; + }), + addActivityLog: protectedProcedure .input(z.object({ date: dateKeySchema, diff --git a/packages/db/drizzle/0018_outgoing_betty_ross.sql b/packages/db/drizzle/0018_outgoing_betty_ross.sql new file mode 100644 index 0000000..cbc4e9b --- /dev/null +++ b/packages/db/drizzle/0018_outgoing_betty_ross.sql @@ -0,0 +1 @@ +ALTER TABLE "weekly_calorie_plans" ADD COLUMN "extraCalories" integer DEFAULT 0 NOT NULL; diff --git a/packages/db/drizzle/meta/0018_snapshot.json b/packages/db/drizzle/meta/0018_snapshot.json new file mode 100644 index 0000000..7c65838 --- /dev/null +++ b/packages/db/drizzle/meta/0018_snapshot.json @@ -0,0 +1,4624 @@ +{ + "id": "52d2105c-e89b-4da7-8337-ff46cd3afd97", + "prevId": "88d311e4-7c5b-4ee0-996d-952afc7cdb10", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "accountId": { + "name": "accountId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerId": { + "name": "providerId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "accessToken": { + "name": "accessToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idToken": { + "name": "idToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessTokenExpiresAt": { + "name": "accessTokenExpiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refreshTokenExpiresAt": { + "name": "refreshTokenExpiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.passkey": { + "name": "passkey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credentialID": { + "name": "credentialID", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "deviceType": { + "name": "deviceType", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backedUp": { + "name": "backedUp", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "aaguid": { + "name": "aaguid", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "passkey_userId_user_id_fk": { + "name": "passkey_userId_user_id_fk", + "tableFrom": "passkey", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userAgent": { + "name": "userAgent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "impersonatedBy": { + "name": "impersonatedBy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.twoFactor": { + "name": "twoFactor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backupCodes": { + "name": "backupCodes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "twoFactor_userId_user_id_fk": { + "name": "twoFactor_userId_user_id_fk", + "tableFrom": "twoFactor", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "twoFactorEnabled": { + "name": "twoFactorEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "twoFactorRecoveryRequired": { + "name": "twoFactorRecoveryRequired", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "twoFactorRecoveryStartedAt": { + "name": "twoFactorRecoveryStartedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "twoFactorRecoveryStartedBy": { + "name": "twoFactorRecoveryStartedBy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "banReason": { + "name": "banReason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banExpires": { + "name": "banExpires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "emailVerified": { + "name": "emailVerified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "theme": { + "name": "theme", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.platform_settings": { + "name": "platform_settings", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.coach_principles": { + "name": "coach_principles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'general'" + }, + "isActive": { + "name": "isActive", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sortOrder": { + "name": "sortOrder", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.coach_recommendations": { + "name": "coach_recommendations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "coach_priority", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'medium'" + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'daily'" + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'rule'" + }, + "validFrom": { + "name": "validFrom", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "validTo": { + "name": "validTo", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "dismissedAt": { + "name": "dismissedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "coach_recommendations_user_idx": { + "name": "coach_recommendations_user_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "coach_recommendations_userId_user_id_fk": { + "name": "coach_recommendations_userId_user_id_fk", + "tableFrom": "coach_recommendations", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.health_goals": { + "name": "health_goals", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "drinkMl": { + "name": "drinkMl", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 2500 + }, + "calories": { + "name": "calories", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 2000 + }, + "carbsG": { + "name": "carbsG", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 200 + }, + "fatG": { + "name": "fatG", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 67 + }, + "proteinG": { + "name": "proteinG", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 150 + }, + "weightKg": { + "name": "weightKg", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "fastingHours": { + "name": "fastingHours", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 12 + }, + "weeklyCalories": { + "name": "weeklyCalories", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sportAddsCalories": { + "name": "sportAddsCalories", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "health_goals_userId_user_id_fk": { + "name": "health_goals_userId_user_id_fk", + "tableFrom": "health_goals", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.health_profiles": { + "name": "health_profiles", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "goal": { + "name": "goal", + "type": "health_goal", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'healthier'" + }, + "age": { + "name": "age", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heightCm": { + "name": "heightCm", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "currentWeightKg": { + "name": "currentWeightKg", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "targetWeightKg": { + "name": "targetWeightKg", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "activityLevel": { + "name": "activityLevel", + "type": "activity_level", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'moderate'" + }, + "dietStyle": { + "name": "dietStyle", + "type": "diet_style", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'flexible'" + }, + "dietPreferences": { + "name": "dietPreferences", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"vegetarian\",\"vegan\",\"meat\",\"fish\"]'::jsonb" + }, + "mealsPerDay": { + "name": "mealsPerDay", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + }, + "cooksPerDay": { + "name": "cooksPerDay", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "preferredMealPrepDays": { + "name": "preferredMealPrepDays", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "meatFishFrequency": { + "name": "meatFishFrequency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'flexible'" + }, + "cookingTime": { + "name": "cookingTime", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'medium'" + }, + "budgetFocus": { + "name": "budgetFocus", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "excludedFoods": { + "name": "excludedFoods", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "biggestChallenge": { + "name": "biggestChallenge", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "medicalNote": { + "name": "medicalNote", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "completedAt": { + "name": "completedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "health_profiles_userId_user_id_fk": { + "name": "health_profiles_userId_user_id_fk", + "tableFrom": "health_profiles", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reminder_rules": { + "name": "reminder_rules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "reminder_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "intervalMinutes": { + "name": "intervalMinutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 90 + }, + "startTime": { + "name": "startTime", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'08:00'" + }, + "endTime": { + "name": "endTime", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'20:00'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quietDays": { + "name": "quietDays", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reminder_rules_user_kind_unique": { + "name": "reminder_rules_user_kind_unique", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reminder_rules_userId_user_id_fk": { + "name": "reminder_rules_userId_user_id_fk", + "tableFrom": "reminder_rules", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.weekly_calorie_plans": { + "name": "weekly_calorie_plans", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "plannedCalories": { + "name": "plannedCalories", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "extraCalories": { + "name": "extraCalories", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "weekly_calorie_plans_userId_user_id_fk": { + "name": "weekly_calorie_plans_userId_user_id_fk", + "tableFrom": "weekly_calorie_plans", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "weekly_calorie_plans_userId_date_pk": { + "name": "weekly_calorie_plans_userId_date_pk", + "columns": [ + "userId", + "date" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.food_nutrition_sources": { + "name": "food_nutrition_sources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "foodId": { + "name": "foodId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerFoodId": { + "name": "providerFoodId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kcalPer100g": { + "name": "kcalPer100g", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "proteinPer100g": { + "name": "proteinPer100g", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "carbsPer100g": { + "name": "carbsPer100g", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "fatPer100g": { + "name": "fatPer100g", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "servingG": { + "name": "servingG", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 70 + }, + "raw": { + "name": "raw", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "food_nutrition_sources_food_idx": { + "name": "food_nutrition_sources_food_idx", + "columns": [ + { + "expression": "foodId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "food_nutrition_sources_provider_idx": { + "name": "food_nutrition_sources_provider_idx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "providerFoodId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "food_nutrition_sources_foodId_foods_id_fk": { + "name": "food_nutrition_sources_foodId_foods_id_fk", + "tableFrom": "food_nutrition_sources", + "tableTo": "foods", + "columnsFrom": [ + "foodId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.food_portions": { + "name": "food_portions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "foodId": { + "name": "foodId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "unit": { + "name": "unit", + "type": "portion_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true + }, + "gramsEquivalent": { + "name": "gramsEquivalent", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "food_portions_food_idx": { + "name": "food_portions_food_idx", + "columns": [ + { + "expression": "foodId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "food_portions_foodId_foods_id_fk": { + "name": "food_portions_foodId_foods_id_fk", + "tableFrom": "food_portions", + "tableTo": "foods", + "columnsFrom": [ + "foodId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.food_user_preferences": { + "name": "food_user_preferences", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "foodId": { + "name": "foodId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vote": { + "name": "vote", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "food_user_preferences_user_idx": { + "name": "food_user_preferences_user_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "food_user_preferences_food_idx": { + "name": "food_user_preferences_food_idx", + "columns": [ + { + "expression": "foodId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "food_user_preferences_userId_user_id_fk": { + "name": "food_user_preferences_userId_user_id_fk", + "tableFrom": "food_user_preferences", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "food_user_preferences_foodId_foods_id_fk": { + "name": "food_user_preferences_foodId_foods_id_fk", + "tableFrom": "food_user_preferences", + "tableTo": "foods", + "columnsFrom": [ + "foodId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "food_user_preferences_userId_foodId_pk": { + "name": "food_user_preferences_userId_foodId_pk", + "columns": [ + "userId", + "foodId" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.foods": { + "name": "foods", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ownerUserId": { + "name": "ownerUserId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "barcode": { + "name": "barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Sonstiges'" + }, + "kcalPer100g": { + "name": "kcalPer100g", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "proteinPer100g": { + "name": "proteinPer100g", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "carbsPer100g": { + "name": "carbsPer100g", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "fatPer100g": { + "name": "fatPer100g", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "servingG": { + "name": "servingG", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'100'" + }, + "preferredUnit": { + "name": "preferredUnit", + "type": "portion_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'g'" + }, + "densityGPerMl": { + "name": "densityGPerMl", + "type": "numeric(8, 4)", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "food_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "isVerified": { + "name": "isVerified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isCommunity": { + "name": "isCommunity", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "availableEverywhere": { + "name": "availableEverywhere", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "foods_owner_idx": { + "name": "foods_owner_idx", + "columns": [ + { + "expression": "ownerUserId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "foods_barcode_idx": { + "name": "foods_barcode_idx", + "columns": [ + { + "expression": "barcode", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "foods_name_idx": { + "name": "foods_name_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "foods_ownerUserId_user_id_fk": { + "name": "foods_ownerUserId_user_id_fk", + "tableFrom": "foods", + "tableTo": "user", + "columnsFrom": [ + "ownerUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.food_stores": { + "name": "food_stores", + "schema": "", + "columns": { + "foodId": { + "name": "foodId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storeId": { + "name": "storeId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "departmentId": { + "name": "departmentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lastPrice": { + "name": "lastPrice", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": false + }, + "availabilityTags": { + "name": "availabilityTags", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "food_stores_store_idx": { + "name": "food_stores_store_idx", + "columns": [ + { + "expression": "storeId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "food_stores_foodId_foods_id_fk": { + "name": "food_stores_foodId_foods_id_fk", + "tableFrom": "food_stores", + "tableTo": "foods", + "columnsFrom": [ + "foodId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "food_stores_storeId_stores_id_fk": { + "name": "food_stores_storeId_stores_id_fk", + "tableFrom": "food_stores", + "tableTo": "stores", + "columnsFrom": [ + "storeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "food_stores_departmentId_store_departments_id_fk": { + "name": "food_stores_departmentId_store_departments_id_fk", + "tableFrom": "food_stores", + "tableTo": "store_departments", + "columnsFrom": [ + "departmentId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "food_stores_foodId_storeId_pk": { + "name": "food_stores_foodId_storeId_pk", + "columns": [ + "foodId", + "storeId" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.store_change_requests": { + "name": "store_change_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storeId": { + "name": "storeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requestType": { + "name": "requestType", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'structure_update'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "proposedStore": { + "name": "proposedStore", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "proposedDepartments": { + "name": "proposedDepartments", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "reviewedBy": { + "name": "reviewedBy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reviewedAt": { + "name": "reviewedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "reviewNote": { + "name": "reviewNote", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acceptedStoreId": { + "name": "acceptedStoreId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "store_change_requests_user_idx": { + "name": "store_change_requests_user_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "store_change_requests_status_idx": { + "name": "store_change_requests_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "createdAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "store_change_requests_userId_user_id_fk": { + "name": "store_change_requests_userId_user_id_fk", + "tableFrom": "store_change_requests", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "store_change_requests_storeId_stores_id_fk": { + "name": "store_change_requests_storeId_stores_id_fk", + "tableFrom": "store_change_requests", + "tableTo": "stores", + "columnsFrom": [ + "storeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "store_change_requests_reviewedBy_user_id_fk": { + "name": "store_change_requests_reviewedBy_user_id_fk", + "tableFrom": "store_change_requests", + "tableTo": "user", + "columnsFrom": [ + "reviewedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "store_change_requests_acceptedStoreId_stores_id_fk": { + "name": "store_change_requests_acceptedStoreId_stores_id_fk", + "tableFrom": "store_change_requests", + "tableTo": "stores", + "columnsFrom": [ + "acceptedStoreId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.store_departments": { + "name": "store_departments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "storeId": { + "name": "storeId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sortOrder": { + "name": "sortOrder", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "store_departments_store_idx": { + "name": "store_departments_store_idx", + "columns": [ + { + "expression": "storeId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "store_departments_storeId_stores_id_fk": { + "name": "store_departments_storeId_stores_id_fk", + "tableFrom": "store_departments", + "tableTo": "stores", + "columnsFrom": [ + "storeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.store_user_preferences": { + "name": "store_user_preferences", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storeId": { + "name": "storeId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "favorite": { + "name": "favorite", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "store_user_preferences_user_idx": { + "name": "store_user_preferences_user_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "store_user_preferences_userId_user_id_fk": { + "name": "store_user_preferences_userId_user_id_fk", + "tableFrom": "store_user_preferences", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "store_user_preferences_storeId_stores_id_fk": { + "name": "store_user_preferences_storeId_stores_id_fk", + "tableFrom": "store_user_preferences", + "tableTo": "stores", + "columnsFrom": [ + "storeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "store_user_preferences_userId_storeId_pk": { + "name": "store_user_preferences_userId_storeId_pk", + "columns": [ + "userId", + "storeId" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.stores": { + "name": "stores", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ownerUserId": { + "name": "ownerUserId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "placeId": { + "name": "placeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "latitude": { + "name": "latitude", + "type": "numeric(9, 6)", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "numeric(9, 6)", + "primaryKey": false, + "notNull": false + }, + "favorite": { + "name": "favorite", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "stores_ownerUserId_user_id_fk": { + "name": "stores_ownerUserId_user_id_fk", + "tableFrom": "stores", + "tableTo": "user", + "columnsFrom": [ + "ownerUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_ingredients": { + "name": "recipe_ingredients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "recipeId": { + "name": "recipeId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "foodId": { + "name": "foodId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amountG": { + "name": "amountG", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true + }, + "displayAmount": { + "name": "displayAmount", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": false + }, + "displayUnit": { + "name": "displayUnit", + "type": "portion_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "portionId": { + "name": "portionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sortOrder": { + "name": "sortOrder", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "recipe_ingredients_recipe_idx": { + "name": "recipe_ingredients_recipe_idx", + "columns": [ + { + "expression": "recipeId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_ingredients_recipeId_recipes_id_fk": { + "name": "recipe_ingredients_recipeId_recipes_id_fk", + "tableFrom": "recipe_ingredients", + "tableTo": "recipes", + "columnsFrom": [ + "recipeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_ingredients_foodId_foods_id_fk": { + "name": "recipe_ingredients_foodId_foods_id_fk", + "tableFrom": "recipe_ingredients", + "tableTo": "foods", + "columnsFrom": [ + "foodId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "recipe_ingredients_portionId_food_portions_id_fk": { + "name": "recipe_ingredients_portionId_food_portions_id_fk", + "tableFrom": "recipe_ingredients", + "tableTo": "food_portions", + "columnsFrom": [ + "portionId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_ratings": { + "name": "recipe_ratings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "recipeId": { + "name": "recipeId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rating": { + "name": "rating", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "comment": { + "name": "comment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_ratings_user_recipe_unique": { + "name": "recipe_ratings_user_recipe_unique", + "columns": [ + { + "expression": "recipeId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_ratings_recipeId_recipes_id_fk": { + "name": "recipe_ratings_recipeId_recipes_id_fk", + "tableFrom": "recipe_ratings", + "tableTo": "recipes", + "columnsFrom": [ + "recipeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_ratings_userId_user_id_fk": { + "name": "recipe_ratings_userId_user_id_fk", + "tableFrom": "recipe_ratings", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_submission_ingredients": { + "name": "recipe_submission_ingredients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "submissionId": { + "name": "submissionId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "foodId": { + "name": "foodId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amountG": { + "name": "amountG", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true + }, + "displayAmount": { + "name": "displayAmount", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": false + }, + "displayUnit": { + "name": "displayUnit", + "type": "portion_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "portionId": { + "name": "portionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sortOrder": { + "name": "sortOrder", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "recipe_submission_ingredients_submission_idx": { + "name": "recipe_submission_ingredients_submission_idx", + "columns": [ + { + "expression": "submissionId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_submission_ingredients_submissionId_recipe_submissions_id_fk": { + "name": "recipe_submission_ingredients_submissionId_recipe_submissions_id_fk", + "tableFrom": "recipe_submission_ingredients", + "tableTo": "recipe_submissions", + "columnsFrom": [ + "submissionId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_submission_ingredients_foodId_foods_id_fk": { + "name": "recipe_submission_ingredients_foodId_foods_id_fk", + "tableFrom": "recipe_submission_ingredients", + "tableTo": "foods", + "columnsFrom": [ + "foodId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "recipe_submission_ingredients_portionId_food_portions_id_fk": { + "name": "recipe_submission_ingredients_portionId_food_portions_id_fk", + "tableFrom": "recipe_submission_ingredients", + "tableTo": "food_portions", + "columnsFrom": [ + "portionId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_submissions": { + "name": "recipe_submissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portions": { + "name": "portions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "minutes": { + "name": "minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mealPrepDays": { + "name": "mealPrepDays", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "reviewNote": { + "name": "reviewNote", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reviewedBy": { + "name": "reviewedBy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reviewedAt": { + "name": "reviewedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "acceptedRecipeId": { + "name": "acceptedRecipeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_submissions_user_idx": { + "name": "recipe_submissions_user_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_submissions_status_idx": { + "name": "recipe_submissions_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "createdAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_submissions_userId_user_id_fk": { + "name": "recipe_submissions_userId_user_id_fk", + "tableFrom": "recipe_submissions", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_submissions_reviewedBy_user_id_fk": { + "name": "recipe_submissions_reviewedBy_user_id_fk", + "tableFrom": "recipe_submissions", + "tableTo": "user", + "columnsFrom": [ + "reviewedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "recipe_submissions_acceptedRecipeId_recipes_id_fk": { + "name": "recipe_submissions_acceptedRecipeId_recipes_id_fk", + "tableFrom": "recipe_submissions", + "tableTo": "recipes", + "columnsFrom": [ + "acceptedRecipeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipes": { + "name": "recipes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ownerUserId": { + "name": "ownerUserId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portions": { + "name": "portions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "minutes": { + "name": "minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mealPrepDays": { + "name": "mealPrepDays", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "isPublic": { + "name": "isPublic", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipes_owner_idx": { + "name": "recipes_owner_idx", + "columns": [ + { + "expression": "ownerUserId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipes_ownerUserId_user_id_fk": { + "name": "recipes_ownerUserId_user_id_fk", + "tableFrom": "recipes", + "tableTo": "user", + "columnsFrom": [ + "ownerUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.activity_logs": { + "name": "activity_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "templateId": { + "name": "templateId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "minutes": { + "name": "minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "points": { + "name": "points", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "calories": { + "name": "calories", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "source": { + "name": "source", + "type": "activity_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "externalId": { + "name": "externalId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "activity_logs_user_date_idx": { + "name": "activity_logs_user_date_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "activity_logs_source_external_unique": { + "name": "activity_logs_source_external_unique", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "externalId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "activity_logs_userId_user_id_fk": { + "name": "activity_logs_userId_user_id_fk", + "tableFrom": "activity_logs", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "activity_logs_templateId_activity_templates_id_fk": { + "name": "activity_logs_templateId_activity_templates_id_fk", + "tableFrom": "activity_logs", + "tableTo": "activity_templates", + "columnsFrom": [ + "templateId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.activity_templates": { + "name": "activity_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ownerUserId": { + "name": "ownerUserId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'habit'" + }, + "defaultMinutes": { + "name": "defaultMinutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10 + }, + "defaultPoints": { + "name": "defaultPoints", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10 + }, + "defaultCalories": { + "name": "defaultCalories", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'custom'" + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "activity_templates_owner_idx": { + "name": "activity_templates_owner_idx", + "columns": [ + { + "expression": "ownerUserId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "activity_templates_ownerUserId_user_id_fk": { + "name": "activity_templates_ownerUserId_user_id_fk", + "tableFrom": "activity_templates", + "tableTo": "user", + "columnsFrom": [ + "ownerUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.drink_logs": { + "name": "drink_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "amountMl": { + "name": "amountMl", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "drink_logs_user_date_idx": { + "name": "drink_logs_user_date_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "drink_logs_userId_user_id_fk": { + "name": "drink_logs_userId_user_id_fk", + "tableFrom": "drink_logs", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.meal_logs": { + "name": "meal_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "mealType": { + "name": "mealType", + "type": "meal_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "foodId": { + "name": "foodId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "recipeId": { + "name": "recipeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amountLabel": { + "name": "amountLabel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amountG": { + "name": "amountG", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": false + }, + "calories": { + "name": "calories", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "proteinG": { + "name": "proteinG", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "carbsG": { + "name": "carbsG", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "fatG": { + "name": "fatG", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "meal_logs_user_date_idx": { + "name": "meal_logs_user_date_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "meal_logs_userId_user_id_fk": { + "name": "meal_logs_userId_user_id_fk", + "tableFrom": "meal_logs", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "meal_logs_foodId_foods_id_fk": { + "name": "meal_logs_foodId_foods_id_fk", + "tableFrom": "meal_logs", + "tableTo": "foods", + "columnsFrom": [ + "foodId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "meal_logs_recipeId_recipes_id_fk": { + "name": "meal_logs_recipeId_recipes_id_fk", + "tableFrom": "meal_logs", + "tableTo": "recipes", + "columnsFrom": [ + "recipeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.meal_plans": { + "name": "meal_plans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "mealType": { + "name": "mealType", + "type": "meal_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "recipeId": { + "name": "recipeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portions": { + "name": "portions", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'1'" + }, + "plannedCalories": { + "name": "plannedCalories", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "meal_plans_user_date_idx": { + "name": "meal_plans_user_date_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "meal_plans_userId_user_id_fk": { + "name": "meal_plans_userId_user_id_fk", + "tableFrom": "meal_plans", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "meal_plans_recipeId_recipes_id_fk": { + "name": "meal_plans_recipeId_recipes_id_fk", + "tableFrom": "meal_plans", + "tableTo": "recipes", + "columnsFrom": [ + "recipeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.weight_logs": { + "name": "weight_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "weightKg": { + "name": "weightKg", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "weight_logs_user_date_unique": { + "name": "weight_logs_user_date_unique", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "weight_logs_userId_user_id_fk": { + "name": "weight_logs_userId_user_id_fk", + "tableFrom": "weight_logs", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.shopping_list_items": { + "name": "shopping_list_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "shoppingListId": { + "name": "shoppingListId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "foodId": { + "name": "foodId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Sonstiges'" + }, + "amountG": { + "name": "amountG", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "purchaseAmount": { + "name": "purchaseAmount", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checked": { + "name": "checked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sortOrder": { + "name": "sortOrder", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "shopping_list_items_list_idx": { + "name": "shopping_list_items_list_idx", + "columns": [ + { + "expression": "shoppingListId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "shopping_list_items_shoppingListId_shopping_lists_id_fk": { + "name": "shopping_list_items_shoppingListId_shopping_lists_id_fk", + "tableFrom": "shopping_list_items", + "tableTo": "shopping_lists", + "columnsFrom": [ + "shoppingListId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "shopping_list_items_foodId_foods_id_fk": { + "name": "shopping_list_items_foodId_foods_id_fk", + "tableFrom": "shopping_list_items", + "tableTo": "foods", + "columnsFrom": [ + "foodId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.shopping_lists": { + "name": "shopping_lists", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storeId": { + "name": "storeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "startsOn": { + "name": "startsOn", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "endsOn": { + "name": "endsOn", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "shopping_lists_user_idx": { + "name": "shopping_lists_user_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "shopping_lists_userId_user_id_fk": { + "name": "shopping_lists_userId_user_id_fk", + "tableFrom": "shopping_lists", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "shopping_lists_storeId_stores_id_fk": { + "name": "shopping_lists_storeId_stores_id_fk", + "tableFrom": "shopping_lists", + "tableTo": "stores", + "columnsFrom": [ + "storeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.club_activities": { + "name": "club_activities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "hostUserId": { + "name": "hostUserId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'other'" + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'nationwide'" + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "latitude": { + "name": "latitude", + "type": "numeric(9, 6)", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "numeric(9, 6)", + "primaryKey": false, + "notNull": false + }, + "locationLabel": { + "name": "locationLabel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "startsAt": { + "name": "startsAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "durationMinutes": { + "name": "durationMinutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 30 + }, + "points": { + "name": "points", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20 + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "club_activities_starts_at_idx": { + "name": "club_activities_starts_at_idx", + "columns": [ + { + "expression": "startsAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "club_activities_hostUserId_user_id_fk": { + "name": "club_activities_hostUserId_user_id_fk", + "tableFrom": "club_activities", + "tableTo": "user", + "columnsFrom": [ + "hostUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.club_activity_participants": { + "name": "club_activity_participants", + "schema": "", + "columns": { + "activityId": { + "name": "activityId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "displayName": { + "name": "displayName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "joinedAt": { + "name": "joinedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completedAt": { + "name": "completedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "club_activity_participants_activityId_club_activities_id_fk": { + "name": "club_activity_participants_activityId_club_activities_id_fk", + "tableFrom": "club_activity_participants", + "tableTo": "club_activities", + "columnsFrom": [ + "activityId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "club_activity_participants_userId_user_id_fk": { + "name": "club_activity_participants_userId_user_id_fk", + "tableFrom": "club_activity_participants", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "club_activity_participants_activityId_userId_pk": { + "name": "club_activity_participants_activityId_userId_pk", + "columns": [ + "activityId", + "userId" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.club_profiles": { + "name": "club_profiles", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Bayern'" + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "latitude": { + "name": "latitude", + "type": "numeric(9, 6)", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "numeric(9, 6)", + "primaryKey": false, + "notNull": false + }, + "privacyMode": { + "name": "privacyMode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'state'" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "club_profiles_userId_user_id_fk": { + "name": "club_profiles_userId_user_id_fk", + "tableFrom": "club_profiles", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wearable_connections": { + "name": "wearable_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "wearable_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "providerUserId": { + "name": "providerUserId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessTokenEncrypted": { + "name": "accessTokenEncrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshTokenEncrypted": { + "name": "refreshTokenEncrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "lastSyncAt": { + "name": "lastSyncAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "syncEnabled": { + "name": "syncEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "wearable_connections_user_provider_unique": { + "name": "wearable_connections_user_provider_unique", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "wearable_connections_userId_user_id_fk": { + "name": "wearable_connections_userId_user_id_fk", + "tableFrom": "wearable_connections", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wearable_imports": { + "name": "wearable_imports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connectionId": { + "name": "connectionId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "wearable_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "externalId": { + "name": "externalId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "importedAt": { + "name": "importedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "wearable_imports_provider_external_unique": { + "name": "wearable_imports_provider_external_unique", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "externalId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "wearable_imports_user_date_idx": { + "name": "wearable_imports_user_date_idx", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "wearable_imports_connectionId_wearable_connections_id_fk": { + "name": "wearable_imports_connectionId_wearable_connections_id_fk", + "tableFrom": "wearable_imports", + "tableTo": "wearable_connections", + "columnsFrom": [ + "connectionId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wearable_imports_userId_user_id_fk": { + "name": "wearable_imports_userId_user_id_fk", + "tableFrom": "wearable_imports", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.activity_level": { + "name": "activity_level", + "schema": "public", + "values": [ + "low", + "moderate", + "active" + ] + }, + "public.activity_source": { + "name": "activity_source", + "schema": "public", + "values": [ + "manual", + "fitbit", + "health_connect", + "system" + ] + }, + "public.coach_priority": { + "name": "coach_priority", + "schema": "public", + "values": [ + "high", + "medium", + "low" + ] + }, + "public.diet_style": { + "name": "diet_style", + "schema": "public", + "values": [ + "mixed", + "vegetarian", + "vegan", + "low_carb", + "flexible" + ] + }, + "public.food_source": { + "name": "food_source", + "schema": "public", + "values": [ + "seed", + "manual", + "openfoodfacts", + "ocr", + "community" + ] + }, + "public.health_goal": { + "name": "health_goal", + "schema": "public", + "values": [ + "lose_weight", + "maintain", + "build_muscle", + "healthier" + ] + }, + "public.meal_type": { + "name": "meal_type", + "schema": "public", + "values": [ + "breakfast", + "lunch", + "dinner", + "snack" + ] + }, + "public.portion_unit": { + "name": "portion_unit", + "schema": "public", + "values": [ + "g", + "ml", + "tsp", + "tbsp", + "piece", + "pack", + "bottle", + "cup", + "slice" + ] + }, + "public.reminder_kind": { + "name": "reminder_kind", + "schema": "public", + "values": [ + "water", + "snack", + "movement", + "meal", + "shopping", + "activity" + ] + }, + "public.wearable_provider": { + "name": "wearable_provider", + "schema": "public", + "values": [ + "fitbit", + "health_connect", + "google_fit" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json index f287ace..1b6cdd0 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -127,6 +127,13 @@ "when": 1779962400000, "tag": "0017_store_change_requests", "breakpoints": true + }, + { + "idx": 18, + "version": "7", + "when": 1780046007288, + "tag": "0018_outgoing_betty_ross", + "breakpoints": true } ] -} +} \ No newline at end of file diff --git a/packages/db/src/schema/profiles.ts b/packages/db/src/schema/profiles.ts index 2c8a16f..951a8da 100644 --- a/packages/db/src/schema/profiles.ts +++ b/packages/db/src/schema/profiles.ts @@ -64,6 +64,9 @@ export const weeklyCaloriePlans = pgTable( userId: text("userId").notNull().references(() => user.id, { onDelete: "cascade" }), date: date("date").notNull(), plannedCalories: integer("plannedCalories").notNull(), + // Vom Nutzer eingeplanter Mehrbedarf an diesem Tag (z. B. Familienfeier). + // plannedCalories wird daraus + Wochenausgleich abgeleitet. + extraCalories: integer("extraCalories").notNull().default(0), note: text("note"), updatedAt: timestamp("updatedAt").notNull().defaultNow(), },