mirror of
https://github.com/Snouzy/workout-cool.git
synced 2026-06-18 14:44:31 +00:00
* feat(exercises): add new route for fetching exercises with filtering options chore(import-exercises): improve formatting and clarity in the prompt for exercise attributes * feat(shuffle): add shuffle exercise route with input validation and error handling chore(delete-program-button): simplify button formatting and update confirmation message for clarity chore(workspace): create workspace configuration for easier project management across frontend and mobile directories * feat(auth): implement signup route with validation and error handling for user registration * feat(api): add user password and profile management routes for updating and retrieving user information * chore(route.ts): remove console.log statements for cleaner code and improved performance * feat(profile): implement cookie cleaning and logging for user profile updates to enhance session management and debugging fix(package): add @better-auth/expo dependency for improved authentication support fix(better-auth): update trusted origins to include expo and workoutfast protocols for better compatibility * feat(auth): implement mobile-compatible session handling to address malformed cookies from the mobile app and improve authentication reliability chore(api): refactor user password and profile routes to utilize new mobile session utilities for better cookie management docs(api): add README for mobile authentication support detailing issues and solutions for mobile app cookie handling * feat(password): refactor password update logic to separate core functionality from action client for better reusability and clarity fix(password): improve error handling in password update route to provide more specific feedback on validation failures * refactor(mobile-auth.ts, mobile-cookie-utils.ts, mobile-safe-actions.ts): extract cleanMobileCookies function to a new utility file for better code organization and reuse across modules * refactor(password route): simplify error handling by returning the original error message instead of custom messages for better maintainability and clarity * feat(workout-sessions): add POST route for syncing workout sessions to handle mobile compatibility and session management * feat(workout-sessions): add user-specific workout session retrieval and error handling for syncing sessions fix(sync-workout-sessions): improve error handling for missing users and exercises during session sync process * feat(feedback, rating, summary): implement feedback and rating endpoints for workout sessions, add summary metrics and update database schema to support ratings and comments docs: create CLAUDE.md to provide project overview, architecture, key features, and deployment details * refactor(route.ts): simplify GET request handler by removing userId parameter check and adding rating fields to response for enhanced session details * feat(workout-sessions): implement DELETE route for deleting workout sessions with authentication checks and error handling * refactor(workout-sessions): update DELETE route to handle sessionId as a resolved promise and simplify authentication checks in delete action * feat(revenuecat): integrate RevenueCat for subscription management and user linking, add webhook handling for subscription events, and enhance premium status checks with RevenueCat data fix(premium/status): update premium status endpoint to include RevenueCat information and improve error handling chore(database): add RevenueCat fields to subscription model and create webhook event logging table in the database build(dependencies): add RevenueCat backend wrapper and crypto-js for secure webhook signature verification * chore: update .gitignore to exclude product-development and .claude directories feat(locales): add French translation for lats in locales/fr.ts fix(better-auth): update trustedOrigins to replace workoutfast with workoutcool for better authentication configuration * fix(route.ts): update import path for getExercisesAction to reflect new actions directory structure for better organization * feat(route.ts): add logging for session details to assist with debugging feat(sync-workout-sessions.action.ts): extend workout session schema to include optional rating and ratingComment fields for enhanced user feedback * feat(analytics): add premium analytics tracking endpoint and enhance subscription details with premium features * feat(revenuecat): implement RevenueCat API client for subscription management and enhance premium service with syncing capabilities * feat(statistics): implement exercise statistics feature with charts and data fetching for weight progression, one-rep max, and volume statistics, including premium access controls and UI components * feat(exercises): enhance exercise attributes handling and UI for better user experience and performance tracking * feat(ExercisesBrowser): enhance exercise selection with filters, statistics, and improved UI for better user experience * feat(exercises): enhance exercise filtering and statistics fetching with improved query parameters and error handling in the ExercisesBrowser component * refactor(statistics): replace ExerciseStatisticsTab with ExerciseCharts for improved clarity and functionality across multiple components * refactor(statistics): clean up ExerciseStatisticsTab and ExercisesBrowser components by removing unused imports, simplifying modal structure, and enhancing styling for better readability and maintainability * fix(ExercisesBrowser): add console log for selectedExercise and adjust styling for exercise image container to enhance layout and responsiveness * feat(statistics): integrate recharts for improved charting and add theme support for better UI consistency * feat(statistics): enhance exercise statistics page with new charts and localization updates, and remove unused statistics page for better organization * feat: add lodash.debounce type definitions and clean up ExercisesBrowser component markup * feat(statistics): remove exercises page and implement statistics page with localization and navigation updates * fix(statistics): update alert variant for error handling and refactor tooltip implementation for better clarity and usability * feat(statistics): enhance StatisticsPage with premium features and overlays for better user engagement and insights * feat(premium-gate): add upgrade to premium message and streamline component structure for better readability * feat(exercise): add new exercise attributes handling and refactor ExerciseSelection component to utilize updated types and methods for better attribute management * refactor(exercise): simplify muscle attribute retrieval functions and improve type consistency across components * feat: refactor workout session and exercise components for improved attribute handling and remove unused HomePage component * fix(statistics.types.ts): reorder imports to maintain consistency and clarity in module structure * feat(exercise-video-modal): implement tabbed interface for video and statistics with timeframe selection to enhance user experience * feat(statistics): enhance statistics page with dynamic translations and improved user experience through localized content and updated UI elements * feat(statistics): refactor ExercisesBrowser and ExerciseVideoModal to use TimeframeSelector component for improved code reusability and maintainability * fix(locales/fr.ts): update French translations for consistency and accuracy in exercise terminology; fix(ExercisesBrowser.tsx): enhance muscle group labels to match updated translation keys and improve code readability * feat(simple-select): add SimpleSelect component for reusable select functionality and integrate it into ExercisesBrowser for improved filtering options * fix(fr.ts): update French translations for timeframes to use lowercase for consistency * feat(statistics): enhance date formatting by integrating locale support across multiple components for improved user experience * feat(locales): add statistics and tooltip translations in multiple languages for improved user experience * feat(revenuecat): implement webhook handler for subscription updates and add advanced workout data seeding script * feat(revenuecat): deprecate user ID assignment and linking endpoints, add sync-status endpoint for automatic management of RevenueCat IDs * feat(webhook): add support for SUBSCRIPTION_PAUSED event and enhance logging for cancellation events * fix(webhooks): rename expiration_at to expiration_at_ms for consistency in RevenueCatWebhookEvent interface and update related logic in processSubscriptionEvent function * feat(revenuecat): implement new webhook handling and subscription sync logic to improve premium status management and simplify database schema * chore: update USER_ID in seed-workout-data-advanced and simplify premium status check in premium.service to improve clarity and maintainability * feat(api): add program enrollment and retrieval endpoints for better program management functionality * feat(programs): enhance program enrollment and progress routes with new functionality and improve parameter handling * refactor(api): update parameter types to use Promise for slug and sessionSlug in route handlers for better async handling and clarity * refactor(premium.service.ts): remove revenueCatEntitlement references to simplify premium access logic and improve code clarity * chore(ci): add RevenueCat environment variables for CI workflow testing * chore: remove unused RevenueCat project ID references to clean up codebase * chore: remove deprecated API routes and clean up unused code to streamline the codebase * chore: baseline migration
732 lines
19 KiB
Plaintext
732 lines
19 KiB
Plaintext
// This is your Prisma schema file
|
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
enum UserRole {
|
|
admin
|
|
user
|
|
}
|
|
|
|
model User {
|
|
id String @id
|
|
firstName String @default("")
|
|
lastName String @default("")
|
|
name String
|
|
email String @unique
|
|
emailVerified Boolean
|
|
image String?
|
|
locale String? @default("fr")
|
|
createdAt DateTime
|
|
updatedAt DateTime
|
|
sessions Session[]
|
|
accounts Account[]
|
|
feedbacks Feedbacks[]
|
|
|
|
role UserRole? @default(user)
|
|
banned Boolean? @default(false)
|
|
banReason String?
|
|
banExpires DateTime?
|
|
WorkoutSession WorkoutSession[]
|
|
FavoriteExercises UserFavoriteExercise[]
|
|
|
|
// Subscription fields
|
|
subscriptions Subscription[]
|
|
licenses License[]
|
|
isPremium Boolean? @default(false)
|
|
|
|
// Training programs
|
|
programEnrollments UserProgramEnrollment[]
|
|
|
|
@@map("user")
|
|
}
|
|
|
|
model UserFavoriteExercise {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
exerciseId String
|
|
exercise Exercise @relation(fields: [exerciseId], references: [id], onDelete: Cascade)
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, exerciseId])
|
|
@@map("user_favorite_exercises")
|
|
}
|
|
|
|
model Session {
|
|
id String @id
|
|
expiresAt DateTime
|
|
token String @unique
|
|
createdAt DateTime
|
|
updatedAt DateTime
|
|
ipAddress String?
|
|
userAgent String?
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
impersonatedBy String?
|
|
|
|
@@map("session")
|
|
}
|
|
|
|
model Account {
|
|
id String @id
|
|
accountId String
|
|
providerId String
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
accessToken String?
|
|
refreshToken String?
|
|
idToken String?
|
|
accessTokenExpiresAt DateTime?
|
|
refreshTokenExpiresAt DateTime?
|
|
scope String?
|
|
password String?
|
|
createdAt DateTime
|
|
updatedAt DateTime
|
|
|
|
@@map("account")
|
|
}
|
|
|
|
model Verification {
|
|
id String @id
|
|
identifier String
|
|
value String
|
|
expiresAt DateTime
|
|
createdAt DateTime?
|
|
updatedAt DateTime?
|
|
|
|
@@map("verification")
|
|
}
|
|
|
|
model Feedbacks {
|
|
id String @id @default(cuid())
|
|
review Int
|
|
message String
|
|
email String?
|
|
userId String?
|
|
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@map("feedbacks")
|
|
}
|
|
|
|
model Exercise {
|
|
id String @id @default(cuid())
|
|
name String
|
|
nameEn String?
|
|
description String? @db.Text
|
|
descriptionEn String? @db.Text
|
|
fullVideoUrl String? @db.Text
|
|
fullVideoImageUrl String? @db.Text
|
|
introduction String? @db.Text
|
|
introductionEn String? @db.Text
|
|
slug String? @unique
|
|
slugEn String? @unique
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
attributes ExerciseAttribute[]
|
|
WorkoutSessionExercise WorkoutSessionExercise[]
|
|
ProgramSessionExercise ProgramSessionExercise[]
|
|
favoritesByUsers UserFavoriteExercise[]
|
|
|
|
@@map("exercises")
|
|
}
|
|
|
|
model ExerciseAttributeName {
|
|
id String @id @default(cuid())
|
|
name ExerciseAttributeNameEnum @unique
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
values ExerciseAttributeValue[]
|
|
attributes ExerciseAttribute[]
|
|
|
|
@@map("exercise_attribute_names")
|
|
}
|
|
|
|
model ExerciseAttributeValue {
|
|
id String @id @default(cuid())
|
|
attributeNameId String
|
|
value ExerciseAttributeValueEnum
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
attributeName ExerciseAttributeName @relation(fields: [attributeNameId], references: [id])
|
|
attributes ExerciseAttribute[]
|
|
|
|
@@unique([attributeNameId, value])
|
|
@@map("exercise_attribute_values")
|
|
}
|
|
|
|
model ExerciseAttribute {
|
|
id String @id @default(cuid())
|
|
exerciseId String
|
|
attributeNameId String
|
|
attributeValueId String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
exercise Exercise @relation(fields: [exerciseId], references: [id], onDelete: Cascade)
|
|
attributeName ExerciseAttributeName @relation(fields: [attributeNameId], references: [id])
|
|
attributeValue ExerciseAttributeValue @relation(fields: [attributeValueId], references: [id])
|
|
|
|
@@unique([exerciseId, attributeNameId, attributeValueId])
|
|
@@map("exercise_attributes")
|
|
}
|
|
|
|
// Enums
|
|
enum ExercisePrivacy {
|
|
PUBLIC
|
|
PRIVATE
|
|
}
|
|
|
|
// Noms d'attributs
|
|
enum ExerciseAttributeNameEnum {
|
|
TYPE
|
|
PRIMARY_MUSCLE
|
|
SECONDARY_MUSCLE
|
|
EQUIPMENT
|
|
MECHANICS_TYPE
|
|
}
|
|
|
|
// Toutes les valeurs possibles
|
|
enum ExerciseAttributeValueEnum {
|
|
// Types d'exercices
|
|
BODYWEIGHT
|
|
STRENGTH
|
|
POWERLIFTING
|
|
CALISTHENIC
|
|
PLYOMETRICS
|
|
STRETCHING
|
|
STRONGMAN
|
|
CARDIO
|
|
STABILIZATION
|
|
POWER
|
|
RESISTANCE
|
|
CROSSFIT
|
|
WEIGHTLIFTING
|
|
|
|
// Groupes musculaires
|
|
BICEPS
|
|
SHOULDERS
|
|
CHEST
|
|
BACK
|
|
GLUTES
|
|
TRICEPS
|
|
HAMSTRINGS
|
|
QUADRICEPS
|
|
FOREARMS
|
|
CALVES
|
|
TRAPS
|
|
ABDOMINALS
|
|
NECK
|
|
LATS
|
|
ADDUCTORS
|
|
ABDUCTORS
|
|
OBLIQUES
|
|
GROIN
|
|
FULL_BODY
|
|
ROTATOR_CUFF
|
|
HIP_FLEXOR
|
|
ACHILLES_TENDON
|
|
FINGERS
|
|
|
|
// Équipements
|
|
DUMBBELL
|
|
KETTLEBELLS
|
|
BARBELL
|
|
SMITH_MACHINE
|
|
BODY_ONLY
|
|
OTHER
|
|
BANDS
|
|
EZ_BAR
|
|
MACHINE
|
|
DESK
|
|
PULLUP_BAR
|
|
NONE
|
|
CABLE
|
|
MEDICINE_BALL
|
|
SWISS_BALL
|
|
FOAM_ROLL
|
|
WEIGHT_PLATE
|
|
TRX
|
|
BOX
|
|
ROPES
|
|
SPIN_BIKE
|
|
STEP
|
|
BOSU
|
|
TYRE
|
|
SANDBAG
|
|
POLE
|
|
BENCH
|
|
WALL
|
|
BAR
|
|
RACK
|
|
CAR
|
|
SLED
|
|
CHAIN
|
|
SKIERG
|
|
ROPE
|
|
NA
|
|
|
|
// Types de mécanique
|
|
ISOLATION
|
|
COMPOUND
|
|
}
|
|
|
|
model WorkoutSession {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id])
|
|
startedAt DateTime
|
|
endedAt DateTime?
|
|
duration Int? // en secondes
|
|
exercises WorkoutSessionExercise[]
|
|
muscles ExerciseAttributeValueEnum[] @default([])
|
|
|
|
rating Int? // 1-5 star rating
|
|
ratingComment String? @db.Text // Optional comment for rating
|
|
|
|
// Program progress tracking
|
|
userSessionProgress UserSessionProgress?
|
|
|
|
@@map("workout_sessions")
|
|
}
|
|
|
|
model WorkoutSessionExercise {
|
|
id String @id @default(cuid())
|
|
workoutSessionId String
|
|
exerciseId String
|
|
order Int
|
|
workoutSession WorkoutSession @relation(fields: [workoutSessionId], references: [id], onDelete: Cascade)
|
|
exercise Exercise @relation(fields: [exerciseId], references: [id])
|
|
sets WorkoutSet[]
|
|
|
|
@@map("workout_session_exercises")
|
|
}
|
|
|
|
model WorkoutSet {
|
|
id String @id @default(cuid())
|
|
workoutSessionExerciseId String
|
|
setIndex Int
|
|
type WorkoutSetType
|
|
types WorkoutSetType[] @default([])
|
|
valuesInt Int[] @default([])
|
|
valuesSec Int[] @default([])
|
|
units WorkoutSetUnit[] @default([])
|
|
completed Boolean @default(false)
|
|
workoutSessionExercise WorkoutSessionExercise @relation(fields: [workoutSessionExerciseId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("workout_sets")
|
|
}
|
|
|
|
enum WorkoutSetType {
|
|
TIME
|
|
WEIGHT
|
|
REPS
|
|
BODYWEIGHT
|
|
NA
|
|
}
|
|
|
|
enum WorkoutSetUnit {
|
|
kg
|
|
lbs
|
|
}
|
|
|
|
// ========================================
|
|
// BILLING & SUBSCRIPTION MODELS
|
|
// ========================================
|
|
|
|
// Plans d'abonnement - provider-agnostic, minimal KISS approach
|
|
model SubscriptionPlan {
|
|
id String @id @default(cuid())
|
|
|
|
// Pricing - internal source of truth
|
|
priceMonthly Decimal? @db.Decimal(10, 2)
|
|
priceYearly Decimal? @db.Decimal(10, 2)
|
|
currency String @default("EUR")
|
|
interval String @default("month") // month, year
|
|
|
|
// Availability
|
|
isActive Boolean @default(true)
|
|
availableRegions String[] @default([]) // ["EU", "US", "UK"]
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
subscriptions Subscription[]
|
|
providerMappings PlanProviderMapping[]
|
|
|
|
@@map("subscription_plans")
|
|
}
|
|
|
|
// Provider mapping for external payment systems
|
|
model PlanProviderMapping {
|
|
id String @id @default(cuid())
|
|
planId String
|
|
provider PaymentProcessor
|
|
|
|
// External provider IDs
|
|
externalId String // price_1ABC for Stripe, variant_123 for LemonSqueezy, etc.
|
|
region String? // EU, US, UK for regional pricing
|
|
|
|
// Provider-specific metadata
|
|
metadata Json? // Additional provider-specific data
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
plan SubscriptionPlan @relation(fields: [planId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([planId, provider, region])
|
|
@@index([provider, externalId])
|
|
@@map("plan_provider_mappings")
|
|
}
|
|
|
|
// Abonnements utilisateurs (source de vérité : RevenueCat pour mobile)
|
|
model Subscription {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
planId String
|
|
plan SubscriptionPlan @relation(fields: [planId], references: [id])
|
|
|
|
// RevenueCat data (simplified)
|
|
revenueCatUserId String? // The RevenueCat user ID for sync
|
|
|
|
// Status
|
|
status SubscriptionStatus
|
|
|
|
// Dates importantes
|
|
startedAt DateTime
|
|
currentPeriodEnd DateTime?
|
|
cancelledAt DateTime?
|
|
|
|
// Platform info
|
|
platform Platform?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, platform])
|
|
@@map("subscriptions")
|
|
}
|
|
|
|
// Licences pour self-hosted (alternative aux subscriptions)
|
|
model License {
|
|
id String @id @default(cuid())
|
|
key String @unique
|
|
|
|
// Propriétaire
|
|
userId String?
|
|
user User? @relation(fields: [userId], references: [id])
|
|
|
|
// Validité
|
|
validFrom DateTime @default(now())
|
|
validUntil DateTime?
|
|
|
|
// Limites
|
|
maxUsers Int? @default(1)
|
|
features Json?
|
|
|
|
// Activation
|
|
activatedAt DateTime?
|
|
lastCheckedAt DateTime?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@map("licenses")
|
|
}
|
|
|
|
// RevenueCat webhook event logging for debugging and monitoring
|
|
model RevenueCatWebhookEvent {
|
|
id String @id @default(cuid())
|
|
|
|
// Event metadata
|
|
eventType String // INITIAL_PURCHASE, RENEWAL, CANCELLATION, etc.
|
|
eventTimestamp DateTime
|
|
appUserId String
|
|
environment String // SANDBOX or PRODUCTION
|
|
|
|
// Transaction details
|
|
productId String?
|
|
transactionId String?
|
|
originalTransactionId String?
|
|
entitlementIds String? // JSON array as string
|
|
|
|
// Processing metadata
|
|
processed Boolean @default(false)
|
|
processingError String? @db.Text
|
|
retryCount Int @default(0)
|
|
|
|
// Raw event data for debugging
|
|
rawEventData Json
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([appUserId])
|
|
@@index([eventType])
|
|
@@index([processed])
|
|
@@index([eventTimestamp])
|
|
@@map("revenuecat_webhook_events")
|
|
}
|
|
|
|
// ========================================
|
|
// BILLING ENUMS
|
|
// ========================================
|
|
|
|
enum SubscriptionStatus {
|
|
ACTIVE
|
|
TRIAL
|
|
CANCELLED
|
|
EXPIRED
|
|
PAUSED
|
|
}
|
|
|
|
enum Platform {
|
|
WEB
|
|
IOS
|
|
ANDROID
|
|
}
|
|
|
|
enum PaymentProcessor {
|
|
STRIPE
|
|
PAYPAL
|
|
LEMONSQUEEZY
|
|
PADDLE
|
|
APPLE_PAY
|
|
GOOGLE_PAY
|
|
REVENUECAT
|
|
NONE // self-hosted
|
|
OTHER
|
|
}
|
|
|
|
// ========================================
|
|
// TRAINING PROGRAMS
|
|
// ========================================
|
|
|
|
model Program {
|
|
id String @id @default(cuid())
|
|
slug String @unique
|
|
slugEn String @unique
|
|
slugEs String @unique
|
|
slugPt String @unique
|
|
slugRu String @unique
|
|
slugZhCn String @unique
|
|
title String
|
|
titleEn String
|
|
titleEs String
|
|
titlePt String
|
|
titleRu String
|
|
titleZhCn String
|
|
description String @db.Text
|
|
descriptionEn String @db.Text
|
|
descriptionEs String @db.Text
|
|
descriptionPt String @db.Text
|
|
descriptionRu String @db.Text
|
|
descriptionZhCn String @db.Text
|
|
category String
|
|
image String
|
|
level ProgramLevel
|
|
type ExerciseAttributeValueEnum
|
|
durationWeeks Int @default(4)
|
|
sessionsPerWeek Int @default(3)
|
|
sessionDurationMin Int @default(30)
|
|
equipment ExerciseAttributeValueEnum[] @default([])
|
|
isPremium Boolean @default(true)
|
|
isActive Boolean @default(true)
|
|
visibility ProgramVisibility @default(DRAFT)
|
|
participantCount Int @default(0)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
weeks ProgramWeek[]
|
|
enrollments UserProgramEnrollment[]
|
|
coaches ProgramCoach[]
|
|
|
|
@@map("programs")
|
|
}
|
|
|
|
model ProgramCoach {
|
|
id String @id @default(cuid())
|
|
programId String
|
|
name String
|
|
image String
|
|
order Int @default(0)
|
|
|
|
program Program @relation(fields: [programId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("program_coaches")
|
|
}
|
|
|
|
model ProgramWeek {
|
|
id String @id @default(cuid())
|
|
programId String
|
|
weekNumber Int
|
|
title String
|
|
titleEn String
|
|
titleEs String
|
|
titlePt String
|
|
titleRu String
|
|
titleZhCn String
|
|
description String @db.Text
|
|
descriptionEn String @db.Text
|
|
descriptionEs String @db.Text
|
|
descriptionPt String @db.Text
|
|
descriptionRu String @db.Text
|
|
descriptionZhCn String @db.Text
|
|
|
|
program Program @relation(fields: [programId], references: [id], onDelete: Cascade)
|
|
sessions ProgramSession[]
|
|
|
|
@@unique([programId, weekNumber])
|
|
@@map("program_weeks")
|
|
}
|
|
|
|
model ProgramSession {
|
|
id String @id @default(cuid())
|
|
weekId String
|
|
sessionNumber Int
|
|
title String
|
|
titleEn String
|
|
titleEs String
|
|
titlePt String
|
|
titleRu String
|
|
titleZhCn String
|
|
slug String
|
|
slugEn String
|
|
slugEs String
|
|
slugPt String
|
|
slugRu String
|
|
slugZhCn String
|
|
description String @db.Text
|
|
descriptionEn String @db.Text
|
|
descriptionEs String @db.Text
|
|
descriptionPt String @db.Text
|
|
descriptionRu String @db.Text
|
|
descriptionZhCn String @db.Text
|
|
equipment ExerciseAttributeValueEnum[] @default([])
|
|
estimatedMinutes Int
|
|
isPremium Boolean @default(true)
|
|
|
|
week ProgramWeek @relation(fields: [weekId], references: [id], onDelete: Cascade)
|
|
exercises ProgramSessionExercise[]
|
|
userProgress UserSessionProgress[]
|
|
|
|
@@unique([weekId, sessionNumber])
|
|
@@unique([weekId, slug])
|
|
@@unique([weekId, slugEn])
|
|
@@unique([weekId, slugEs])
|
|
@@unique([weekId, slugPt])
|
|
@@unique([weekId, slugRu])
|
|
@@unique([weekId, slugZhCn])
|
|
@@map("program_sessions")
|
|
}
|
|
|
|
model ProgramSessionExercise {
|
|
id String @id @default(cuid())
|
|
sessionId String
|
|
exerciseId String
|
|
order Int
|
|
instructions String @db.Text
|
|
instructionsEn String @db.Text
|
|
instructionsEs String @db.Text
|
|
instructionsPt String @db.Text
|
|
instructionsRu String @db.Text
|
|
instructionsZhCn String @db.Text
|
|
|
|
session ProgramSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
exercise Exercise @relation(fields: [exerciseId], references: [id])
|
|
suggestedSets ProgramSuggestedSet[]
|
|
|
|
@@unique([sessionId, order])
|
|
@@map("program_session_exercises")
|
|
}
|
|
|
|
model ProgramSuggestedSet {
|
|
id String @id @default(cuid())
|
|
programSessionExerciseId String
|
|
setIndex Int
|
|
types WorkoutSetType[] @default([])
|
|
valuesInt Int[] @default([])
|
|
valuesSec Int[] @default([])
|
|
units WorkoutSetUnit[] @default([])
|
|
|
|
programSessionExercise ProgramSessionExercise @relation(fields: [programSessionExerciseId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([programSessionExerciseId, setIndex])
|
|
@@map("program_suggested_sets")
|
|
}
|
|
|
|
// User enrollment and progress tracking
|
|
model UserProgramEnrollment {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
programId String
|
|
enrolledAt DateTime @default(now())
|
|
currentWeek Int @default(1)
|
|
currentSession Int @default(1)
|
|
completedSessions Int @default(0)
|
|
isActive Boolean @default(true)
|
|
completedAt DateTime?
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
program Program @relation(fields: [programId], references: [id])
|
|
sessionProgress UserSessionProgress[]
|
|
|
|
@@unique([userId, programId])
|
|
@@map("user_program_enrollments")
|
|
}
|
|
|
|
model UserSessionProgress {
|
|
id String @id @default(cuid())
|
|
enrollmentId String
|
|
sessionId String
|
|
startedAt DateTime @default(now())
|
|
completedAt DateTime?
|
|
workoutSessionId String? @unique // Link to actual workout session
|
|
|
|
enrollment UserProgramEnrollment @relation(fields: [enrollmentId], references: [id], onDelete: Cascade)
|
|
session ProgramSession @relation(fields: [sessionId], references: [id])
|
|
workoutSession WorkoutSession? @relation(fields: [workoutSessionId], references: [id])
|
|
|
|
@@unique([enrollmentId, sessionId])
|
|
@@map("user_session_progress")
|
|
}
|
|
|
|
enum ProgramLevel {
|
|
BEGINNER
|
|
INTERMEDIATE
|
|
ADVANCED
|
|
EXPERT
|
|
}
|
|
|
|
enum ProgramVisibility {
|
|
DRAFT
|
|
PUBLISHED
|
|
ARCHIVED
|
|
}
|