feat: add user settings save and reading plans with progress tracking

User Settings:
- Add /api/user/settings endpoint for persisting theme and fontSize preferences
- Update settings page with working save functionality
- Add validation and localized error messages

Reading Plans:
- Add database schema with ReadingPlan, UserReadingPlan, and UserReadingProgress models
- Create CRUD API endpoints for reading plans and progress tracking
- Build UI for browsing available plans and managing user enrollments
- Implement progress tracking with daily reading schedule
- Add streak calculation and statistics display
- Create seed data with 5 predefined plans (Bible in 1 year, 90 days, NT 30 days, Psalms 30, Gospels 30)
- Add navigation link with internationalization support

Technical:
- Update to MUI v7 Grid API (using size prop instead of xs/sm/md and removing item prop)
- Fix Next.js 15 dynamic route params (await params pattern)
- Add translations for readingPlans in all languages (en, ro, es, it)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-12 23:07:47 +00:00
parent 9d82e719ed
commit 63082c825a
15 changed files with 2065 additions and 3 deletions

View File

@@ -40,6 +40,8 @@ model User {
userPrayers UserPrayer[]
readingHistory ReadingHistory[]
preferences UserPreference[]
userReadingPlans UserReadingPlan[]
readingProgress UserReadingProgress[]
createdPages Page[] @relation("PageCreator")
updatedPages Page[] @relation("PageUpdater")
uploadedFiles MediaFile[]
@@ -500,3 +502,87 @@ enum SubscriptionStatus {
INCOMPLETE_EXPIRED
UNPAID
}
// Reading Plans
model ReadingPlan {
id String @id @default(uuid())
name String // "Bible in One Year", "New Testament in 30 Days"
description String? @db.Text
type ReadingPlanType @default(PREDEFINED)
duration Int // Number of days
schedule Json // Daily reading schedule: {day: 1, readings: [{book, chapter, verses}]}
difficulty String @default("beginner") // beginner, intermediate, advanced
language String @default("en")
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userPlans UserReadingPlan[]
@@index([type])
@@index([language])
@@index([isActive])
}
enum ReadingPlanType {
PREDEFINED
CUSTOM
}
model UserReadingPlan {
id String @id @default(uuid())
userId String
planId String? // Null for custom plans
name String // Plan name (especially for custom plans)
startDate DateTime @default(now())
targetEndDate DateTime // Expected completion date
actualEndDate DateTime? // When actually completed
status ReadingPlanStatus @default(ACTIVE)
currentDay Int @default(1) // Current day in plan
completedDays Int @default(0) // Total days completed
streak Int @default(0) // Current consecutive days
longestStreak Int @default(0) // Best streak achieved
customSchedule Json? // For custom plans: same format as ReadingPlan.schedule
reminderEnabled Boolean @default(true)
reminderTime String? // "08:00" - time of day for reminder
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
plan ReadingPlan? @relation(fields: [planId], references: [id], onDelete: SetNull)
progress UserReadingProgress[]
@@index([userId])
@@index([status])
@@index([userId, status])
}
enum ReadingPlanStatus {
ACTIVE
COMPLETED
PAUSED
CANCELLED
}
model UserReadingProgress {
id String @id @default(uuid())
userId String
userPlanId String
planDay Int // Day number in the reading plan
date DateTime @default(now()) // Date of reading
bookId String // Bible book read
chapterNum Int
versesRead String? // "1-10" or "all" or null for whole chapter
completed Boolean @default(true)
notes String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userPlan UserReadingPlan @relation(fields: [userPlanId], references: [id], onDelete: Cascade)
@@unique([userPlanId, planDay, bookId, chapterNum]) // One entry per chapter per day per plan
@@index([userId])
@@index([userPlanId])
@@index([userId, date])
}