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