datasource db { provider = "sqlite" } generator client { provider = "prisma-client" output = "../src/generated/prisma" } model Class { id String @id @default(uuid()) slug String @unique name String sortOrder Int @default(0) createdAt DateTime @default(now()) decks Deck[] quizSets QuizSet[] materialGroups MaterialGroup[] spacedRepetitionSets SpacedRepetitionSet[] } model Deck { id String @id @default(uuid()) classId String name String description String? sortOrder Int @default(0) createdAt DateTime @default(now()) class Class @relation(fields: [classId], references: [id], onDelete: Cascade) groupId String? group MaterialGroup? @relation(fields: [groupId], references: [id], onDelete: SetNull) cards Flashcard[] shareLink ShareLink? progress StudyProgress[] spacedRepetitionMemberships SpacedRepetitionSetDeck[] } model Flashcard { id String @id @default(uuid()) deckId String front String back String sortOrder Int @default(0) deck Deck @relation(fields: [deckId], references: [id], onDelete: Cascade) spacedRepetitionStates SpacedRepetitionCardState[] } model QuizSet { id String @id @default(uuid()) classId String name String description String? sortOrder Int @default(0) createdAt DateTime @default(now()) class Class @relation(fields: [classId], references: [id], onDelete: Cascade) groupId String? group MaterialGroup? @relation(fields: [groupId], references: [id], onDelete: SetNull) questions Question[] attempts QuizAttempt[] shareLink ShareLink? progress StudyProgress[] } model Question { id String @id @default(uuid()) quizSetId String type String // "MULTIPLE_CHOICE" | "SATA" prompt String rationale String category String sortOrder Int @default(0) quizSet QuizSet @relation(fields: [quizSetId], references: [id], onDelete: Cascade) options AnswerOption[] } model AnswerOption { id String @id @default(uuid()) questionId String text String isCorrect Boolean @default(false) sortOrder Int @default(0) question Question @relation(fields: [questionId], references: [id], onDelete: Cascade) } model StudyProgress { id String @id @default(uuid()) contentType String // "DECK" | "QUIZ" deckId String? quizSetId String? mode String // "SEQUENTIAL" | "SHUFFLED" currentIndex Int @default(0) orderJson String // JSON array of card/question ids answersJson String? // in-progress quiz answers cardResultsJson String? // per-card grades for decks updatedAt DateTime @updatedAt deck Deck? @relation(fields: [deckId], references: [id], onDelete: Cascade) quizSet QuizSet? @relation(fields: [quizSetId], references: [id], onDelete: Cascade) @@unique([deckId, mode]) @@unique([quizSetId, mode]) } model QuizAttempt { id String @id @default(uuid()) quizSetId String score Float maxScore Int answersJson String isPartialRetake Boolean @default(false) completedAt DateTime @default(now()) quizSet QuizSet @relation(fields: [quizSetId], references: [id], onDelete: Cascade) } model ShareLink { id String @id @default(uuid()) targetType String // "DECK" | "QUIZ" | "GROUP" deckId String? @unique quizSetId String? @unique groupId String? @unique createdAt DateTime @default(now()) deck Deck? @relation(fields: [deckId], references: [id], onDelete: Cascade) quizSet QuizSet? @relation(fields: [quizSetId], references: [id], onDelete: Cascade) group MaterialGroup? @relation(fields: [groupId], references: [id], onDelete: Cascade) } model AuthSecurity { id Int @id @default(1) failedAttempts Int @default(0) lockedUntil DateTime? lastAttemptAt DateTime? } model Setting { key String @id value String } model StudyActivity { id String @id @default(uuid()) type String // "FLASHCARD" | "QUIZ_QUESTION" occurredAt DateTime @default(now()) @@index([occurredAt]) } model MaterialGroup { id String @id @default(uuid()) classId String name String type String // "DECK" | "QUIZ" sortOrder Int @default(0) createdAt DateTime @default(now()) class Class @relation(fields: [classId], references: [id], onDelete: Cascade) decks Deck[] quizSets QuizSet[] shareLink ShareLink? } model SpacedRepetitionSet { id String @id @default(uuid()) classId String name String description String? newCardsPerDay Int @default(30) sortOrder Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt class Class @relation(fields: [classId], references: [id], onDelete: Cascade) decks SpacedRepetitionSetDeck[] cardStates SpacedRepetitionCardState[] @@index([classId, sortOrder]) } model SpacedRepetitionSetDeck { setId String deckId String sortOrder Int @default(0) addedAt DateTime @default(now()) set SpacedRepetitionSet @relation(fields: [setId], references: [id], onDelete: Cascade) deck Deck @relation(fields: [deckId], references: [id], onDelete: Cascade) @@id([setId, deckId]) @@index([deckId]) } model SpacedRepetitionCardState { id String @id @default(uuid()) setId String flashcardId String due DateTime stability Float difficulty Float elapsedDays Int scheduledDays Int learningSteps Int @default(0) reps Int lapses Int state Int firstReviewedAt DateTime @default(now()) lastReview DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt set SpacedRepetitionSet @relation(fields: [setId], references: [id], onDelete: Cascade) flashcard Flashcard @relation(fields: [flashcardId], references: [id], onDelete: Cascade) @@unique([setId, flashcardId]) @@index([setId, due]) @@index([setId, firstReviewedAt]) @@index([flashcardId]) }