From 0c042ac89fc0822fcbe09c48702857faa5494ae1 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 7 Dec 2025 23:34:03 +0900 Subject: feat(client): add sync status indicator component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add SyncStatusIndicator component to display current sync state in the UI header. The component shows online/offline status, syncing progress, pending changes count, and sync errors. - Create SyncProvider context to wrap SyncManager for React components - Add SyncStatusIndicator component with visual status indicators - Integrate indicator into HomePage header - Add comprehensive tests for SyncStatusIndicator and SyncProvider - Update existing tests to include SyncProvider wrapper 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/server/routes/sync.test.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'src/server/routes') diff --git a/src/server/routes/sync.test.ts b/src/server/routes/sync.test.ts index 22efada..9deb5ac 100644 --- a/src/server/routes/sync.test.ts +++ b/src/server/routes/sync.test.ts @@ -196,7 +196,9 @@ describe("POST /api/sync/push", () => { const mockResult: SyncPushResult = { decks: [], cards: [], - reviewLogs: [{ id: "550e8400-e29b-41d4-a716-446655440002", syncVersion: 1 }], + reviewLogs: [ + { id: "550e8400-e29b-41d4-a716-446655440002", syncVersion: 1 }, + ], conflicts: { decks: [], cards: [] }, }; vi.mocked(mockSyncRepo.pushChanges).mockResolvedValue(mockResult); @@ -217,7 +219,9 @@ describe("POST /api/sync/push", () => { expect(res.status).toBe(200); const body = (await res.json()) as SyncPushResponse; expect(body.reviewLogs).toHaveLength(1); - expect(body.reviewLogs?.[0]?.id).toBe("550e8400-e29b-41d4-a716-446655440002"); + expect(body.reviewLogs?.[0]?.id).toBe( + "550e8400-e29b-41d4-a716-446655440002", + ); }); it("returns conflicts when server data is newer", async () => { @@ -250,7 +254,9 @@ describe("POST /api/sync/push", () => { expect(res.status).toBe(200); const body = (await res.json()) as SyncPushResponse; - expect(body.conflicts?.decks).toContain("550e8400-e29b-41d4-a716-446655440003"); + expect(body.conflicts?.decks).toContain( + "550e8400-e29b-41d4-a716-446655440003", + ); }); it("validates deck schema", async () => { @@ -380,7 +386,9 @@ describe("POST /api/sync/push", () => { const mockResult: SyncPushResult = { decks: [{ id: "550e8400-e29b-41d4-a716-446655440004", syncVersion: 1 }], cards: [{ id: "550e8400-e29b-41d4-a716-446655440005", syncVersion: 1 }], - reviewLogs: [{ id: "550e8400-e29b-41d4-a716-446655440006", syncVersion: 1 }], + reviewLogs: [ + { id: "550e8400-e29b-41d4-a716-446655440006", syncVersion: 1 }, + ], conflicts: { decks: [], cards: [] }, }; vi.mocked(mockSyncRepo.pushChanges).mockResolvedValue(mockResult); @@ -649,7 +657,9 @@ describe("GET /api/sync/pull", () => { expect(res.status).toBe(200); const body = (await res.json()) as SyncPullResponse; expect(body.reviewLogs).toHaveLength(1); - expect(body.reviewLogs?.[0]?.id).toBe("550e8400-e29b-41d4-a716-446655440002"); + expect(body.reviewLogs?.[0]?.id).toBe( + "550e8400-e29b-41d4-a716-446655440002", + ); expect(body.reviewLogs?.[0]?.rating).toBe(3); expect(body.reviewLogs?.[0]?.durationMs).toBe(5000); }); -- cgit v1.2.3-70-g09d2