From 1afb825860cd293b8065d51746f4b23e4e8dab5d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Feb 2026 14:54:18 +0000 Subject: feat: 学習カード数の上限を撤廃 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit REVIEW_CARDS_LIMIT(復習カード80枚制限)とnewCardsPerDay(1日の新規カード制限) を削除し、期日が来たすべてのカードを制限なく返すように変更。 削除した主な要素: - REVIEW_CARDS_LIMIT定数とカード取得時のlimitパラメータ - newCardsPerDayフィールド(DB schema, 型定義, Zod schema, sync, CRDT) - countDueNewCards, countDueReviewCards, findDueNewCardsForStudy, findDueReviewCardsForStudy(CardRepository) - countTodayNewCardReviews(ReviewLogRepository) - デッキルートからのReviewLogRepository依存 https://claude.ai/code/session_018hrEJ9vg3RPoeAPyEc17gS --- src/client/atoms/decks.ts | 1 - src/client/components/CreateDeckModal.test.tsx | 2 - src/client/components/EditDeckModal.test.tsx | 2 - src/client/components/EditDeckModal.tsx | 1 - src/client/db/index.test.ts | 1 - src/client/db/index.ts | 1 - src/client/db/repositories.test.ts | 17 ---- src/client/db/repositories.ts | 2 +- src/client/pages/DeckCardsPage.test.tsx | 1 - src/client/pages/DeckDetailPage.test.tsx | 1 - src/client/pages/HomePage.test.tsx | 4 - src/client/sync/conflict.test.ts | 21 ----- src/client/sync/conflict.ts | 1 - src/client/sync/crdt/concurrent-edits.test.ts | 33 ++++---- src/client/sync/crdt/document-manager.test.ts | 13 --- src/client/sync/crdt/document-manager.ts | 3 - src/client/sync/crdt/migration.test.ts | 1 - src/client/sync/crdt/repositories.test.ts | 14 +--- src/client/sync/crdt/types.test.ts | 3 - src/client/sync/crdt/types.ts | 1 - src/client/sync/manager.test.ts | 2 - src/client/sync/pull.test.ts | 22 ----- src/client/sync/pull.ts | 2 - src/client/sync/push.test.ts | 17 ---- src/client/sync/push.ts | 2 - src/client/sync/queue.test.ts | 12 --- src/server/db/schema.ts | 1 - src/server/repositories/card.test.ts | 4 - src/server/repositories/card.ts | 93 ++------------------- src/server/repositories/deck.test.ts | 5 -- src/server/repositories/deck.ts | 3 - src/server/repositories/review-log.ts | 21 +---- src/server/repositories/sync.test.ts | 1 - src/server/repositories/sync.ts | 3 - src/server/repositories/types.ts | 25 +----- src/server/routes/cards.test.ts | 5 -- src/server/routes/decks.test.ts | 53 ------------ src/server/routes/decks.ts | 43 +--------- src/server/routes/notes.test.ts | 1 - src/server/routes/study.test.ts | 109 ++----------------------- src/server/routes/study.ts | 17 +--- src/server/routes/sync.test.ts | 9 -- src/server/routes/sync.ts | 1 - src/server/schemas/index.ts | 3 - src/server/types/index.ts | 1 - 45 files changed, 41 insertions(+), 537 deletions(-) diff --git a/src/client/atoms/decks.ts b/src/client/atoms/decks.ts index 5a624cf..5a4d44e 100644 --- a/src/client/atoms/decks.ts +++ b/src/client/atoms/decks.ts @@ -6,7 +6,6 @@ export interface Deck { id: string; name: string; description: string | null; - newCardsPerDay: number; dueCardCount: number; createdAt: string; updatedAt: string; diff --git a/src/client/components/CreateDeckModal.test.tsx b/src/client/components/CreateDeckModal.test.tsx index cdc5f97..fcaa572 100644 --- a/src/client/components/CreateDeckModal.test.tsx +++ b/src/client/components/CreateDeckModal.test.tsx @@ -146,7 +146,6 @@ describe("CreateDeckModal", () => { id: "deck-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }, }), }), @@ -187,7 +186,6 @@ describe("CreateDeckModal", () => { id: "deck-1", name: "Test Deck", description: "A test description", - newCardsPerDay: 20, }, }), }), diff --git a/src/client/components/EditDeckModal.test.tsx b/src/client/components/EditDeckModal.test.tsx index fce17f6..b22cb1d 100644 --- a/src/client/components/EditDeckModal.test.tsx +++ b/src/client/components/EditDeckModal.test.tsx @@ -42,7 +42,6 @@ describe("EditDeckModal", () => { id: "deck-123", name: "Test Deck", description: "Test description", - newCardsPerDay: 20, }; const defaultProps = { @@ -60,7 +59,6 @@ describe("EditDeckModal", () => { id: "deck-123", name: "Test Deck", description: "Test description", - newCardsPerDay: 20, }, }); }); diff --git a/src/client/components/EditDeckModal.tsx b/src/client/components/EditDeckModal.tsx index 3babeb5..8e95295 100644 --- a/src/client/components/EditDeckModal.tsx +++ b/src/client/components/EditDeckModal.tsx @@ -5,7 +5,6 @@ interface Deck { id: string; name: string; description: string | null; - newCardsPerDay: number; } interface EditDeckModalProps { diff --git a/src/client/db/index.test.ts b/src/client/db/index.test.ts index 0a4882d..0dd3758 100644 --- a/src/client/db/index.test.ts +++ b/src/client/db/index.test.ts @@ -64,7 +64,6 @@ describe("KiokuDatabase", () => { userId: "user-1", name: "Test Deck", description: "A test deck", - newCardsPerDay: 20, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), deletedAt: null, diff --git a/src/client/db/index.ts b/src/client/db/index.ts index 59cc526..50d8bbd 100644 --- a/src/client/db/index.ts +++ b/src/client/db/index.ts @@ -77,7 +77,6 @@ export interface LocalDeck { userId: string; name: string; description: string | null; - newCardsPerDay: number; createdAt: Date; updatedAt: Date; deletedAt: Date | null; diff --git a/src/client/db/repositories.test.ts b/src/client/db/repositories.test.ts index d591205..b461990 100644 --- a/src/client/db/repositories.test.ts +++ b/src/client/db/repositories.test.ts @@ -33,14 +33,12 @@ describe("localDeckRepository", () => { userId: "user-1", name: "Test Deck", description: "A test deck", - newCardsPerDay: 20, }); expect(deck.id).toBeDefined(); expect(deck.userId).toBe("user-1"); expect(deck.name).toBe("Test Deck"); expect(deck.description).toBe("A test deck"); - expect(deck.newCardsPerDay).toBe(20); expect(deck.createdAt).toBeInstanceOf(Date); expect(deck.updatedAt).toBeInstanceOf(Date); expect(deck.deletedAt).toBeNull(); @@ -53,7 +51,6 @@ describe("localDeckRepository", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 10, }); const found = await db.decks.get(created.id); @@ -67,7 +64,6 @@ describe("localDeckRepository", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const found = await localDeckRepository.findById(created.id); @@ -86,19 +82,16 @@ describe("localDeckRepository", () => { userId: "user-1", name: "Deck 1", description: null, - newCardsPerDay: 20, }); await localDeckRepository.create({ userId: "user-1", name: "Deck 2", description: null, - newCardsPerDay: 20, }); await localDeckRepository.create({ userId: "user-2", name: "Other User Deck", description: null, - newCardsPerDay: 20, }); const decks = await localDeckRepository.findByUserId("user-1"); @@ -111,7 +104,6 @@ describe("localDeckRepository", () => { userId: "user-1", name: "Deleted Deck", description: null, - newCardsPerDay: 20, }); await localDeckRepository.delete(deck.id); @@ -126,7 +118,6 @@ describe("localDeckRepository", () => { userId: "user-1", name: "Original Name", description: null, - newCardsPerDay: 20, }); const updated = await localDeckRepository.update(deck.id, { @@ -156,7 +147,6 @@ describe("localDeckRepository", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const result = await localDeckRepository.delete(deck.id); @@ -179,13 +169,11 @@ describe("localDeckRepository", () => { userId: "user-1", name: "Unsynced", description: null, - newCardsPerDay: 20, }); const deck2 = await localDeckRepository.create({ userId: "user-1", name: "Synced", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck2.id, 1); @@ -201,7 +189,6 @@ describe("localDeckRepository", () => { userId: "user-1", name: "Test", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck.id, 5); @@ -225,7 +212,6 @@ describe("localCardRepository", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); deckId = deck.id; }); @@ -425,7 +411,6 @@ describe("localReviewLogRepository", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); deckId = deck.id; @@ -950,7 +935,6 @@ describe("localNoteRepository", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); deckId = deck.id; @@ -1110,7 +1094,6 @@ describe("localNoteFieldValueRepository", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const noteType = await localNoteTypeRepository.create({ diff --git a/src/client/db/repositories.ts b/src/client/db/repositories.ts index 4c302bf..a0663e1 100644 --- a/src/client/db/repositories.ts +++ b/src/client/db/repositories.ts @@ -63,7 +63,7 @@ export const localDeckRepository = { */ async update( id: string, - data: Partial>, + data: Partial>, ): Promise { const deck = await db.decks.get(id); if (!deck) return undefined; diff --git a/src/client/pages/DeckCardsPage.test.tsx b/src/client/pages/DeckCardsPage.test.tsx index 7bc97fb..7c3c184 100644 --- a/src/client/pages/DeckCardsPage.test.tsx +++ b/src/client/pages/DeckCardsPage.test.tsx @@ -72,7 +72,6 @@ const mockDeck = { id: "deck-1", name: "Japanese Vocabulary", description: "Common Japanese words", - newCardsPerDay: 20, dueCardCount: 0, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z", diff --git a/src/client/pages/DeckDetailPage.test.tsx b/src/client/pages/DeckDetailPage.test.tsx index 3c741ad..9dcb152 100644 --- a/src/client/pages/DeckDetailPage.test.tsx +++ b/src/client/pages/DeckDetailPage.test.tsx @@ -59,7 +59,6 @@ const mockDeck = { id: "deck-1", name: "Japanese Vocabulary", description: "Common Japanese words", - newCardsPerDay: 20, dueCardCount: 0, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z", diff --git a/src/client/pages/HomePage.test.tsx b/src/client/pages/HomePage.test.tsx index 8b17506..179c649 100644 --- a/src/client/pages/HomePage.test.tsx +++ b/src/client/pages/HomePage.test.tsx @@ -92,7 +92,6 @@ const mockDecks = [ id: "deck-1", name: "Japanese Vocabulary", description: "Common Japanese words", - newCardsPerDay: 20, dueCardCount: 5, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z", @@ -101,7 +100,6 @@ const mockDecks = [ id: "deck-2", name: "Spanish Verbs", description: null, - newCardsPerDay: 10, dueCardCount: 0, createdAt: "2024-01-02T00:00:00Z", updatedAt: "2024-01-02T00:00:00Z", @@ -254,7 +252,6 @@ describe("HomePage", () => { id: "deck-1", name: "No Description Deck", description: null, - newCardsPerDay: 20, dueCardCount: 0, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z", @@ -334,7 +331,6 @@ describe("HomePage", () => { id: "deck-new", name: "New Deck", description: "A new deck", - newCardsPerDay: 20, dueCardCount: 0, createdAt: "2024-01-03T00:00:00Z", updatedAt: "2024-01-03T00:00:00Z", diff --git a/src/client/sync/conflict.test.ts b/src/client/sync/conflict.test.ts index bcd8dae..d11e150 100644 --- a/src/client/sync/conflict.test.ts +++ b/src/client/sync/conflict.test.ts @@ -155,7 +155,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Local Name", description: "Local description", - newCardsPerDay: 10, }); const serverDeck = { @@ -163,7 +162,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Server Name", description: "Server description", - newCardsPerDay: 20, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-03"), deletedAt: null, @@ -178,7 +176,6 @@ describe("ConflictResolver", () => { const updatedDeck = await localDeckRepository.findById(localDeck.id); expect(updatedDeck?.name).toBe("Server Name"); expect(updatedDeck?.description).toBe("Server description"); - expect(updatedDeck?.newCardsPerDay).toBe(20); expect(updatedDeck?._synced).toBe(true); }); }); @@ -189,7 +186,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const localCard = await localCardRepository.create({ @@ -240,13 +236,11 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Local Deck 1", description: null, - newCardsPerDay: 10, }); const deck2 = await localDeckRepository.create({ userId: "user-1", name: "Local Deck 2", description: null, - newCardsPerDay: 10, }); const pushResult: SyncPushResult = { @@ -273,7 +267,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Server Deck 1", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, @@ -284,7 +277,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Server Deck 2", description: null, - newCardsPerDay: 25, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, @@ -314,7 +306,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const card = await localCardRepository.create({ @@ -397,7 +388,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Server Deck", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, @@ -425,7 +415,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Local Only Deck", description: null, - newCardsPerDay: 10, }); const pushResult: SyncPushResult = { @@ -462,7 +451,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Local Name", description: null, - newCardsPerDay: 10, }); const pushResult: SyncPushResult = { @@ -483,7 +471,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Server Name", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, @@ -514,7 +501,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Local Deck Name", description: "Local description", - newCardsPerDay: 10, }); // Store local CRDT document @@ -532,7 +518,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Server Deck Name", description: "Server description", - newCardsPerDay: 20, createdAt: localDeck.createdAt, updatedAt: new Date(Date.now() + 1000), deletedAt: null, @@ -590,7 +575,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Local Name", description: null, - newCardsPerDay: 10, }); const serverDeck = { @@ -598,7 +582,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Server Name", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, @@ -649,7 +632,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Local Name", description: null, - newCardsPerDay: 10, }); const serverDeck = { @@ -657,7 +639,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Server Name", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, @@ -699,7 +680,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Local Name", description: null, - newCardsPerDay: 10, }); const serverDeck = { @@ -707,7 +687,6 @@ describe("ConflictResolver", () => { userId: "user-1", name: "Server Name", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, diff --git a/src/client/sync/conflict.ts b/src/client/sync/conflict.ts index 7ad0805..64287a1 100644 --- a/src/client/sync/conflict.ts +++ b/src/client/sync/conflict.ts @@ -67,7 +67,6 @@ function serverDeckToLocal(deck: ServerDeck): LocalDeck { userId: deck.userId, name: deck.name, description: deck.description, - newCardsPerDay: deck.newCardsPerDay, createdAt: new Date(deck.createdAt), updatedAt: new Date(deck.updatedAt), deletedAt: deck.deletedAt ? new Date(deck.deletedAt) : null, diff --git a/src/client/sync/crdt/concurrent-edits.test.ts b/src/client/sync/crdt/concurrent-edits.test.ts index 2b6f182..d55b233 100644 --- a/src/client/sync/crdt/concurrent-edits.test.ts +++ b/src/client/sync/crdt/concurrent-edits.test.ts @@ -34,7 +34,6 @@ function createTestDeck(overrides: Partial = {}): LocalDeck { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: now, updatedAt: now, deletedAt: null, @@ -89,10 +88,10 @@ describe("Concurrent edit scenarios", () => { d.meta.lastModified = Date.now(); }); - // Device B: Offline edit - change newCardsPerDay + // Device B: Offline edit - change description const deviceBDoc = Automerge.clone(serverDoc); const deviceBEdited = updateDocument(deviceBDoc, (d) => { - d.data.newCardsPerDay = 30; + d.data.description = "Updated by Device B"; d.meta.lastModified = Date.now(); }); @@ -101,7 +100,7 @@ describe("Concurrent edit scenarios", () => { // Both changes should be present expect(mergeResult.merged.data.name).toBe("Updated by Device A"); - expect(mergeResult.merged.data.newCardsPerDay).toBe(30); + expect(mergeResult.merged.data.description).toBe("Updated by Device B"); expect(mergeResult.hasChanges).toBe(true); }); @@ -120,7 +119,7 @@ describe("Concurrent edit scenarios", () => { const deviceBDoc = Automerge.clone(serverDoc); const deviceBEdited = updateDocument(deviceBDoc, (d) => { - d.data.newCardsPerDay = 50; + d.data.userId = "user-updated"; d.meta.lastModified = Date.now(); }); @@ -129,7 +128,7 @@ describe("Concurrent edit scenarios", () => { expect(mergedDeck.name).toBe("New Name"); expect(mergedDeck.description).toBe("Added by Device A"); - expect(mergedDeck.newCardsPerDay).toBe(50); + expect(mergedDeck.userId).toBe("user-updated"); }); }); @@ -342,7 +341,7 @@ describe("Concurrent edit scenarios", () => { // Device B makes a different edit const deviceBEdited = updateDocument(deviceBDoc, (d) => { - d.data.newCardsPerDay = 100; + d.data.description = "Description from B"; d.meta.lastModified = Date.now(); }); @@ -354,7 +353,7 @@ describe("Concurrent edit scenarios", () => { // Both changes should be present expect(serverWithB.data.name).toBe("Edit 1 from A"); - expect(serverWithB.data.newCardsPerDay).toBe(100); + expect(serverWithB.data.description).toBe("Description from B"); }); it("should handle three-way merge correctly", () => { @@ -377,7 +376,7 @@ describe("Concurrent edit scenarios", () => { }); const deviceCEdited = updateDocument(deviceCDoc, (d) => { - d.data.newCardsPerDay = 75; + d.data.userId = "user-from-C"; }); // Sequential merge: A + B @@ -389,7 +388,7 @@ describe("Concurrent edit scenarios", () => { // All three changes should be present expect(mergeABC.merged.data.name).toBe("Name from A"); expect(mergeABC.merged.data.description).toBe("Description from B"); - expect(mergeABC.merged.data.newCardsPerDay).toBe(75); + expect(mergeABC.merged.data.userId).toBe("user-from-C"); }); }); @@ -575,7 +574,7 @@ describe("Concurrent edit scenarios", () => { // Edit 3 const beforeEdit3 = Automerge.clone(deviceADoc); deviceADoc = updateDocument(deviceADoc, (d) => { - d.data.newCardsPerDay = 42; + d.data.userId = "user-offline"; }); offlineEdits.push(getChanges(beforeEdit3, deviceADoc)); @@ -588,7 +587,7 @@ describe("Concurrent edit scenarios", () => { // Verify all offline edits are applied expect(currentServer.data.name).toBe("Offline edit 1"); expect(currentServer.data.description).toBe("Offline edit 2"); - expect(currentServer.data.newCardsPerDay).toBe(42); + expect(currentServer.data.userId).toBe("user-offline"); }); it("should handle two devices syncing after extended offline periods", () => { @@ -612,22 +611,22 @@ describe("Concurrent edit scenarios", () => { // Device B: Different offline edits let deviceBDoc = Automerge.clone(serverDoc); deviceBDoc = updateDocument(deviceBDoc, (d) => { - d.data.newCardsPerDay = 50; + d.data.userId = "B: First user"; }); deviceBDoc = updateDocument(deviceBDoc, (d) => { - d.data.newCardsPerDay = 60; + d.data.userId = "B: Second user"; }); deviceBDoc = updateDocument(deviceBDoc, (d) => { - d.data.newCardsPerDay = 100; + d.data.userId = "B: Final user"; }); // Both devices come online and sync const mergeResult = mergeDocuments(deviceADoc, deviceBDoc); - // Device A's content edits and Device B's card setting + // Device A's content edits and Device B's user edits expect(mergeResult.merged.data.name).toBe("A: Final name"); expect(mergeResult.merged.data.description).toBe("A: Added description"); - expect(mergeResult.merged.data.newCardsPerDay).toBe(100); + expect(mergeResult.merged.data.userId).toBe("B: Final user"); }); }); }); diff --git a/src/client/sync/crdt/document-manager.test.ts b/src/client/sync/crdt/document-manager.test.ts index 7c0fc00..b578c77 100644 --- a/src/client/sync/crdt/document-manager.test.ts +++ b/src/client/sync/crdt/document-manager.test.ts @@ -51,7 +51,6 @@ describe("createDocument", () => { userId: "user-1", name: "My Deck", description: null, - newCardsPerDay: 20, createdAt: Date.now(), deletedAt: null, }, @@ -154,7 +153,6 @@ describe("saveDocument and loadDocument", () => { userId: "user-1", name: "Test Deck", description: "A test deck", - newCardsPerDay: 15, createdAt: 1234567890, deletedAt: null, }, @@ -168,7 +166,6 @@ describe("saveDocument and loadDocument", () => { const loaded = loadDocument(binary); expect(loaded.meta.entityId).toBe("deck-123"); expect(loaded.data.name).toBe("Test Deck"); - expect(loaded.data.newCardsPerDay).toBe(15); }); }); @@ -195,7 +192,6 @@ describe("createEmptyDocument", () => { expect(doc.meta.entityId).toBe(""); expect(doc.meta.deleted).toBe(false); expect(doc.data.name).toBe(""); - expect(doc.data.newCardsPerDay).toBe(20); }); it("should create empty card document", () => { @@ -226,7 +222,6 @@ describe("deckToCrdtDocument and crdtDocumentToDeck", () => { userId: "user-1", name: "My Deck", description: "A deck for testing", - newCardsPerDay: 25, createdAt: now, updatedAt: now, deletedAt: null, @@ -240,7 +235,6 @@ describe("deckToCrdtDocument and crdtDocumentToDeck", () => { expect(crdtDoc.meta.deleted).toBe(false); expect(crdtDoc.data.name).toBe("My Deck"); expect(crdtDoc.data.description).toBe("A deck for testing"); - expect(crdtDoc.data.newCardsPerDay).toBe(25); expect(crdtDoc.data.createdAt).toBe(now.getTime()); }); @@ -252,7 +246,6 @@ describe("deckToCrdtDocument and crdtDocumentToDeck", () => { userId: "user-1", name: "Deleted Deck", description: null, - newCardsPerDay: 20, createdAt: now, updatedAt: deletedAt, deletedAt: deletedAt, @@ -278,7 +271,6 @@ describe("deckToCrdtDocument and crdtDocumentToDeck", () => { userId: "user-2", name: "Converted Deck", description: "Converted from CRDT", - newCardsPerDay: 30, createdAt: now - 10000, deletedAt: null, }, @@ -289,7 +281,6 @@ describe("deckToCrdtDocument and crdtDocumentToDeck", () => { expect(localDeck.id).toBe("deck-3"); expect(localDeck.userId).toBe("user-2"); expect(localDeck.name).toBe("Converted Deck"); - expect(localDeck.newCardsPerDay).toBe(30); expect(localDeck.deletedAt).toBeNull(); expect(localDeck.syncVersion).toBe(0); // Set by sync layer }); @@ -465,7 +456,6 @@ describe("createDocumentFromEntity", () => { userId: "user-1", name: "Test", description: null, - newCardsPerDay: 20, createdAt: now, updatedAt: now, deletedAt: null, @@ -546,7 +536,6 @@ describe("getLastModified", () => { userId: "user-1", name: "Test", description: null, - newCardsPerDay: 20, createdAt: timestamp, deletedAt: null, }, @@ -569,7 +558,6 @@ describe("isDeleted", () => { userId: "user-1", name: "Test", description: null, - newCardsPerDay: 20, createdAt: Date.now(), deletedAt: null, }, @@ -590,7 +578,6 @@ describe("isDeleted", () => { userId: "user-1", name: "Test", description: null, - newCardsPerDay: 20, createdAt: Date.now(), deletedAt: Date.now(), }, diff --git a/src/client/sync/crdt/document-manager.ts b/src/client/sync/crdt/document-manager.ts index 5c32b67..b753d88 100644 --- a/src/client/sync/crdt/document-manager.ts +++ b/src/client/sync/crdt/document-manager.ts @@ -187,7 +187,6 @@ function getEmptyDocumentData( userId: "", name: "", description: null, - newCardsPerDay: 20, createdAt: 0, deletedAt: null, }, @@ -301,7 +300,6 @@ export function deckToCrdtDocument(deck: LocalDeck): CrdtDeckDocument { userId: deck.userId, name: deck.name, description: deck.description, - newCardsPerDay: deck.newCardsPerDay, createdAt: deck.createdAt.getTime(), deletedAt: deck.deletedAt?.getTime() ?? null, }, @@ -319,7 +317,6 @@ export function crdtDocumentToDeck( userId: doc.data.userId, name: doc.data.name, description: doc.data.description, - newCardsPerDay: doc.data.newCardsPerDay, createdAt: new Date(doc.data.createdAt), updatedAt: new Date(doc.meta.lastModified), deletedAt: doc.data.deletedAt ? new Date(doc.data.deletedAt) : null, diff --git a/src/client/sync/crdt/migration.test.ts b/src/client/sync/crdt/migration.test.ts index 22f311d..ba90be2 100644 --- a/src/client/sync/crdt/migration.test.ts +++ b/src/client/sync/crdt/migration.test.ts @@ -109,7 +109,6 @@ describe("migration", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, diff --git a/src/client/sync/crdt/repositories.test.ts b/src/client/sync/crdt/repositories.test.ts index f237536..f7b75b3 100644 --- a/src/client/sync/crdt/repositories.test.ts +++ b/src/client/sync/crdt/repositories.test.ts @@ -35,7 +35,6 @@ describe("crdtDeckRepository", () => { userId: "user-1", name: "Test Deck", description: "A test deck", - newCardsPerDay: 20, createdAt: now, updatedAt: now, deletedAt: null, @@ -77,14 +76,14 @@ describe("crdtDeckRepository", () => { d.data.name = "Updated Name"; }); const updated2 = Automerge.change(doc2, (d) => { - d.data.newCardsPerDay = 30; + d.data.description = "Updated Description"; }); const result = crdtDeckRepository.merge(updated1, updated2); expect(result.hasChanges).toBe(true); expect(result.merged.data.name).toBe("Updated Name"); - expect(result.merged.data.newCardsPerDay).toBe(30); + expect(result.merged.data.description).toBe("Updated Description"); }); it("should convert CRDT document to local entity", () => { @@ -379,7 +378,6 @@ describe("entitiesToCrdtDocuments", () => { userId: "user-1", name: "Deck 1", description: null, - newCardsPerDay: 20, createdAt: now, updatedAt: now, deletedAt: null, @@ -391,7 +389,6 @@ describe("entitiesToCrdtDocuments", () => { userId: "user-1", name: "Deck 2", description: "Second deck", - newCardsPerDay: 15, createdAt: now, updatedAt: now, deletedAt: null, @@ -418,7 +415,6 @@ describe("mergeAndConvert", () => { userId: "user-1", name: "Remote Deck", description: null, - newCardsPerDay: 20, createdAt: now, updatedAt: now, deletedAt: null, @@ -440,7 +436,6 @@ describe("mergeAndConvert", () => { userId: "user-1", name: "Original", description: null, - newCardsPerDay: 20, createdAt: now, updatedAt: now, deletedAt: null, @@ -452,7 +447,7 @@ describe("mergeAndConvert", () => { // Create remote with different changes const remoteDoc = Automerge.change(Automerge.clone(localDoc), (d) => { - d.data.newCardsPerDay = 30; + d.data.description = "Remote Description"; }); const remoteBinary = saveDocument(remoteDoc); @@ -471,7 +466,7 @@ describe("mergeAndConvert", () => { expect(result.hasChanges).toBe(true); // Both changes should be merged expect(result.entity.name).toBe("Updated Local"); - expect(result.entity.newCardsPerDay).toBe(30); + expect(result.entity.description).toBe("Remote Description"); }); it("should detect no changes when documents are identical", () => { @@ -481,7 +476,6 @@ describe("mergeAndConvert", () => { userId: "user-1", name: "Same", description: null, - newCardsPerDay: 20, createdAt: now, updatedAt: now, deletedAt: null, diff --git a/src/client/sync/crdt/types.test.ts b/src/client/sync/crdt/types.test.ts index 15fd79b..07ae0f2 100644 --- a/src/client/sync/crdt/types.test.ts +++ b/src/client/sync/crdt/types.test.ts @@ -149,7 +149,6 @@ describe("CRDT Document type structures", () => { userId: "user-1", name: "My Deck", description: "A test deck", - newCardsPerDay: 20, createdAt: now, deletedAt: null, }, @@ -157,7 +156,6 @@ describe("CRDT Document type structures", () => { expect(doc.meta.entityId).toBe("deck-1"); expect(doc.data.name).toBe("My Deck"); - expect(doc.data.newCardsPerDay).toBe(20); }); it("should allow creating a valid CrdtNoteTypeDocument", () => { @@ -305,7 +303,6 @@ describe("CRDT Document type structures", () => { userId: "user-1", name: "Deleted Deck", description: null, - newCardsPerDay: 20, createdAt: now - 86400000, deletedAt: now, }, diff --git a/src/client/sync/crdt/types.ts b/src/client/sync/crdt/types.ts index 1dfae1a..e2f5d4c 100644 --- a/src/client/sync/crdt/types.ts +++ b/src/client/sync/crdt/types.ts @@ -36,7 +36,6 @@ export interface CrdtDeckDocument { userId: string; name: string; description: string | null; - newCardsPerDay: number; createdAt: number; // Unix timestamp in ms deletedAt: number | null; }; diff --git a/src/client/sync/manager.test.ts b/src/client/sync/manager.test.ts index 8af6e6f..a9be10d 100644 --- a/src/client/sync/manager.test.ts +++ b/src/client/sync/manager.test.ts @@ -83,7 +83,6 @@ describe("SyncManager", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); } @@ -412,7 +411,6 @@ describe("SyncManager", () => { userId: "user-1", name: "Server Deck", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, diff --git a/src/client/sync/pull.test.ts b/src/client/sync/pull.test.ts index 8bbf7cf..0f1b689 100644 --- a/src/client/sync/pull.test.ts +++ b/src/client/sync/pull.test.ts @@ -48,7 +48,6 @@ describe("pullResultToLocalData", () => { userId: "user-1", name: "Test Deck", description: "A description", - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -69,7 +68,6 @@ describe("pullResultToLocalData", () => { userId: "user-1", name: "Test Deck", description: "A description", - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -85,7 +83,6 @@ describe("pullResultToLocalData", () => { userId: "user-1", name: "Deleted Deck", description: null, - newCardsPerDay: 10, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-03T12:00:00Z"), deletedAt: new Date("2024-01-03T12:00:00Z"), @@ -538,7 +535,6 @@ describe("PullService", () => { userId: "user-1", name: "Server Deck", description: "From server", - newCardsPerDay: 15, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T10:00:00Z"), deletedAt: null, @@ -571,7 +567,6 @@ describe("PullService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck.id, 1); @@ -673,7 +668,6 @@ describe("PullService", () => { userId: "user-1", name: "Old Name", description: null, - newCardsPerDay: 10, }); const pullFromServer = vi.fn().mockResolvedValue({ @@ -683,7 +677,6 @@ describe("PullService", () => { userId: "user-1", name: "Updated Name", description: "Updated description", - newCardsPerDay: 25, createdAt: existingDeck.createdAt, updatedAt: new Date(), deletedAt: null, @@ -705,7 +698,6 @@ describe("PullService", () => { const updatedDeck = await localDeckRepository.findById(existingDeck.id); expect(updatedDeck?.name).toBe("Updated Name"); expect(updatedDeck?.description).toBe("Updated description"); - expect(updatedDeck?.newCardsPerDay).toBe(25); expect(updatedDeck?._synced).toBe(true); }); @@ -717,7 +709,6 @@ describe("PullService", () => { userId: "user-1", name: "Deck", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, @@ -874,7 +865,6 @@ describe("PullService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck.id, 1); @@ -929,7 +919,6 @@ describe("PullService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck.id, 1); @@ -999,7 +988,6 @@ describe("PullService", () => { userId: "user-1", name: "Deck", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, @@ -1179,7 +1167,6 @@ describe("applyCrdtChanges", () => { userId: "user-1", name: "Test Deck", description: "A test description", - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -1212,7 +1199,6 @@ describe("applyCrdtChanges", () => { userId: "user-1", name: "Local Deck", description: "Local description", - newCardsPerDay: 10, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-01T12:00:00Z"), deletedAt: null, @@ -1235,7 +1221,6 @@ describe("applyCrdtChanges", () => { userId: "user-1", name: "Remote Deck", description: "Remote description", - newCardsPerDay: 25, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), // Later timestamp deletedAt: null, @@ -1266,7 +1251,6 @@ describe("applyCrdtChanges", () => { userId: "user-1", name: "Deck 1", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -1278,7 +1262,6 @@ describe("applyCrdtChanges", () => { userId: "user-1", name: "Deck 2", description: "Second deck", - newCardsPerDay: 15, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -1317,7 +1300,6 @@ describe("applyCrdtChanges", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -1372,7 +1354,6 @@ describe("applyCrdtChanges", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -1470,7 +1451,6 @@ describe("PullService with CRDT changes", () => { userId: "user-1", name: "CRDT Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -1521,7 +1501,6 @@ describe("PullService with CRDT changes", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, @@ -1598,7 +1577,6 @@ describe("PullService with CRDT changes", () => { userId: "user-1", name: "Regular Deck", description: null, - newCardsPerDay: 20, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, diff --git a/src/client/sync/pull.ts b/src/client/sync/pull.ts index 4771757..ce4d992 100644 --- a/src/client/sync/pull.ts +++ b/src/client/sync/pull.ts @@ -30,7 +30,6 @@ export interface ServerDeck { userId: string; name: string; description: string | null; - newCardsPerDay: number; createdAt: Date; updatedAt: Date; deletedAt: Date | null; @@ -168,7 +167,6 @@ function serverDeckToLocal(deck: ServerDeck): LocalDeck { userId: deck.userId, name: deck.name, description: deck.description, - newCardsPerDay: deck.newCardsPerDay, createdAt: new Date(deck.createdAt), updatedAt: new Date(deck.updatedAt), deletedAt: deck.deletedAt ? new Date(deck.deletedAt) : null, diff --git a/src/client/sync/push.test.ts b/src/client/sync/push.test.ts index 19b39da..8605ede 100644 --- a/src/client/sync/push.test.ts +++ b/src/client/sync/push.test.ts @@ -81,7 +81,6 @@ describe("pendingChangesToPushData", () => { userId: "user-1", name: "Test Deck", description: "A description", - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -102,7 +101,6 @@ describe("pendingChangesToPushData", () => { id: "deck-1", name: "Test Deck", description: "A description", - newCardsPerDay: 20, createdAt: "2024-01-01T10:00:00.000Z", updatedAt: "2024-01-02T15:30:00.000Z", deletedAt: null, @@ -116,7 +114,6 @@ describe("pendingChangesToPushData", () => { userId: "user-1", name: "Deleted Deck", description: null, - newCardsPerDay: 10, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-03T12:00:00Z"), deletedAt: new Date("2024-01-03T12:00:00Z"), @@ -530,7 +527,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const pushToServer = vi.fn().mockResolvedValue({ @@ -569,7 +565,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck.id, 1); @@ -616,7 +611,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck.id, 1); @@ -674,7 +668,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const pushToServer = vi.fn().mockResolvedValue({ @@ -702,7 +695,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const pushToServer = vi.fn().mockResolvedValue({ @@ -728,7 +720,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const pushToServer = vi @@ -748,7 +739,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const card = await localCardRepository.create({ @@ -894,7 +884,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck.id, 1); @@ -942,7 +931,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck.id, 1); @@ -1006,7 +994,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const noteType = await localNoteTypeRepository.create({ @@ -1093,7 +1080,6 @@ describe("PushService", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const pushService = new PushService({ @@ -1116,7 +1102,6 @@ describe("generateCrdtChanges", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -1354,7 +1339,6 @@ describe("generateCrdtChanges", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, @@ -1496,7 +1480,6 @@ describe("pendingChangesToPushData with crdtChanges", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01T10:00:00Z"), updatedAt: new Date("2024-01-02T15:30:00Z"), deletedAt: null, diff --git a/src/client/sync/push.ts b/src/client/sync/push.ts index 61c7f30..e5c5fd4 100644 --- a/src/client/sync/push.ts +++ b/src/client/sync/push.ts @@ -39,7 +39,6 @@ export interface SyncDeckData { id: string; name: string; description: string | null; - newCardsPerDay: number; createdAt: string; updatedAt: string; deletedAt: string | null; @@ -154,7 +153,6 @@ function deckToSyncData(deck: LocalDeck): SyncDeckData { id: deck.id, name: deck.name, description: deck.description, - newCardsPerDay: deck.newCardsPerDay, createdAt: deck.createdAt.toISOString(), updatedAt: deck.updatedAt.toISOString(), deletedAt: deck.deletedAt?.toISOString() ?? null, diff --git a/src/client/sync/queue.test.ts b/src/client/sync/queue.test.ts index e815282..436046b 100644 --- a/src/client/sync/queue.test.ts +++ b/src/client/sync/queue.test.ts @@ -68,7 +68,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const changes = await syncQueue.getPendingChanges(); @@ -81,7 +80,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localCardRepository.create({ deckId: deck.id, @@ -101,7 +99,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const card = await localCardRepository.create({ deckId: deck.id, @@ -130,7 +127,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck.id, 1); @@ -145,7 +141,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localCardRepository.create({ deckId: deck.id, @@ -179,7 +174,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const hasPending = await syncQueue.hasPendingChanges(); @@ -304,7 +298,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await syncQueue.markSynced({ @@ -327,7 +320,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const card = await localCardRepository.create({ deckId: deck.id, @@ -357,7 +349,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const card = await localCardRepository.create({ deckId: deck.id, @@ -417,7 +408,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Server Deck", description: null, - newCardsPerDay: 15, createdAt: new Date(), updatedAt: new Date(), deletedAt: null, @@ -445,7 +435,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); await localDeckRepository.markSynced(deck.id, 1); @@ -492,7 +481,6 @@ describe("SyncQueue", () => { userId: "user-1", name: "Test Deck", description: null, - newCardsPerDay: 20, }); const card = await localCardRepository.create({ deckId: deck.id, diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index 50caf85..55be210 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -101,7 +101,6 @@ export const decks = pgTable("decks", { .references(() => users.id), name: varchar("name", { length: 255 }).notNull(), description: text("description"), - newCardsPerDay: integer("new_cards_per_day").notNull().default(20), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), diff --git a/src/server/repositories/card.test.ts b/src/server/repositories/card.test.ts index 21e5485..b492fd7 100644 --- a/src/server/repositories/card.test.ts +++ b/src/server/repositories/card.test.ts @@ -112,12 +112,8 @@ function createMockCardRepo(): CardRepository { softDeleteByNoteId: vi.fn(), findDueCards: vi.fn(), countDueCards: vi.fn(), - countDueNewCards: vi.fn(), - countDueReviewCards: vi.fn(), findDueCardsWithNoteData: vi.fn(), findDueCardsForStudy: vi.fn(), - findDueNewCardsForStudy: vi.fn(), - findDueReviewCardsForStudy: vi.fn(), updateFSRSFields: vi.fn(), }; } diff --git a/src/server/repositories/card.ts b/src/server/repositories/card.ts index d382f4d..0f1ef79 100644 --- a/src/server/repositories/card.ts +++ b/src/server/repositories/card.ts @@ -1,4 +1,4 @@ -import { and, eq, isNull, lt, ne, sql } from "drizzle-orm"; +import { and, eq, isNull, lt, sql } from "drizzle-orm"; import { getEndOfStudyDayBoundary } from "../../shared/date.js"; import { db } from "../db/index.js"; import { @@ -185,11 +185,7 @@ export const cardRepository: CardRepository = { return result.length > 0; }, - async findDueCards( - deckId: string, - now: Date, - limit: number, - ): Promise { + async findDueCards(deckId: string, now: Date): Promise { const boundary = getEndOfStudyDayBoundary(now); const result = await db .select() @@ -201,8 +197,7 @@ export const cardRepository: CardRepository = { lt(cards.due, boundary), ), ) - .orderBy(cards.due) - .limit(limit); + .orderBy(cards.due); return result; }, @@ -221,44 +216,11 @@ export const cardRepository: CardRepository = { return result[0]?.count ?? 0; }, - async countDueNewCards(deckId: string, now: Date): Promise { - const boundary = getEndOfStudyDayBoundary(now); - const result = await db - .select({ count: sql`count(*)::int` }) - .from(cards) - .where( - and( - eq(cards.deckId, deckId), - isNull(cards.deletedAt), - lt(cards.due, boundary), - eq(cards.state, CardState.New), - ), - ); - return result[0]?.count ?? 0; - }, - - async countDueReviewCards(deckId: string, now: Date): Promise { - const boundary = getEndOfStudyDayBoundary(now); - const result = await db - .select({ count: sql`count(*)::int` }) - .from(cards) - .where( - and( - eq(cards.deckId, deckId), - isNull(cards.deletedAt), - lt(cards.due, boundary), - ne(cards.state, CardState.New), - ), - ); - return result[0]?.count ?? 0; - }, - async findDueCardsWithNoteData( deckId: string, now: Date, - limit: number, ): Promise { - const dueCards = await this.findDueCards(deckId, now, limit); + const dueCards = await this.findDueCards(deckId, now); const cardsWithNoteData: CardWithNoteData[] = []; @@ -292,56 +254,11 @@ export const cardRepository: CardRepository = { async findDueCardsForStudy( deckId: string, now: Date, - limit: number, ): Promise { - const dueCards = await this.findDueCards(deckId, now, limit); + const dueCards = await this.findDueCards(deckId, now); return enrichCardsForStudy(dueCards); }, - async findDueNewCardsForStudy( - deckId: string, - now: Date, - limit: number, - ): Promise { - const boundary = getEndOfStudyDayBoundary(now); - const result = await db - .select() - .from(cards) - .where( - and( - eq(cards.deckId, deckId), - isNull(cards.deletedAt), - lt(cards.due, boundary), - eq(cards.state, CardState.New), - ), - ) - .orderBy(cards.due) - .limit(limit); - return enrichCardsForStudy(result); - }, - - async findDueReviewCardsForStudy( - deckId: string, - now: Date, - limit: number, - ): Promise { - const boundary = getEndOfStudyDayBoundary(now); - const result = await db - .select() - .from(cards) - .where( - and( - eq(cards.deckId, deckId), - isNull(cards.deletedAt), - lt(cards.due, boundary), - ne(cards.state, CardState.New), - ), - ) - .orderBy(cards.due) - .limit(limit); - return enrichCardsForStudy(result); - }, - async updateFSRSFields( id: string, deckId: string, diff --git a/src/server/repositories/deck.test.ts b/src/server/repositories/deck.test.ts index 945f844..ab6e2fc 100644 --- a/src/server/repositories/deck.test.ts +++ b/src/server/repositories/deck.test.ts @@ -7,7 +7,6 @@ function createMockDeck(overrides: Partial = {}): Deck { userId: "user-uuid-123", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), deletedAt: null, @@ -35,7 +34,6 @@ describe("DeckRepository mock factory", () => { expect(deck.userId).toBe("user-uuid-123"); expect(deck.name).toBe("Test Deck"); expect(deck.description).toBeNull(); - expect(deck.newCardsPerDay).toBe(20); expect(deck.deletedAt).toBeNull(); expect(deck.syncVersion).toBe(0); }); @@ -45,13 +43,11 @@ describe("DeckRepository mock factory", () => { id: "custom-id", name: "Custom Deck", description: "A description", - newCardsPerDay: 50, }); expect(deck.id).toBe("custom-id"); expect(deck.name).toBe("Custom Deck"); expect(deck.description).toBe("A description"); - expect(deck.newCardsPerDay).toBe(50); }); }); @@ -130,7 +126,6 @@ describe("Deck interface contracts", () => { expect(deck).toHaveProperty("name"); expect(deck).toHaveProperty("description"); - expect(deck).toHaveProperty("newCardsPerDay"); }); }); diff --git a/src/server/repositories/deck.ts b/src/server/repositories/deck.ts index 647c5cb..97af5f7 100644 --- a/src/server/repositories/deck.ts +++ b/src/server/repositories/deck.ts @@ -31,7 +31,6 @@ export const deckRepository: DeckRepository = { userId: string; name: string; description?: string | null; - newCardsPerDay?: number; }): Promise { const [deck] = await db .insert(decks) @@ -39,7 +38,6 @@ export const deckRepository: DeckRepository = { userId: data.userId, name: data.name, description: data.description ?? null, - newCardsPerDay: data.newCardsPerDay ?? 20, }) .returning(); if (!deck) { @@ -54,7 +52,6 @@ export const deckRepository: DeckRepository = { data: { name?: string; description?: string | null; - newCardsPerDay?: number; }, ): Promise { const result = await db diff --git a/src/server/repositories/review-log.ts b/src/server/repositories/review-log.ts index 97488d2..c8950d6 100644 --- a/src/server/repositories/review-log.ts +++ b/src/server/repositories/review-log.ts @@ -1,7 +1,5 @@ -import { and, eq, gte, sql } from "drizzle-orm"; -import { getStartOfStudyDayBoundary } from "../../shared/date.js"; import { db } from "../db/index.js"; -import { CardState, cards, reviewLogs } from "../db/schema.js"; +import { reviewLogs } from "../db/schema.js"; import type { ReviewLog, ReviewLogRepository } from "./types.js"; export const reviewLogRepository: ReviewLogRepository = { @@ -31,21 +29,4 @@ export const reviewLogRepository: ReviewLogRepository = { } return reviewLog; }, - - async countTodayNewCardReviews(deckId: string, now: Date): Promise { - const startOfDay = getStartOfStudyDayBoundary(now); - - const result = await db - .select({ count: sql`count(distinct ${reviewLogs.cardId})::int` }) - .from(reviewLogs) - .innerJoin(cards, eq(reviewLogs.cardId, cards.id)) - .where( - and( - eq(cards.deckId, deckId), - eq(reviewLogs.state, CardState.New), - gte(reviewLogs.reviewedAt, startOfDay), - ), - ); - return result[0]?.count ?? 0; - }, }; diff --git a/src/server/repositories/sync.test.ts b/src/server/repositories/sync.test.ts index ce59cb5..8425839 100644 --- a/src/server/repositories/sync.test.ts +++ b/src/server/repositories/sync.test.ts @@ -16,7 +16,6 @@ function createMockDeck(overrides: Partial = {}): Deck { userId: "user-uuid-123", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), deletedAt: null, diff --git a/src/server/repositories/sync.ts b/src/server/repositories/sync.ts index ca4c208..e197d37 100644 --- a/src/server/repositories/sync.ts +++ b/src/server/repositories/sync.ts @@ -53,7 +53,6 @@ export interface SyncDeckData { id: string; name: string; description: string | null; - newCardsPerDay: number; createdAt: string; updatedAt: string; deletedAt: string | null; @@ -224,7 +223,6 @@ export const syncRepository: SyncRepository = { userId, name: deckData.name, description: deckData.description, - newCardsPerDay: deckData.newCardsPerDay, createdAt: new Date(deckData.createdAt), updatedAt: clientUpdatedAt, deletedAt: deckData.deletedAt ? new Date(deckData.deletedAt) : null, @@ -248,7 +246,6 @@ export const syncRepository: SyncRepository = { .set({ name: deckData.name, description: deckData.description, - newCardsPerDay: deckData.newCardsPerDay, updatedAt: clientUpdatedAt, deletedAt: deckData.deletedAt ? new Date(deckData.deletedAt) diff --git a/src/server/repositories/types.ts b/src/server/repositories/types.ts index 4042daf..71cb811 100644 --- a/src/server/repositories/types.ts +++ b/src/server/repositories/types.ts @@ -50,7 +50,6 @@ export interface Deck { userId: string; name: string; description: string | null; - newCardsPerDay: number; createdAt: Date; updatedAt: Date; deletedAt: Date | null; @@ -64,7 +63,6 @@ export interface DeckRepository { userId: string; name: string; description?: string | null; - newCardsPerDay?: number; }): Promise; update( id: string, @@ -72,7 +70,6 @@ export interface DeckRepository { data: { name?: string; description?: string | null; - newCardsPerDay?: number; }, ): Promise; softDelete(id: string, userId: string): Promise; @@ -146,30 +143,13 @@ export interface CardRepository { ): Promise; softDelete(id: string, deckId: string): Promise; softDeleteByNoteId(noteId: string): Promise; - findDueCards(deckId: string, now: Date, limit: number): Promise; + findDueCards(deckId: string, now: Date): Promise; countDueCards(deckId: string, now: Date): Promise; findDueCardsWithNoteData( deckId: string, now: Date, - limit: number, ): Promise; - findDueCardsForStudy( - deckId: string, - now: Date, - limit: number, - ): Promise; - findDueNewCardsForStudy( - deckId: string, - now: Date, - limit: number, - ): Promise; - findDueReviewCardsForStudy( - deckId: string, - now: Date, - limit: number, - ): Promise; - countDueNewCards(deckId: string, now: Date): Promise; - countDueReviewCards(deckId: string, now: Date): Promise; + findDueCardsForStudy(deckId: string, now: Date): Promise; updateFSRSFields( id: string, deckId: string, @@ -210,7 +190,6 @@ export interface ReviewLogRepository { elapsedDays: number; durationMs?: number | null; }): Promise; - countTodayNewCardReviews(deckId: string, now: Date): Promise; } export interface NoteType { diff --git a/src/server/routes/cards.test.ts b/src/server/routes/cards.test.ts index 4595e28..a063c95 100644 --- a/src/server/routes/cards.test.ts +++ b/src/server/routes/cards.test.ts @@ -26,12 +26,8 @@ function createMockCardRepo(): CardRepository { softDeleteByNoteId: vi.fn(), findDueCards: vi.fn(), countDueCards: vi.fn(), - countDueNewCards: vi.fn(), - countDueReviewCards: vi.fn(), findDueCardsWithNoteData: vi.fn(), findDueCardsForStudy: vi.fn(), - findDueNewCardsForStudy: vi.fn(), - findDueReviewCardsForStudy: vi.fn(), updateFSRSFields: vi.fn(), }; } @@ -66,7 +62,6 @@ function createMockDeck(overrides: Partial = {}): Deck { userId: "user-uuid-123", name: "Test Deck", description: "Test description", - newCardsPerDay: 20, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), deletedAt: null, diff --git a/src/server/routes/decks.test.ts b/src/server/routes/decks.test.ts index d0854c1..f686024 100644 --- a/src/server/routes/decks.test.ts +++ b/src/server/routes/decks.test.ts @@ -6,7 +6,6 @@ import type { CardRepository, Deck, DeckRepository, - ReviewLogRepository, } from "../repositories/index.js"; import { createDecksRouter } from "./decks.js"; @@ -32,23 +31,12 @@ function createMockCardRepo(): CardRepository { softDeleteByNoteId: vi.fn(), findDueCards: vi.fn(), countDueCards: vi.fn().mockResolvedValue(0), - countDueNewCards: vi.fn().mockResolvedValue(0), - countDueReviewCards: vi.fn().mockResolvedValue(0), findDueCardsWithNoteData: vi.fn(), findDueCardsForStudy: vi.fn(), - findDueNewCardsForStudy: vi.fn(), - findDueReviewCardsForStudy: vi.fn(), updateFSRSFields: vi.fn(), }; } -function createMockReviewLogRepo(): ReviewLogRepository { - return { - create: vi.fn(), - countTodayNewCardReviews: vi.fn().mockResolvedValue(0), - }; -} - const JWT_SECRET = process.env.JWT_SECRET || "test-secret"; async function createTestToken(userId: string): Promise { @@ -69,7 +57,6 @@ function createMockDeck(overrides: Partial = {}): Deck { userId: "user-uuid-123", name: "Test Deck", description: "Test description", - newCardsPerDay: 20, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), deletedAt: null, @@ -92,18 +79,15 @@ describe("GET /api/decks", () => { let app: Hono; let mockDeckRepo: ReturnType; let mockCardRepo: ReturnType; - let mockReviewLogRepo: ReturnType; let authToken: string; beforeEach(async () => { vi.clearAllMocks(); mockDeckRepo = createMockDeckRepo(); mockCardRepo = createMockCardRepo(); - mockReviewLogRepo = createMockReviewLogRepo(); const decksRouter = createDecksRouter({ deckRepo: mockDeckRepo, cardRepo: mockCardRepo, - reviewLogRepo: mockReviewLogRepo, }); app = new Hono(); app.onError(errorHandler); @@ -155,18 +139,15 @@ describe("POST /api/decks", () => { let app: Hono; let mockDeckRepo: ReturnType; let mockCardRepo: ReturnType; - let mockReviewLogRepo: ReturnType; let authToken: string; beforeEach(async () => { vi.clearAllMocks(); mockDeckRepo = createMockDeckRepo(); mockCardRepo = createMockCardRepo(); - mockReviewLogRepo = createMockReviewLogRepo(); const decksRouter = createDecksRouter({ deckRepo: mockDeckRepo, cardRepo: mockCardRepo, - reviewLogRepo: mockReviewLogRepo, }); app = new Hono(); app.onError(errorHandler); @@ -194,7 +175,6 @@ describe("POST /api/decks", () => { userId: "user-uuid-123", name: "New Deck", description: undefined, - newCardsPerDay: 20, }); }); @@ -202,7 +182,6 @@ describe("POST /api/decks", () => { const newDeck = createMockDeck({ name: "Full Deck", description: "Full description", - newCardsPerDay: 30, }); vi.mocked(mockDeckRepo.create).mockResolvedValue(newDeck); @@ -215,7 +194,6 @@ describe("POST /api/decks", () => { body: JSON.stringify({ name: "Full Deck", description: "Full description", - newCardsPerDay: 30, }), }); @@ -226,7 +204,6 @@ describe("POST /api/decks", () => { userId: "user-uuid-123", name: "Full Deck", description: "Full description", - newCardsPerDay: 30, }); }); @@ -271,18 +248,15 @@ describe("GET /api/decks/:id", () => { let app: Hono; let mockDeckRepo: ReturnType; let mockCardRepo: ReturnType; - let mockReviewLogRepo: ReturnType; let authToken: string; beforeEach(async () => { vi.clearAllMocks(); mockDeckRepo = createMockDeckRepo(); mockCardRepo = createMockCardRepo(); - mockReviewLogRepo = createMockReviewLogRepo(); const decksRouter = createDecksRouter({ deckRepo: mockDeckRepo, cardRepo: mockCardRepo, - reviewLogRepo: mockReviewLogRepo, }); app = new Hono(); app.onError(errorHandler); @@ -344,18 +318,15 @@ describe("PUT /api/decks/:id", () => { let app: Hono; let mockDeckRepo: ReturnType; let mockCardRepo: ReturnType; - let mockReviewLogRepo: ReturnType; let authToken: string; beforeEach(async () => { vi.clearAllMocks(); mockDeckRepo = createMockDeckRepo(); mockCardRepo = createMockCardRepo(); - mockReviewLogRepo = createMockReviewLogRepo(); const decksRouter = createDecksRouter({ deckRepo: mockDeckRepo, cardRepo: mockCardRepo, - reviewLogRepo: mockReviewLogRepo, }); app = new Hono(); app.onError(errorHandler); @@ -405,27 +376,6 @@ describe("PUT /api/decks/:id", () => { expect(body.deck?.description).toBe("New description"); }); - it("updates newCardsPerDay", async () => { - const updatedDeck = createMockDeck({ newCardsPerDay: 50 }); - vi.mocked(mockDeckRepo.update).mockResolvedValue(updatedDeck); - - const res = await app.request( - "/api/decks/00000000-0000-0000-0000-000000000000", - { - method: "PUT", - headers: { - Authorization: `Bearer ${authToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ newCardsPerDay: 50 }), - }, - ); - - expect(res.status).toBe(200); - const body = (await res.json()) as DeckResponse; - expect(body.deck?.newCardsPerDay).toBe(50); - }); - it("returns 404 for non-existent deck", async () => { vi.mocked(mockDeckRepo.update).mockResolvedValue(undefined); @@ -477,18 +427,15 @@ describe("DELETE /api/decks/:id", () => { let app: Hono; let mockDeckRepo: ReturnType; let mockCardRepo: ReturnType; - let mockReviewLogRepo: ReturnType; let authToken: string; beforeEach(async () => { vi.clearAllMocks(); mockDeckRepo = createMockDeckRepo(); mockCardRepo = createMockCardRepo(); - mockReviewLogRepo = createMockReviewLogRepo(); const decksRouter = createDecksRouter({ deckRepo: mockDeckRepo, cardRepo: mockCardRepo, - reviewLogRepo: mockReviewLogRepo, }); app = new Hono(); app.onError(errorHandler); diff --git a/src/server/routes/decks.ts b/src/server/routes/decks.ts index d73aa0c..ed7077e 100644 --- a/src/server/routes/decks.ts +++ b/src/server/routes/decks.ts @@ -7,25 +7,20 @@ import { cardRepository, type DeckRepository, deckRepository, - type ReviewLogRepository, - reviewLogRepository, } from "../repositories/index.js"; import { createDeckSchema, updateDeckSchema } from "../schemas/index.js"; export interface DeckDependencies { deckRepo: DeckRepository; cardRepo: CardRepository; - reviewLogRepo: ReviewLogRepository; } const deckIdParamSchema = z.object({ id: z.uuid(), }); -const REVIEW_CARDS_LIMIT = 80; - export function createDecksRouter(deps: DeckDependencies) { - const { deckRepo, cardRepo, reviewLogRepo } = deps; + const { deckRepo, cardRepo } = deps; return new Hono() .use("*", authMiddleware) @@ -35,26 +30,7 @@ export function createDecksRouter(deps: DeckDependencies) { const now = new Date(); const decksWithDueCount = await Promise.all( decks.map(async (deck) => { - const [dueNewCards, dueReviewCards, reviewedNewCards] = - await Promise.all([ - cardRepo.countDueNewCards(deck.id, now), - cardRepo.countDueReviewCards(deck.id, now), - reviewLogRepo.countTodayNewCardReviews(deck.id, now), - ]); - - // Apply the same limits as the study screen - const newCardBudget = Math.max( - 0, - deck.newCardsPerDay - reviewedNewCards, - ); - const newCardsToStudy = Math.min(dueNewCards, newCardBudget); - const reviewCardsToStudy = Math.min( - dueReviewCards, - REVIEW_CARDS_LIMIT, - ); - - const dueCardCount = newCardsToStudy + reviewCardsToStudy; - + const dueCardCount = await cardRepo.countDueCards(deck.id, now); return { ...deck, dueCardCount }; }), ); @@ -68,7 +44,6 @@ export function createDecksRouter(deps: DeckDependencies) { userId: user.id, name: data.name, description: data.description, - newCardsPerDay: data.newCardsPerDay, }); return c.json({ deck }, 201); @@ -83,18 +58,7 @@ export function createDecksRouter(deps: DeckDependencies) { } const now = new Date(); - const [dueNewCards, dueReviewCards, reviewedNewCards] = await Promise.all( - [ - cardRepo.countDueNewCards(deck.id, now), - cardRepo.countDueReviewCards(deck.id, now), - reviewLogRepo.countTodayNewCardReviews(deck.id, now), - ], - ); - - const newCardBudget = Math.max(0, deck.newCardsPerDay - reviewedNewCards); - const newCardsToStudy = Math.min(dueNewCards, newCardBudget); - const reviewCardsToStudy = Math.min(dueReviewCards, REVIEW_CARDS_LIMIT); - const dueCardCount = newCardsToStudy + reviewCardsToStudy; + const dueCardCount = await cardRepo.countDueCards(deck.id, now); return c.json({ deck: { ...deck, dueCardCount } }, 200); }) @@ -131,5 +95,4 @@ export function createDecksRouter(deps: DeckDependencies) { export const decks = createDecksRouter({ deckRepo: deckRepository, cardRepo: cardRepository, - reviewLogRepo: reviewLogRepository, }); diff --git a/src/server/routes/notes.test.ts b/src/server/routes/notes.test.ts index e354fa6..116a57f 100644 --- a/src/server/routes/notes.test.ts +++ b/src/server/routes/notes.test.ts @@ -57,7 +57,6 @@ function createMockDeck(overrides: Partial = {}): Deck { userId: "user-uuid-123", name: "Test Deck", description: "Test description", - newCardsPerDay: 20, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), deletedAt: null, diff --git a/src/server/routes/study.test.ts b/src/server/routes/study.test.ts index 514d966..119b25d 100644 --- a/src/server/routes/study.test.ts +++ b/src/server/routes/study.test.ts @@ -26,12 +26,8 @@ function createMockCardRepo(): CardRepository { softDeleteByNoteId: vi.fn(), findDueCards: vi.fn(), countDueCards: vi.fn(), - countDueNewCards: vi.fn(), - countDueReviewCards: vi.fn(), findDueCardsWithNoteData: vi.fn(), findDueCardsForStudy: vi.fn(), - findDueNewCardsForStudy: vi.fn(), - findDueReviewCardsForStudy: vi.fn(), updateFSRSFields: vi.fn(), }; } @@ -49,7 +45,6 @@ function createMockDeckRepo(): DeckRepository { function createMockReviewLogRepo(): ReviewLogRepository { return { create: vi.fn(), - countTodayNewCardReviews: vi.fn().mockResolvedValue(0), }; } @@ -73,7 +68,6 @@ function createMockDeck(overrides: Partial = {}): Deck { userId: "user-uuid-123", name: "Test Deck", description: "Test description", - newCardsPerDay: 20, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), deletedAt: null, @@ -152,14 +146,13 @@ describe("GET /api/decks/:deckId/study", () => { let app: Hono; let mockCardRepo: ReturnType; let mockDeckRepo: ReturnType; - let mockReviewLogRepo: ReturnType; let authToken: string; beforeEach(async () => { vi.clearAllMocks(); mockCardRepo = createMockCardRepo(); mockDeckRepo = createMockDeckRepo(); - mockReviewLogRepo = createMockReviewLogRepo(); + const mockReviewLogRepo = createMockReviewLogRepo(); const studyRouter = createStudyRouter({ cardRepo: mockCardRepo, deckRepo: mockDeckRepo, @@ -175,8 +168,7 @@ describe("GET /api/decks/:deckId/study", () => { vi.mocked(mockDeckRepo.findById).mockResolvedValue( createMockDeck({ id: DECK_ID }), ); - vi.mocked(mockCardRepo.findDueNewCardsForStudy).mockResolvedValue([]); - vi.mocked(mockCardRepo.findDueReviewCardsForStudy).mockResolvedValue([]); + vi.mocked(mockCardRepo.findDueCardsForStudy).mockResolvedValue([]); const res = await app.request(`/api/decks/${DECK_ID}/study`, { method: "GET", @@ -190,20 +182,14 @@ describe("GET /api/decks/:deckId/study", () => { DECK_ID, "user-uuid-123", ); - expect(mockCardRepo.findDueNewCardsForStudy).toHaveBeenCalledWith( - DECK_ID, - expect.any(Date), - 20, - ); - expect(mockCardRepo.findDueReviewCardsForStudy).toHaveBeenCalledWith( + expect(mockCardRepo.findDueCardsForStudy).toHaveBeenCalledWith( DECK_ID, expect.any(Date), - 80, ); }); it("returns due cards", async () => { - const newCards = [ + const mockCards = [ createMockCardForStudy({ id: "card-1", front: "Q1", @@ -211,8 +197,6 @@ describe("GET /api/decks/:deckId/study", () => { state: CardState.New, fieldValuesMap: {}, }), - ]; - const reviewCards = [ createMockCardForStudy({ id: "card-2", front: "Q2", @@ -224,10 +208,7 @@ describe("GET /api/decks/:deckId/study", () => { vi.mocked(mockDeckRepo.findById).mockResolvedValue( createMockDeck({ id: DECK_ID }), ); - vi.mocked(mockCardRepo.findDueNewCardsForStudy).mockResolvedValue(newCards); - vi.mocked(mockCardRepo.findDueReviewCardsForStudy).mockResolvedValue( - reviewCards, - ); + vi.mocked(mockCardRepo.findDueCardsForStudy).mockResolvedValue(mockCards); const res = await app.request(`/api/decks/${DECK_ID}/study`, { method: "GET", @@ -259,10 +240,7 @@ describe("GET /api/decks/:deckId/study", () => { vi.mocked(mockDeckRepo.findById).mockResolvedValue( createMockDeck({ id: DECK_ID }), ); - vi.mocked(mockCardRepo.findDueNewCardsForStudy).mockResolvedValue( - mockCards, - ); - vi.mocked(mockCardRepo.findDueReviewCardsForStudy).mockResolvedValue([]); + vi.mocked(mockCardRepo.findDueCardsForStudy).mockResolvedValue(mockCards); const res = await app.request(`/api/decks/${DECK_ID}/study`, { method: "GET", @@ -276,81 +254,6 @@ describe("GET /api/decks/:deckId/study", () => { expect(body.cards?.[0]?.fieldValuesMap?.Front).toBe("Question"); }); - it("limits new cards based on newCardsPerDay", async () => { - vi.mocked(mockDeckRepo.findById).mockResolvedValue( - createMockDeck({ id: DECK_ID, newCardsPerDay: 5 }), - ); - vi.mocked(mockReviewLogRepo.countTodayNewCardReviews).mockResolvedValue(3); - vi.mocked(mockCardRepo.findDueNewCardsForStudy).mockResolvedValue([]); - vi.mocked(mockCardRepo.findDueReviewCardsForStudy).mockResolvedValue([]); - - await app.request(`/api/decks/${DECK_ID}/study`, { - method: "GET", - headers: { Authorization: `Bearer ${authToken}` }, - }); - - expect(mockCardRepo.findDueNewCardsForStudy).toHaveBeenCalledWith( - DECK_ID, - expect.any(Date), - 2, - ); - }); - - it("returns 0 new cards when daily limit is reached", async () => { - vi.mocked(mockDeckRepo.findById).mockResolvedValue( - createMockDeck({ id: DECK_ID, newCardsPerDay: 5 }), - ); - vi.mocked(mockReviewLogRepo.countTodayNewCardReviews).mockResolvedValue(5); - vi.mocked(mockCardRepo.findDueNewCardsForStudy).mockResolvedValue([]); - vi.mocked(mockCardRepo.findDueReviewCardsForStudy).mockResolvedValue([]); - - await app.request(`/api/decks/${DECK_ID}/study`, { - method: "GET", - headers: { Authorization: `Bearer ${authToken}` }, - }); - - expect(mockCardRepo.findDueNewCardsForStudy).toHaveBeenCalledWith( - DECK_ID, - expect.any(Date), - 0, - ); - }); - - it("places new cards before review cards in response", async () => { - const newCards = [ - createMockCardForStudy({ - id: "new-1", - state: CardState.New, - fieldValuesMap: {}, - }), - ]; - const reviewCards = [ - createMockCardForStudy({ - id: "review-1", - state: CardState.Review, - fieldValuesMap: {}, - }), - ]; - vi.mocked(mockDeckRepo.findById).mockResolvedValue( - createMockDeck({ id: DECK_ID }), - ); - vi.mocked(mockCardRepo.findDueNewCardsForStudy).mockResolvedValue(newCards); - vi.mocked(mockCardRepo.findDueReviewCardsForStudy).mockResolvedValue( - reviewCards, - ); - - const res = await app.request(`/api/decks/${DECK_ID}/study`, { - method: "GET", - headers: { Authorization: `Bearer ${authToken}` }, - }); - - expect(res.status).toBe(200); - const body = (await res.json()) as StudyResponse; - expect(body.cards).toHaveLength(2); - expect(body.cards?.[0]?.id).toBe("new-1"); - expect(body.cards?.[1]?.id).toBe("review-1"); - }); - it("returns 404 for non-existent deck", async () => { vi.mocked(mockDeckRepo.findById).mockResolvedValue(undefined); diff --git a/src/server/routes/study.ts b/src/server/routes/study.ts index d05f3ca..0f42f93 100644 --- a/src/server/routes/study.ts +++ b/src/server/routes/study.ts @@ -52,20 +52,9 @@ export function createStudyRouter(deps: StudyDependencies) { const now = new Date(); - // Calculate new card budget based on today's already-reviewed new cards - const reviewedNewCards = await reviewLogRepo.countTodayNewCardReviews( - deckId, - now, - ); - const newCardBudget = Math.max(0, deck.newCardsPerDay - reviewedNewCards); - - // Fetch new cards (limited) and review cards separately - const [newCards, reviewCards] = await Promise.all([ - cardRepo.findDueNewCardsForStudy(deckId, now, newCardBudget), - cardRepo.findDueReviewCardsForStudy(deckId, now, 80), - ]); - - return c.json({ cards: [...newCards, ...reviewCards] }, 200); + const cards = await cardRepo.findDueCardsForStudy(deckId, now); + + return c.json({ cards }, 200); }) .post( "/:cardId", diff --git a/src/server/routes/sync.test.ts b/src/server/routes/sync.test.ts index 8ea2ce3..4c0d8d8 100644 --- a/src/server/routes/sync.test.ts +++ b/src/server/routes/sync.test.ts @@ -143,7 +143,6 @@ describe("POST /api/sync/push", () => { id: "550e8400-e29b-41d4-a716-446655440000", name: "Test Deck", description: "A test deck", - newCardsPerDay: 20, createdAt: "2024-01-01T00:00:00.000Z", updatedAt: "2024-01-02T00:00:00.000Z", deletedAt: null, @@ -302,7 +301,6 @@ describe("POST /api/sync/push", () => { id: "550e8400-e29b-41d4-a716-446655440003", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: "2024-01-01T00:00:00.000Z", updatedAt: "2024-01-02T00:00:00.000Z", deletedAt: null, @@ -349,7 +347,6 @@ describe("POST /api/sync/push", () => { id: "not-a-uuid", name: "", description: null, - newCardsPerDay: -1, createdAt: "invalid-date", updatedAt: "2024-01-01T00:00:00.000Z", deletedAt: null, @@ -432,7 +429,6 @@ describe("POST /api/sync/push", () => { id: "550e8400-e29b-41d4-a716-446655440004", name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: "2024-01-01T00:00:00.000Z", updatedAt: "2024-01-01T00:00:00.000Z", deletedAt: null, @@ -634,7 +630,6 @@ describe("POST /api/sync/push", () => { id: "550e8400-e29b-41d4-a716-446655440007", name: "Deleted Deck", description: null, - newCardsPerDay: 20, createdAt: "2024-01-01T00:00:00.000Z", updatedAt: "2024-01-02T00:00:00.000Z", deletedAt: "2024-01-02T00:00:00.000Z", @@ -830,7 +825,6 @@ describe("GET /api/sync/pull", () => { userId, name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01T00:00:00.000Z"), updatedAt: new Date("2024-01-02T00:00:00.000Z"), deletedAt: null, @@ -906,7 +900,6 @@ describe("GET /api/sync/pull", () => { userId, name: "Test Deck", description: "A test description", - newCardsPerDay: 20, createdAt: new Date("2024-01-01T00:00:00.000Z"), updatedAt: new Date("2024-01-02T00:00:00.000Z"), deletedAt: null, @@ -1041,7 +1034,6 @@ describe("GET /api/sync/pull", () => { userId, name: "Test Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01T00:00:00.000Z"), updatedAt: new Date("2024-01-01T00:00:00.000Z"), deletedAt: null, @@ -1215,7 +1207,6 @@ describe("GET /api/sync/pull", () => { userId, name: "Deleted Deck", description: null, - newCardsPerDay: 20, createdAt: new Date("2024-01-01T00:00:00.000Z"), updatedAt: new Date("2024-01-02T00:00:00.000Z"), deletedAt: new Date("2024-01-02T00:00:00.000Z"), diff --git a/src/server/routes/sync.ts b/src/server/routes/sync.ts index c571f8a..a9ea3b3 100644 --- a/src/server/routes/sync.ts +++ b/src/server/routes/sync.ts @@ -18,7 +18,6 @@ const syncDeckSchema = z.object({ id: z.uuid(), name: z.string().min(1).max(255), description: z.string().nullable(), - newCardsPerDay: z.number().int().min(0).max(1000), createdAt: z.string().datetime(), updatedAt: z.string().datetime(), deletedAt: z.string().datetime().nullable(), diff --git a/src/server/schemas/index.ts b/src/server/schemas/index.ts index fc1bd77..aa9ceea 100644 --- a/src/server/schemas/index.ts +++ b/src/server/schemas/index.ts @@ -48,7 +48,6 @@ export const deckSchema = z.object({ userId: z.uuid(), name: z.string().min(1).max(255), description: z.string().max(1000).nullable(), - newCardsPerDay: z.number().int().min(0).default(20), createdAt: z.coerce.date(), updatedAt: z.coerce.date(), deletedAt: z.coerce.date().nullable(), @@ -59,14 +58,12 @@ export const deckSchema = z.object({ export const createDeckSchema = z.object({ name: z.string().min(1).max(255), description: z.string().max(1000).nullable().optional(), - newCardsPerDay: z.number().int().min(0).default(20), }); // Deck update input schema export const updateDeckSchema = z.object({ name: z.string().min(1).max(255).optional(), description: z.string().max(1000).nullable().optional(), - newCardsPerDay: z.number().int().min(0).optional(), }); // Card schema diff --git a/src/server/types/index.ts b/src/server/types/index.ts index bfba06f..08f8379 100644 --- a/src/server/types/index.ts +++ b/src/server/types/index.ts @@ -33,7 +33,6 @@ export interface Deck { userId: string; name: string; description: string | null; - newCardsPerDay: number; createdAt: Date; updatedAt: Date; deletedAt: Date | null; -- cgit v1.3-1-g0d28