WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Conversation

@ryufuta
Copy link
Contributor

@ryufuta ryufuta commented Dec 2, 2025

Issue

概要

相談部屋の「対応済にするボタン」とお問い合わせ個別ページの「対応済にするボタン」は全く同じ機能・見た目だが、前者はViewComponentとバニラJSで、後者はReactで実装されている。
後者を非React化する上記のIssueに取り組むにあたって、本PRでは前者を再利用可能にした。
後者の非React化は本PRのマージ後に別のPRで、お問い合わせ機能の設計変更とともに実施する。

本PRの変更内容は以下。

  • ActionCompletedButtonComponentを再利用可能にした(このコミット
  • 二重クリックできないようにした
  • その他、リファクタリングや軽微な修正、テスト追加

#8535 (comment) にある通り、「対応済にするボタン」は今後、助成金コースの申請や研修の申し込みにも利用する予定のため、もともと再利用できるように実装する必要があった。

変更確認方法

  1. chore/make-action-completed-button-reusableをローカルに取り込む
  2. ローカルサーバーを起動する
  3. komagata(任意の管理者)でログインする
  4. 個別の相談部屋 (任意のページで良い)にアクセスして「対応済にするボタン」が正常に動作することを確認する

参考:#8488 (comment)

Screenshot

見た目の変更はありません。

Summary by CodeRabbit

リリースノート

  • 新機能

    • アクション完了ボタンが動的な更新先を使用できるようになりました。
  • 改善

    • ボタン押下時に読み込み状態を表示して重複クリックを防止します。
    • 成功時に詳細な説明を表示し、成功/失敗をトーストで通知します。
    • 更新処理が確実に結果を返すようHTTPレスポンス処理を改善(204応答対応)。
  • テスト

    • ボタン表示の単体テストを追加しました。

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 2, 2025

Walkthrough

ActionCompletedButtonComponentの初期化引数をcommentable_idからupdate_pathmodel_nameへ置換し、関連テンプレートとJavaScriptを更新。フロントからPATCHを送信しAPIは204を返すように変更。テストファイルを追加。

Changes

Cohort / File(s) 変更内容
コンポーネント初期化シグネチャ
app/components/action_completed_button_component.rb
initialize(is_initial_action_completed:, commentable_id:)initialize(is_initial_action_completed:, update_path:, model_name:)。公開attr_readerを削除し、is_action_completed, update_path, model_nameをプライベートに設定。
コンポーネントテンプレート
app/components/action_completed_button_component.html.slim
ボタンのデータ属性をdata-commentable-idからdata-update-pathdata-model-nameへ置換(完了・未完了ボタン両方)。ロジック変更なし。
ビュー呼び出し箇所
app/views/talks/show.html.slim
コンポーネント呼び出しの引数をcommentable_idupdate_path, model_nameへ更新。
フロントエンド挙動
app/javascript/action_completed_button.js
fetchベースのPATCHから@rails/request.jspatchへ移行。動的なリクエストボディ({ [modelName]: { action_completed } })、ロードガード(isLoading)、ボタン無効化、async/awaitとtry/catch、UI更新とトースト表示を追加。
APIコントローラー
app/controllers/api/talks_controller.rb
talk.update(talk_params)talk.update!(talk_params) に変更し、成功時は head :no_content(204) を返すように変更。
テスト追加
test/components/action_completed_button_component_test.rb
コンポーネントの表示確認テストを追加(完了・未完了の文言確認)。

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant JS as action_completed_button.js
    participant API as API::TalksController
    participant DB as Database

    Browser->>JS: ボタンクリック
    JS-->>JS: isLoading=true, ボタン無効化
    JS->>API: PATCH update_path (body: { modelName: { action_completed } })
    API->>DB: talk.update!(params) (validation & save)
    DB-->>API: 保存成功
    API-->>JS: 204 No Content
    JS-->>Browser: UI更新(ボタン文言・アイコン・説明、トースト)
    JS-->>JS: isLoading=false, ボタン有効化
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 分

  • コンポーネントAPI変更がビュー・テンプレート・JS・テストへ波及しているため横断的確認が必要
  • 注目ポイント:
    • update_path/model_nameの値が全呼び出しで正しく渡されているか
    • @rails/request.jspatch呼び出しとCSRF扱い(ヘッダ不要か)の確認
    • talk.update! による例外処理(APIの例外ハンドリング方針)および204応答の意図確認
    • テストが静的表示のみでAPIとの結合をカバーしていない点

Possibly related issues

Suggested reviewers

  • komagata
  • Miya096jp

Poem

🐰✨ ぴょんと押せば道が変わる
commentableはさよなら、updatePathが道しるべ
パッチを送りて204が返る
ボタンは光り、トーストが歌うよ
うさぎの耳で祝杯をあげるんだ 🥕🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed タイトルは変更セットの主要な意図を明確に表現しており、ActionCompletedButtonComponentを再利用可能にするという中心的な変更を正確に要約しています。
Description check ✅ Passed PRの説明はテンプレートの必須セクションである「Issue」「概要」「変更確認方法」が充実して記載されており、詳細な背景・目的・動作確認手順が明記されている。
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/make-action-completed-button-reusable

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ryufuta ryufuta self-assigned this Dec 2, 2025
@ryufuta ryufuta marked this pull request as ready for review December 2, 2025 07:22
@github-actions github-actions bot requested a review from komagata December 2, 2025 07:22
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/javascript/action_completed_button.js (1)

4-17: 将来の複数ボタン対応を見越すなら querySelectorAll + ボタン単位の状態にしておくと安心です

現状 .check-button は1つだけ存在する前提で querySelector とモジュールスコープの isLoading を使っており、このPRの要件は満たしています。ただ、同一ページに複数の「対応済にするボタン」を置きたくなった場合、先頭の1つしか動かない形になるので、再利用性を少し上げるならボタンごとにハンドラと isLoading を持たせる実装にしておくと拡張が楽だと思います。

例えば次のような形です:

-document.addEventListener('DOMContentLoaded', () => {
-  const button = document.querySelector('.check-button')
-  if (!button) {
-    return
-  }
-
-  let isLoading = false;
-  const updatePath = button.dataset.updatePath
-  const modelName = button.dataset.modelName
-  button.addEventListener('click', async () => {
-    if (isLoading) return
-
-    isLoading = true
-    button.disabled = true
+document.addEventListener('DOMContentLoaded', () => {
+  const buttons = document.querySelectorAll('.check-button')
+  if (!buttons.length) {
+    return
+  }
+
+  buttons.forEach((button) => {
+    let isLoading = false
+    const updatePath = button.dataset.updatePath
+    const modelName = button.dataset.modelName
+
+    button.addEventListener('click', async () => {
+      if (isLoading) return
+
+      isLoading = true
+      button.disabled = true
@@ 以降のクリックハンドラ内部は現行ロジックのまま
-    isLoading = false
-    button.disabled = false
-  })
+      isLoading = false
+      button.disabled = false
+    })
+  })
 })

現状の要件では必須ではないので、余裕があるタイミングでの対応で十分だと思います。

Also applies to: 22-27, 54-56

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35763c8 and 3484fba.

📒 Files selected for processing (6)
  • app/components/action_completed_button_component.html.slim (2 hunks)
  • app/components/action_completed_button_component.rb (1 hunks)
  • app/controllers/api/talks_controller.rb (1 hunks)
  • app/javascript/action_completed_button.js (2 hunks)
  • app/views/talks/show.html.slim (1 hunks)
  • test/components/action_completed_button_component_test.rb (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
app/**/*.{rb,js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Rails app code should be organized in app/ directory with subdirectories: models/, controllers/, views/, jobs/, helpers/, and frontend code under javascript/ (Shakapacker)

Files:

  • app/components/action_completed_button_component.rb
  • app/controllers/api/talks_controller.rb
  • app/javascript/action_completed_button.js
**/*.rb

📄 CodeRabbit inference engine (AGENTS.md)

Ruby code should use 2-space indentation, snake_case for method names, and CamelCase for class names, enforced by RuboCop (.rubocop.yml)

Files:

  • app/components/action_completed_button_component.rb
  • app/controllers/api/talks_controller.rb
  • test/components/action_completed_button_component_test.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • app/components/action_completed_button_component.rb
  • app/controllers/api/talks_controller.rb
  • test/components/action_completed_button_component_test.rb
**/*.slim

📄 CodeRabbit inference engine (AGENTS.md)

Slim templates should be linted according to config/slim_lint.yml

Files:

  • app/components/action_completed_button_component.html.slim
  • app/views/talks/show.html.slim
app/javascript/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

JavaScript/TypeScript code in app/javascript/ should be linted with ESLint and formatted with Prettier via yarn lint scripts; use React 17 and Shakapacker/Webpack 5

Files:

  • app/javascript/action_completed_button.js
**/*.js

⚙️ CodeRabbit configuration file

**/*.js: - どうしても避けられない時以外に新規にVue.js, Reactのコードを追加しない。

  • fetchメソッドの代わりにrequest.jsを使う。

Files:

  • app/javascript/action_completed_button.js
test/**/*_test.rb

📄 CodeRabbit inference engine (AGENTS.md)

test/**/*_test.rb: Test suite should use Minitest with structure: system/, models/, controllers/, and fixtures in test/fixtures/; test files should be named *_test.rb
Use Minitest + Capybara for system tests; place unit and integration tests under matching test/* directories

Files:

  • test/components/action_completed_button_component_test.rb
test/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep tests deterministic and use fixtures stored in test/fixtures/ for test data

Files:

  • test/components/action_completed_button_component_test.rb
test/**/*

⚙️ CodeRabbit configuration file

test/**/*: # Test

  • TestCase名は英語で書く。
  • どうしても避けられない時以外にsystem testでsleepは使わない。

Unit Test

model, helper, decorator, view_componentについてはメソッドを追加した場合は必ず対応したUnit TestのTestCaseを1つは書く。

Files:

  • test/components/action_completed_button_component_test.rb
🧠 Learnings (3)
📓 Common learnings
Learnt from: tyrrell-IH
Repo: fjordllc/bootcamp PR: 9306
File: app/javascript/components/Bookmarks.jsx:248-265
Timestamp: 2025-11-17T00:46:30.794Z
Learning: fjordllc/bootcamp プロジェクトでは、Reactからバニラ JavaScript への移行作業が進行中である。そのため、新しいReactファイルの作成は避け、既存のReactコンポーネント内で完結する実装が推奨される。BookmarksInDashboard.jsx は issue #9045 で削除予定。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:131-135
Timestamp: 2025-09-12T21:18:00.834Z
Learning: Rails アップグレードPRにおいて、product_review_not_completed メソッドの webhook URL 設定の shared フォールバック追加も、設定システム全体の変更として別PRで対応すべきである。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/models/graduation_notifier.rb:23-28
Timestamp: 2025-09-12T21:16:47.639Z
Learning: Rails upgrade PRにおいて、configuration systemの変更やwebhook設定の改善提案も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。
Learnt from: sjabcdefin
Repo: fjordllc/bootcamp PR: 9090
File: app/javascript/user-sns.js:2-25
Timestamp: 2025-08-25T08:00:11.369Z
Learning: app/javascript/user-sns.js において、アクセシビリティ改善(aria-label属性の追加など)は他の箇所との整合性を保つため、別途統一して対応する方針である
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。
Learnt from: ryufuta
Repo: fjordllc/bootcamp PR: 9155
File: app/controllers/api/correct_answers_controller.rb:20-22
Timestamp: 2025-09-15T07:39:39.350Z
Learning: API::CorrectAnswersController#update では、ベストアンサー取り下げという単純な状態変更のため、update! を使用して DB エラー時に 500 を返し、成功時はデフォルトレスポンスを使用するのが適切。クライアント側のバリデーションエラーは発生しにくく、明示的なエラーハンドリングは不要。
📚 Learning: 2025-09-15T07:39:39.350Z
Learnt from: ryufuta
Repo: fjordllc/bootcamp PR: 9155
File: app/controllers/api/correct_answers_controller.rb:20-22
Timestamp: 2025-09-15T07:39:39.350Z
Learning: API::CorrectAnswersController#update では、ベストアンサー取り下げという単純な状態変更のため、update! を使用して DB エラー時に 500 を返し、成功時はデフォルトレスポンスを使用するのが適切。クライアント側のバリデーションエラーは発生しにくく、明示的なエラーハンドリングは不要。

Applied to files:

  • app/controllers/api/talks_controller.rb
📚 Learning: 2025-09-04T01:39:22.261Z
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。

Applied to files:

  • app/javascript/action_completed_button.js
🔇 Additional comments (5)
app/components/action_completed_button_component.html.slim (1)

7-8: data属性の追加でコンポーネントの再利用性がきれいに担保されています

data-update-path / data-model-name が両方のボタンに付与されており、JS側の dataset.updatePath / dataset.modelName とも整合しているので、他画面からも同じコンポーネントをそのまま使い回しやすい構造になっていて良いと思います。

Also applies to: 22-23

app/controllers/api/talks_controller.rb (1)

10-11: 状態更新APIとして update!204 No Content の構成は妥当です

単純な action_completed のトグルであれば update! にしてDBエラー時のみ500に任せる実装としつつ、成功時は head :no_content でボディなし204を返す構成で問題ないと思います。API::CorrectAnswersController の方針とも揃っており、一貫した設計になっていて良さそうです。Based on learnings, ...

test/components/action_completed_button_component_test.rb (1)

1-18: 完了/未完了の両状態をカバーするViewComponentテストが追加されていて良いです

is_initial_action_completed の true/false それぞれで期待テキストを検証しており、同時に update_path / model_name をコンストラクタ引数に含めることで、新しいインターフェースが壊れていないことも担保できていて良いと思います。

app/views/talks/show.html.slim (1)

65-65: ActionCompletedButtonComponent 呼び出しのインターフェース更新が他レイヤーと整合しています

api_talk_path(@talk)model_name: 'talk' の組み合わせにより、JS側の { talk: { action_completed } } というリクエストボディと talk_params の strong parameters がちょうど一致しており、コンポーネントの再利用化に向けた最初の呼び出し箇所として問題ないと思います。

app/components/action_completed_button_component.rb (1)

4-12: コンストラクタの引数整理とprivateなreader化がシンプルで扱いやすいです

is_initial_action_completed / update_path / model_name を明示的なキーワード引数にしつつ、内部では @is_action_completed などにマッピングし、Viewからはprivateなreader経由でしか触れないようにしてあるのは責務がはっきりしていて良いと思います。HTMLテンプレート側の呼び出しとも齟齬がなく、インターフェース変更がきれいにまとまっています。

@ryufuta ryufuta force-pushed the chore/make-action-completed-button-reusable branch from 3484fba to 77163f5 Compare December 2, 2025 09:16
@komagata
Copy link
Member

komagata commented Dec 3, 2025

@ryufuta まずComponentを再利用可能にしてからやるのいいですね。そしてそれを別Issueとするのもとってもいいですね。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants