🐙
【開発ログ】学習イベントの記録と発音ボタンによる学習完了の実装
学習イベントを登録する際、当初は「学習完了」のボタンを作るか迷いました。しかし、このアプリは子どもが操作することを想定しているため、発音を聞くと自動で学習完了として記録する方針に変更しました。これにより、子どもが操作を迷わず、自然に学習イベントが記録されるようになっています。
前回
-
学習関連API
- 今日の単語を10件, 学習済み単語からランダムに10件抽出(テスト用)
- 取得データをダッシュボードに反映
今回
-
学習イベントの保存
-action,lang,wordIdを記録
- 例:{ action: "learn", lang: "ja", wordId: "xxxx" }
① 学習イベントAPI
単語カードで発音ボタンを押したときに、学習イベントを登録するAPIを作成しました。
api/study-event/route.ts
import { getDecodedSessionOrRedirect } from "@/lib/authServer";
import { prisma } from "@/lib/prisma";
import { NextResponse } from "next/server";
export async function POST(request: Request) {
// ユーザ情報取得
const user = "ユーザ情報GET";
if (!user) {
return NextResponse.json({ error: "User not found" }, { status: 404 });
}
// body から情報を取得
const { wordId, action, lang } = await request.json();
if (!wordId || !action) {
return NextResponse.json({ error: "Missing wordId or action" }, { status: 400 });
}
// 有効な action チェック
if (!["learn"].includes(action)) {
return NextResponse.json({ error: "Invalid action type" }, { status: 400 });
}
try {
// 学習イベント記録
const studyEvent = await prisma.studyEvent.create({
data: {
userId: user.id,
wordId,
action,
lang,
},
});
return NextResponse.json(studyEvent, { status: 201 });
} catch (error) {
console.error("Error recording study event:", error);
return NextResponse.json({ error: "Server error" }, { status: 500 });
}
}
② コンポーネント修正
学習カードの発音ボタンを押すと、学習イベントを登録するようにしました。
components/learning/LearningCard.tsx
export default function LearningCard() {
/** 一部省略 */
// 学習イベント情報
const [learnedWords, setLearnedWords] = useState<LearnedWord[]>([]);
// 発音ボタン(学習イベント完了)
const handleLearned = (wordId: string) => {
if (learnedWords.some(w => w.id === wordId)) return; // 重複チェック
const newLearnedWord: LearnedWord = {
id: wordId,
action: "learn",
lang: "ja",
};
setLearnedWords(prev => [...prev, newLearnedWord]);
// サーバー記録
fetch('/api/study-event', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wordId, action: "learn", lang: "ja" }),
})
.then(res => {
if (!res.ok) throw new Error('学習イベントの記録に失敗しました');
return res.json();
})
.then(data => {
console.log('学習イベントが記録されました:', data);
})
.catch(err => {
console.error(err);
});
};
return (
<>
{/* 一部省略 */}
{/** 発音ボタン */}
<div className="mt-4 flex gap-2">
<button
type="button"
onClick={() => handleLearned(card.id)} // WordID
className="w-full rounded bg-blue-500 px-4 py-2 text-white"
>
はつおん
</button>
</div>
</>
);
}
悩んだこと / 学んだこと
- 発音ボタンと学習完了の関係をどうするか考えました。
- 重複登録を防ぐために、状態管理とサーバー側のチェックを組み合わせる必要があります。
次回やること
-
Firebaseセッションの安定化
- Cookieの有効期限切れに備え、認証ロジックを再確認・修正 -
UI/UX改善
- ダッシュボードとエディタのデザイン統一
- スマホ表示での余白や配置調整 -
TTS機能
- 韓国語・日本語の発音再生機能
Discussion