diff --git a/apps/mobile/src/app/(tabs)/plan.tsx b/apps/mobile/src/app/(tabs)/plan.tsx index 941af5c..6162138 100644 --- a/apps/mobile/src/app/(tabs)/plan.tsx +++ b/apps/mobile/src/app/(tabs)/plan.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useState } from "react"; import { Alert, Pressable, Text, View } from "react-native"; -import { useMutation, useQuery } from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { api } from "@/lib/api"; import { getLanguageTag, t } from "@/lib/i18n"; import { colors, radius, spacing } from "@/lib/theme"; @@ -71,6 +71,7 @@ function cap(s: string): string { export default function PlanScreen() { const locale = getLanguageTag(); + const queryClient = useQueryClient(); const [selectedKey, setSelectedKey] = useState(() => toKey(new Date())); const [generating, setGenerating] = useState(false); const todayKey = toKey(new Date()); @@ -86,6 +87,8 @@ export default function PlanScreen() { refetchInterval: generating ? 3000 : false, }); const plan = query.data?.plans?.[0] ?? null; + const planId = plan?.id ?? null; + const draftPlanId = plan && plan.status === "draft" ? plan.id : null; const entriesByDate = useMemo(() => { const map: Record = {}; @@ -114,6 +117,10 @@ export default function PlanScreen() { return () => clearTimeout(timer); }, [generating]); + const invalidate = () => queryClient.invalidateQueries({ queryKey: ["week-plan", mondayKey] }); + const onMutationError = (err: unknown) => + Alert.alert(t("common.oops"), err instanceof Error ? err.message : t("common.error")); + const generate = useMutation({ mutationFn: () => api("/v1/week-plans/generate", { @@ -121,10 +128,40 @@ export default function PlanScreen() { body: { weekStartDate: mondayKey, mealTypes: ["lunch", "dinner"] }, }), onSuccess: () => setGenerating(true), - onError: (err) => - Alert.alert(t("common.oops"), err instanceof Error ? err.message : t("common.error")), + onError: onMutationError, }); + const patchEntry = useMutation({ + mutationFn: (vars: { planId: string; entryId: string; status: string }) => + api(`/v1/week-plans/${vars.planId}/entries/${vars.entryId}`, { + method: "PATCH", + body: { status: vars.status }, + }), + onSuccess: () => invalidate(), + onError: onMutationError, + }); + + const activate = useMutation({ + mutationFn: (id: string) => api(`/v1/week-plans/${id}/activate`, { method: "POST" }), + onSuccess: () => invalidate(), + onError: onMutationError, + }); + + const openActions = (entry: WeekPlanEntry) => { + if (!planId) return; + Alert.alert(entry.titleSv, undefined, [ + { + text: t("recipe.iCookedThis"), + onPress: () => patchEntry.mutate({ planId, entryId: entry.id, status: "cooked" }), + }, + { + text: t("common.skip"), + onPress: () => patchEntry.mutate({ planId, entryId: entry.id, status: "skipped" }), + }, + { text: t("common.cancel"), style: "cancel" }, + ]); + }; + const monthLabel = cap( new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(selected), ); @@ -219,6 +256,14 @@ export default function PlanScreen() { {dayHeader} + {draftPlanId ? ( +