f4a603e977
feat(recommendation-engine,api): S4 Smak/Hälsa/Lager-vyer för 'Vad ska vi äta?' - Ersätter hårdkodat 'Cibello' i scale-batch, scale-smoke-test och export-verified. - DB-backfill: 224 recept hade 'Cibello AI' i creator_display_name (värdena motsvarar nuvarande BRAND.name, ingen rad ändrades men kontrollen är gjord). - Lägger till view-query-param (default|taste|health|pantry) med fördefinierade ScoringWeights och samtyckesgrind. - Unit-tester för vyer; integrationstester för vy-param, validering och fallback utan personalization-samtycke. - brand-guard grön; pnpm typecheck 19/19; pnpm test --force x2 grönt (34 tasks, 275 tester).
21 lines
1.0 KiB
TypeScript
21 lines
1.0 KiB
TypeScript
import { z } from "zod";
|
||
import { MEAL_TYPES } from "@app/shared-types";
|
||
|
||
/**
|
||
* "Vad ska vi äta?" (spec §18) + "Jag är sugen på" (spec §19).
|
||
* Alla parametrar är valfria – motorn använder hushållets kontext som standard.
|
||
*/
|
||
export const whatToEatQuerySchema = z.object({
|
||
mealType: z.enum(MEAL_TYPES).default("dinner"),
|
||
persons: z.coerce.number().int().min(1).max(20).optional(),
|
||
maxMinutes: z.coerce.number().int().min(5).max(600).optional(),
|
||
maxCostMinorPerPortion: z.coerce.number().int().min(0).max(100_000).optional(),
|
||
/** Fritext eller röst-transkription: "krämigt", "asiatiskt", "under 500 kcal" … */
|
||
craving: z.string().max(300).optional(),
|
||
includeLeftovers: z.coerce.boolean().default(true),
|
||
limit: z.coerce.number().int().min(1).max(20).default(5),
|
||
/** Personaliseringsvy: smak, hälsa eller lager. Kräver personalization-samtycke. */
|
||
view: z.enum(["default", "taste", "health", "pantry"]).default("default"),
|
||
});
|
||
export type WhatToEatQuery = z.infer<typeof whatToEatQuerySchema>;
|