cleanup
and AI recommnedation fix
This commit is contained in:
1 parent
670e6675cc
commit
8353a24f6b
17 files changed
+134
-138
No files matched your search
Binary file not shown.
@@ -172,9 +172,9 @@ export async function POST(req: Request) {
|
||||
const recommendation = await db.createRecommendation({
|
||||
id: crypto.randomUUID(),
|
||||
userId,
|
||||
fitnessProfileId: profile.userId,
|
||||
fitnessProfileId: profile.id,
|
||||
type: 'ai_plan',
|
||||
content: parsedResponse.recommendationText,
|
||||
recommendationText: parsedResponse.recommendationText,
|
||||
activityPlan: parsedResponse.activityPlan,
|
||||
dietPlan: parsedResponse.dietPlan,
|
||||
status: 'pending'
|
||||
|
||||
@@ -76,7 +76,7 @@ export async function POST(request: Request) {
|
||||
userId,
|
||||
fitnessProfileId,
|
||||
type: 'ai_plan',
|
||||
content: recommendationText,
|
||||
recommendationText: recommendationText,
|
||||
activityPlan,
|
||||
dietPlan,
|
||||
status: status || 'pending'
|
||||
@@ -90,7 +90,7 @@ export async function POST(request: Request) {
|
||||
id: crypto.randomUUID(),
|
||||
userId,
|
||||
type,
|
||||
content,
|
||||
recommendationText: content,
|
||||
status: status || 'pending'
|
||||
})
|
||||
return NextResponse.json(recommendation)
|
||||
@@ -122,8 +122,8 @@ export async function PUT(request: Request) {
|
||||
|
||||
const updated = await db.updateRecommendation(id, {
|
||||
...(status && { status }),
|
||||
...(recommendationText && { content: recommendationText }), // Map legacy field
|
||||
...(content && { content }),
|
||||
...(recommendationText && { recommendationText }),
|
||||
...(content && { recommendationText: content }),
|
||||
...(activityPlan && { activityPlan }),
|
||||
...(dietPlan && { dietPlan })
|
||||
})
|
||||
|
||||
@@ -219,7 +219,7 @@ export default function RecommendationsPage() {
|
||||
</div>
|
||||
<div className="space-y-2 text-sm mb-4">
|
||||
<div>
|
||||
<span className="font-semibold">Advice:</span> {rec.content}
|
||||
<span className="font-semibold">Advice:</span> {rec.recommendationText}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-semibold">Activity:</span> {rec.activityPlan}
|
||||
|
||||
@@ -8,7 +8,7 @@ interface Recommendation {
|
||||
id: string;
|
||||
userId: string;
|
||||
type: "short_term" | "medium_term" | "long_term" | "ai_plan";
|
||||
content: string;
|
||||
recommendationText: string;
|
||||
activityPlan?: string;
|
||||
dietPlan?: string;
|
||||
status: "pending" | "completed" | "approved" | "rejected";
|
||||
@@ -97,7 +97,7 @@ export function Recommendations({ userId }: RecommendationsProps) {
|
||||
}`}
|
||||
>
|
||||
<div className="w-full">
|
||||
<p className="text-sm font-medium">{rec.content}</p>
|
||||
<p className="text-gray-700">{rec.recommendationText}</p>
|
||||
{rec.type === 'ai_plan' && (
|
||||
<div className="mt-2 text-xs text-gray-600 space-y-1">
|
||||
{rec.activityPlan && <p><span className="font-semibold">Activity:</span> {rec.activityPlan}</p>}
|
||||
|
||||
@@ -224,14 +224,14 @@ export function UserGrid({
|
||||
const gridRef = React.useRef<AgGridReact<User>>(null);
|
||||
|
||||
const gridOptions = {
|
||||
theme: "legacy",
|
||||
theme: "legacy" as const,
|
||||
columnDefs,
|
||||
defaultColDef,
|
||||
rowData: users,
|
||||
rowSelection: "multiple",
|
||||
rowSelection: "multiple" as const,
|
||||
onSelectionChanged: () => {
|
||||
const selectedNodes = gridRef.current?.api.getSelectedNodes();
|
||||
const selectedData = selectedNodes?.map((node) => node.data) || [];
|
||||
const selectedData = selectedNodes?.map((node) => node.data).filter((u): u is User => !!u) || [];
|
||||
setSelectedUsers(selectedData);
|
||||
if (selectedData.length === 1 && onUserSelect) {
|
||||
onUserSelect(selectedData[0]);
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { getDatabase } from '@/lib/database';
|
||||
import type { FitnessGoal } from '@/lib/database/types';
|
||||
|
||||
// Import types from database package
|
||||
import type {
|
||||
FitnessProfile as DBFitnessProfile,
|
||||
Recommendation as DBRecommendation,
|
||||
} from '@fitai/database';
|
||||
import type { FitnessGoal, FitnessProfile, Recommendation } from '@/lib/database/types';
|
||||
|
||||
export interface AIContext {
|
||||
profile: DBFitnessProfile;
|
||||
profile: FitnessProfile;
|
||||
activeGoals: FitnessGoal[];
|
||||
completedGoals: FitnessGoal[];
|
||||
recentRecommendations: DBRecommendation[];
|
||||
recentRecommendations: Recommendation[];
|
||||
progressSummary: {
|
||||
goalsCompleted: number;
|
||||
goalsActive: number;
|
||||
|
||||
@@ -32,7 +32,7 @@ export function buildEnhancedPrompt(context: AIContext): string {
|
||||
- Weight: ${profile.weight || 'Not specified'} kg
|
||||
- Age: ${profile.age || 'Not specified'}
|
||||
- Gender: ${profile.gender || 'Not specified'}
|
||||
- Primary Goal: ${profile.fitnessGoal || 'General fitness'}
|
||||
- Primary Goal: ${profile.fitnessGoals?.[0] || 'General fitness'}
|
||||
- Activity Level: ${profile.activityLevel || 'Not specified'}
|
||||
- Medical Conditions: ${profile.medicalConditions || 'None'}
|
||||
- Allergies: ${profile.allergies || 'None'}
|
||||
@@ -77,7 +77,7 @@ Generate a detailed daily recommendation for a user with the following profile:
|
||||
- Weight: ${profile.weight} kg
|
||||
- Age: ${profile.age}
|
||||
- Gender: ${profile.gender}
|
||||
- Goal: ${profile.fitnessGoal}
|
||||
- Goal: ${profile.fitnessGoals?.[0]}
|
||||
- Activity Level: ${profile.activityLevel}
|
||||
- Medical Conditions: ${profile.medicalConditions || "None"}
|
||||
- Injuries: ${profile.injuries || "None"}
|
||||
|
||||
@@ -246,7 +246,7 @@ export class DrizzleDatabase implements IDatabase {
|
||||
}
|
||||
|
||||
// Fitness Profile operations
|
||||
async createFitnessProfile(profileData: Omit<FitnessProfile, 'createdAt' | 'updatedAt'>): Promise<FitnessProfile> {
|
||||
async createFitnessProfile(profileData: Omit<FitnessProfile, 'id' | 'createdAt' | 'updatedAt'>): Promise<FitnessProfile> {
|
||||
const now = new Date()
|
||||
const id = Math.random().toString(36).substr(2, 9)
|
||||
const newProfile = {
|
||||
@@ -257,7 +257,7 @@ export class DrizzleDatabase implements IDatabase {
|
||||
}
|
||||
|
||||
await this.db.insert(fitnessProfiles).values(newProfile as any)
|
||||
return { ...profileData, createdAt: now, updatedAt: now }
|
||||
return { id, ...profileData, createdAt: now, updatedAt: now }
|
||||
}
|
||||
|
||||
async getFitnessProfileByUserId(userId: string): Promise<FitnessProfile | null> {
|
||||
|
||||
@@ -1,86 +1,12 @@
|
||||
import { User as SharedUser, Client, FitnessProfile, Attendance, Recommendation, FitnessGoal } from "@fitai/shared";
|
||||
|
||||
// Database Entity Types
|
||||
export interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
export interface User extends SharedUser {
|
||||
password: string;
|
||||
phone?: string;
|
||||
role: "superAdmin" | "admin" | "trainer" | "client";
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface Client {
|
||||
id: string;
|
||||
userId: string;
|
||||
membershipType: "basic" | "premium" | "vip";
|
||||
membershipStatus: "active" | "inactive" | "expired";
|
||||
joinDate: Date;
|
||||
lastVisit?: Date;
|
||||
}
|
||||
export type { Client, FitnessProfile, Attendance, Recommendation, FitnessGoal };
|
||||
|
||||
export interface FitnessProfile {
|
||||
userId: string;
|
||||
height: string;
|
||||
weight: string;
|
||||
age: string;
|
||||
gender: "male" | "female" | "other";
|
||||
activityLevel: "sedentary" | "lightly_active" | "moderately_active" | "very_active" | "extremely_active";
|
||||
fitnessGoals: string[];
|
||||
exerciseHabits: string;
|
||||
dietHabits: string;
|
||||
medicalConditions: string;
|
||||
allergies?: string;
|
||||
injuries?: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface Attendance {
|
||||
id: string;
|
||||
userId: string;
|
||||
checkInTime: Date;
|
||||
checkOutTime?: Date;
|
||||
type: "gym" | "class" | "personal_training";
|
||||
notes?: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface Recommendation {
|
||||
id: string;
|
||||
userId: string;
|
||||
fitnessProfileId?: string;
|
||||
type: "short_term" | "medium_term" | "long_term" | "ai_plan";
|
||||
content: string;
|
||||
activityPlan?: string;
|
||||
dietPlan?: string;
|
||||
status: "pending" | "approved" | "rejected" | "completed";
|
||||
createdAt: Date;
|
||||
approvedAt?: Date;
|
||||
approvedBy?: string;
|
||||
}
|
||||
|
||||
export interface FitnessGoal {
|
||||
id: string;
|
||||
userId: string;
|
||||
fitnessProfileId?: string;
|
||||
goalType: "weight_target" | "strength_milestone" | "endurance_target" | "flexibility_goal" | "habit_building" | "custom";
|
||||
title: string;
|
||||
description?: string;
|
||||
targetValue?: number;
|
||||
currentValue?: number;
|
||||
unit?: string;
|
||||
startDate: Date;
|
||||
targetDate?: Date;
|
||||
completedDate?: Date;
|
||||
status: "active" | "completed" | "abandoned" | "paused";
|
||||
progress: number;
|
||||
priority: "low" | "medium" | "high";
|
||||
notes?: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// Database Interface - allows us to swap implementations
|
||||
export interface IDatabase {
|
||||
|
||||
@@ -1,14 +1 @@
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
||||
export function formatDate(date: string | Date) {
|
||||
return new Date(date).toLocaleDateString("en-US", {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
})
|
||||
}
|
||||
export { cn, formatDate } from "@fitai/shared"
|
||||
Reference in new issue
Block a user