137 lines
4.1 KiB
TypeScript
137 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { UserReport as UserReportType } from "@fitai/shared";
|
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
|
import { WeeklyCheckInsCard } from "./WeeklyCheckInsCard";
|
|
import { NutritionSummaryCard } from "./NutritionSummaryCard";
|
|
import { HydrationSummaryCard } from "./HydrationSummaryCard";
|
|
import { GoalsSummaryCard } from "./GoalsSummaryCard";
|
|
import { RecommendationsCard } from "./RecommendationsCard";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Download } from "lucide-react";
|
|
|
|
interface UserReportProps {
|
|
userId: string;
|
|
startDate: string;
|
|
endDate: string;
|
|
}
|
|
|
|
export function UserReport({ userId, startDate, endDate }: UserReportProps) {
|
|
const [report, setReport] = useState<UserReportType | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
fetchReport();
|
|
}, [userId, startDate, endDate]);
|
|
|
|
const fetchReport = async () => {
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
const response = await fetch(
|
|
`/api/reports/user/${userId}?startDate=${startDate}&endDate=${endDate}`,
|
|
);
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.error || "Failed to fetch report");
|
|
}
|
|
|
|
const data = await response.json();
|
|
setReport(data);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : "Failed to load report");
|
|
console.error("Report fetch error:", err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleExportPDF = () => {
|
|
if (!report) return;
|
|
|
|
const filename = `FitAI_Report_${report.user.firstName}_${report.user.lastName}_${startDate}_${endDate}.pdf`;
|
|
const url = `/api/reports/user/${userId}?startDate=${startDate}&endDate=${endDate}&format=pdf`;
|
|
|
|
// Open in new tab to trigger download
|
|
window.open(url, "_blank");
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<Card>
|
|
<CardContent className="flex items-center justify-center h-64">
|
|
<div className="text-lg text-gray-600">Loading report...</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<Card>
|
|
<CardContent className="flex items-center justify-center h-64">
|
|
<div className="text-lg text-red-600">{error}</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
if (!report) {
|
|
return (
|
|
<Card>
|
|
<CardContent className="flex items-center justify-center h-64">
|
|
<div className="text-lg text-gray-600">No report data available</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Report Header */}
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex justify-between items-center">
|
|
<div>
|
|
<CardTitle className="text-2xl">
|
|
{report.user.firstName} {report.user.lastName}
|
|
</CardTitle>
|
|
<p className="text-sm text-gray-600 mt-1">
|
|
{report.user.email} • {report.client?.membershipType || "N/A"}{" "}
|
|
Member
|
|
</p>
|
|
<p className="text-xs text-gray-500 mt-1">
|
|
Report Period: {report.reportPeriod.startDate} to{" "}
|
|
{report.reportPeriod.endDate}
|
|
</p>
|
|
</div>
|
|
<Button onClick={handleExportPDF} variant="default">
|
|
<Download className="w-4 h-4 mr-2" />
|
|
Export PDF
|
|
</Button>
|
|
</div>
|
|
</CardHeader>
|
|
</Card>
|
|
|
|
{/* Report Sections */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<WeeklyCheckInsCard weeklyCheckIns={report.weeklyCheckIns} />
|
|
<NutritionSummaryCard nutrition={report.nutrition} />
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<HydrationSummaryCard hydration={report.hydration} />
|
|
<GoalsSummaryCard goals={report.goals} />
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-6">
|
|
<RecommendationsCard recommendations={report.recommendations} />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|