# 画面設計書 94-コメント投稿フォーム

## 概要

本ドキュメントは、Ghost公開画面における「コメント投稿フォーム」（MainForm）の画面設計書です。投稿記事に対して新規コメントを投稿するためのリッチテキストエディタを含むフォームコンポーネントです。

### 本画面の処理概要

コメント投稿フォームは、ログイン済み会員が投稿記事に対して新しいコメントを投稿するためのメインフォームです。

**業務上の目的・背景**：読者が投稿記事に対する感想・意見を共有し、サイトコミュニティへの参加を促進します。Tiptapベースのリッチテキストエディタを採用し、直感的なコメント入力体験を提供します。

**画面へのアクセス方法**：
- コメント一覧（Content）コンポーネント内に自動表示
- ログイン済み会員かつコメント可能な投稿の場合に表示
- キーボードショートカット「C」でフォーカス可能

**主要な操作・処理内容**：
1. リッチテキストエディタでのコメント入力
2. 「Add comment」/「Comment」ボタンでコメント送信
3. 名前・専門分野（Expertise）の編集（フォーカス時）
4. Cmd/Ctrl + Enterでコメント送信
5. ESCでエディタからフォーカスを外す

**画面遷移**：
- 画面内遷移: 詳細追加ポップアップ（名前未設定時）
- 送信後: コメント一覧に新規コメントが追加

**権限による表示制御**：
- ログイン済み会員のみに表示
- 名前未設定の会員は、フォーカス時に詳細追加ポップアップが表示される

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | 記事コメント | 主機能 | 新規コメントの投稿 |
| 72 | Comments UI | 補助機能 | コメントUIウィジェットの表示 |

## 画面種別

登録（フォーム）

## URL/ルーティング

- コメント一覧内に埋め込み（独自URLなし）

## 入出力項目

| 項目名 | 入出力 | データ型 | 必須 | 説明 |
|--------|--------|----------|------|------|
| html | 入力 | string | 必須 | コメント本文（HTML形式） |
| post_id | 入力 | string | 必須 | 対象投稿のID（コンテキストから取得） |
| status | 入力 | string | 必須 | コメントステータス（'published'固定） |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| member.avatar_image | string | 会員のアバター画像 |
| member.name | string | 会員の名前 |
| member.expertise | string | 会員の専門分野 |
| placeholder | string | エディタのプレースホルダー |
| submitText | ReactNode | 送信ボタンのテキスト |

## イベント仕様

### 1-コメント送信（submit）

「Add comment」ボタンクリックまたはCmd/Ctrl + Enterでコメントを送信します。

- 処理: `dispatchAction('addComment', {post_id, status: 'published', html})`
- 成功時: エディタをクリア、コメント一覧に新規コメント追加
- 失敗時: エラー状態をセット、エラーメッセージ表示

### 2-フォーカス時の名前入力要求（focusEditor）

エディタにフォーカスした際、名前が未設定の場合は詳細追加ポップアップを表示します。

- 条件: `!memberName` の場合
- 処理: `editName()` → 詳細追加ポップアップを開く

### 3-キーボードショートカット（C）

「C」キーでメインフォームにフォーカスします。

- 条件: エディタにフォーカスがない、他の入力要素にフォーカスがない
- 処理: `editor.commands.focus()` + `scrollToElement(formEl)`

### 4-名前・専門分野の編集

フォームヘッダーの名前または専門分野をクリックして編集します。

- 処理: `openEditDetails({expertiseAutofocus: true/false})`
- 結果: 詳細追加ポップアップが開く

### 5-ESCキーでフォーカス解除

エディタにフォーカス中にESCキーを押すとフォーカスを解除します。

- 処理: `editor.commands.blur()`

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| コメント送信 | comments | INSERT | 新規コメントの作成 |

### テーブル別更新項目詳細

#### comments

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | post_id | 対象投稿のID | コンテキストから取得 |
| INSERT | member_id | ログイン中の会員ID | セッションから取得 |
| INSERT | html | エディタから取得したHTML | Tiptapエディタの出力 |
| INSERT | status | 'published' | 固定値 |
| INSERT | created_at | 現在日時 | サーバー側で設定 |

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|---------|--------------|
| MSG001 | プレースホルダー | コメント0件時 | "Start the conversation" |
| MSG002 | プレースホルダー | コメントあり時 | "Join the discussion" |
| MSG003 | ボタン | PC表示 | "Add comment" |
| MSG004 | ボタン | モバイル表示 | "Comment" |
| MSG005 | ラベル | 専門分野未設定時 | "Add your expertise" |

## 例外処理

| 例外種別 | 発生条件 | 対応処理 |
|---------|---------|---------|
| 送信エラー | API呼び出し失敗 | progress: 'error' をセット |
| 名前未設定 | memberNameがnull/undefined | 詳細追加ポップアップを表示 |
| 空のコメント | editor.isEmpty が true | 送信ボタンを無効化 |

## 備考

- Tiptap（ProseMirror）ベースのリッチテキストエディタを使用
- レスポンシブ対応（PC/モバイルで送信ボタンテキストが異なる）
- アニメーション付きのフォーム展開/折りたたみ

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、コメントの追加に必要なデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | app-context.ts | `apps/comments-ui/src/app-context.ts` | AddComment型（L44-48） |

**読解のコツ**: AddComment型はpost_id, status, htmlの3つのフィールドを持つ。

#### Step 2: MainFormコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | main-form.tsx | `apps/comments-ui/src/components/content/forms/main-form.tsx` | メインフォームコンポーネント（1-111行目） |

**主要処理フロー**:
1. **12-13行目**: postId, dispatchActionをコンテキストから取得
2. **14-17行目**: editorConfigでプレースホルダーを設定
3. **19行目**: useEditorフックでTiptapエディタを取得
4. **21-30行目**: submit関数でaddCommentアクションを実行
5. **35-82行目**: キーボードショートカット（C）のリスナー設定
6. **84-92行目**: submitPropsの設定
7. **96-107行目**: JSXレンダリング

#### Step 3: 共通フォームコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | form.tsx | `apps/comments-ui/src/components/content/forms/form.tsx` | Form, FormWrapper, FormEditorコンポーネント（1-395行目） |

**主要処理フロー**:
- **27-159行目**: FormEditor - エディタUI、送信ボタン、キーボードショートカット
- **161-214行目**: FormHeader - 名前・専門分野表示
- **216-289行目**: Form - フォーム全体のラッパー
- **291-391行目**: FormWrapper - アバター、ヘッダー、フォーカス制御

#### Step 4: useEditorフックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | hooks.ts | `apps/comments-ui/src/utils/hooks.ts` | useEditorフック |

**主要処理フロー**:
- Tiptapエディタのインスタンス作成
- プレースホルダー、オートフォーカス等の設定

### プログラム呼び出し階層図

```
Content
    │
    └─ MainForm
           │
           ├─ useEditor() - Tiptapエディタ取得
           │
           ├─ submit()
           │      └─ dispatchAction('addComment', {...})
           │             └─ API: POST /comments
           │
           └─ FormWrapper
                  │
                  ├─ Avatar (member)
                  │
                  ├─ FormHeader
                  │      ├─ editName() → openEditDetails()
                  │      └─ editExpertise() → openEditDetails()
                  │
                  └─ Form
                         └─ FormEditor
                                ├─ EditorContent (Tiptap)
                                ├─ Cancel button (close用)
                                └─ Submit button
```

### データフロー図

```
[入力]                    [処理]                       [出力]

エディタ入力 ────────────▶ editor.getHTML() ────────▶ html
                           │
postId (context) ─────────│
                           │
status: 'published' ──────│
                           ▼
                        submit()
                           │
                           ▼
                   dispatchAction('addComment')
                           │
                           ▼
                   API: POST /comments ──────────▶ 新規コメント作成
                           │
                           ▼
                   editor.clearContent() ────────▶ エディタクリア
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| main-form.tsx | `apps/comments-ui/src/components/content/forms/main-form.tsx` | ソース | メインフォームコンポーネント |
| form.tsx | `apps/comments-ui/src/components/content/forms/form.tsx` | ソース | 共通フォームコンポーネント |
| app-context.ts | `apps/comments-ui/src/app-context.ts` | ソース | コンテキスト定義、型定義 |
| hooks.ts | `apps/comments-ui/src/utils/hooks.ts` | ソース | useEditorフック |
| helpers.ts | `apps/comments-ui/src/utils/helpers.ts` | ソース | scrollToElement等のユーティリティ |
| avatar.tsx | `apps/comments-ui/src/components/avatar.tsx` | ソース | アバターコンポーネント |
| actions.ts | `apps/comments-ui/src/actions.ts` | ソース | addCommentアクション |
