feat(mobile): Sparade recept-sida (sökbar favoritlista) + länk på hemma (SAVED-1)
This commit is contained in:
@@ -114,6 +114,10 @@ export default function HomeScreen() {
|
||||
<QuickLink label={t("profile.title")} glyph="⚙️" onPress={() => router.push("/profile")} />
|
||||
</Row>
|
||||
|
||||
<Card onPress={() => router.push("/saved-recipes")}>
|
||||
<Body>⭐ Dina sparade recept</Body>
|
||||
</Card>
|
||||
|
||||
{budget.data && (
|
||||
<Card>
|
||||
<Heading>{t("home.budget")}</Heading>
|
||||
|
||||
@@ -84,6 +84,7 @@ export default function RootLayout() {
|
||||
<Stack.Screen name="meal-boxes" options={{ title: t("mealbox.title"), presentation: "modal" }} />
|
||||
<Stack.Screen name="household" options={{ title: t("home.household"), presentation: "modal" }} />
|
||||
<Stack.Screen name="memory" options={{ title: t("memory.title") }} />
|
||||
<Stack.Screen name="saved-recipes" options={{ title: "Sparade recept" }} />
|
||||
<Stack.Screen name="profile" options={{ title: t("profile.title"), presentation: "modal" }} />
|
||||
<Stack.Screen
|
||||
name="paywall"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { useState } from "react";
|
||||
import { router } from "expo-router";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { api } from "@/lib/api";
|
||||
import { Card, EmptyState, ErrorView, Heading, Input, LoadingView, Screen, Small } from "@/components/ui";
|
||||
import { spacing } from "@/lib/theme";
|
||||
|
||||
/** Dina sparade recept (favoriter) – sökbar lista. Backend: GET /v1/recipes/favorites/mine. */
|
||||
|
||||
interface SavedRecipe {
|
||||
id: string;
|
||||
titleSv: string;
|
||||
totalTimeMinutes: number | null;
|
||||
nutritionPerPortion: { kcal?: number } | null;
|
||||
}
|
||||
interface FavoritesResponse {
|
||||
recipes: SavedRecipe[];
|
||||
}
|
||||
|
||||
export default function SavedRecipesScreen() {
|
||||
const [query, setQuery] = useState("");
|
||||
const favs = useQuery<FavoritesResponse>({
|
||||
queryKey: ["favorites-mine"],
|
||||
queryFn: () => api<FavoritesResponse>("/v1/recipes/favorites/mine"),
|
||||
});
|
||||
|
||||
if (favs.isLoading) return <LoadingView />;
|
||||
if (favs.isError) return <ErrorView onRetry={() => void favs.refetch()} />;
|
||||
|
||||
const all = favs.data?.recipes ?? [];
|
||||
const q = query.trim().toLowerCase();
|
||||
const filtered = q ? all.filter((r) => r.titleSv.toLowerCase().includes(q)) : all;
|
||||
|
||||
return (
|
||||
<Screen style={{ gap: spacing.md }}>
|
||||
<Input
|
||||
placeholder="Sök bland dina sparade recept"
|
||||
autoCapitalize="none"
|
||||
value={query}
|
||||
onChangeText={setQuery}
|
||||
/>
|
||||
{all.length === 0 ? (
|
||||
<EmptyState text="Du har inga sparade recept än. Tryck på ☆ Spara på ett recept så hamnar det här." />
|
||||
) : filtered.length === 0 ? (
|
||||
<EmptyState text="Inga sparade recept matchar din sökning." />
|
||||
) : (
|
||||
filtered.map((r) => (
|
||||
<Card key={r.id} onPress={() => router.push(`/recipe/${r.id}`)} style={{ gap: spacing.xs }}>
|
||||
<Heading>{r.titleSv}</Heading>
|
||||
<Small>
|
||||
{[
|
||||
r.totalTimeMinutes != null ? `${r.totalTimeMinutes} min` : null,
|
||||
r.nutritionPerPortion?.kcal != null
|
||||
? `${Math.round(r.nutritionPerPortion.kcal)} kcal`
|
||||
: null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" · ")}
|
||||
</Small>
|
||||
</Card>
|
||||
))
|
||||
)}
|
||||
</Screen>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user