context extended

maybe need polishing
This commit is contained in:
echo 2025-11-26 01:13:11 +01:00
parent 684481fd2c
commit 28b5b52a8f
11 changed files with 761 additions and 20 deletions

Binary file not shown.

View File

@ -0,0 +1,49 @@
const Database = require('better-sqlite3');
const path = require('path');
const dbPath = path.join(__dirname, '../data/fitai.db');
const db = new Database(dbPath);
console.log('Creating fitness_goals table...');
try {
db.exec(`
CREATE TABLE IF NOT EXISTS fitness_goals (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
fitness_profile_id TEXT,
goal_type TEXT NOT NULL CHECK(goal_type IN ('weight_target', 'strength_milestone', 'endurance_target', 'flexibility_goal', 'habit_building', 'custom')),
title TEXT NOT NULL,
description TEXT,
target_value REAL,
current_value REAL,
unit TEXT,
start_date INTEGER NOT NULL,
target_date INTEGER,
completed_date INTEGER,
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'abandoned', 'paused')),
progress REAL DEFAULT 0,
priority TEXT DEFAULT 'medium' CHECK(priority IN ('low', 'medium', 'high')),
notes TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (fitness_profile_id) REFERENCES fitness_profiles(id) ON DELETE CASCADE
)
`);
// Create indexes for better query performance
db.exec(`
CREATE INDEX IF NOT EXISTS idx_fitness_goals_user_id ON fitness_goals(user_id);
CREATE INDEX IF NOT EXISTS idx_fitness_goals_status ON fitness_goals(status);
CREATE INDEX IF NOT EXISTS idx_fitness_goals_profile_id ON fitness_goals(fitness_profile_id);
`);
console.log('✅ fitness_goals table created successfully');
console.log('✅ Indexes created successfully');
} catch (error) {
console.error('❌ Migration failed:', error);
process.exit(1);
} finally {
db.close();
}

View File

@ -0,0 +1,39 @@
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@clerk/nextjs/server';
import { getDatabase } from '@/lib/database';
// POST - Mark goal as complete
export async function POST(
req: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { id } = await params;
const db = await getDatabase();
// Verify goal exists and user owns it
const existingGoal = await db.getFitnessGoalById(id);
if (!existingGoal) {
return NextResponse.json({ error: 'Goal not found' }, { status: 404 });
}
if (existingGoal.userId !== userId) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
// Mark as completed
const completedGoal = await db.completeGoal(id);
return NextResponse.json(completedGoal);
} catch (error) {
console.error('Error completing fitness goal:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}

View File

@ -0,0 +1,119 @@
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@clerk/nextjs/server';
import { getDatabase } from '@/lib/database';
// GET - Get specific goal
export async function GET(
req: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { id } = await params;
const db = await getDatabase();
const goal = await db.getFitnessGoalById(id);
if (!goal) {
return NextResponse.json({ error: 'Goal not found' }, { status: 404 });
}
// Verify ownership
if (goal.userId !== userId) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
return NextResponse.json(goal);
} catch (error) {
console.error('Error fetching fitness goal:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
// PUT - Update goal
export async function PUT(
req: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { id } = await params;
const db = await getDatabase();
// Verify goal exists and user owns it
const existingGoal = await db.getFitnessGoalById(id);
if (!existingGoal) {
return NextResponse.json({ error: 'Goal not found' }, { status: 404 });
}
if (existingGoal.userId !== userId) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
const updates = await req.json();
// Don't allow changing userId or id
delete updates.userId;
delete updates.id;
delete updates.createdAt;
const updatedGoal = await db.updateFitnessGoal(id, updates);
return NextResponse.json(updatedGoal);
} catch (error) {
console.error('Error updating fitness goal:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
// DELETE - Delete goal
export async function DELETE(
req: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { id } = await params;
const db = await getDatabase();
// Verify goal exists and user owns it
const existingGoal = await db.getFitnessGoalById(id);
if (!existingGoal) {
return NextResponse.json({ error: 'Goal not found' }, { status: 404 });
}
if (existingGoal.userId !== userId) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
const deleted = await db.deleteFitnessGoal(id);
if (deleted) {
return NextResponse.json({ success: true });
} else {
return NextResponse.json({ error: 'Failed to delete goal' }, { status: 500 });
}
} catch (error) {
console.error('Error deleting fitness goal:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}

View File

@ -0,0 +1,95 @@
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@clerk/nextjs/server';
import { getDatabase } from '@/lib/database';
import { randomBytes } from 'crypto';
// GET - List user's fitness goals
export async function GET(req: NextRequest) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { searchParams } = new URL(req.url);
const targetUserId = searchParams.get('userId') || userId;
const status = searchParams.get('status'); // active, completed, all
const db = await getDatabase();
// Fetch goals with optional status filter
const goals = await db.getFitnessGoalsByUserId(
targetUserId,
status && status !== 'all' ? status : undefined
);
return NextResponse.json(goals);
} catch (error) {
console.error('Error fetching fitness goals:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
// POST - Create new fitness goal
export async function POST(req: NextRequest) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const body = await req.json();
const {
goalType,
title,
description,
targetValue,
currentValue,
unit,
targetDate,
priority,
notes,
fitnessProfileId
} = body;
// Validation
if (!goalType || !title) {
return NextResponse.json(
{ error: 'goalType and title are required' },
{ status: 400 }
);
}
const db = await getDatabase();
// Create the goal
const goal = await db.createFitnessGoal({
id: randomBytes(16).toString('hex'),
userId,
fitnessProfileId: fitnessProfileId || undefined,
goalType,
title,
description: description || undefined,
targetValue: targetValue || undefined,
currentValue: currentValue || 0,
unit: unit || undefined,
startDate: new Date(),
targetDate: targetDate ? new Date(targetDate) : undefined,
status: 'active',
progress: 0,
priority: priority || 'medium',
notes: notes || undefined
});
return NextResponse.json(goal, { status: 201 });
} catch (error) {
console.error('Error creating fitness goal:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}

View File

@ -1,5 +1,7 @@
import { NextResponse } from "next/server";
import { getDatabase } from "@/lib/database";
import { buildAIContext } from "@/lib/ai/ai-context";
import { buildEnhancedPrompt, buildBasicPrompt } from "@/lib/ai/prompt-builder";
export async function POST(req: Request) {
try {
@ -21,26 +23,17 @@ export async function POST(req: Request) {
);
}
// Construct prompt
const prompt = `
You are a professional fitness trainer and nutritionist.
Generate a detailed daily recommendation for a user with the following profile:
- Height: ${profile.height} cm
- Weight: ${profile.weight} kg
- Age: ${profile.age}
- Gender: ${profile.gender}
- Goal: ${profile.fitnessGoals.join(", ")}
- Activity Level: ${profile.activityLevel}
- Medical Conditions: ${profile.medicalConditions || "None"}
- Injuries: ${profile.injuries || "None"}
Please provide the response in the following JSON format ONLY, no other text. Do not use markdown formatting or code blocks:
{
"recommendationText": "General advice and motivation for today.",
"activityPlan": "Detailed workout or activity plan for today.",
"dietPlan": "Detailed meal plan for today."
// Build AI context with goals and recommendations
let prompt: string;
try {
const context = await buildAIContext(userId);
prompt = buildEnhancedPrompt(context);
console.log('Using enhanced AI context with goals and history');
} catch (error) {
// Fallback to basic prompt if context building fails
console.warn('Failed to build AI context, using basic prompt:', error);
prompt = buildBasicPrompt(profile);
}
`;
let parsedResponse;

View File

@ -0,0 +1,88 @@
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';
export interface AIContext {
profile: DBFitnessProfile;
activeGoals: FitnessGoal[];
completedGoals: FitnessGoal[];
recentRecommendations: DBRecommendation[];
progressSummary: {
goalsCompleted: number;
goalsActive: number;
averageProgress: number;
};
}
/**
* Build comprehensive AI context for a user
* Aggregates fitness profile, goals, and recommendation history
*/
export async function buildAIContext(userId: string): Promise<AIContext> {
const db = await getDatabase();
// Fetch fitness profile
const profile = await db.getFitnessProfileByUserId(userId);
if (!profile) {
throw new Error(`Fitness profile not found for user ${userId}`);
}
// Fetch all fitness goals
const allGoals = await db.getFitnessGoalsByUserId(userId);
// Separate active and completed goals
const activeGoals = allGoals.filter((g) => g.status === 'active');
const completedGoals = allGoals.filter((g) => g.status === 'completed');
// Get all recommendations
const allRecommendations = await db.getRecommendationsByUserId(userId);
// Filter recent recommendations (last 7 days)
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const recentRecommendations = allRecommendations
.filter((r) => new Date(r.createdAt) > sevenDaysAgo)
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
.slice(0, 5);
// Calculate progress summary
const progressSummary = {
goalsCompleted: completedGoals.length,
goalsActive: activeGoals.length,
averageProgress:
activeGoals.length > 0
? activeGoals.reduce((sum, g) => sum + (g.progress || 0), 0) / activeGoals.length
: 0,
};
return {
profile,
activeGoals,
completedGoals,
recentRecommendations,
progressSummary,
};
}
/**
* Format goal for display in AI prompt
*/
export function formatGoalForPrompt(goal: FitnessGoal): string {
const progress = goal.progress || 0;
const current = goal.currentValue || 0;
const target = goal.targetValue;
const unit = goal.unit || '';
if (target) {
return `${goal.title}: ${current}/${target} ${unit} (${progress.toFixed(1)}% complete)`;
}
return `${goal.title}: ${progress.toFixed(1)}% complete`;
}

View File

@ -0,0 +1,91 @@
import type { AIContext } from './ai-context';
import { formatGoalForPrompt } from './ai-context';
/**
* Build enhanced AI prompt with comprehensive user context
* Includes profile, goals, progress, and recommendation history
*/
export function buildEnhancedPrompt(context: AIContext): string {
const { profile, activeGoals, completedGoals, recentRecommendations, progressSummary } = context;
// Build goals section
const activeGoalsText =
activeGoals.length > 0
? activeGoals.map((g) => `- ${formatGoalForPrompt(g)}`).join('\n')
: '- No active goals set';
const completedGoalsText =
completedGoals.length > 0
? completedGoals.slice(0, 3).map((g) => g.title).join(', ')
: 'None yet';
// Build context about recent recommendations
const recommendationContextText =
recentRecommendations.length > 0
? `The user has received ${recentRecommendations.length} recommendations in the past week. Consider their progress and avoid repetitive advice.`
: 'This is a new user or they haven\'t received recent recommendations. Provide comprehensive guidance.';
return `You are a professional fitness trainer and nutritionist with access to the user's complete fitness journey.
## User Profile
- Height: ${profile.height || 'Not specified'} cm
- Weight: ${profile.weight || 'Not specified'} kg
- Age: ${profile.age || 'Not specified'}
- Gender: ${profile.gender || 'Not specified'}
- Primary Goal: ${profile.fitnessGoal || 'General fitness'}
- Activity Level: ${profile.activityLevel || 'Not specified'}
- Medical Conditions: ${profile.medicalConditions || 'None'}
- Allergies: ${profile.allergies || 'None'}
- Injuries: ${profile.injuries || 'None'}
## Active Goals (${activeGoals.length})
${activeGoalsText}
## Recent Progress
- Goals Completed: ${progressSummary.goalsCompleted}
- Active Goals: ${progressSummary.goalsActive}
- Average Progress on Active Goals: ${progressSummary.averageProgress.toFixed(1)}%
${completedGoals.length > 0 ? `- Recently Completed: ${completedGoalsText}` : ''}
## Recommendation History
${recommendationContextText}
## Task
Generate a personalized daily recommendation that:
1. Acknowledges their progress on active goals
2. Provides specific, actionable steps toward goal completion
3. Builds on previous recommendations without being repetitive
4. Adapts to their current progress level
5. Takes into account their medical conditions, allergies, and injuries
Respond in the following JSON format ONLY, no other text. Do not use markdown formatting or code blocks:
{
"recommendationText": "Personalized motivation and progress acknowledgment (2-3 sentences)",
"activityPlan": "Specific workout plan aligned with active goals (detailed, 3-5 exercises or activities)",
"dietPlan": "Nutrition plan supporting their goals and restrictions (detailed, include meals and portions)"
}`;
}
/**
* Build basic prompt for users without complete context
* Fallback when goals or other data is missing
*/
export function buildBasicPrompt(profile: any): string {
return `You are a professional fitness trainer and nutritionist.
Generate a detailed daily recommendation for a user with the following profile:
- Height: ${profile.height} cm
- Weight: ${profile.weight} kg
- Age: ${profile.age}
- Gender: ${profile.gender}
- Goal: ${profile.fitnessGoal}
- Activity Level: ${profile.activityLevel}
- Medical Conditions: ${profile.medicalConditions || "None"}
- Injuries: ${profile.injuries || "None"}
Please provide the response in the following JSON format ONLY, no other text. Do not use markdown formatting or code blocks:
{
"recommendationText": "General advice and motivation for today.",
"activityPlan": "Detailed workout or activity plan for today.",
"dietPlan": "Detailed meal plan for today."
}`;
}

View File

@ -614,6 +614,174 @@ export class SQLiteDatabase implements IDatabase {
}
}
// Fitness Goals operations
async createFitnessGoal(goalData: Omit<import('./types').FitnessGoal, 'createdAt' | 'updatedAt'>): Promise<import('./types').FitnessGoal> {
if (!this.db) throw new Error('Database not connected')
const now = new Date()
const goal: import('./types').FitnessGoal = {
...goalData,
createdAt: now,
updatedAt: now
}
const stmt = this.db.prepare(
`INSERT INTO fitness_goals (
id, user_id, fitness_profile_id, goal_type, title, description,
target_value, current_value, unit, start_date, target_date,
completed_date, status, progress, priority, notes, created_at, updated_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
)
stmt.run(
goal.id,
goal.userId,
goal.fitnessProfileId || null,
goal.goalType,
goal.title,
goal.description || null,
goal.targetValue || null,
goal.currentValue || null,
goal.unit || null,
goal.startDate.toISOString(),
goal.targetDate ? goal.targetDate.toISOString() : null,
goal.completedDate ? goal.completedDate.toISOString() : null,
goal.status,
goal.progress,
goal.priority,
goal.notes || null,
goal.createdAt.toISOString(),
goal.updatedAt.toISOString()
)
return goal
}
async getFitnessGoalById(id: string): Promise<import('./types').FitnessGoal | null> {
if (!this.db) throw new Error('Database not connected')
const stmt = this.db.prepare('SELECT * FROM fitness_goals WHERE id = ?')
const row = stmt.get(id)
return row ? this.mapRowToFitnessGoal(row) : null
}
async getFitnessGoalsByUserId(userId: string, status?: string): Promise<import('./types').FitnessGoal[]> {
if (!this.db) throw new Error('Database not connected')
let query = 'SELECT * FROM fitness_goals WHERE user_id = ?'
const params: any[] = [userId]
if (status) {
query += ' AND status = ?'
params.push(status)
}
query += ' ORDER BY created_at DESC'
const stmt = this.db.prepare(query)
const rows = stmt.all(...params)
return rows.map(row => this.mapRowToFitnessGoal(row))
}
async updateFitnessGoal(id: string, updates: Partial<import('./types').FitnessGoal>): Promise<import('./types').FitnessGoal | null> {
if (!this.db) throw new Error('Database not connected')
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'createdAt')
if (fields.length === 0) return this.getFitnessGoalById(id)
// Map camelCase to snake_case for database columns
const columnMap: Record<string, string> = {
userId: 'user_id',
fitnessProfileId: 'fitness_profile_id',
goalType: 'goal_type',
targetValue: 'target_value',
currentValue: 'current_value',
startDate: 'start_date',
targetDate: 'target_date',
completedDate: 'completed_date',
updatedAt: 'updated_at'
}
const setClause = fields.map(field => `${columnMap[field] || field} = ?`).join(', ')
const values = fields.map(field => {
const val = (updates as any)[field]
return val instanceof Date ? val.toISOString() : val
})
values.push(new Date().toISOString()) // updatedAt
values.push(id)
const stmt = this.db.prepare(`UPDATE fitness_goals SET ${setClause}, updated_at = ? WHERE id = ?`)
stmt.run(values)
return this.getFitnessGoalById(id)
}
async deleteFitnessGoal(id: string): Promise<boolean> {
if (!this.db) throw new Error('Database not connected')
const stmt = this.db.prepare('DELETE FROM fitness_goals WHERE id = ?')
const result = stmt.run(id)
return (result.changes || 0) > 0
}
async updateGoalProgress(id: string, currentValue: number): Promise<import('./types').FitnessGoal | null> {
if (!this.db) throw new Error('Database not connected')
// Get the goal to calculate progress
const goal = await this.getFitnessGoalById(id)
if (!goal) return null
let progress = goal.progress
if (goal.targetValue && goal.targetValue > 0) {
progress = Math.min(100, (currentValue / goal.targetValue) * 100)
}
const stmt = this.db.prepare(
'UPDATE fitness_goals SET current_value = ?, progress = ?, updated_at = ? WHERE id = ?'
)
stmt.run(currentValue, progress, new Date().toISOString(), id)
return this.getFitnessGoalById(id)
}
async completeGoal(id: string): Promise<import('./types').FitnessGoal | null> {
if (!this.db) throw new Error('Database not connected')
const now = new Date()
const stmt = this.db.prepare(
'UPDATE fitness_goals SET status = ?, progress = ?, completed_date = ?, updated_at = ? WHERE id = ?'
)
stmt.run('completed', 100, now.toISOString(), now.toISOString(), id)
return this.getFitnessGoalById(id)
}
private mapRowToFitnessGoal(row: any): import('./types').FitnessGoal {
return {
id: row.id,
userId: row.user_id,
fitnessProfileId: row.fitness_profile_id,
goalType: row.goal_type,
title: row.title,
description: row.description,
targetValue: row.target_value,
currentValue: row.current_value,
unit: row.unit,
startDate: new Date(row.start_date),
targetDate: row.target_date ? new Date(row.target_date) : undefined,
completedDate: row.completed_date ? new Date(row.completed_date) : undefined,
status: row.status,
progress: row.progress,
priority: row.priority,
notes: row.notes,
createdAt: new Date(row.created_at),
updatedAt: new Date(row.updated_at)
}
}
async getDashboardStats(): Promise<{
totalUsers: number;
activeClients: number;

View File

@ -61,6 +61,27 @@ export interface Recommendation {
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 {
// Connection management
@ -121,6 +142,20 @@ export interface IDatabase {
): Promise<Recommendation | null>;
deleteRecommendation(id: string): Promise<boolean>;
// Fitness Goals operations
createFitnessGoal(
goal: Omit<FitnessGoal, "createdAt" | "updatedAt">
): Promise<FitnessGoal>;
getFitnessGoalById(id: string): Promise<FitnessGoal | null>;
getFitnessGoalsByUserId(userId: string, status?: string): Promise<FitnessGoal[]>;
updateFitnessGoal(
id: string,
updates: Partial<FitnessGoal>
): Promise<FitnessGoal | null>;
deleteFitnessGoal(id: string): Promise<boolean>;
updateGoalProgress(id: string, currentValue: number): Promise<FitnessGoal | null>;
completeGoal(id: string): Promise<FitnessGoal | null>;
// Dashboard operations
getDashboardStats(): Promise<{
totalUsers: number;

View File

@ -145,6 +145,67 @@ export const fitnessProfiles = sqliteTable("fitness_profiles", {
.$defaultFn(() => new Date()),
});
export const fitnessGoals = sqliteTable("fitness_goals", {
id: text("id").primaryKey(),
userId: text("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
fitnessProfileId: text("fitness_profile_id").references(
() => fitnessProfiles.id,
{ onDelete: "cascade" }
),
// Goal details
goalType: text("goal_type", {
enum: [
"weight_target",
"strength_milestone",
"endurance_target",
"flexibility_goal",
"habit_building",
"custom",
],
}).notNull(),
title: text("title").notNull(),
description: text("description"),
// Measurable targets
targetValue: real("target_value"), // e.g., 70 (kg), 100 (kg bench press)
currentValue: real("current_value"), // Current progress
unit: text("unit"), // kg, km, reps, etc.
// Timeline
startDate: integer("start_date", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
targetDate: integer("target_date", { mode: "timestamp" }),
completedDate: integer("completed_date", { mode: "timestamp" }),
// Status tracking
status: text("status", {
enum: ["active", "completed", "abandoned", "paused"],
})
.notNull()
.default("active"),
progress: real("progress").default(0), // 0-100 percentage
// Metadata
priority: text("priority", {
enum: ["low", "medium", "high"],
}).default("medium"),
notes: text("notes"),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
export const recommendations = sqliteTable("recommendations", {
id: text("id").primaryKey(),
userId: text("user_id")
@ -186,5 +247,8 @@ export type Notification = typeof notifications.$inferSelect;
export type NewNotification = typeof notifications.$inferInsert;
export type FitnessProfile = typeof fitnessProfiles.$inferSelect;
export type NewFitnessProfile = typeof fitnessProfiles.$inferInsert;
export type FitnessGoal = typeof fitnessGoals.$inferSelect;
export type NewFitnessGoal = typeof fitnessGoals.$inferInsert;
export type Recommendation = typeof recommendations.$inferSelect;
export type NewRecommendation = typeof recommendations.$inferInsert;