From fe101104cdd50256d4ef5c61e1bf099ed2da68e3 Mon Sep 17 00:00:00 2001
From: nsfisis <nsfisis@gmail.com>
Date: Sun, 7 Dec 2025 19:16:48 +0900
Subject: feat(server): add POST /api/sync/push endpoint
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Implement sync push endpoint with Last-Write-Wins conflict resolution.
Includes Zod validation for decks, cards, and review logs.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---
 src/client/pages/StudyPage.tsx | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

(limited to 'src/client/pages/StudyPage.tsx')

diff --git a/src/client/pages/StudyPage.tsx b/src/client/pages/StudyPage.tsx
index 05e9943..c6f8665 100644
--- a/src/client/pages/StudyPage.tsx
+++ b/src/client/pages/StudyPage.tsx
@@ -144,17 +144,14 @@ export function StudyPage() {
 				throw new ApiClientError("Not authenticated", 401);
 			}
 
-			const res = await fetch(
-				`/api/decks/${deckId}/study/${currentCard.id}`,
-				{
-					method: "POST",
-					headers: {
-						...authHeader,
-						"Content-Type": "application/json",
-					},
-					body: JSON.stringify({ rating, durationMs }),
+			const res = await fetch(`/api/decks/${deckId}/study/${currentCard.id}`, {
+				method: "POST",
+				headers: {
+					...authHeader,
+					"Content-Type": "application/json",
 				},
-			);
+				body: JSON.stringify({ rating, durationMs }),
+			});
 
 			if (!res.ok) {
 				const errorBody = await res.json().catch(() => ({}));
@@ -312,7 +309,13 @@ export function StudyPage() {
 								<strong data-testid="completed-count">{completedCount}</strong>{" "}
 								card{completedCount !== 1 ? "s" : ""}.
 							</p>
-							<div style={{ display: "flex", gap: "1rem", justifyContent: "center" }}>
+							<div
+								style={{
+									display: "flex",
+									gap: "1rem",
+									justifyContent: "center",
+								}}
+							>
 								<Link href={`/decks/${deckId}`}>
 									<button type="button">Back to Deck</button>
 								</Link>
@@ -336,7 +339,9 @@ export function StudyPage() {
 								}}
 								role="button"
 								tabIndex={0}
-								aria-label={isFlipped ? "Card showing answer" : "Click to reveal answer"}
+								aria-label={
+									isFlipped ? "Card showing answer" : "Click to reveal answer"
+								}
 								style={{
 									border: "1px solid #ccc",
 									borderRadius: "8px",
-- 
cgit v1.2.3-70-g09d2

