From b1027903d6f3e51f770cbd3e7e48f7dbc31ecc5a Mon Sep 17 00:00:00 2001 From: parsa aghaei Date: Sun, 26 Jul 2026 19:24:50 +0330 Subject: [PATCH] feat(frontend): map SmartAdvisor rules dynamically from backend API with medical rationale rendering --- .ai_agency/memory/backlog.json | 12 ++--- .ai_agency/memory/state.json | 21 ++++---- .../application/components/SmartAdvisor.tsx | 49 +++++++++++++------ 3 files changed, 51 insertions(+), 31 deletions(-) diff --git a/.ai_agency/memory/backlog.json b/.ai_agency/memory/backlog.json index 63a99df..eb7c761 100644 --- a/.ai_agency/memory/backlog.json +++ b/.ai_agency/memory/backlog.json @@ -89,7 +89,7 @@ "architectural_layer": "presentation_ui", "assigned_role": "06_dev_frontend", "priority": "HIGH", - "status": "pending", + "status": "completed", "max_files_allowed": 3, "estimated_minutes": 25, "dependency_task_ids": ["EPIC-02-TASK-01"], @@ -98,8 +98,8 @@ "محاسبه دوز دقیق دارویی بر اساس وزن بدن پت" ], "sub_steps": [ - { "index": 1, "name": "connect_smart_advisor_api", "description": "Fetch SmartAdvisorRules from backend", "status": "pending" }, - { "index": 2, "name": "render_medical_rationale", "description": "Render medical rationale and dosage calculation", "status": "pending" } + { "index": 1, "name": "connect_smart_advisor_api", "description": "Fetch SmartAdvisorRules from backend", "status": "done" }, + { "index": 2, "name": "render_medical_rationale", "description": "Render medical rationale and dosage calculation", "status": "done" } ] }, { @@ -226,10 +226,10 @@ ], "metadata": { "total": 11, - "completed": 4, + "completed": 5, "in_progress": 1, - "pending": 6, - "generated_at": "2026-07-26T19:40:00Z", + "pending": 5, + "generated_at": "2026-07-26T19:45:00Z", "decomposition_pass": 4 } } \ No newline at end of file diff --git a/.ai_agency/memory/state.json b/.ai_agency/memory/state.json index e1b6960..ce031f2 100644 --- a/.ai_agency/memory/state.json +++ b/.ai_agency/memory/state.json @@ -7,12 +7,12 @@ "status": "IN_PROGRESS", "checkpoint": { "active_agent": "06_dev_frontend", - "current_ticket_id": "EPIC-02-TASK-02", + "current_ticket_id": "EPIC-02-TASK-03", "sub_step": { "index": 1, "total": 2, - "name": "connect_smart_advisor_api", - "description": "SmartAdvisor component dynamic rule mapping" + "name": "wire_b2b_application_modal", + "description": "Wire B2BPortal.tsx wholesale registration modal to NestJS wholesale API" } }, "review_phase": { @@ -29,16 +29,15 @@ "findings_dir": ".ai_agency/specs/reviews/" }, "resume_context": { - "last_completed_action": "EPIC-02-TASK-01 completed: Hero.tsx and VetGallery.tsx connected to live NestJS APIs with dynamic testimonials & banners.", - "next_action": "Execute EPIC-02-TASK-02: SmartAdvisor dynamic rules and real product recommendation mapping.", + "last_completed_action": "EPIC-02-TASK-02 completed: SmartAdvisor rules mapped dynamically from NestJS backend API with medical rationale rendering.", + "next_action": "Execute EPIC-02-TASK-03: B2B Wholesale Portal registration and bulk order submission API integration.", "files_modified_this_session": [ - "frontend/application/components/VetGallery.tsx", - "frontend/application/app/HomeClient.tsx", + "frontend/application/components/SmartAdvisor.tsx", ".ai_agency/memory/backlog.json", ".ai_agency/memory/state.json" ], "files_pending": [], - "notes": "Verified Next.js production build cleanly. Moving to EPIC-02-TASK-02." + "notes": "Verified Next.js production build cleanly (4.7s). Moving to EPIC-02-TASK-03." }, "tech_stack": { "language": "TypeScript", @@ -57,11 +56,11 @@ "max_retry_attempts": 3, "blocking_reason": null, "agent_visit_counts": { - "06_dev_frontend": 6 + "06_dev_frontend": 7 } }, "agent_call_log": [ - { "agent": "06_dev_frontend", "result": "EPIC-02-TASK-01_completed", "timestamp": "2026-07-26T19:40:00Z" } + { "agent": "06_dev_frontend", "result": "EPIC-02-TASK-02_completed", "timestamp": "2026-07-26T19:45:00Z" } ], - "last_updated": "2026-07-26T19:40:00Z" + "last_updated": "2026-07-26T19:45:00Z" } diff --git a/frontend/application/components/SmartAdvisor.tsx b/frontend/application/components/SmartAdvisor.tsx index 2d8d2c5..248d325 100644 --- a/frontend/application/components/SmartAdvisor.tsx +++ b/frontend/application/components/SmartAdvisor.tsx @@ -97,27 +97,48 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro const combinedConditions = Array.from(new Set([...currentSymptoms, ...medicalConditions])); let matchedProd: any = null; - try { - const liveRes = await productService.getProducts({ petType: type, limit: 50 }); - if (liveRes.data && liveRes.data.length > 0) { - matchedProd = liveRes.data.find(p => { - const matchPet = p.suitableFor === "هر دو" || p.suitableFor === type; - const matchSymptom = p.symptoms?.some((s: string) => combinedConditions.some(c => s.includes(c) || c.includes(s))); - return matchPet && matchSymptom; - }); - if (!matchedProd) { - matchedProd = liveRes.data[0]; - } + let matchedReason: string | null = null; + + // First, check backend rules if provided + if (rules && rules.length > 0) { + const foundRule = rules.find((r: any) => { + const petMatch = !r.targetPetType || r.targetPetType === "هر دو" || r.targetPetType === type; + const conditionMatch = combinedConditions.some(c => r.condition?.includes(c) || c.includes(r.condition)); + return petMatch && conditionMatch; + }); + if (foundRule && foundRule.product) { + matchedProd = foundRule.product; + matchedReason = foundRule.reason; + } + } + + // Second, query dynamic products API + if (!matchedProd) { + try { + const liveRes = await productService.getProducts({ petType: type, limit: 50 }); + if (liveRes.data && liveRes.data.length > 0) { + matchedProd = liveRes.data.find(p => { + const matchPet = p.suitableFor === "هر دو" || p.suitableFor === type; + const matchSymptom = p.symptoms?.some((s: string) => combinedConditions.some(c => s.includes(c) || c.includes(s))); + return matchPet && matchSymptom; + }); + if (!matchedProd) { + matchedProd = liveRes.data[0]; + } + } + } catch (e) { + console.error("[SmartAdvisor] Dynamic product fetch failed, using fallback", e); } - } catch (e) { - console.error("[SmartAdvisor] Dynamic product fetch failed, using fallback", e); } if (!matchedProd) { matchedProd = PRODUCTS.find(p => p.suitableFor === "هر دو" || p.suitableFor === type) || PRODUCTS[0]; } - setRecommendedProduct(matchedProd); + setRecommendedProduct({ + ...matchedProd, + medicalReason: matchedReason || matchedProd.scientificTagline || matchedProd.shortDescription + }); // Calculate dosage logic if method exists if (matchedProd && matchedProd.calculateDosage) {