feat(mobile): veckoplan – skapa inköpslista från planen (stänger plan→laga→handla-loopen)
CI / Typecheck, test & build (push) Successful in 1m34s

This commit is contained in:
Sven (AAMOS AI)
2026-08-14 03:12:49 +07:00
parent 4d9c69d0ce
commit e3d50707e8
13 changed files with 36 additions and 0 deletions
+24
View File
@@ -1,5 +1,6 @@
import { useEffect, useMemo, useState } from "react";
import { Alert, Pressable, Text, View } from "react-native";
import { router } from "expo-router";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { api } from "@/lib/api";
import { getLanguageTag, t } from "@/lib/i18n";
@@ -89,6 +90,7 @@ export default function PlanScreen() {
const plan = query.data?.plans?.[0] ?? null;
const planId = plan?.id ?? null;
const draftPlanId = plan && plan.status === "draft" ? plan.id : null;
const hasEntries = (plan?.entries?.length ?? 0) > 0;
const entriesByDate = useMemo(() => {
const map: Record<string, WeekPlanEntry[]> = {};
@@ -147,6 +149,16 @@ export default function PlanScreen() {
onError: onMutationError,
});
const createList = useMutation({
mutationFn: (weekPlanId: string) =>
api("/v1/shopping-lists", {
method: "POST",
body: { weekPlanId, generateFromPlan: true },
}),
onSuccess: () => router.push("/shopping"),
onError: onMutationError,
});
const openActions = (entry: WeekPlanEntry) => {
if (!planId) return;
Alert.alert(entry.titleSv, undefined, [
@@ -306,6 +318,18 @@ export default function PlanScreen() {
)}
</>
)}
{hasEntries ? (
<>
<Spacer size={spacing.sm} />
<Button
label={t("plan.createShopping")}
variant="secondary"
onPress={() => planId && createList.mutate(planId)}
loading={createList.isPending}
/>
</>
) : null}
</Screen>
);
}
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Tilberedt",
"plan.skipped": "Sprunget over",
"plan.activate": "Aktivér plan",
"plan.createShopping": "Opret indkøbsliste",
"wte.coverage": "{pct} % derhjemme",
"wte.cravingPlaceholder": "Jeg har lyst til … (fx cremet, asiatisk, under 500 kcal)",
"wte.empty": "Ingen forslag endnu læg lidt mad på lager eller løsn filtrene.",
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Gekocht",
"plan.skipped": "Übersprungen",
"plan.activate": "Plan aktivieren",
"plan.createShopping": "Einkaufsliste erstellen",
"wte.coverage": "{pct} % zuhause",
"wte.cravingPlaceholder": "Ich habe Lust auf … (z. B. cremig, asiatisch, unter 500 kcal)",
"wte.empty": "Noch keine Vorschläge fülle den Vorrat oder lockere die Filter.",
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Cooked",
"plan.skipped": "Skipped",
"plan.activate": "Activate plan",
"plan.createShopping": "Create shopping list",
"wte.coverage": "{pct}% at home",
"wte.cravingPlaceholder": "I'm craving … (e.g. creamy, Asian, under 500 kcal)",
"wte.empty": "No suggestions yet add some food to your inventory or loosen the filters.",
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Cocinado",
"plan.skipped": "Omitido",
"plan.activate": "Activar plan",
"plan.createShopping": "Crear lista de compra",
"wte.coverage": "{pct}% en casa",
"wte.cravingPlaceholder": "Me apetece … (p. ej. cremoso, asiático, menos de 500 kcal)",
"wte.empty": "Aún no hay sugerencias: añade comida al inventario o relaja los filtros.",
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Valmistettu",
"plan.skipped": "Ohitettu",
"plan.activate": "Aktivoi suunnitelma",
"plan.createShopping": "Luo ostoslista",
"wte.coverage": "{pct} % kotona",
"wte.cravingPlaceholder": "Tekisi mieli … (esim. kermaista, aasialaista, alle 500 kcal)",
"wte.empty": "Ei vielä ehdotuksia lisää ruokaa varastoon tai löysää suodattimia.",
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Préparé",
"plan.skipped": "Ignoré",
"plan.activate": "Activer le plan",
"plan.createShopping": "Créer la liste de courses",
"wte.coverage": "{pct} % à la maison",
"wte.cravingPlaceholder": "J'ai envie de … (ex. crémeux, asiatique, moins de 500 kcal)",
"wte.empty": "Pas encore de suggestions : ajoutez des aliments ou assouplissez les filtres.",
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Cucinato",
"plan.skipped": "Saltato",
"plan.activate": "Attiva piano",
"plan.createShopping": "Crea lista della spesa",
"wte.coverage": "{pct}% in casa",
"wte.cravingPlaceholder": "Ho voglia di … (es. cremoso, asiatico, meno di 500 kcal)",
"wte.empty": "Ancora nessun suggerimento: aggiungi cibo alla dispensa o allenta i filtri.",
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Laget",
"plan.skipped": "Hoppet over",
"plan.activate": "Aktiver plan",
"plan.createShopping": "Lag handleliste",
"wte.coverage": "{pct} % hjemme",
"wte.cravingPlaceholder": "Jeg har lyst på … (f.eks. kremet, asiatisk, under 500 kcal)",
"wte.empty": "Ingen forslag ennå legg inn litt mat på lageret eller løsne filtrene.",
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Gekookt",
"plan.skipped": "Overgeslagen",
"plan.activate": "Plan activeren",
"plan.createShopping": "Boodschappenlijst maken",
"wte.coverage": "{pct}% in huis",
"wte.cravingPlaceholder": "Ik heb zin in … (bijv. romig, Aziatisch, onder 500 kcal)",
"wte.empty": "Nog geen suggesties vul de voorraad aan of versoepel de filters.",
+1
View File
@@ -365,6 +365,7 @@
"plan.cooked": "Ugotowane",
"plan.skipped": "Pominięte",
"plan.activate": "Aktywuj plan",
"plan.createShopping": "Utwórz listę zakupów",
"wte.coverage": "{pct}% w domu",
"wte.cravingPlaceholder": "Mam ochotę na … (np. kremowe, azjatyckie, poniżej 500 kcal)",
"wte.empty": "Brak propozycji dodaj jedzenie do spiżarni albo poluzuj filtry.",
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Cozinhado",
"plan.skipped": "Ignorado",
"plan.activate": "Ativar plano",
"plan.createShopping": "Criar lista de compras",
"wte.coverage": "{pct}% em casa",
"wte.cravingPlaceholder": "Apetece-me … (ex.: cremoso, asiático, menos de 500 kcal)",
"wte.empty": "Ainda sem sugestões adicione comida à despensa ou alivie os filtros.",
+1
View File
@@ -351,6 +351,7 @@
"plan.cooked": "Lagad",
"plan.skipped": "Överhoppad",
"plan.activate": "Aktivera plan",
"plan.createShopping": "Skapa inköpslista",
"wte.coverage": "{pct} % hemma",
"wte.cravingPlaceholder": "Jag är sugen på … (t.ex. krämigt, asiatiskt, under 500 kcal)",
"wte.empty": "Inga förslag ännu lägg in lite mat i lagret eller lätta på filtren.",