feat(recept): Baka-kategori + 16 nya redaktionella recept
- Ny 'baking'-tagg (RECIPE_TAGS, text[] - ingen migration). Browse-vyn far en 'Baka'-chip som filtrerar pa taggen; befintliga lussekatter + saffransbullar taggas ocksa. - extra-recipes.ts: 16 nya recept som fyller de glesa kategorierna - 4 bak (kanelbullar, kardemummabullar, appelsmulpaj, frukostfrallor), 3 frukost, 3 mellanmal (tomma kategorin -> fylld), 3 efterratt, 3 lunch. - Endast verifierade canonical-ingredienser + berakningsbara enheter, sa seed harleder narings-/allergendata utan att krascha (run.ts 675/691). - Nytt rent invariant-test (ingen DB) kor riktiga computeRecipeNutrition pa alla 16: id:n finns, naring berakningsbar, inga slug-krockar. 5 tester grona. - Seed ar additiv (onConflictDoUpdate) - 'pnpm db:seed' lagger till + taggar om utan dataforlust. Full typecheck 20/20.
This commit is contained in:
@@ -42,14 +42,26 @@ interface BrowseRecipe {
|
||||
verificationStatus: string;
|
||||
}
|
||||
|
||||
const CATEGORIES = [
|
||||
// De flesta kategorier filtrerar på måltidstyp (MEAL_TYPES). "Baka" filtrerar i
|
||||
// stället på taggen "baking" (bullar/bröd/pajer) – därför bär varje kategori
|
||||
// antingen ett mealType eller ett tag. Etiketter återanvänder myday.mealType.*
|
||||
// där de finns; annars en egen svensk etikett.
|
||||
const CATEGORIES: ReadonlyArray<{
|
||||
key: string;
|
||||
labelKey: string | null;
|
||||
label?: string;
|
||||
mealType?: string;
|
||||
tag?: string;
|
||||
glyph: string;
|
||||
}> = [
|
||||
{ key: "all", labelKey: null, glyph: "🍴" },
|
||||
{ key: "breakfast", labelKey: "myday.mealType.breakfast", glyph: "🥣" },
|
||||
{ key: "lunch", labelKey: "myday.mealType.lunch", glyph: "🥗" },
|
||||
{ key: "dinner", labelKey: "myday.mealType.dinner", glyph: "🍽️" },
|
||||
{ key: "snack", labelKey: "myday.mealType.snack", glyph: "🍎" },
|
||||
{ key: "dessert", labelKey: "myday.mealType.dessert", glyph: "🍰" },
|
||||
] as const;
|
||||
{ key: "breakfast", labelKey: "myday.mealType.breakfast", mealType: "breakfast", glyph: "🥣" },
|
||||
{ key: "lunch", labelKey: "myday.mealType.lunch", mealType: "lunch", glyph: "🥗" },
|
||||
{ key: "dinner", labelKey: "myday.mealType.dinner", mealType: "dinner", glyph: "🍽️" },
|
||||
{ key: "snack", labelKey: "myday.mealType.snack", mealType: "snack", glyph: "🍎" },
|
||||
{ key: "dessert", labelKey: "myday.mealType.dessert", mealType: "dessert", glyph: "🍰" },
|
||||
{ key: "baking", labelKey: null, label: "Baka", tag: "baking", glyph: "🥐" },
|
||||
];
|
||||
|
||||
export default function BrowseRecipesScreen() {
|
||||
const [category, setCategory] = useState<string>("all");
|
||||
@@ -60,8 +72,10 @@ export default function BrowseRecipesScreen() {
|
||||
queryKey: ["browse-recipes", category, term],
|
||||
queryFn: () => {
|
||||
// Bygg query-strängen som resten av appen (template + encodeURIComponent).
|
||||
const cat = CATEGORIES.find((c) => c.key === category);
|
||||
let path = "/v1/recipes?limit=50";
|
||||
if (category !== "all") path += `&mealType=${category}`;
|
||||
if (cat?.mealType) path += `&mealType=${cat.mealType}`;
|
||||
if (cat?.tag) path += `&tags=${cat.tag}`;
|
||||
if (term) path += `&search=${encodeURIComponent(term)}`;
|
||||
return api<{ recipes: BrowseRecipe[] }>(path);
|
||||
},
|
||||
@@ -85,7 +99,7 @@ export default function BrowseRecipesScreen() {
|
||||
{CATEGORIES.map((c) => (
|
||||
<Button
|
||||
key={c.key}
|
||||
label={`${c.glyph} ${c.labelKey ? t(c.labelKey as never) : "Alla"}`}
|
||||
label={`${c.glyph} ${c.labelKey ? t(c.labelKey as never) : (c.label ?? "Alla")}`}
|
||||
variant={category === c.key ? "secondary" : "ghost"}
|
||||
onPress={() => setCategory(c.key)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user