admin ui enhacement

This commit is contained in:
dimitar committed 2026-03-18 02:36:12 +01:00
1 parent 02c9681aca
commit 0abb41f573
16 files changed
+988 -435

No files matched your search

+99 -115
View File
@@ -12,6 +12,7 @@ import {
useUpdateRecommendation,
type Recommendation,
} from "@/hooks/use-api";
import { PageHeader } from "@/components/ui/PageHeader";
export default function RecommendationsPage() {
const { user } = useUser();
@@ -83,135 +84,118 @@ export default function RecommendationsPage() {
if (usersLoading || recsLoading) {
return (
<div className="flex items-center justify-center h-screen">
<div className="text-xl">Loading...</div>
<div className="flex items-center justify-center h-64">
<div className="text-lg">Loading...</div>
</div>
);
}
return (
<div className="container mx-auto py-10 px-4">
<div className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold">AI Recommendations</h1>
<div className="space-y-8">
<PageHeader
title="AI Recommendations"
description="Generate and manage AI-powered fitness recommendations"
breadcrumbs={[{ label: "AI Recommendations" }]}
/>
{/* Model Selection Toggle */}
<div className="flex items-center gap-3 bg-white px-4 py-2 rounded-lg shadow">
<span className="text-sm font-medium text-gray-700">
{useExternalModel ? "DeepSeek AI" : "Local Ollama"}
</span>
<button
onClick={() => setUseExternalModel(!useExternalModel)}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
useExternalModel ? "bg-blue-600" : "bg-gray-300"
}`}
>
<span
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
useExternalModel ? "translate-x-6" : "translate-x-1"
}`}
/>
</button>
<span className="text-xs text-gray-500">
{useExternalModel ? "External" : "Local"}
</span>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Generate Section */}
<div>
<h2 className="text-2xl font-semibold mb-4">
Generate Recommendations
</h2>
<div className="bg-white shadow rounded-lg p-6">
<p className="mb-4 text-gray-600">
<div className="card-modern">
<div className="mb-6">
<h3 className="text-lg font-semibold">Generate Recommendations</h3>
<p className="text-sm text-muted-foreground">
Select a user to generate a new daily recommendation.
</p>
<ul className="space-y-4">
{users.map((user) => (
<li
key={user.id}
className="flex items-center justify-between border-b pb-2"
>
<div>
<p className="font-medium">
{user.firstName} {user.lastName}
</p>
<p className="text-sm text-gray-500">{user.email}</p>
</div>
<button
onClick={() => handleGenerate(user.id)}
disabled={generateRec.isPending}
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 disabled:opacity-50"
>
{generateRec.isPending ? "Generating..." : "Generate"}
</button>
</li>
))}
{users.length === 0 && (
<p className="text-gray-500 italic">No users found.</p>
)}
</ul>
</div>
<ul className="space-y-4">
{users.map((user) => (
<li
key={user.id}
className="flex items-center justify-between border-b border-border/50 pb-4 last:border-0 last:pb-0"
>
<div>
<p className="font-medium">
{user.firstName} {user.lastName}
</p>
<p className="text-sm text-muted-foreground">{user.email}</p>
</div>
<button
onClick={() => handleGenerate(user.id)}
disabled={generateRec.isPending}
className="btn-primary"
>
{generateRec.isPending ? "Generating..." : "Generate"}
</button>
</li>
))}
{users.length === 0 && (
<p className="text-muted-foreground italic">No users found.</p>
)}
</ul>
</div>
{/* Pending Approvals Section */}
<div>
<h2 className="text-2xl font-semibold mb-4">Pending Approvals</h2>
<div className="bg-white shadow rounded-lg p-6">
{pendingRecommendations.length === 0 ? (
<p className="text-gray-500 italic">
No pending recommendations.
</p>
) : (
<ul className="space-y-6">
{pendingRecommendations.map((rec) => (
<li key={rec.id} className="border rounded p-4">
<div className="flex justify-between items-start mb-2">
<h3 className="font-bold">For: User {rec.userId}</h3>
<span className="text-xs text-gray-500">
{new Date(rec.createdAt).toLocaleDateString()}
</span>
</div>
<div className="space-y-2 text-sm mb-4">
<div>
<span className="font-semibold">Advice:</span>{" "}
{rec.recommendationText}
</div>
<div>
<span className="font-semibold">Activity:</span>{" "}
{rec.activityPlan}
</div>
<div>
<span className="font-semibold">Diet:</span>{" "}
{rec.dietPlan}
</div>
</div>
<div className="flex space-x-2">
<button
onClick={() => handleEdit(rec)}
className="flex-1 bg-blue-500 text-white py-2 rounded hover:bg-blue-600"
>
Edit
</button>
<button
onClick={() => handleApprove(rec.id, "approved")}
className="flex-1 bg-green-600 text-white py-2 rounded hover:bg-green-700"
>
Approve
</button>
<button
onClick={() => handleApprove(rec.id, "rejected")}
className="flex-1 bg-red-600 text-white py-2 rounded hover:bg-red-700"
>
Reject
</button>
</div>
</li>
))}
</ul>
)}
<div className="card-modern">
<div className="mb-6">
<h3 className="text-lg font-semibold">Pending Approvals</h3>
<p className="text-sm text-muted-foreground">
Review and approve AI-generated recommendations
</p>
</div>
{pendingRecommendations.length === 0 ? (
<p className="text-muted-foreground italic">
No pending recommendations.
</p>
) : (
<ul className="space-y-4">
{pendingRecommendations.map((rec) => (
<li
key={rec.id}
className="border border-border rounded-lg p-4"
>
<div className="flex justify-between items-start mb-3">
<h4 className="font-semibold">For: User {rec.userId}</h4>
<span className="text-xs text-muted-foreground">
{new Date(rec.createdAt).toLocaleDateString()}
</span>
</div>
<div className="space-y-2 text-sm mb-4">
<div>
<span className="font-medium">Advice:</span>{" "}
{rec.recommendationText}
</div>
<div>
<span className="font-medium">Activity:</span>{" "}
{rec.activityPlan}
</div>
<div>
<span className="font-medium">Diet:</span> {rec.dietPlan}
</div>
</div>
<div className="flex gap-2">
<button
onClick={() => handleEdit(rec)}
className="btn-secondary flex-1"
>
Edit
</button>
<button
onClick={() => handleApprove(rec.id, "approved")}
className="btn-primary flex-1 bg-emerald-600 hover:bg-emerald-700"
>
Approve
</button>
<button
onClick={() => handleApprove(rec.id, "rejected")}
className="btn-danger flex-1"
>
Reject
</button>
</div>
</li>
))}
</ul>
)}
</div>
</div>
</div>