From c877577fbaa8b730add43b2f4f086c982eaa2dac Mon Sep 17 00:00:00 2001 From: echo Date: Tue, 31 Mar 2026 19:21:00 +0200 Subject: [PATCH] fix ai activity plan conversion and immediate goals refresh --- .../recommendations/generate-self/route.ts | 39 ++++++++++++++++--- apps/mobile/src/app/(tabs)/goals.tsx | 4 +- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/apps/admin/src/app/api/recommendations/generate-self/route.ts b/apps/admin/src/app/api/recommendations/generate-self/route.ts index abf9f71..e07dab3 100644 --- a/apps/admin/src/app/api/recommendations/generate-self/route.ts +++ b/apps/admin/src/app/api/recommendations/generate-self/route.ts @@ -66,11 +66,13 @@ function inferGoalType(text: string): ParsedPlanItem["goalType"] { function parseActivityPlanToItems(activityPlan: string): ParsedPlanItem[] { const lines = activityPlan - .split("\n") + .replace(/\r\n/g, "\n") + .split(/\n+/) + .flatMap((line) => line.split(/(?<=[.!?])\s+(?=[A-Z0-9])/g)) .map((line) => line.trim()) .filter(Boolean) .map((line) => line.replace(/^[-*•\d.)\s]+/, "").trim()) - .filter((line) => line.length > 4) + .filter((line) => line.length > 10) .slice(0, 8); const uniqueLines = Array.from(new Set(lines)); @@ -83,6 +85,21 @@ function parseActivityPlanToItems(activityPlan: string): ParsedPlanItem[] { })); } +function getDefaultPlanItems(): ParsedPlanItem[] { + const defaults = [ + "Complete 3 strength sessions this week with progressive overload.", + "Add 2 cardio sessions of 25-30 minutes for endurance.", + "Do a 10-minute mobility routine daily after training.", + ]; + + return defaults.map((line) => ({ + id: crypto.randomUUID(), + title: line.length > 72 ? `${line.slice(0, 69)}...` : line, + description: line, + goalType: inferGoalType(line), + })); +} + export async function POST() { try { const { userId } = await auth(); @@ -266,9 +283,21 @@ export async function POST() { ), ); - const planItems = parseActivityPlanToItems( - parsedResponse.activityPlan || "", - ); + let planItems = parseActivityPlanToItems(parsedResponse.activityPlan || ""); + + if (planItems.length === 0 && parsedResponse.recommendationText) { + planItems = parseActivityPlanToItems(parsedResponse.recommendationText); + } + + if (planItems.length === 0) { + planItems = getDefaultPlanItems(); + } + + log.debug("AI plan parsed into goal items", { + recommendationId: recommendation.id, + userId, + parsedItems: planItems.length, + }); const createdGoals = await Promise.all( planItems.map((item) => diff --git a/apps/mobile/src/app/(tabs)/goals.tsx b/apps/mobile/src/app/(tabs)/goals.tsx index 9b37058..af76f20 100644 --- a/apps/mobile/src/app/(tabs)/goals.tsx +++ b/apps/mobile/src/app/(tabs)/goals.tsx @@ -200,10 +200,10 @@ export default function GoalsScreen() { try { setIsGeneratingPlan(true); await generateSelfPlan(); - await refetchRecommendations(); + await Promise.all([refetchRecommendations(), refetchGoals()]); Alert.alert( "Plan Ready", - "Your new AI activity plan has been generated.", + "Your new AI activity plan has been generated and active goals were added.", ); } catch (error) { const message =