import { useEffect, useState } from "react"; import { Pressable, View } from "react-native"; import { router } from "expo-router"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { api, ApiError } from "@/lib/api"; import { useAnalytics } from "@/lib/analytics"; import { recommendationsViewed } from "@app/analytics"; import { getLanguageTag, t } from "@/lib/i18n"; import { Body, Button, Card, EmptyState, ErrorView, Heading, Input, LoadingView, Row, Screen, Small, Spacer, Tag, Title, } from "@/components/ui"; import { colors, spacing } from "@/lib/theme"; /** "Vad ska vi äta?" (spec §4.1, §18) – appens viktigaste vy. */ interface Recommendation { recipeId: string; titleSv: string; title?: string; score: number; whySv: string; missingIngredients: string[]; usesExpiring: Array<{ nameSv: string; name?: string; daysLeft: number | null }>; coveragePercent: number; ratingAverage: number | null; ratingCount: number; } interface MealBoxSuggestion { mealBoxId: string; titleSv: string; title?: string; portionsRemaining: number; recommendedUseBy: string; useByUrgent: boolean; } interface WhatToEatResponse { recommendations: Recommendation[]; mealBoxSuggestions: MealBoxSuggestion[]; context: { activeHolidays: Array<{ slug: string; name: string }>; remainingKcal: number; remainingProteinG: number; searchSuggestion?: string | null; }; } // Kategori-flikar. Alla använder SAMMA rekommendations-motor (what-to-eat) så // vyn blir identisk överallt: %-hemma, stjärnor och "Visa fler". De flesta // filtrerar på måltidstyp; "Baka" på taggen "baking" (ctxMeal styr bara // poängsättningen så bakverk inte nedviktas som "fel måltid"). const CATS: ReadonlyArray<{ key: string; labelKey: string | null; label?: string; mealType?: string; tag?: string; ctxMeal?: string; glyph: string; }> = [ { 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: "cat.baking", tag: "baking", ctxMeal: "dessert", glyph: "🥐" }, ]; const errText = (e: unknown): string | undefined => e instanceof ApiError ? `[${e.status}] ${e.message}` : e instanceof Error ? e.message : undefined; export default function WhatToEatScreen() { const [searchText, setSearchText] = useState(""); const [submittedSearch, setSubmittedSearch] = useState(""); const [page, setPage] = useState(0); const [cat, setCat] = useState(null); // null = Rekommenderat const [holiday, setHoliday] = useState(null); // vald högtid (slug) const { track } = useAnalytics(); const hour = new Date().getHours(); const currentMeal = hour < 10 ? "breakfast" : hour < 14 ? "lunch" : "dinner"; const activeCat = cat === null ? null : CATS.find((c) => c.key === cat); const query = useQuery({ queryKey: ["what-to-eat", cat, holiday, submittedSearch, currentMeal], queryFn: () => { let path = "/v1/recommendations/what-to-eat?limit=20&view=default"; if (activeCat?.tag) { path += `&tag=${activeCat.tag}&mealType=${activeCat.ctxMeal ?? "dessert"}`; } else { path += `&mealType=${activeCat?.mealType ?? currentMeal}`; } if (holiday) path += `&holiday=${encodeURIComponent(holiday)}`; // Sök bland alla recept (på titel) oavsett vald flik/högtid. if (submittedSearch) path += `&search=${encodeURIComponent(submittedSearch)}`; return api(path); }, placeholderData: keepPreviousData, }); // Sök, flik och högtid är ömsesidigt uteslutande – ett aktivt filter i taget. const submitSearch = (value: string) => { setSearchText(value); setSubmittedSearch(value.trim()); setHoliday(null); setPage(0); }; const selectCat = (value: string | null) => { setCat(value); setHoliday(null); setSearchText(""); setSubmittedSearch(""); setPage(0); }; const selectHoliday = (slug: string) => { setHoliday((cur) => (cur === slug ? null : slug)); // toggla av/på setCat(null); setSearchText(""); setSubmittedSearch(""); setPage(0); }; const allRecs = query.data?.recommendations ?? []; const shownRecs: Recommendation[] = allRecs.length ? Array.from( { length: Math.min(5, allRecs.length) }, (_, i) => allRecs[(page * 5 + i) % allRecs.length], ).filter((r): r is Recommendation => r != null) : []; useEffect(() => { if (query.data) { track( recommendationsViewed({ properties: { craving: submittedSearch || undefined, count: query.data.recommendations.length, hasMealBoxSuggestions: query.data.mealBoxSuggestions.length > 0, }, }), ); } }, [query.data]); // eslint-disable-line react-hooks/exhaustive-deps return ( {t("wte.title")} {t("wte.subtitle")} {/* Sök bland recept (fungerar i alla flikar, söker på namn). */} submitSearch(searchText)} returnKeyType="search" autoCorrect={false} /> {/* Aktuella högtider/säsonger – snabbknappar direkt under sökrutan. */} {query.data && query.data.context.activeHolidays.length > 0 && ( <> {query.data.context.activeHolidays.map((h) => { // Säsongsnamn på användarens språk; fallback till serverns namn (egennamn m.m.). const name = t(`season.${h.slug}`, { defaultValue: h.name }); return ( selectHoliday(h.slug)}> ); })} )} {/* Kategori-flikar med enhetliga rutor (2 kolumner). Filtrerar in-place. */}