feat: セッションQA機能のフロントエンド実装 - 質問投稿、投票、回答、削除機能 (Vibe Kanban)#578
Open
feat: セッションQA機能のフロントエンド実装 - 質問投稿、投票、回答、削除機能 (Vibe Kanban)#578
Conversation
- SessionQAコンポーネントとその内部コンポーネントを再作成 - QuestionForm, QuestionList, QuestionItem, VoteButton, SpeakerAnswerForm, AnswerItem - RTK QueryでAPIと通信 - ActionCableでリアルタイム更新 - Track.tsxでSessionQAを使用するように修正 - Apollo Clientのエラーハンドリングを追加(errorPolicy: 'ignore') - GraphQL/ネットワークエラーが発生してもアプリを停止しない - useViewerCountにもエラーハンドリングを追加
gitops-for-cloudnativedays bot
added a commit
to cloudnativedaysjp/dreamkast-infra
that referenced
this pull request
Jan 2, 2026
gitops-for-cloudnativedays bot
added a commit
to cloudnativedaysjp/dreamkast-infra
that referenced
this pull request
Jan 2, 2026
commit: cloudnativedaysjp/dreamkast-ui@d5a298a action URL: https://github.com/cloudnativedaysjp/dreamkast-ui/actions/runs/20652106776 Co-authored-by: gitops-for-cloudnativedays[bot] <113280573+gitops-for-cloudnativedays[bot]@users.noreply.github.com>
…QA and related components - Simplified import statement for actionCable in SessionQA component - Streamlined sorting logic for questions by creation date - Enhanced button formatting in QuestionItem component - Consolidated props destructuring in SpeakerAnswerForm component - Improved error logging format in Apollo Client error handling
gitops-for-cloudnativedays bot
added a commit
to cloudnativedaysjp/dreamkast-infra
that referenced
this pull request
Jan 2, 2026
gitops-for-cloudnativedays bot
added a commit
to cloudnativedaysjp/dreamkast-infra
that referenced
this pull request
Jan 2, 2026
commit: cloudnativedaysjp/dreamkast-ui@1a85b4d action URL: https://github.com/cloudnativedaysjp/dreamkast-ui/actions/runs/20652356905 Co-authored-by: gitops-for-cloudnativedays[bot] <113280573+gitops-for-cloudnativedays[bot]@users.noreply.github.com>
Contributor
Author
|
@claude このPRをレビューして |
## 修正内容
### バックエンド(Rails)
1. **`SessionQuestion`モデル**: `length: { maximum: 512 }`バリデーションを削除
2. **`SessionQuestionAnswer`モデル**: `length: { maximum: 512 }`バリデーションを削除
3. **ビュー**: すべてのフォームから`maxlength: 512`属性を削除
- `talks/partial_show/_qa_section.html.erb`
- `speaker_dashboards/_question_item.html.erb`
- `speaker_dashboards/create_answer.turbo_stream.erb`
- `speaker_dashboard/speakers/_talk.html.erb`
4. **プレースホルダーと説明文**: 「最大512文字」の記述を削除
### フロントエンド(React)
1. **`QuestionForm.tsx`**:
- `maxLength`定数を削除
- `maxLength`バリデーションを削除
- プレースホルダーから「最大512文字」を削除
- 文字数カウント表示を「{bodyLength}/{maxLength}」から「{bodyLength}文字」に変更
2. **`SpeakerAnswerForm.tsx`**:
- `maxLength`定数を削除
- `maxLength`バリデーションを削除
- プレースホルダーから「最大512文字」を削除
- 文字数カウント表示を「{bodyLength}/{maxLength}」から「{bodyLength}文字」に変更
これで、質問と回答に文字数制限はなくなり、長い質問・回答にも対応できます。データベースの`text`型は十分な容量があるため、問題ありません。
## 実装したテスト ### バックエンド(Rails/RSpec) 1. **Factory(3つ)** - `session_question` - `session_question_answer` - `session_question_vote` 2. **モデルテスト(3つ)** - `session_question_spec.rb`: バリデーション、スコープ、メソッド - `session_question_answer_spec.rb`: バリデーション、スコープ - `session_question_vote_spec.rb`: バリデーション、コールバック、エラーハンドリング 3. **コントローラーテスト(5つ)** - `api/v1/session_questions_controller_spec.rb`: index, create, vote - `api/v1/session_question_answers_controller_spec.rb`: index, create - `talks/create_question_spec.rb`: create_question, show - `speaker_dashboards/qa_spec.rb`: show, questions, create_answer, destroy_answer - `admin/session_questions_spec.rb`: index, show, toggle_hidden 4. **チャンネルテスト(1つ)** - `channels/qa_channel_spec.rb`: subscribedメソッドのセキュリティチェック ### フロントエンド(React/Jest) 1. **コンポーネントテスト(3つ)** - `SessionQA.spec.tsx`: 基本表示、WebSocket接続 - `QuestionForm.spec.tsx`: フォーム送信、バリデーション - `SpeakerAnswerForm.spec.tsx`: フォーム送信、キャンセル ## テストのカバレッジ - モデルのバリデーションとスコープ - APIコントローラーの認証・認可 - 質問の作成・取得・投票 - 回答の作成・削除 - 非表示機能 - ActionCableブロードキャスト - Turbo Streams - XSS対策(プロフィール名の表示) テストを実行して動作を確認してください。必要に応じて追加・修正します。
## 変更内容 ### バックエンド(Rails) 1. **管理画面の質問一覧** (`admin/session_questions/index.html.erb`) - 「質問者」列を削除 - テーブルのcolspanを8から7に更新 2. **管理画面の質問詳細** (`admin/session_questions/show.html.erb`) - 「質問者:」行を削除 - 投票者一覧から名前を削除(タイムスタンプのみ表示) 3. **スピーカーダッシュボードの質問一覧** (`speaker_dashboards/_question_item.html.erb`) - 「質問者:」行を削除 4. **セッションページのQAセクション** (`talks/partial_show/_qa_section.html.erb`) - 「質問者:」行を削除 5. **スピーカーダッシュボードのセッション詳細** (`speaker_dashboard/speakers/_talk.html.erb`) - 「質問者:」行を削除 ### フロントエンド(React) 6. **QuestionItemコンポーネント** (`SessionQA/internal/QuestionList/QuestionItem/QuestionItem.tsx`) - `question.profile.name`の表示を削除 ### テスト 7. **ビューのテスト** (`spec/requests/talks/create_question_spec.rb`) - プロフィール名の表示を確認するテストを削除 すべてのビューから質問者名(PublicName含む)の表示を削除しました。APIは引き続きプロフィール名を返しますが、UIには表示しません。
## 変更内容 ### バックエンド(Rails API) 1. **`Api::V1::SessionQuestionsController#question_json`** - `profile` オブジェクト(`id` と `name`)をレスポンスから削除 - 認証やデータ保存に必要な `@profile` は残しています ### フロントエンド(TypeScript) 2. **`dreamkast-ui/src/types/session-qa.ts`** - `SessionQuestion` 型から `profile` フィールドを削除 ### テスト 3. **`spec/requests/api/v1/session_questions_controller_spec.rb`** - プロフィール名をチェックするテストを削除 - 作成時のテストで `profile` キーが存在しないことを確認するアサーションに変更 ActionCableのブロードキャストでも `question_json` を使用しているため、自動的に更新されています。APIレスポンスから質問者名は返されません。
変更内容: - `SessionQA.tsx`の`autoScroll`の初期値を`true`から`false`に変更 これで質問投稿後も現在のスクロール位置が維持されます。
## 修正内容 1. **`handleQuestionSubmit`から`refetchQuestions()`を削除** - WebSocketで質問が追加されるため、`refetchQuestions()`は不要 - これにより、WebSocketの更新と`refetchQuestions()`の結果が競合して一時的にソートが崩れる問題を解消 2. **投票更新時にソートを再適用** - `question_voted`メッセージ受信時に、投票数を更新した後、`sortQuestions`を適用してソート順を維持 これにより、時間順でソートしている場合でも、質問投稿時に並び順が崩れなくなります。WebSocketでリアルタイムに質問が追加され、正しいソート順が維持されます。
## 修正内容 1. **`useEffect`で`questionsData`を更新する際のマージ処理を改善** - APIから取得した質問をベースにし、WebSocketで追加されたがまだAPIに反映されていない質問を保持 - ソート後の順序が変わっていない場合は既存の状態を返し、不要な再レンダリングを防止 2. **重複チェックと最適化** - APIの質問IDセットを作成し、WebSocketのみの質問を識別 - ソート後の配列が同じ場合は既存の状態を返し、一瞬の並び順の乱れを防止 これにより、質問投稿時にWebSocketで追加された質問が一時的に失われたり、並び順が崩れたりする問題を解消します。時間順でソートしている場合でも、正しい順序が維持されます。
## 修正内容
1. **`actioncable`のトップレベルインポートを削除**
- `import * as actionCable from 'actioncable'`を削除
2. **`useEffect`内で動的インポートに変更**
- `typeof window === 'undefined'`でサーバーサイドをチェック
- `import('actioncable')`でクライアントサイドでのみ動的インポート
- クリーンアップ関数を正しく設定
これにより、Next.jsのサーバーサイドレンダリング時に`window is not defined`エラーが発生しなくなります。`actioncable`はクライアントサイドでのみ実行されます。
## 修正内容 1. **`createSessionQuestion`の`invalidatesTags`を削除** - 質問投稿時にAPI再取得が発生しないように変更 - WebSocket更新の���で処理 2. **`useEffect`の更新ロジックを簡素化** - 初回ロード時のみ`questionsData`を使用 - その後はWebSocket更新のみで処理 - ソート変更時は既存の質問を再ソート 3. **WebSocket更新を優先** - `handleWebSocketMessage`で質問を追加し、即座にソート - `useEffect`との競合を回避 これにより、質問投稿時にWebSocketで追加された質問が即座に正しい位置に表示され、一時的な並び順の崩れを防止します。時間順でソートしている場合でも、新しい質問が最上部に正しく表示されます。
…esc`(新しい順)です。 ## 修正内容 1. **ソート関数の可読性を向上** - `created_at`のパースを明示的に変数に分離 - コメントを追加して意図を明確化 2. **時間順のソートロジック** - 新しい順(降順): `timeB - timeA` - バックエンドの`order_by_time`(`created_at: :desc`)と一致 現在の実装では、時間順を選択すると新しい質問が上に表示されます。もし「古い順」を期待している場合は、ソートロジックを変更できます。どちらにしますか?
## 変更内容 - `sortBy`の初期値を`'votes'`から`'time'`に変更 - デフォルトで時間順(新しい順)で表示されます これで、QA画面を開いた際に、デフォルトで時間順(新しい質問が上)で表示されます。
## 修正内容 1. **`sortBy`をrefで管理** - `sortByRef`を追加し、`sortBy`の変更時にrefも更新 - `setQuestions`のコールバック内で最新の`sortBy`を参照可能に 2. **`handleWebSocketMessage`の修正** - `sortByRef.current`を使用して最新の`sortBy`を参照 - 依存配列から`sortBy`を削除し、`sortQuestions`のみに これにより、投票数順を選択している時に質問を投稿しても、正しく投票数順でソートされます。WebSocketで追加された質問も、現在選択されているソート順(投票数順または時間順)に従って表示されます。
## 変更内容 - ソートボタンの順序を変更 - 左: 時間順 - 右: 投票数順 これで、時間順が左、投票数順が右に表示されます。
## 変更内容
1. **swagger.ymlにSessionQuestionのAPIエンドポイントを追加**
- GET `/api/v1/talks/{talkId}/session_questions` - 質問一覧取得
- POST `/api/v1/talks/{talkId}/session_questions` - 質問作成
- POST `/api/v1/talks/{talkId}/session_questions/{id}/vote` - 投票
- GET `/api/v1/talks/{talkId}/session_questions/{sessionQuestionId}/session_question_answers` - 回答一覧取得
- POST `/api/v1/talks/{talkId}/session_questions/{sessionQuestionId}/session_question_answers` - 回答作成
2. **スキーマ定義を追加**
- `SessionQuestion`, `SessionQuestionAnswer`, `SessionQuestionsResponse`, `SessionQuestionCreateRequest`, `SessionQuestionVoteResponse`, `SessionQuestionAnswersResponse`, `SessionQuestionAnswerCreateRequest`, `SpeakerInfo`
3. **コード生成を実行**
- `yarn rtk-query-codegen`を実行してAPIフックを生成
4. **SessionQAコンポーネントを更新**
- 手動で定義したエンドポイントを削除
- 生成されたAPIフック(`useGetApiV1TalksByTalkIdSessionQuestionsQuery`, `usePostApiV1TalksByTalkIdSessionQuestionsMutation`など)を使用
- 型定義を生成された型に置き換え
- `enhanceEndpoints`で`invalidatesTags`を削除し、WebSocket更新を優先
これで、DreamkastとDreamkast-UI間のAPIはswagger.ymlと`yarn rtk-query-codegen`で管理されるようになりました。
…`を使っていたため、`id`に変更しました。 これで投票が正常に動作するはずです。
## 実装内容 ### バックエンド(Rails) 1. **`Api::V1::SessionQuestionsController#destroy`を追加** - 自分の質問のみ削除可能(`profile_id`でチェック) - 削除時にActionCableでブロードキャスト 2. **`TalksController#destroy_question`を追加** - Rails側のビューから削除可能 3. **ルートを追加** - API: `DELETE /api/v1/talks/:talk_id/session_questions/:id` - Rails: `DELETE /:event/talks/:id/destroy_question` 4. **APIレスポンスに`profile_id`を追加** - フロントエンドで自分の質問かどうかを判定可能に ### フロントエンド(React) 5. **削除機能を追加** - `QuestionItem`に削除ボタンを追加(自分の質問のみ表示) - WebSocketで`question_deleted`メッセージを受信して質問を削除 ### Rails側のビュー 6. **削除ボタンを追加** - `talks/partial_show/_qa_section.html.erb`に削除ボタンを追加(自分の質問のみ表示) ### Swagger/OpenAPI 7. **swagger.ymlに削除エンドポイントを追加** - コード生成を実行してAPIフックを生成 これで、UIとDKの両方から、自分が投稿した質問を削除できます。
## 変更内容 1. **3点メニューボタンを追加** - Voteアイコンの右に「⋯」ボタンを配置(自分の質問の場合のみ表示) 2. **ドロップダウンメニューを実装** - メニューボタンをクリックするとメニューが開く - メニューから「削除」を選択可能 - メニュー外をクリックすると閉じる 3. **スタイルを追加** - `MenuButton`: 3点メニューボタン - `MenuOverlay`: メニュー外クリック検出用のオーバーレイ - `Menu`: ドロップダウンメニューコンテナ - `MenuItem`: メニュー項目(削除) これで、Voteアイコンの右に3点メニューが表示され、そこから削除できます。
gitops-for-cloudnativedays bot
added a commit
to cloudnativedaysjp/dreamkast-infra
that referenced
this pull request
Jan 3, 2026
gitops-for-cloudnativedays bot
added a commit
to cloudnativedaysjp/dreamkast-infra
that referenced
this pull request
Jan 3, 2026
commit: cloudnativedaysjp/dreamkast-ui@c69d9c7 action URL: https://github.com/cloudnativedaysjp/dreamkast-ui/actions/runs/20670897324 Co-authored-by: gitops-for-cloudnativedays[bot] <113280573+gitops-for-cloudnativedays[bot]@users.noreply.github.com>
## 修正内容 1. **未使用のインポートを削除** - `SessionQuestionAnswer`を削除(使用されていない) - `dayjs`と`setupDayjs`を削除(使用されていない) 2. **関数の定義順序を修正** - `sortQuestions`を`useEffect`の前に移動 - `handleWebSocketMessage`を`useEffect`の前に移動 3. **型エラーを修正** - `profile.speakerId`の参照を修正(Profile型に`speakerId`が含まれていないため、暫定的に`false`を返すように変更) 4. **未使用の変数を修正** - `event`パラメータをオプショナルに変更 - `setAutoScroll`を削除(`autoScroll`のみ使用) 5. **テストのモックデータを更新** - `profile`オブジェクトを`profile_id`に変更(最新のAPIレスポンス形式に合わせる) すべてのテストが成功しています。
gitops-for-cloudnativedays bot
added a commit
to cloudnativedaysjp/dreamkast-infra
that referenced
this pull request
Jan 3, 2026
gitops-for-cloudnativedays bot
added a commit
to cloudnativedaysjp/dreamkast-infra
that referenced
this pull request
Jan 3, 2026
commit: cloudnativedaysjp/dreamkast-ui@0bb2ead action URL: https://github.com/cloudnativedaysjp/dreamkast-ui/actions/runs/20670974992 Co-authored-by: gitops-for-cloudnativedays[bot] <113280573+gitops-for-cloudnativedays[bot]@users.noreply.github.com>
Contributor
|
@claude このPRをレビューして |
1 similar comment
Contributor
|
@claude このPRをレビューして |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
セッション配信画面のチャット機能をQA機能に置き換えるため、フロントエンド側の実装を行いました。イベント参加者がセッションごとに質問を投稿し、スピーカーが回答できる機能を提供します。また、自分の質問を削除できる機能も追加しました。
主な変更内容
新規コンポーネント
SessionQA コンポーネント
内部コンポーネント
技術実装
Swagger/OpenAPI統合
swagger.ymlにAPIエンドポイントを定義yarn rtk-query-codegenでAPIフックを自動生成ActionCable統合
QaChannelへの接続(クライアントサイドでのみ動的インポート)question_deletedメッセージの処理質問削除機能
変更されたファイル
src/components/Track/Track.tsx: ChatコンポーネントをSessionQAに置き換えsrc/components/SessionQA/SessionQA.tsx: 生成されたAPIフックを使用、削除機能を追加src/components/SessionQA/internal/QuestionList/QuestionItem/QuestionItem.tsx: 3点メニューを追加src/types/session-qa.ts: 型定義を生成されたAPIからインポートschemas/swagger.yml: SessionQuestion APIエンドポイントを追加機能詳細
参加者向け機能
スピーカー向け機能
実装のポイント
swagger.ymlからyarn rtk-query-codegenでAPIフックを自動生成し、型安全性を確保actioncableを動的インポートしてサーバーサイドエラーを回避技術スタック
@rtk-query/codegen-openapi)This PR was written using Vibe Kanban