recommendation flow implemented

POC phase compleated
This commit is contained in:
dimitar committed 2025-11-20 19:10:16 +01:00
1 parent 118efad70f
commit 8b4cef33dc
10 files changed
+802 -17

No files matched your search

@@ -0,0 +1,170 @@
"use client";
import { useState, useEffect } from "react";
import { Button } from "@/components/ui/Button";
import { Card, CardHeader, CardContent } from "@/components/ui/card";
interface Recommendation {
id: string;
userId: string;
type: "short_term" | "medium_term" | "long_term";
content: string;
status: "pending" | "completed";
createdAt: string;
}
interface RecommendationsProps {
userId: string;
}
export function Recommendations({ userId }: RecommendationsProps) {
const [recommendations, setRecommendations] = useState<Recommendation[]>([]);
const [loading, setLoading] = useState(true);
const [newRec, setNewRec] = useState<{
type: "short_term" | "medium_term" | "long_term";
content: string;
}>({ type: "short_term", content: "" });
useEffect(() => {
fetchRecommendations();
}, [userId]);
const fetchRecommendations = async () => {
setLoading(true);
try {
const response = await fetch(`/api/recommendations?userId=${userId}`);
if (response.ok) {
const data = await response.json();
setRecommendations(data);
}
} catch (error) {
console.error("Failed to fetch recommendations:", error);
} finally {
setLoading(false);
}
};
const handleAddRecommendation = async (e: React.FormEvent) => {
e.preventDefault();
try {
const response = await fetch("/api/recommendations", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
userId,
type: newRec.type,
content: newRec.content,
}),
});
if (response.ok) {
setNewRec({ ...newRec, content: "" });
fetchRecommendations();
} else {
alert("Failed to add recommendation");
}
} catch (error) {
console.error(error);
}
};
const handleDelete = async (id: string) => {
if (!confirm("Are you sure?")) return;
// Note: Delete API not implemented in route.ts yet, but good to have UI ready or we can add it.
// For now, let's assume we might add it or just omit.
// Actually, I didn't add DELETE to route.ts. Let's skip for now.
alert("Delete functionality not available yet.");
};
const groupedRecs = {
short_term: recommendations.filter((r) => r.type === "short_term"),
medium_term: recommendations.filter((r) => r.type === "medium_term"),
long_term: recommendations.filter((r) => r.type === "long_term"),
};
const renderSection = (
title: string,
type: "short_term" | "medium_term" | "long_term",
items: Recommendation[]
) => (
<div className="mb-6">
<h4 className="font-semibold text-lg mb-3 capitalize">{title}</h4>
<div className="space-y-2">
{items.length === 0 && (
<p className="text-gray-500 text-sm italic">No recommendations yet.</p>
)}
{items.map((rec) => (
<div
key={rec.id}
className={`p-3 rounded border flex justify-between items-start ${rec.status === "completed"
? "bg-green-50 border-green-200"
: "bg-white border-gray-200"
}`}
>
<div>
<p className="text-sm">{rec.content}</p>
<p className="text-xs text-gray-400 mt-1">
{new Date(rec.createdAt).toLocaleDateString()} -{" "}
<span
className={
rec.status === "completed"
? "text-green-600 font-medium"
: "text-yellow-600"
}
>
{rec.status === "completed" ? "Completed" : "Pending"}
</span>
</p>
</div>
</div>
))}
</div>
<form onSubmit={handleAddRecommendation} className="mt-3 flex gap-2">
<input
type="hidden"
value={type}
onChange={() => setNewRec({ ...newRec, type })}
/>
{newRec.type === type && (
<>
<input
type="text"
placeholder={`Add ${title.toLowerCase()}...`}
className="flex-1 border rounded px-3 py-1 text-sm"
value={newRec.content}
onChange={(e) =>
setNewRec({ ...newRec, content: e.target.value })
}
required
/>
<Button type="submit" variant="secondary">
Add
</Button>
</>
)}
{newRec.type !== type && (
<Button type="button" variant="secondary" onClick={() => setNewRec({ type, content: "" })} className="text-xs text-gray-500">
+ Add New
</Button>
)}
</form>
</div>
);
if (loading) return <div>Loading recommendations...</div>;
return (
<Card>
<CardHeader>
<h3 className="text-xl font-bold">Fitness Recommendations</h3>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{renderSection("Short Term Goals", "short_term", groupedRecs.short_term)}
{renderSection("Medium Term Goals", "medium_term", groupedRecs.medium_term)}
{renderSection("Long Term Goals", "long_term", groupedRecs.long_term)}
</div>
</CardContent>
</Card>
);
}