import Foundation /// Rating-Werte für `POST /reviews/:cardId/:subIndex/grade`. /// Aus `cards/packages/cards-domain/src/schemas/review.ts:RatingSchema`. enum Rating: String, Codable, CaseIterable { case again case hard case good case easy /// Anzeige-Label auf dem Rating-Button. var label: String { switch self { case .again: "Nochmal" case .hard: "Schwer" case .good: "Gut" case .easy: "Leicht" } } /// Kurz-Symbol für minimalistische UI. var shortcut: String { switch self { case .again: "1" case .hard: "2" case .good: "3" case .easy: "4" } } } /// FSRS-Review-State. Aus `ReviewStateSchema`. enum ReviewState: String, Codable { case new case learning case review case relearning } /// Review-DTO. Wire-Format aus `cards/apps/api/src/routes/reviews.ts:toReviewDto`. struct Review: Codable, Hashable { let cardId: String let subIndex: Int let userId: String let due: Date let stability: Double let difficulty: Double let elapsedDays: Double let scheduledDays: Double let learningSteps: Int let reps: Int let lapses: Int let state: ReviewState let lastReview: Date? enum CodingKeys: String, CodingKey { case cardId = "card_id" case subIndex = "sub_index" case userId = "user_id" case due case stability case difficulty case elapsedDays = "elapsed_days" case scheduledDays = "scheduled_days" case learningSteps = "learning_steps" case reps case lapses case state case lastReview = "last_review" } } /// Eintrag aus `/reviews/due?deck_id=X` — Review + zugehörige Card. struct DueReview: Codable, Hashable, Identifiable { let review: Review let card: ReviewCard var id: String { "\(review.cardId)-\(review.subIndex)" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // Flat-Decoding: Review-Felder + card-Objekt im selben JSON-Objekt review = try Review(from: decoder) card = try container.decode(ReviewCard.self, forKey: .card) } func encode(to encoder: Encoder) throws { try review.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(card, forKey: .card) } enum CodingKeys: String, CodingKey { case card } } /// Wrapper-Response von `GET /api/v1/reviews/due?deck_id=X`. struct DueReviewsListResponse: Decodable { let reviews: [DueReview] let total: Int } /// Body für `POST /reviews/:cardId/:subIndex/grade`. struct GradeReviewBody: Encodable { let rating: Rating let reviewedAt: Date enum CodingKeys: String, CodingKey { case rating case reviewedAt = "reviewed_at" } }