fitaiProto/packages/database/src/schema.ts
echo 28b5b52a8f context extended
maybe need polishing
2025-11-26 01:13:11 +01:00

255 lines
8.3 KiB
TypeScript

import { sqliteTable, text, integer, real } from "drizzle-orm/sqlite-core";
export const users = sqliteTable("users", {
id: text("id").primaryKey(),
email: text("email").notNull().unique(),
firstName: text("first_name").notNull(),
lastName: text("last_name").notNull(),
password: text("password"), // Optional - Clerk handles authentication
role: text("role", { enum: ["superAdmin", "admin", "trainer", "client"] })
.notNull()
.default("client"),
phone: text("phone"),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
export const clients = sqliteTable("clients", {
id: text("id").primaryKey(),
userId: text("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
membershipType: text("membership_type", { enum: ["basic", "premium", "vip"] })
.notNull()
.default("basic"),
membershipStatus: text("membership_status", {
enum: ["active", "inactive", "suspended"],
})
.notNull()
.default("active"),
joinDate: integer("join_date", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
lastVisit: integer("last_visit", { mode: "timestamp" }),
emergencyContactName: text("emergency_contact_name"),
emergencyContactPhone: text("emergency_contact_phone"),
emergencyContactRelationship: text("emergency_contact_relationship"),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
export const payments = sqliteTable("payments", {
id: text("id").primaryKey(),
clientId: text("client_id")
.notNull()
.references(() => clients.id, { onDelete: "cascade" }),
amount: real("amount").notNull(),
currency: text("currency").notNull().default("USD"),
status: text("status", {
enum: ["pending", "completed", "failed", "refunded"],
})
.notNull()
.default("pending"),
paymentMethod: text("payment_method", {
enum: ["cash", "card", "bank_transfer"],
}).notNull(),
dueDate: integer("due_date", { mode: "timestamp" }).notNull(),
paidAt: integer("paid_at", { mode: "timestamp" }),
description: text("description").notNull(),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
export const attendance = sqliteTable("attendance", {
id: text("id").primaryKey(),
clientId: text("client_id")
.notNull()
.references(() => clients.id, { onDelete: "cascade" }),
checkInTime: integer("check_in_time", { mode: "timestamp" }).notNull(),
checkOutTime: integer("check_out_time", { mode: "timestamp" }),
type: text("type", { enum: ["gym", "class", "personal_training"] })
.notNull()
.default("gym"),
notes: text("notes"),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
export const notifications = sqliteTable("notifications", {
id: text("id").primaryKey(),
userId: text("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
title: text("title").notNull(),
message: text("message").notNull(),
type: text("type", {
enum: ["payment_reminder", "attendance", "promotion", "system"],
}).notNull(),
read: integer("read", { mode: "boolean" }).notNull().default(false),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
export const fitnessProfiles = sqliteTable("fitness_profiles", {
id: text("id").primaryKey(),
userId: text("user_id")
.notNull()
.unique()
.references(() => users.id, { onDelete: "cascade" }),
height: real("height"), // in cm
weight: real("weight"), // in kg
age: integer("age"),
gender: text("gender", {
enum: ["male", "female", "other", "prefer_not_to_say"],
}),
fitnessGoal: text("fitness_goal", {
enum: [
"weight_loss",
"muscle_gain",
"endurance",
"flexibility",
"general_fitness",
],
}),
activityLevel: text("activity_level", {
enum: [
"sedentary",
"lightly_active",
"moderately_active",
"very_active",
"extremely_active",
],
}),
medicalConditions: text("medical_conditions"),
allergies: text("allergies"),
injuries: text("injuries"),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull()
.$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")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
fitnessProfileId: text("fitness_profile_id")
.notNull()
.references(() => fitnessProfiles.id, { onDelete: "cascade" }),
recommendationText: text("recommendation_text").notNull(),
activityPlan: text("activity_plan").notNull(),
dietPlan: text("diet_plan").notNull(),
status: text("status", {
enum: ["pending", "approved", "rejected"],
})
.notNull()
.default("pending"),
generatedAt: integer("generated_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
approvedAt: integer("approved_at", { mode: "timestamp" }),
approvedBy: text("approved_by"), // User ID of admin/trainer
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
export type Client = typeof clients.$inferSelect;
export type NewClient = typeof clients.$inferInsert;
export type Payment = typeof payments.$inferSelect;
export type NewPayment = typeof payments.$inferInsert;
export type Attendance = typeof attendance.$inferSelect;
export type NewAttendance = typeof attendance.$inferInsert;
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;