Mstudio

解決策提案アプリ

Solution Plan Generator

解決策提案アプリ

AI解決策

解決策プランを提案

あなたの悩みを入力すると、AIが期間別のタスクリスト形式で具体的な解決策を提案します。

技術解説

1. Google Gemini APIの利用

このアプリでは、Google Gemini APIを使って、ユーザーの悩みに対する解決策を生成しています。APIルート(Next.js API Routes)を使って、サーバーサイドでAPIを呼び出しています。

// APIルートでGoogle Gemini APIを呼び出す
const apiUrl = `https://generativelanguage.googleapis.com/v1/models/${modelName}:generateContent?key=${apiKey}`;

const response = await fetch(apiUrl, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    contents: [{
      parts: [{ text: prompt }]
    }]
  })
});

const data = await response.json();
const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
  • Next.js API Routes:app/apiディレクトリにファイルを配置すると、自動的にAPIエンドポイントとして機能します。サーバーサイドで実行されるため、APIキーなどの秘密情報を安全に扱えます。
  • 環境変数:process.env.GEMINI_API_KEYでAPIキーを取得しています。APIキーは公開リポジトリに含めず、環境変数で管理するのがベストプラクティスです。
  • JSON形式のレスポンス: AIからの応答をJSON形式で受け取り、期間別タスクリストとして構造化しています。

2. 状態管理(useState)

フォームの入力値、解決策プラン、ローディング状態、エラー状態などを管理するために、ReactのuseStateフックを使っています。

// 複数の状態を管理
const [problem, setProblem] = useState("");
const [solutionPlan, setSolutionPlan] = useState<SolutionPlan | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

// 状態を更新
setLoading(true);  // ローディング開始
setSolutionPlan(data.plan);  // プランを設定
setError("エラーメッセージ");  // エラーを設定
  • useState: コンポーネント内で状態を管理するためのフックです。[値, 更新関数]の形式で返され、更新関数を呼ぶと画面が自動的に更新されます。
  • 型定義:useState<SolutionPlan | null>(null)のように型を指定することで、TypeScriptが型チェックを行い、エラーを防げます。

3. 期間別タスクリストの実装

AIから受け取ったタスクリストを期間別にグループ化して表示しています。

// 期間別にタスクをグループ化
const tasksByPeriod = solutionPlan
  ? solutionPlan.tasks.reduce((acc, task) => {
      if (!acc[task.period]) {
        acc[task.period] = [];
      }
      acc[task.period].push(task);
      return acc;
    }, {} as Record<string, TaskItem[]>)
  : {};

// 期間別に表示
{Object.entries(tasksByPeriod).map(([period, tasks]) => (
  <div key={period}>
    <h4>{period}</h4>
    {tasks.map((task) => (
      <div key={task.id}>{task.content}</div>
    ))}
  </div>
))}
  • reduce(): 配列をオブジェクトに変換するために使っています。期間をキーとして、その期間のタスクを配列として格納します。
  • Object.entries(): オブジェクトを[キー, 値]の配列に変換して、map()で各期間を順番に処理しています。

4. チェックボックスの状態管理

タスクの完了状態をチェックボックスで管理し、完了したタスクは視覚的に区別できるようにしています。

// タスクの完了状態を切り替え
const toggleTask = (taskId: string) => {
  if (!solutionPlan) return;
  setSolutionPlan({
    ...solutionPlan,
    tasks: solutionPlan.tasks.map((task) =>
      task.id === taskId
        ? { ...task, completed: !task.completed }
        : task
    ),
  });
};

// チェックボックス
<input
  type="checkbox"
  checked={task.completed}
  onChange={() => toggleTask(task.id)}
/>
  • スプレッド構文(...):{...solutionPlan}で既存のオブジェクトを展開し、tasksプロパティだけを更新した新しいオブジェクトを作成します。
  • map(): タスク配列を順番に処理し、該当するタスクのcompletedプロパティだけを反転させます。
  • 条件付きスタイル: 完了したタスクにはcompletedクラスを追加し、スタイルで打ち消し線や背景色を変更しています。

5. 優先度と難易度によるソート

AIから受け取ったタスクを、優先度が高く、達成しやすい順に並べ替えています。

// 優先度と難易度でソート
const tasks = solutionPlan.tasks
  .map((task, index) => ({
    ...task,
    id: `task-${index}`,
    completed: false,
  }))
  .sort((a, b) => {
    // 優先度が高い順、同じ優先度なら難易度が低い順(簡単な順)
    if (a.priority !== b.priority) {
      return a.priority - b.priority;
    }
    return a.difficulty - b.difficulty;
  });
  • sort(): 配列を並べ替えるメソッドです。比較関数を渡すことで、カスタムの並べ替えルールを定義できます。
  • 優先度と難易度: 優先度(1-5、1が最高)と難易度(1-5、1が最も簡単)の2つの基準でソートしています。

6. エラーハンドリングとローディング状態

API呼び出し中はローディング表示を出し、エラーが発生した場合はユーザーに分かりやすいメッセージを表示しています。

// エラーハンドリング
try {
  setLoading(true);
  setError(null);
  
  const response = await fetch("/api/solution-steps", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ problem, triedActions }),
  });
  
  if (!response.ok) {
    const errorData = await response.json();
    throw new Error(errorData.error || "エラーが発生しました");
  }
  
  const data = await response.json();
  setSolutionPlan(data.plan);
} catch (err) {
  setError(err.message);
} finally {
  setLoading(false);
}
  • try-catch-finally:tryブロックでエラーが発生する可能性のある処理を囲み、catchブロックでエラーを処理します。finallyブロックは、成功・失敗に関わらず必ず実行されます。
  • ローディング状態: API呼び出し中はloadingtrueに設定し、ユーザーに処理中であることを伝えます。