Skip to content

Conversation

@sharoa119
Copy link
Contributor

@sharoa119 sharoa119 commented Sep 17, 2025

Issue

概要

UnconfirmedLink.jsxコンポーネントをReactからバニラJavaScriptに移行する。(非React化)

実装内容

  • React コンポーネントを削除し、バニラ JS で同等の機能を実装
  • 未確認リンクの一括開封ボタンの動作をバニラ JS で再現
  • 既存 CSS クラスとの整合性を維持

挙動について

一括で開封した直後は「一括で開く」ボタンは画面上に残ります。
ただし「全て」タブを再選択すると非表示になります。
これは、React 版で行っていたような即時非表示の挙動を再現するには状態管理や非同期処理が必要となるため、今回の移行では対応していません。
今回の issue の趣旨(状態管理を使わず、バニラ JS でシンプルに移行する)に沿った実装です。

確認事項

  • 警告メッセージの表示確認済み
  • 既存機能(未確認リンクの一括開封)が正常に動作することを確認
  • React ファイルを削除済み

変更確認方法

  1. chore/replace_vanilla_js_in_unconfirmed_link_jsxをローカルに取り込む
git fetch origin chore/replace_vanilla_js_in_unconfirmed_link_jsx
git checkout chore/replace_vanilla_js_in_unconfirmed_link_jsx
  1. foreman start -f Procfile.devでローカル環境を立ち上げる
  2. komagataのアカウントでログイン。パスワードはtesttest
  3. 以下のそれぞれのページにアクセス:
  • 通知:未読 /notifications?status=unread→ 表示されるボタン: 未読の通知を一括で開く
  • 提出物:全て /products → 表示されるボタン: 全ての提出物を一括で開く
    • 提出物:未完了(全て) /products/unchecked → 表示されるボタン: 未完了の提出物を一括で開く
    • 提出物:未完了(未返信)/products/unchecked?target=unchecked_no_replied → 表示されるボタン: 未返信の提出物を一括で開く
    • 提出物:未アサイン /unassigned → 表示されるボタン: 未アサインの提出物を一括で開く
    • 提出物:自分の担当(全て) /products/self_assigned → 表示されるボタン: 自分の担当の提出物を一括で開く
    • 提出物:自分の担当(未返信) /products/self_assigned?target=self_assigned_no_replied → 表示されるボタン: 未返信の担当の提出物を一括で開く
  1. 該当項目がある場合、画面下部に「◯◯を一括で開く」ボタンが表示される。
  2. 「一括で開く」ボタンを押す。
    各リンクが新しいタブで開けばOK。
    もし1件しか開かない場合は、Chromeのポップアップブロックが原因。(発生した場合は、以下の動作確認時の注意を読んでください。)
スクリーンショット 2025-10-03 10 48 30
2025-10-03.11.10.09.mov

※自分が担当の提出物において、最初は何もありませんが、
未完了のところからどれかを選んで「担当する」ボタンを押していただくと、
自分の担当タブの中に表示されるので、そちらでボタンが表示されるかなどのご確認をお願いいたします。

動作確認時の注意

表示されている「一括で開く」ボタンを押した際、先頭の1件しか開かない場合があります。
これはChromeのポップアップブロック機能(window.openを利用したタブの同時オープンも対象)が原因です。

動作確認を行う場合は、以下いずれかでポップアップを許可してください。

  • Chrome の設定で「ポップアップとリダイレクト」を許可する
  • ボタン押下後、アドレスバーの通知から「常にこのサイトのポップアップを許可」を選択する

Screenshot

内部変更で画面の変更がないので、スクリーンショットはありません。

テスト方針について

前提

通知: 開封すると既読処理が入るため、通常のテストで補える
日報・提出物: 開封しても未チェックのままなので、JS でリンクが正しく開かれることを保証する必要がある

テスト方針

今回の変更で使用しているバニラ JavaScript は、
「ボタンをクリックすると未確認リンクをwindow.openで一括開封する」処理です。

しかし、window.openの挙動をシステムテストで直接検証するのは難しいため、
以下の 3 つの観点で間接的にテストを行っています。

1. モデル単体テスト

  • 「未チェック」「未返信」などのスコープが正しく動作するかを確認
  • バックエンド側で正しい対象データが返ることを保証

2. システムテスト(ボタン表示)

  • 未チェックの対象がある場合に一括開封ボタンが表示されること
  • 対象がない場合はボタンが非表示になることが多いですが、タブによっては対象がなくてもボタンが表示される仕様のものがあります。

※本テストでは、移行後のコードが元々の仕様に沿って動作することを確認しています。

3. システムテスト(リンク存在)

  • 実際に .js-unconfirmed-linkが期待通りレンダリングされているかを確認
  • これにより「ボタンを押したときに開かれる対象」が正しいことを保証しています

※従来のテストを活かしつつ、内容を「.js-unconfirmed-linkが正しくレンダリングされていることを確認する」形式に修正しています。


補足: window.open の挙動について

本テストでは、ボタンが正しく表示されクリック可能であることまでを確認しています。
ただし、ボタン押下時に新しいタブが開くかどうか(window.open の挙動)はMinitest(Rails の System Test)では直接検証できない仕様です。
この動作はフロントエンド側のJavaScriptに依存しており、手動またはブラウザ上での確認により保証しています。
👉 System Test では最低限、ユーザーがボタンを認識して操作できることを確認することが目的です。


補足: webpacker のエラーについて

テスト実行時にpackが最新でない場合、JavaScriptCSSが正しく読み込まれずエラーになることがあります。
その場合は以下を実行してください。

  RAILS_ENV=test bin/rails webpacker:compile

👉 この 3 点で、直接 JS をテストしなくても
「対象データが正しくレンダリングされ、必要な場面でボタンが表示される」ことを確認しています。

Summary by CodeRabbit

リリースノート

  • 新機能

    • 通知一覧表示機能を追加しました。ステータスやターゲット別のフィルタリング、ページネーション対応です
  • 改善

    • 未チェック提出物・日報の一括開く機能を改善しました
    • ユーザーインターフェースを最適化し、複数アイテムへのアクセス利便性を向上させました

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

@coderabbitai
Copy link

coderabbitai bot commented Sep 17, 2025

Walkthrough

複数の提出物・日報ビューで一括オープン機能を統一するため、ターゲットマッピング機構を導入。React の UnconfirmedLink コンポーネントを削除し、vanilla JavaScript 実装に置き換え。ヘルパーメソッドでターゲット値に応じた日本語ラベルを提供し、パーシャルで条件付きレンダリング実装。

Changes

Cohort / File(s) Summary
コントローラー層 - ターゲット設定
app/controllers/products_controller.rb, app/controllers/products/unchecked_controller.rb, app/controllers/products/unassigned_controller.rb
@target 属性を各コントローラーに追加。ProductsController に before_action :set_target を導入し、未チェック・未アサイン各ビューで対応するターゲット値を設定。
コントローラー層 - その他
app/controllers/products/self_assigned_controller.rb, app/controllers/users/reports_controller.rb
self_assigned_controller に形式的な改行追加。reports_controller に set_unchecked_count before_action と私有メソッドを追加。
ヘルパー層
app/helpers/products_helper.rb
unconfirmed_links_label(target) ヘルパーメソッドを追加。各ターゲット値('all', 'unchecked', 'unassigned' など)を日本語ラベルにマッピング。
ビュー層 - レイアウト更新
app/views/products/index.html.slim, app/views/products/unchecked/index.html.slim, app/views/products/unassigned/index.html.slim, app/views/products/self_assigned/index.html.slim
スタッフタブ削除または unconfirmed_links_open パーシャルを条件付きレンダリング追加(@products.present? 時)。
ビュー層 - 日報関連
app/views/users/reports/index.html.slim, app/views/application/_unconfirmed_links_open.html.slim
reports ビューをコンテナーレイアウト内に配置し、メンター向け未チェック日報の一括オープン UI を追加。パーシャルの DOM 構造を form-actions コンテナーに更新(カード構造から変更)。
JavaScript フロントエンド
app/javascript/components/Products.jsx, app/javascript/components/Reports.jsx, app/javascript/components/Product.jsx, app/javascript/unconfirmed-links-open.js
UnconfirmedLink コンポーネントをインポート・利用から削除。Product コンポーネントに js-unconfirmed-link クラス追加。unconfirmed-links-open.js で window.open ターゲットを '_target' から '_blank' に変更、リンク open 後に 100ms 遅延ページリロード追加。
新規 JavaScript コンポーネント
app/javascript/components/Notifications.jsx
新規 Notifications コンポーネント追加。SWR + pagination(per = 20)で通知一覧を表示。status・target・page パラメータで URL ドリブンフィルタリング対応。
削除
app/javascript/components/UnconfirmedLink.jsx
UnconfirmedLink React コンポーネント削除(vanilla JS に移行)。
モデルテスト
test/models/product_test.rb
unchecked・unassigned・self_assigned_product スコープ追加テスト。self_assigned_no_replied_products テストにメンターコメント後の除外シナリオ追加。
モデルテスト - Report
test/models/report_test.rb
unchecked スコープの検証テスト追加。
ヘルパーテスト
test/helpers/products_helper_test.rb
unconfirmed_links_label ヘルパーの各ターゲット値の日本語ラベル対応を検証。
システムテスト - 提出物
test/system/product/unconfirmed_links_open_test.rb, test/system/product/unchecked_test.rb, test/system/product/unassigned_test.rb, test/system/product/self_assigned_test.rb
一括オープン UI をテスト。従来のウィンドウ操作型テストから直接リンク存在確認型テストに更新(クラス js-unconfirmed-link で selector)。
システムテスト - 日報
test/system/report/unconfirmed_links_open_test.rb
メンター向け未チェック日報の一括オープン UI テスト追加。チェック済み・未チェック状態に応じた表示制御を検証。

Sequence Diagram

sequenceDiagram
    participant User as ユーザー<br/>(メンター)
    participant Controller as コントローラー<br/>(e.g. ProductsController)
    participant Helper as ヘルパー<br/>(ProductsHelper)
    participant View as ビュー<br/>(.slim)
    participant JS as JavaScript<br/>(unconfirmed-links-open.js)
    participant Browser as ブラウザ<br/>(新規タブ)

    User->>Controller: /products へアクセス
    Controller->>Controller: set_target<br/>(`@target` = 'all')
    Controller->>Controller: index<br/>(クエリ実行)
    Controller->>View: `@target`, `@products`<br/>をテンプレートに渡す
    View->>Helper: unconfirmed_links_label(`@target`)
    Helper-->>View: 日本語ラベル<br/>('全ての提出物を...')
    
    alt `@products.present`?
        View->>View: unconfirmed_links_open<br/>パーシャル render
        View->>JS: DOM 生成<br/>(js-unconfirmed-link)
    end
    
    View-->>User: HTML 返す<br/>(ボタン付き)
    
    User->>JS: ボタンクリック
    JS->>JS: document.querySelectorAll<br/>('js-unconfirmed-link')
    loop 各リンク
        JS->>Browser: window.open(href, '_blank')
    end
    
    Note over JS: 100ms 遅延
    JS->>Browser: location.reload()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

重点確認項目:

  • UnconfirmedLink コンポーネント削除に伴う他箇所への波及影響確認(Products.jsx, Reports.jsx など)
  • 新規導入された @target マッピング機構の全コントローラー間での一貫性確認
  • unconfirmed_links_label ヘルパーの日本語ラベルマッピングが全ターゲット値をカバーしているか確認
  • window.open の '_target' から '_blank' への変更が意図通りか、100ms 遅延リロードの副作用確認
  • 新規 Notifications.jsx コンポーネントの SWR 統合と pagination ロジック

Possibly related issues

Possibly related PRs

Suggested reviewers

  • komagata

Poem

🐰 ウサギが飛び跳ねて、リンクもシュビッ!
React を卒業、vanilla で軽く
ターゲットマッピング、ラベル統一、
一括オープン、ページもリロード、
提出物も日報も、さあさあ開こう! 🚀✨

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 Pull request title clearly describes the main change: converting the UnconfirmedLink.jsx React component to vanilla JavaScript.
Description check ✅ Passed The PR description includes required sections: Issue reference (#9010), Overview, Implementation details, Testing strategy, and verification steps with screenshots and branch information.
✨ 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/replace_vanilla_js_in_unconfirmed_link_jsx

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.

@sharoa119 sharoa119 force-pushed the chore/replace_vanilla_js_in_unconfirmed_link_jsx branch from 27f9f33 to 9a8b870 Compare October 3, 2025 02:56
@sharoa119 sharoa119 marked this pull request as ready for review October 3, 2025 02:56
@github-actions github-actions bot requested a review from okuramasafumi October 3, 2025 02:57
@sharoa119 sharoa119 self-assigned this Oct 3, 2025
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: 2

🧹 Nitpick comments (2)
app/views/products/unchecked/index.html.slim (1)

25-26: LGTM! ただし、将来的なリファクタリングの検討を推奨

条件付きレンダリングのロジックは正しく実装されています。ただし、同じ条件式が複数のビュー(index, unassigned, unchecked, self_assigned)で繰り返されています。

将来的には、ViewComponentパターンを使用して条件判定をコンポーネント内にカプセル化することを検討してください(コーディングガイドラインに沿った実装)。これにより、DRY原則に従いながら保守性が向上します。

コーディングガイドラインより: "Viewにpartialを作る場合はViewComponentを使うことを検討する。"

app/helpers/products_helper.rb (1)

6-11: 国際化(i18n)の検討を推奨

ラベル文字列がハードコーディングされていますが、将来的な多言語対応を考慮すると、i18nファイルに移行することを推奨します。

例:

 def unconfirmed_links_label(target)
-  case target
-  when 'all' then '全ての提出物を一括で開く'
-  when 'unchecked', 'unchecked_all' then '未完了の提出物を一括で開く'
-  when 'unchecked_no_replied' then '未返信の提出物を一括で開く'
-  when 'unassigned' then '未アサインの提出物を一括で開く'
-  when 'self_assigned', 'self_assigned_all' then '自分の担当の提出物を一括で開く'
-  when 'self_assigned_no_replied' then '未返信の担当提出物を一括で開く'
-  else ''
-  end
+  I18n.t("products.unconfirmed_links.#{target}", default: '')
 end

対応するi18nファイル (config/locales/ja.yml) に追加:

ja:
  products:
    unconfirmed_links:
      all: '全ての提出物を一括で開く'
      unchecked: '未完了の提出物を一括で開く'
      unchecked_all: '未完了の提出物を一括で開く'
      unchecked_no_replied: '未返信の提出物を一括で開く'
      unassigned: '未アサインの提出物を一括で開く'
      self_assigned: '自分の担当の提出物を一括で開く'
      self_assigned_all: '自分の担当の提出物を一括で開く'
      self_assigned_no_replied: '未返信の担当提出物を一括で開く'
📜 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 a8f7dde and 9a8b870.

📒 Files selected for processing (21)
  • app/controllers/products/unassigned_controller.rb (2 hunks)
  • app/controllers/products/unchecked_controller.rb (2 hunks)
  • app/controllers/products_controller.rb (2 hunks)
  • app/helpers/products_helper.rb (1 hunks)
  • app/javascript/components/Notifications.jsx (0 hunks)
  • app/javascript/components/Product.jsx (1 hunks)
  • app/javascript/components/Products.jsx (0 hunks)
  • app/javascript/components/Reports.jsx (0 hunks)
  • app/javascript/components/UnconfirmedLink.jsx (0 hunks)
  • app/javascript/unconfirmed-links-open.js (1 hunks)
  • app/views/notifications/index.html.slim (1 hunks)
  • app/views/products/index.html.slim (1 hunks)
  • app/views/products/self_assigned/index.html.slim (1 hunks)
  • app/views/products/unassigned/index.html.slim (1 hunks)
  • app/views/products/unchecked/index.html.slim (1 hunks)
  • test/models/product_test.rb (1 hunks)
  • test/models/report_test.rb (1 hunks)
  • test/system/product/products_view_system_test.rb (1 hunks)
  • test/system/product/unconfirmed_links_open_test.rb (1 hunks)
  • test/system/report/reports_view_system_test.rb (1 hunks)
  • test/system/report/unconfirmed_links_open_test.rb (1 hunks)
💤 Files with no reviewable changes (4)
  • app/javascript/components/Products.jsx
  • app/javascript/components/Notifications.jsx
  • app/javascript/components/UnconfirmedLink.jsx
  • app/javascript/components/Reports.jsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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/controllers/products/unchecked_controller.rb
  • app/helpers/products_helper.rb
  • test/models/report_test.rb
  • test/system/report/unconfirmed_links_open_test.rb
  • test/models/product_test.rb
  • test/system/report/reports_view_system_test.rb
  • app/controllers/products_controller.rb
  • test/system/product/unconfirmed_links_open_test.rb
  • test/system/product/products_view_system_test.rb
  • app/controllers/products/unassigned_controller.rb
test/**/*

⚙️ CodeRabbit configuration file

test/**/*: # Test

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

Unit Test

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

Files:

  • test/models/report_test.rb
  • test/system/report/unconfirmed_links_open_test.rb
  • test/models/product_test.rb
  • test/system/report/reports_view_system_test.rb
  • test/system/product/unconfirmed_links_open_test.rb
  • test/system/product/products_view_system_test.rb
**/*.js

⚙️ CodeRabbit configuration file

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

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

Files:

  • app/javascript/unconfirmed-links-open.js
🧬 Code graph analysis (8)
app/controllers/products/unchecked_controller.rb (3)
app/controllers/products/unassigned_controller.rb (2)
  • before_action (3-20)
  • set_target (17-19)
app/controllers/products_controller.rb (2)
  • before_action (3-161)
  • set_target (158-160)
app/controllers/products/self_assigned_controller.rb (2)
  • before_action (3-31)
  • target_allowlist (16-18)
test/system/report/unconfirmed_links_open_test.rb (3)
test/system/product/unconfirmed_links_open_test.rb (1)
  • setup (5-55)
test/system/report/reports_view_system_test.rb (1)
  • setup (5-36)
test/models/report_test.rb (1)
  • test (5-76)
test/models/product_test.rb (1)
app/models/product.rb (1)
  • self_assigned_no_replied_products (70-72)
test/system/report/reports_view_system_test.rb (2)
test/system/report/unconfirmed_links_open_test.rb (1)
  • setup (5-24)
test/models/report_test.rb (1)
  • test (5-76)
app/controllers/products_controller.rb (3)
app/controllers/products/unassigned_controller.rb (2)
  • before_action (3-20)
  • set_target (17-19)
app/controllers/products/unchecked_controller.rb (2)
  • before_action (3-27)
  • set_target (23-26)
app/controllers/products/self_assigned_controller.rb (1)
  • before_action (3-31)
test/system/product/unconfirmed_links_open_test.rb (2)
test/system/product/products_view_system_test.rb (1)
  • setup (5-66)
test/system/report/unconfirmed_links_open_test.rb (1)
  • setup (5-24)
test/system/product/products_view_system_test.rb (2)
test/system/product/unconfirmed_links_open_test.rb (1)
  • setup (5-55)
test/models/product_test.rb (1)
  • test (5-288)
app/controllers/products/unassigned_controller.rb (2)
app/controllers/products/unchecked_controller.rb (2)
  • before_action (3-27)
  • set_target (23-26)
app/controllers/products_controller.rb (2)
  • before_action (3-161)
  • set_target (158-160)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (19)
app/controllers/products/unassigned_controller.rb (1)

5-19: LGTM!

before_action :set_targetの追加とset_targetメソッドの実装は、他のコントローラ(UncheckedController、ProductsController)と一貫したパターンに従っています。シンプルで明確な責務分離が保たれています。

test/models/report_test.rb (1)

65-75: LGTM!

uncheckedスコープのユニットテストが適切に追加されています。テスト名が英語で記載されており、コーディングガイドラインに準拠しています。

app/controllers/products_controller.rb (2)

7-7: LGTM!

before_action :set_target, only: %i[index]の追加により、indexアクションのみに適用範囲を限定しています。他のコントローラとの一貫性が保たれています。


157-160: LGTM!

set_targetメソッドの実装はシンプルで明確です。@targetの初期化により、ビューでの条件分岐に必要なコンテキストを提供しています。

test/system/report/reports_view_system_test.rb (1)

1-36: LGTM!

システムテストの実装が適切です:

  • NotificationとWatchの副作用を適切にstub
  • メンターの視点からの未チェックレポート表示を検証
  • js-unconfirmed-linkクラスとhref属性の存在を確認
  • テスト名が英語でコーディングガイドラインに準拠
app/controllers/products/unchecked_controller.rb (2)

5-5: LGTM!

before_action :set_targetの追加により、targetの初期化をindexアクションから分離し、他のコントローラとの一貫性が向上しています。


23-26: LGTM!

set_targetメソッドへのリファクタリングにより、関心の分離が改善されています。ロジックは変更されておらず、許可リストによる検証も適切に維持されています。

test/models/product_test.rb (4)

254-258: LGTM!

uncheckedスコープのテストが適切に実装されています。テスト名が英語で記載され、checksが空であることとスコープへの包含を検証しています。


260-275: LGTM!

self_assigned_no_replied_productsのテストが包括的です:

  • メンターに担当された提出物が含まれることを検証
  • コメント追加後に除外されることを検証
  • 動作の両面をカバーしています

277-281: LGTM!

unassignedスコープのテストが適切です。checker_idがnilであることとスコープへの包含を検証しています。


283-287: LGTM!

self_assigned_productスコープのテストが適切に実装されています。指定されたユーザーに担当された提出物が含まれることを検証しています。

app/javascript/components/Product.jsx (1)

57-57: LGTM!

js-unconfirmed-linkクラスの追加により、システムテストとバニラJSの一括オープン機能が提出物リンクを識別できるようになります。ロジックやレンダリングに影響はありません。

app/views/notifications/index.html.slim (1)

48-49: LGTM!

未読通知の一括オープンUI追加が適切です:

  • メンター権限、未読ステータス、未読通知の存在を適切に条件判定
  • ラベルが明確で分かりやすい
  • 他のビュー(products系)との一貫性が保たれています
app/views/products/unassigned/index.html.slim (1)

16-17: LGTM!

条件付きレンダリングのロジックが正しく、@productsの存在確認と@targetの値チェックが適切に行われています。ヘルパーメソッドを使用したラベル生成も適切です。

app/javascript/unconfirmed-links-open.js (1)

11-11: セキュリティとブラウザ互換性の改善

window.openのターゲットを'_blank'に変更したことで、標準的な新規タブ/ウィンドウでの開き方になり、noopenerパラメータによりセキュリティも向上しています。以前の'_target'は非標準だったため、この変更は適切です。

app/views/products/self_assigned/index.html.slim (1)

18-19: LGTM!

実装は正しく、他のビューファイルと一貫性があります。DRY原則に関する改善提案はapp/views/products/unchecked/index.html.slimのレビューコメントを参照してください。

app/views/products/index.html.slim (1)

17-18: LGTM!

条件付きレンダリングが正しく実装されており、staff_login?ブロック内で適切に配置されています。

test/system/product/unconfirmed_links_open_test.rb (1)

39-54: テスト構造は適切

テストケース名が英語で書かれており、コーディングガイドラインに準拠しています。カバレッジも適切です(ボタンの表示/非表示の両方のケースをテスト)。

ただし、Line 45のバグ修正が必要です(別のコメントを参照)。

test/system/report/unconfirmed_links_open_test.rb (1)

11-23: LGTM!

日報に対する一括開封ボタンのテストが適切に実装されています。既存のReport.uncheckedスコープを活用した効率的なテストです。テストケース名も英語で記述されており、コーディングガイドラインに準拠しています。

@sharoa119 sharoa119 force-pushed the chore/replace_vanilla_js_in_unconfirmed_link_jsx branch 2 times, most recently from d0b5e72 to 2461480 Compare October 7, 2025 04:05
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 (2)
test/system/report/unconfirmed_links_open_test.rb (2)

37-39: 特定のレポートのみをチェック済みにする方が安全です。

Report.unchecked.find_eachは、fixtureに含まれる他の未チェックレポートもすべて対象にしてしまいます。テストの独立性を保つため、setupで作成した2つのレポートのみを明示的にチェック済みにする方が望ましいです。

以下の差分を適用してください:

-    Report.unchecked.find_each do |report|
-      report.checks.create!(user: @mentor)
-    end
+    @unchecked_report1.checks.create!(user: @mentor)
+    @unchecked_report2.checks.create!(user: @mentor)

49-50: waitオプションの追加を検討してください。

Product側のテスト(test/system/product/unconfirmed_links_open_test.rb)では.js-unconfirmed-linkセレクタにwait: 5オプションを使用している箇所があります。JavaScriptによる動的なレンダリングが発生する可能性がある場合、同様にwaitオプションを追加することでテストの安定性が向上します。

以下のように修正できます:

-    assert_selector "a.js-unconfirmed-link[href='#{report_path(@unchecked_report1)}']", count: 1
-    assert_selector "a.js-unconfirmed-link[href='#{report_path(@unchecked_report2)}']", count: 1
+    assert_selector "a.js-unconfirmed-link[href='#{report_path(@unchecked_report1)}']", count: 1, wait: 5
+    assert_selector "a.js-unconfirmed-link[href='#{report_path(@unchecked_report2)}']", count: 1, wait: 5
📜 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 d0b5e72 and 2461480.

📒 Files selected for processing (4)
  • test/helpers/products_helper_test.rb (1 hunks)
  • test/models/product_test.rb (1 hunks)
  • test/system/product/unconfirmed_links_open_test.rb (1 hunks)
  • test/system/report/unconfirmed_links_open_test.rb (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/helpers/products_helper_test.rb
  • test/models/product_test.rb
  • test/system/product/unconfirmed_links_open_test.rb
🧰 Additional context used
📓 Path-based instructions (2)
**/*.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:

  • test/system/report/unconfirmed_links_open_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/system/report/unconfirmed_links_open_test.rb
🧬 Code graph analysis (1)
test/system/report/unconfirmed_links_open_test.rb (2)
test/system/product/unconfirmed_links_open_test.rb (1)
  • setup (5-73)
test/models/report_test.rb (1)
  • test (5-76)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test

@sharoa119 sharoa119 force-pushed the chore/replace_vanilla_js_in_unconfirmed_link_jsx branch 3 times, most recently from 21ab9b6 to c87f685 Compare October 7, 2025 08:47
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: 0

🧹 Nitpick comments (1)
app/views/products/self_assigned/index.html.slim (1)

18-19: ターゲットリストの範囲が広すぎる可能性があります。

この自分の担当提出物ビューで、'all''unchecked_all''unchecked_no_replied''unassigned' といったターゲットが含まれていますが、これらは self_assigned ビューには関連性が低いように見えます。

このビューでは 'self_assigned''self_assigned_all''self_assigned_no_replied' のみをチェックする方が適切ではないでしょうか。

以下の diff を適用して、ターゲットリストを絞り込むことを検討してください:

-    - if @products.present? && %w[all unchecked_all unchecked_no_replied unassigned self_assigned self_assigned_all self_assigned_no_replied].include?(@target)
+    - if @products.present? && %w[self_assigned self_assigned_all self_assigned_no_replied].include?(@target)
       = render 'application/unconfirmed_links_open', label: unconfirmed_links_label(@target)
📜 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 21ab9b6 and c87f685.

📒 Files selected for processing (9)
  • app/javascript/components/Notifications.jsx (1 hunks)
  • app/views/products/index.html.slim (1 hunks)
  • app/views/products/self_assigned/index.html.slim (1 hunks)
  • app/views/products/unassigned/index.html.slim (1 hunks)
  • app/views/products/unchecked/index.html.slim (1 hunks)
  • test/helpers/products_helper_test.rb (1 hunks)
  • test/models/product_test.rb (1 hunks)
  • test/system/product/unconfirmed_links_open_test.rb (1 hunks)
  • test/system/report/unconfirmed_links_open_test.rb (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • app/views/products/unassigned/index.html.slim
  • app/javascript/components/Notifications.jsx
  • test/models/product_test.rb
  • app/views/products/unchecked/index.html.slim
  • test/system/product/unconfirmed_links_open_test.rb
  • app/views/products/index.html.slim
  • test/system/report/unconfirmed_links_open_test.rb
🧰 Additional context used
📓 Path-based instructions (2)
**/*.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:

  • test/helpers/products_helper_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/helpers/products_helper_test.rb
🧬 Code graph analysis (1)
test/helpers/products_helper_test.rb (1)
app/helpers/products_helper.rb (1)
  • unconfirmed_links_label (4-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (2)
app/views/products/self_assigned/index.html.slim (1)

13-13: 削除されたタイトル設定の影響を確認してください。

この行では @target == 'self_assigned_no_replied' の場合にページタイトルを設定していましたが、削除されています。この削除が意図的なものか、またページタイトルの表示に影響がないか確認してください。

test/helpers/products_helper_test.rb (1)

1-21: テストの実装が適切です!

unconfirmed_links_label ヘルパーのすべてのケースを網羅しており、未知のターゲットと nil の処理も確認されています。コーディングガイドラインに従って、英語のテストケース名を使用し、追加されたメソッドに対する単体テストが適切に実装されています。

@sharoa119 sharoa119 force-pushed the chore/replace_vanilla_js_in_unconfirmed_link_jsx branch from c87f685 to 24757b4 Compare October 8, 2025 07:07
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: 0

🧹 Nitpick comments (3)
app/javascript/unconfirmed-links-open.js (1)

1-15: LGTM! セキュリティ対策が適切に実装されています。

window.open'noopener'オプションを指定することで、開かれたウィンドウからwindow.openerへのアクセスを防ぎ、セキュリティリスクを軽減しています。

ただし、ポップアップブロッカーによってリンクが開けない場合のユーザーフィードバックがありません。必要に応じて、以下のような処理を追加することを検討してください:

 allOpenButton.addEventListener('click', () => {
+  let openedCount = 0
   const links = document.querySelectorAll(
     '.card-list-item .js-unconfirmed-link'
   )
   links.forEach((link) => {
-    window.open(link.href, '_blank', 'noopener')
+    const opened = window.open(link.href, '_blank', 'noopener')
+    if (opened) openedCount++
   })
+  if (openedCount === 0 && links.length > 0) {
+    alert('ポップアップがブロックされました。ブラウザの設定を確認してください。')
+  }
 })
test/models/report_test.rb (1)

65-75: LGTM! 基本的なテストケースは適切です。

uncheckedスコープの基本動作を検証するテストが正しく実装されています。テストケース名も英語で記述されており、コーディングガイドラインに準拠しています。

より堅牢なテストのために、チェック済みのレポートがuncheckedスコープに含まれないことを検証するテストケースの追加を検討してください:

test 'unchecked scope does not return reports with checks' do
  checked_report = reports(:report1) # Assuming this has checks
  assert_not_empty checked_report.checks
  assert_not_includes Report.unchecked, checked_report
end
test/system/product/unconfirmed_links_open_test.rb (1)

55-72: wait パラメータの一貫性を確認してください。

Lines 58 と 64 では wait: 5 を使用していますが、Line 70 では使用していません。動的コンテンツの読み込みタイミングが異なる可能性がありますが、一貫性のために Line 70-71 にも wait: 5 を追加することを検討してください。

以下のように修正することを検討してください:

   test 'mentor sees self_assigned products links' do
     visit_with_auth '/products/self_assigned', 'komagata'
 
-    assert_selector "a.js-unconfirmed-link[href$='#{@self_assigned_product.id}']", count: 1
-    assert_selector "a.js-unconfirmed-link[href$='#{@unchecked_no_replied_product.id}']", count: 1
+    assert_selector "a.js-unconfirmed-link[href$='#{@self_assigned_product.id}']", count: 1, wait: 5
+    assert_selector "a.js-unconfirmed-link[href$='#{@unchecked_no_replied_product.id}']", count: 1, wait: 5
   end
📜 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 c87f685 and 24757b4.

📒 Files selected for processing (24)
  • app/controllers/products/unassigned_controller.rb (2 hunks)
  • app/controllers/products/unchecked_controller.rb (2 hunks)
  • app/controllers/products_controller.rb (2 hunks)
  • app/helpers/products_helper.rb (1 hunks)
  • app/javascript/components/Notifications.jsx (1 hunks)
  • app/javascript/components/Product.jsx (1 hunks)
  • app/javascript/components/Products.jsx (0 hunks)
  • app/javascript/components/Reports.jsx (0 hunks)
  • app/javascript/components/UnconfirmedLink.jsx (0 hunks)
  • app/javascript/unconfirmed-links-open.js (1 hunks)
  • app/views/notifications/index.html.slim (1 hunks)
  • app/views/products/index.html.slim (1 hunks)
  • app/views/products/self_assigned/index.html.slim (1 hunks)
  • app/views/products/unassigned/index.html.slim (1 hunks)
  • app/views/products/unchecked/index.html.slim (1 hunks)
  • test/helpers/products_helper_test.rb (1 hunks)
  • test/models/product_test.rb (1 hunks)
  • test/models/report_test.rb (1 hunks)
  • test/system/product/self_assigned_test.rb (1 hunks)
  • test/system/product/unassigned_test.rb (1 hunks)
  • test/system/product/unchecked_test.rb (1 hunks)
  • test/system/product/unconfirmed_links_open_test.rb (1 hunks)
  • test/system/products_test.rb (3 hunks)
  • test/system/report/unconfirmed_links_open_test.rb (1 hunks)
💤 Files with no reviewable changes (3)
  • app/javascript/components/Products.jsx
  • app/javascript/components/UnconfirmedLink.jsx
  • app/javascript/components/Reports.jsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • app/views/products/unassigned/index.html.slim
  • app/controllers/products/unassigned_controller.rb
  • app/controllers/products/unchecked_controller.rb
  • app/helpers/products_helper.rb
  • test/helpers/products_helper_test.rb
  • test/system/report/unconfirmed_links_open_test.rb
  • app/controllers/products_controller.rb
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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:

  • test/models/report_test.rb
  • test/system/product/unchecked_test.rb
  • test/models/product_test.rb
  • test/system/products_test.rb
  • test/system/product/unassigned_test.rb
  • test/system/product/self_assigned_test.rb
  • test/system/product/unconfirmed_links_open_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/models/report_test.rb
  • test/system/product/unchecked_test.rb
  • test/models/product_test.rb
  • test/system/products_test.rb
  • test/system/product/unassigned_test.rb
  • test/system/product/self_assigned_test.rb
  • test/system/product/unconfirmed_links_open_test.rb
**/*.js

⚙️ CodeRabbit configuration file

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

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

Files:

  • app/javascript/unconfirmed-links-open.js
🧬 Code graph analysis (6)
test/system/product/unchecked_test.rb (1)
test/supports/product_helper.rb (1)
  • delete_most_unchecked_products! (4-10)
app/javascript/unconfirmed-links-open.js (1)
app/javascript/user-icon.js (1)
  • link (7-7)
test/models/product_test.rb (1)
app/models/product.rb (1)
  • self_assigned_no_replied_products (70-72)
test/system/products_test.rb (2)
test/system/product/self_assigned_test.rb (1)
  • test (5-207)
test/system/product/checker_test.rb (1)
  • test (5-78)
test/system/product/unassigned_test.rb (2)
test/system/product/self_assigned_test.rb (1)
  • test (5-207)
test/supports/product_helper.rb (1)
  • delete_most_unassigned_products! (12-18)
test/system/product/unconfirmed_links_open_test.rb (4)
test/system/report/unconfirmed_links_open_test.rb (1)
  • setup (5-52)
test/helpers/products_helper_test.rb (1)
  • test (5-21)
test/system/product/self_assigned_test.rb (1)
  • test (5-207)
test/system/products_test.rb (1)
  • test (5-731)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (15)
app/javascript/components/Product.jsx (1)

57-57: LGTM! クラス追加により一括開封機能との連携が適切に実装されています。

js-unconfirmed-linkクラスの追加により、unconfirmed-links-open.jsのセレクタで正しく選択されるようになっています。既存の機能に影響を与えない適切な変更です。

app/javascript/components/Notifications.jsx (1)

9-9: LGTM! React からバニラ JS への移行が適切に反映されています。

isMentorプロップの削除により、未確認リンクの一括開封機能がサーバーサイドレンダリングに移行したことが正しく反映されています。app/views/notifications/index.html.slimで条件付きレンダリングが行われるため、この変更は適切です。

test/models/product_test.rb (1)

254-287: LGTM! スコープの動作を包括的にテストしています。

追加された4つのテストケースは、各スコープの動作を適切に検証しています:

  1. unchecked scope: チェックのない提出物が正しく含まれることを確認
  2. self_assigned_no_replied_products: 担当メンターへの割り当て後、返信がない場合は含まれ、返信後は除外されることを確認(状態変化のテストとして特に優れています)
  3. unassigned scope: チェッカーが割り当てられていない提出物が含まれることを確認
  4. self_assigned_product scope: 特定のメンターに割り当てられた提出物が含まれることを確認

テストケース名も英語で記述されており、コーディングガイドラインに準拠しています。

app/views/products/unchecked/index.html.slim (1)

25-26: LGTM! 条件付きレンダリングが適切に実装されています。

@products.present?@targetのホワイトリストチェックにより、適切な条件下でのみ一括開封ボタンが表示されます。unconfirmed_links_label(@target)ヘルパーの使用も一貫性があり、他のビューと整合性が取れています。

app/views/notifications/index.html.slim (1)

48-49: LGTM! メンター向けの一括開封機能が適切に実装されています。

3つの条件(mentor_login?params[:status] == 'unread'current_user.notifications.unreads.any?)により、適切な状況でのみ一括開封ボタンが表示されます。未読通知が存在しない場合にボタンが表示されないことで、優れたユーザーエクスペリエンスを提供しています。

app/views/products/index.html.slim (1)

17-18: LGTM! 一貫性のある実装です。

条件付きレンダリングの実装が他の提出物ビュー(unchecked、unassigned、self_assigned)と一貫性があり、適切です。@targetのホワイトリストチェックにより、対応するターゲットタイプの場合のみ一括開封機能が表示されます。

app/views/products/self_assigned/index.html.slim (1)

18-19: LGTM!

条件付きレンダリングのロジックは適切です。@products.present?@target の値を確認し、該当する場合に unconfirmed_links_open 部分テンプレートをレンダリングしています。

test/system/product/self_assigned_test.rb (1)

28-39: LGTM!

テストのリファクタリングは適切です。ウィンドウ操作を削除し、js-unconfirmed-link クラスを持つリンク要素の存在確認に変更しています。これは React から vanilla JS への移行に合致しています。

test/system/products_test.rb (3)

285-286: LGTM!

フォーム要素の読み込みを待つために wait: 5 を追加しています。動的コンテンツの読み込みを考慮した適切な変更です。


302-303: LGTM!

Lines 285-286 と同様に、フォーム要素の読み込みを待つために wait: 5 を追加しています。


727-730: LGTM!

一括で開くボタンの表示を確認する新しいテストが追加されています。テスト名は英語で記述されており、コーディングガイドラインに準拠しています。

test/system/product/unassigned_test.rb (1)

24-36: LGTM!

テストのリファクタリングは他のテストファイルと一貫性があり、適切です。js-unconfirmed-link クラスを持つリンク要素の存在を確認するようになっています。

test/system/product/unconfirmed_links_open_test.rb (2)

6-46: LGTM!

setup ブロックは適切に構成されています。Notification.stubWatch.stub を使用して副作用を回避し、テストに必要な3つの提出物(未返信、未アサイン、自分が担当)を作成しています。


49-52: LGTM!

一括で開くボタンの表示を確認するテストです。適切です。

test/system/product/unchecked_test.rb (1)

24-35: LGTM!

テストのリファクタリングは他のテストファイル(unassigned_test.rb、self_assigned_test.rb)と一貫性があり、適切です。js-unconfirmed-link クラスを持つリンク要素の存在を確認するようになっています。

@sharoa119 sharoa119 force-pushed the chore/replace_vanilla_js_in_unconfirmed_link_jsx branch from 24757b4 to fc40e5c Compare October 8, 2025 07:40
@sharoa119 sharoa119 changed the title [wip]UnconfirmedLink.jsxを非React化 UnconfirmedLink.jsxを非React化 Oct 8, 2025
@sharoa119
Copy link
Contributor Author

@tyrrell-IH さん
お疲れ様です。
お手隙の際で構いませんので、レビューをお願いできますでしょうか🙇‍♀️
よろしくお願いいたします。

@sharoa119 sharoa119 requested review from tyrrell-IH and removed request for okuramasafumi October 8, 2025 08:35
@tyrrell-IH
Copy link
Contributor

@sharoa119 レビューやります!
10日ほどお時間いただいて10/18前後でお返しできるように対応いたします

もし「急ぎで〜」とかがあれば先におっしゃってください、その際には優先してレビューします

@tyrrell-IH
Copy link
Contributor

@sharoa119
すいません、まだレビュー終わってないんですけど10/20(月)にお返しできる予定です。遅くなって申し訳ないです🙏

Copy link
Contributor

@tyrrell-IH tyrrell-IH left a comment

Choose a reason for hiding this comment

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

@sharoa119 お疲れ様です!時間がかかって申し訳ございません。ようやくレビュー終わりました。

私も知識がないので自信はないですが、ReactからVanillaJSへの置き換えは上手くできているのではないかと思いました。

以下レビューしていて思ったことがあるので書いておきます。

動作確認手順について

動作確認手順をもう少し詳しく書いて欲しかったです。

決められた手順がないので、自分でコードを読んでから手順を考えないといけなかったのがちょっと大変でした💦

警告メッセージの適切な表示

Issueの注意点に「警告メッセージの適切な表示」とあり、sharoaさんのdescriptionにも

警告メッセージの表示確認済み

とありましたが、それがどの部分の動作を指すのかわかりませんでした。なので警告メッセージの動作確認はできていません。この辺をちょっと教えていただきたいです。

挙動についてのところ

descriptionの「挙動について」のところ

一括で開封した直後は「一括で開く」ボタンは画面上に残ります。
ただし「全て」タブを再選択すると非表示になります。
これは、React 版で行っていたような即時非表示の挙動を再現するには状態管理や非同期処理が必要となるため、今回の移行では対応していません。
今回の issue の趣旨(状態管理を使わず、バニラ JS でシンプルに移行する)に沿った実装です。

と書いていますが、

例えば通知機能だけでよければ

//app/javascript/unconfirmed-links-open.js

document.addEventListener('DOMContentLoaded', () => {
  const allOpenButton = document.querySelector(
    '#js-shortcut-unconfirmed-links-open'
  )
  if (allOpenButton) {
    allOpenButton.addEventListener('click', () => {
      const links = document.querySelectorAll(
        '.card-list-item .js-unconfirmed-link'
      )
      links.forEach((link) => {
        window.open(link.href, '_blank', 'noopener')
      })
      if (links.length > 0) {    // ←追加
        location.reload(); // ←追加
      } // ←追加
    })
  }
})

のようにreloadを挟めば一応表示は消えてくれると思います。

追記 2025/10/21 13:30

提出物を一括開いて確認処理をした後でもボタンが残ってしまう問題↓これ

_development__自分の担当の提出物___FBC

については、この実装でいいか駒形さんによく確認しておいた方が良いと思います。

実装が難しいにしても、以前実現できていたことが、修正によってできなくなるというのは「バグ」と認識されても仕方ないのかなと思います。

Issueにも

未確認リンクの警告を表示する機能。現在はReactコンポーネントとして実装されているが、状態管理が不要な単純な表示コンポーネントのためバニラJSで十分対応可能。

と書いてあるので、何か検討する余地が残されているかもしれません。

(駒形さんに確認した上でこの実装なら、このままで良いと思います👍)

最後に

色々お伝えしましたが、私が間違っていること、勘違いしていることもあるかと思いますので、何か気づくことがあったら教えてください!

.page-body
.container.is-md
= react_component('Products', title: title, selectedTab: 'all', isMentor: mentor_login?, isAdmin: admin_login?, currentUserId: current_user.id)
- if @products.present? && %w[all unchecked_all unchecked_no_replied unassigned self_assigned self_assigned_all self_assigned_no_replied].include?(@target)
Copy link
Contributor

Choose a reason for hiding this comment

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

%w[all unchecked_all unchecked_no_replied unassigned self_assigned self_assigned_all self_assigned_no_replied].include?(@target)の条件は必要でしょうか?

products_controller.rbでは

def set_target
  @target = 'all'
end

となっているので基本的に@targetにはallしか入らないような気がします。

この条件自体いらないかも?と思いましたが、どんなことを想定して書いたものか教えていただきたいです🙏

app/views/products/self_assigned/index.html.slim
app/views/products/unassigned/index.html.slim
app/views/products/unchecked/index.html.slim

についても同様です。

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます。
当初は unconfirmed_links_open(一括開くボタン)の部分テンプレートを
各サブタブ(unchecked, self_assignedなど)でも共通で使う想定があり、
どのタブで表示すべきかを共通条件で判定できるように実装していました。

ただ、 @tyrrell-IH さんのご指摘のとおり、現状は各タブが個別のビューを持ち、@targetも固定値になっているため、
この条件は現在では不要であると再確認しました。
ご教示くださりありがとうございます。該当箇所は全て修正いたします🙇‍♀️

Copy link
Contributor

Choose a reason for hiding this comment

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

当初は unconfirmed_links_open(一括開くボタン)の部分テンプレートを
各サブタブ(unchecked, self_assignedなど)でも共通で使う想定があり、
どのタブで表示すべきかを共通条件で判定できるように実装していました。

なるほど、そうだったんですね!

修正は確認しOKですが、
UnconfirmedLink_jsxを非React化_by_sharoa119_·_Pull_Request__9184_·_fjordllc_bootcamp
コミットメッセージにメンションが入ってしまっているので、今後は少し気をつけたほうがいいかもしれません。

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます!コミットメッセージを編集してメンションを外しました。🙇‍♀️

assert_equal '全ての提出物を一括で開く', unconfirmed_links_label('all')
assert_equal '未完了の提出物を一括で開く', unconfirmed_links_label('unchecked')
assert_equal '未完了の提出物を一括で開く', unconfirmed_links_label('unchecked_all')
assert_equal '未返信の提出物を一括で開く', unconfirmed_links_label('unchecked_no_replied')
Copy link
Contributor

Choose a reason for hiding this comment

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

未返信の提出物を一括で開く(未返信の担当提出物を一括で開くも同様)という分類は以前はなかったように思いますが追加されましたでしょうか?

文言はこのままで問題ないと思いますが、細かなことでも仕様変更があったらどこかに書いといてもらえると助かります🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

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

元々uncheckedself_assignedのサブタブには全て未返信がありましたが、
どちらもボタンラベルが同じで表示されていました。
UX上、サブタブごとにラベルを適切にする方がわかりやすいと判断し、
ProductsHelper#unconfirmed_links_labelunchecked_no_repliedself_assigned_no_repliedを追加して対応しました。
文言は既存のパターンに沿っており、動作も既存と同様です。

ただ、最初にこの意図を説明しておくべきでした。すみません。😞

Copy link
Contributor

Choose a reason for hiding this comment

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

UX上、サブタブごとにラベルを適切にする方がわかりやすいと判断し、
ProductsHelper#unconfirmed_links_labelにunchecked_no_repliedと self_assigned_no_repliedを追加して対応しました。

確かにサブタブごとの方が見やすいですね、了解です🙆

Comment on lines 16 to 17
# 自分が担当かつ未返信提出物
@unchecked_no_replied_product = Product.create!(
Copy link
Contributor

Choose a reason for hiding this comment

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

自分が担当かつ未返信提出物なら@self_assigned_no_replied_productが正しいかも?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます!
確かにです!修正しました!

Comment on lines 727 to 730
test 'sees the open all products button on products page' do
visit_with_auth '/products', 'komagata'
assert_selector 'button', text: '全ての提出物を一括で開く'
end
Copy link
Contributor

@tyrrell-IH tyrrell-IH Oct 20, 2025

Choose a reason for hiding this comment

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

test/system/product/unconfirmed_links_open_test.rbの49行目とテストが重複しているような気がします。

test 'mentor sees bulk open button when unchecked products exist' do

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます!
自分で同じ内容のテストを追加していましたね💦全然気づいていませんでした💦
重複しているテストは削除いたしました。

Comment on lines 61 to 65
test 'mentor sees unassigned products links' do
visit_with_auth '/products/unassigned', 'komagata'

assert_selector "a.js-unconfirmed-link[href$='#{@unassigned_product.id}']", count: 1, wait: 5
end
Copy link
Contributor

Choose a reason for hiding this comment

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

test/system/product/unassigned_test.rbの24行目のテストと重複してるかもしれません。

test 'unassigned products links are rendered correctly' do

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます。
テストの重複について、こちらで探しきれておらず申し訳ありませんでした😞
今後は既存テストの確認も含めて整理するようにします。

Comment on lines 67 to 72
test 'mentor sees self_assigned products links' do
visit_with_auth '/products/self_assigned', 'komagata'

assert_selector "a.js-unconfirmed-link[href$='#{@self_assigned_product.id}']", count: 1
assert_selector "a.js-unconfirmed-link[href$='#{@unchecked_no_replied_product.id}']", count: 1
end
Copy link
Contributor

@tyrrell-IH tyrrell-IH Oct 20, 2025

Choose a reason for hiding this comment

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

test/system/product/self_assigned_test.rbの28行目と重複してるかもしれません。

test 'self-assigned products links are rendered correctly' do

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます。
こちらも上と同様、テストの重複についてこちらで探しきれておらず申し訳ありませんでした😞

Copy link
Contributor

@tyrrell-IH tyrrell-IH Oct 25, 2025

Choose a reason for hiding this comment

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

テストを書く場所が人によってまちまちなので、気づかなくてもしょうがないですよね😅
「どこにテストを書く」みたいな指針があればいいなと思いますが、、

Comment on lines 260 to 275
test 'self_assigned_no_replied_products returns products assigned to user with no reply' do
mentor = users(:komagata)
product = Product.create!(user: users(:hajime), practice: practices(:practice2), body: '提出物', checker_id: mentor.id)

assert_includes Product.self_assigned_no_replied_products(mentor.id), product

Comment.create!(
commentable: product,
user: mentor,
description: '返信済み',
created_at: Time.current,
updated_at: Time.current
)

assert_not_includes Product.self_assigned_no_replied_products(mentor.id), product
end
Copy link
Contributor

Choose a reason for hiding this comment

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

test '.self_assigned_no_replied_products' do

同じテストがあると思います。
Comment.create!以降のコードはself_assigned_no_replied_productsの動作と関係ないので必要ないかもしれません。

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます。
既存の .self_assigned_no_replied_productsのテストでは「返信がない提出物が含まれること」を確認していましたが、
「返信済みの提出物が除外されること」はカバーされていなかったため、
このテストではそのケースも合わせて検証しています。
つまり、スコープの正確な条件分岐のテストとして追加している形になります。
(Comment.create! 以降の処理はその確認のために追加しています)

もしテストとして冗長すぎると判断される場合は、削除していただいて構いませんので、遠慮なくお知らせください☺️

Copy link
Contributor

@tyrrell-IH tyrrell-IH Oct 25, 2025

Choose a reason for hiding this comment

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

「返信済みの提出物が除外されること」はカバーされていなかったため、
このテストではそのケースも合わせて検証しています。

なるほど、おっしゃる通りですね。理解しました👍

テスト内容の意図は別でもタイトルが

  • .self_assigned_no_replied_products
  • self_assigned_no_replied_products returns products assigned to user with no reply

では同じ内容のテストに見えるので

  • .self_assigned_no_replied_products
Comment.create!(
  commentable: product,
  user: mentor,
  description: '返信済み',
  created_at: Time.current,
  updated_at: Time.current
)

assert_not_includes Product.self_assigned_no_replied_products(mentor.id), product

を追記しまう。

  • テスト内容自体はこのままにして、テスト名を例えばself_assigned_no_replied_products excludes products that have already been replied toのように「返信済みのものが除外されていることを確認する」のようなニュアンスにする。

のどちらかの方が良いかなと思うのですが、どうでしょうか?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます。
いただいた案を検討した結果、今回はテスト内容をまとめて .self_assigned_no_replied_productsの1つのテスト内で、「未返信の提出物に含まれること」と「コメントが返されたら除外されること」の両方を確認する形にしました。
テスト名は従来のままにし、コメントで「メンターがコメントすると除外されることを確認」と追記して、意図がわかるようにしています⭐️

Comment on lines 14 to 15
Notification.stub(:create!, nil) do
Watch.stub(:create!, nil) do
Copy link
Contributor

Choose a reason for hiding this comment

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

ここはどういった理由で書いてますか?

特に意図がある質問ではなく、純粋に「何でだろう」と思ったので質問しました🧐

Productを作成してもNotification.create! Watch.create!は呼ばれなそうだし、呼ばれたとしてunconfirmed_links_open_testの動作にはあんまり影響しないように思いました。

Copy link
Contributor Author

@sharoa119 sharoa119 Oct 22, 2025

Choose a reason for hiding this comment

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

ご質問ありがとうございます。

もともとは Product.create!時にNotificationWatchが作成される副作用を抑えるために書いたのですが、今回のテストではボタン表示やリンク確認のみを行うため、NotificationWatchの有無はテスト結果に影響しないことに改めて確認して気づきました。教えていただきありがとうございます🙂

そのため、このテストに関してはstubは不要であり、削除いたします。

@sharoa119
Copy link
Contributor Author

@tyrrell-IH さん
お疲れ様です。たくさんコメントをいただきありがとうございます!

①動作確認手順について

ご指摘ありがとうございます!
動作確認手順について、もう少し具体的に書いたほうが良かったとのことで、追記してみました。
こちらの内容で問題なさそうかご確認いただけますでしょうか?
もしまだ分かりづらい点があれば、「こういう情報があると助かる」といった具体例を教えていただけると嬉しいです🙂
今後の書き方の参考にさせていただきたいです。

②警告メッセージの適切な表示

Issueの『警告メッセージの適切な表示』という文言についてですが、実際のUnconfirmedLink.jsxのコードにはalertconfirmのような警告ダイアログは存在していませんでした。
このコンポーネントは、未確認リンク(例:未読通知や未返信の提出物など)を一括で新しいタブで開くボタンを表示するだけの機能です。
したがって、ここでいう「警告メッセージ」は実際のダイアログではなく、「未確認項目を一括で開く」といったボタンラベルなどのUI上の注意喚起を指していると判断いたしました。🤔

③挙動についてのところ

コメントありがとうございます!
なるほどです、location.reload()を挟むことで確かに見た目上は非表示にできそうですが、
ページ全体のリロードは不要な再読み込みや状態リセットを伴うため、
今回の「状態管理を使わずシンプルに移行する」というIssue趣旨からは少し外れるかなと考えたため、現状の実装になっています。
ただ、tyrrellさんがおっしゃる通り、見た目の問題でバグと判断されてしまう可能性もあるということなので、駒形さんに確認してみたいと思います。🙂

とりあえず、こちらの返信を先にさせていただきました〜。
細かいコードの修正はこれから確認いたしますので、もう少々お待ちください🙇‍♀️

@sharoa119
Copy link
Contributor Author

@komagata さん
お疲れ様です。

#9184 (review)
こちらのコメントの「挙動についてのところ」で以下のご相談をさせてください。🤲

提出物を一括開いた後でもボタンが残る件についての件なのですが、
今回のIssueの趣旨(状態管理を使わずシンプルにバニラJSで移行)に沿って実装を検討しました。
ただ、location.reload() を挟むなどして、見た目だけでも即時非表示にするべきだったのか迷っております。

UXや以前の挙動との差異について、駒形さんにご確認いただいた上で、この実装で問題ないか判断していただけると助かります。

もし「以前のように即時非表示にしたほうが良い」という判断であれば、別Issueで対応を検討することも可能です。

よろしくお願いいたします🙇‍♀️

@tyrrell-IH
Copy link
Contributor

tyrrell-IH commented Oct 22, 2025

@sharoa119
すいません、1点連絡事項があって
@smallmonkeykey のPRで「未読の通知を一括で開く」ボタンをStimulusで実装しているみたいです
#9273 (comment)

https://github.com/fjordllc/bootcamp/pull/9273/files#diff-f8d4ed58ccc04ed86589f69cc658239d47b7dde4e32e18959c079f7d6e4a005dR69-R71

本PRと修正箇所が重なると思うので、どうするか相談して決めた方が良いのかなと思いました

@smallmonkeykey
Copy link
Contributor

@sharoa119
私の担当しているPRと一部重複している箇所がありました。
今回は私の方でStimulusの導入を取りやめ、共通のバニラJS(unconfirmed-links-open.js)に統一する形に変更します。
そのため、sharoaさんの対応はこのまま進めていただいて問題がないようにkomagataさんに確認しようと思います。

@sharoa119
Copy link
Contributor Author

@tyrrell-IH さん
お疲れ様です。確認が遅くなりすみません💦
こちらの重複の件、ご連絡ありがとうございます😫

@smallmonkeykey さん
お疲れ様です。確認が遅くなりすみません💦

今回は私の方でStimulusの導入を取りやめ、共通のバニラJS(unconfirmed-links-open.js)に統一する形に変更します。

こちら、かしこまりました!

1点確認させてください。

sharoaさんの対応はこのまま進めていただいて問題がないようにkomagataさんに確認しようと思います。

とのことですが、リサさんの担当されていた issue を確認したところ、

通知機能をReactからRails viewとStimulus controllerに移行します。
下記の二つのjsxファイルをプロジェクトから削除できるようにする。
app/javascript/components/Notification.jsx
app/javascript/components/Notifications.jsx

という内容でした。

つまり今回は、Stimulus の導入はやめて、削除対象だったファイルを戻し、バニラJSでの対応に変更、という認識で合っていますか?

その場合、通知機能まわりはリサさんが対応される予定という理解でよいでしょうか?
私の方では通知機能には手をつけずにおいた方がよさそうかなと思いまして。

とりあえず、何か変更や調整があればご連絡いただければと思います🙇‍♀️
よろしくお願いいたします。

Comment on lines 24 to 27
def set_target
@target = params[:target]
@target = 'unchecked_all' unless target_allowlist.include?(@target)
end
Copy link
Member

Choose a reason for hiding this comment

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

こちらも app/controllers/products/unassigned_controller.rb と同じようにindexしかアクションがないのであれば、indexで処理した方がわかりやすいかとおもいます。

他にも同様の指摘が当てはまる場所がないかどうか確かめてみるとよいかもです〜

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます。
unchecked_controller indexのみのアクションであったため、
before_actionを使用せず index内で処理する形に修正しました。

他にも同様の指摘が当てはまる場所がないかどうか確かめてみるとよいかもです〜

かしこまりました!ご教示ありがとうございます🙇‍♀️

require 'test_helper'

class ProductsHelperTest < ActionView::TestCase
test 'unconfirmed_links_label returns correct label for all targets' do
Copy link
Member

Choose a reason for hiding this comment

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

helperメソッドを追加したときにしっかりテストも追加しているのいいですね!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ありがとうございます✨

sharoa119 and others added 21 commits December 21, 2025 23:26
- unconfirmed_links_label ヘルパーを更新して self_assigned_all / self_assigned_no_replied に対応
- self_assigned タブでボタンが正しく表示されるよう修正し、他のタブと整合性を揃えた
- window.open の挙動確認は廃止
- モデル・コントローラのテストに追加・修正
- 重複していたテストを削除
- それに伴いsetupなど修正
@sharoa119 sharoa119 force-pushed the chore/replace_vanilla_js_in_unconfirmed_link_jsx branch from 3ade16c to eac76e9 Compare December 21, 2025 14:28
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/views/users/reports/index.html.slim (1)

19-20: ラベルの生成方法を統一することを検討してください

提出物ページでは unconfirmed_links_label(@target) ヘルパーを使用していますが、こちらでは日本語ラベルがハードコードされています。

将来的に日報用のヘルパーメソッドを追加するか、既存の ProductsHelper#unconfirmed_links_labelApplicationHelper に移動して日報のターゲット ('unchecked_reports' など) にも対応させることで、一貫性が向上します。

現時点では動作に問題はありませんが、ラベル管理の一元化を検討する価値があります。

📜 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 3ade16c and eac76e9.

📒 Files selected for processing (26)
  • app/controllers/products/self_assigned_controller.rb (1 hunks)
  • app/controllers/products/unassigned_controller.rb (1 hunks)
  • app/controllers/products/unchecked_controller.rb (1 hunks)
  • app/controllers/products_controller.rb (2 hunks)
  • app/controllers/users/reports_controller.rb (2 hunks)
  • app/helpers/products_helper.rb (1 hunks)
  • app/javascript/components/Notifications.jsx (1 hunks)
  • app/javascript/components/Product.jsx (1 hunks)
  • app/javascript/components/Products.jsx (0 hunks)
  • app/javascript/components/Reports.jsx (0 hunks)
  • app/javascript/components/UnconfirmedLink.jsx (0 hunks)
  • app/javascript/unconfirmed-links-open.js (1 hunks)
  • app/views/application/_unconfirmed_links_open.html.slim (1 hunks)
  • app/views/products/index.html.slim (1 hunks)
  • app/views/products/self_assigned/index.html.slim (1 hunks)
  • app/views/products/unassigned/index.html.slim (1 hunks)
  • app/views/products/unchecked/index.html.slim (1 hunks)
  • app/views/users/reports/index.html.slim (2 hunks)
  • test/helpers/products_helper_test.rb (1 hunks)
  • test/models/product_test.rb (3 hunks)
  • test/models/report_test.rb (1 hunks)
  • test/system/product/self_assigned_test.rb (1 hunks)
  • test/system/product/unassigned_test.rb (1 hunks)
  • test/system/product/unchecked_test.rb (1 hunks)
  • test/system/product/unconfirmed_links_open_test.rb (1 hunks)
  • test/system/report/unconfirmed_links_open_test.rb (1 hunks)
💤 Files with no reviewable changes (3)
  • app/javascript/components/Products.jsx
  • app/javascript/components/Reports.jsx
  • app/javascript/components/UnconfirmedLink.jsx
🚧 Files skipped from review as they are similar to previous changes (14)
  • app/views/products/unchecked/index.html.slim
  • app/javascript/unconfirmed-links-open.js
  • test/system/product/self_assigned_test.rb
  • test/helpers/products_helper_test.rb
  • app/controllers/products/self_assigned_controller.rb
  • app/javascript/components/Product.jsx
  • test/models/product_test.rb
  • app/views/products/unassigned/index.html.slim
  • test/system/product/unconfirmed_links_open_test.rb
  • app/controllers/products/unchecked_controller.rb
  • test/system/report/unconfirmed_links_open_test.rb
  • app/views/products/index.html.slim
  • app/controllers/products/unassigned_controller.rb
  • test/system/product/unchecked_test.rb
🧰 Additional context used
📓 Path-based instructions (7)
**/*.slim

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • app/views/products/self_assigned/index.html.slim
  • app/views/users/reports/index.html.slim
  • app/views/application/_unconfirmed_links_open.html.slim
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/controllers/products_controller.rb
  • app/javascript/components/Notifications.jsx
  • app/helpers/products_helper.rb
  • app/controllers/users/reports_controller.rb
**/*.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/controllers/products_controller.rb
  • test/system/product/unassigned_test.rb
  • test/models/report_test.rb
  • app/helpers/products_helper.rb
  • app/controllers/users/reports_controller.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/controllers/products_controller.rb
  • test/system/product/unassigned_test.rb
  • test/models/report_test.rb
  • app/helpers/products_helper.rb
  • app/controllers/users/reports_controller.rb
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/system/product/unassigned_test.rb
  • test/models/report_test.rb
test/**

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • test/system/product/unassigned_test.rb
  • test/models/report_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/system/product/unassigned_test.rb
  • test/models/report_test.rb
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/components/Notifications.jsx
🧠 Learnings (17)
📓 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: 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属性の追加など)は他の箇所との整合性を保つため、別途統一して対応する方針である
📚 Learning: 2025-11-19T07:55:51.504Z
Learnt from: smallmonkeykey
Repo: fjordllc/bootcamp PR: 9314
File: app/helpers/page_tabs/dashboard_helper.rb:8-12
Timestamp: 2025-11-19T07:55:51.504Z
Learning: fjordllc/bootcamp プロジェクトにおいて、分報(micro_reports)は current_user 専用の機能ではなく、他ユーザーの分報も閲覧・管理する UI で使われているため、ダッシュボードの「自分の分報」タブでは user_micro_reports_path(current_user, ...) を使用し、/users/:id/micro_reports の URL 構造を維持する。current_user 名前空間に新しいルートを追加しない方針である。

Applied to files:

  • app/views/users/reports/index.html.slim
📚 Learning: 2025-09-12T01:00:58.452Z
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/views/mentor/buzzes/edit.html.slim:1-0
Timestamp: 2025-09-12T01:00:58.452Z
Learning: app/views/mentor/buzzes/edit.html.slim では `- title` と `- set_meta_tags` の両方が正しく設定されており、タイトルは削除されていない。変更内容はメタディスクリプションの追加のみ。

Applied to files:

  • app/views/users/reports/index.html.slim
📚 Learning: 2025-08-28T00:34:28.541Z
Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/views/application/_global_nav.slim:48-54
Timestamp: 2025-08-28T00:34:28.541Z
Learning: app/views/application/_global_nav.slim のQ&A/ペアワークバッジの環境分岐は、ペアワーク機能の本番リリース後に削除される一時的な実装である。その際、メンター・管理者のみに表示する制限仕様も撤廃される予定のため、現在の実装にはアクセス権限ガードを追加する必要がない。

Applied to files:

  • app/views/users/reports/index.html.slim
📚 Learning: 2025-07-23T20:31:13.856Z
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 8977
File: app/controllers/reports_controller.rb:63-63
Timestamp: 2025-07-23T20:31:13.856Z
Learning: fjordllc/bootcampプロジェクトの`app/controllers/reports_controller.rb`において、`create`と`update`アクションは両方とも`report.save_uniquely`を使用し、同じ`:report_save`イベントと`'report.save'`イベントを発行する。これは両方とも本質的に「レポートの保存」操作であり、作成と更新を区別する必要がないためである。

Applied to files:

  • app/views/users/reports/index.html.slim
  • test/models/report_test.rb
  • app/controllers/users/reports_controller.rb
📚 Learning: 2025-08-31T03:39:07.792Z
Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/views/pair_works/_body.html.slim:85-85
Timestamp: 2025-08-31T03:39:07.792Z
Learning: app/views/pair_works/_body.html.slimテンプレートには2つの独立したif pair_work.solved?条件分岐があり、user変数は最初の分岐でのみ設定されるため、2番目の分岐での参照には注意が必要。

Applied to files:

  • app/views/users/reports/index.html.slim
📚 Learning: 2025-07-23T20:42:19.974Z
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T20:42:19.974Z
Learning: fjordllc/bootcampプロジェクトでは、hタグ(見出し)の文言は日本語でハードコーディングする方針が確立されており、I18n対応は行わない。例:app/views/welcome/logo.html.slimでh2、h3タグが日本語でハードコーディングされている。

Applied to files:

  • app/views/users/reports/index.html.slim
📚 Learning: 2025-09-12T21:18:00.834Z
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で対応すべきである。

Applied to files:

  • test/system/product/unassigned_test.rb
  • app/views/application/_unconfirmed_links_open.html.slim
  • app/helpers/products_helper.rb
📚 Learning: 2025-09-05T03:16:53.387Z
Learnt from: sharoa119
Repo: fjordllc/bootcamp PR: 9012
File: app/queries/grass_learning_time_query.rb:30-37
Timestamp: 2025-09-05T03:16:53.387Z
Learning: reports テーブルには既に (user_id, reported_on) の一意制約インデックス index_reports_on_user_id_and_reported_on が存在するため、学習時間集計クエリのパフォーマンス最適化には追加のインデックスは不要。

Applied to files:

  • test/models/report_test.rb
📚 Learning: 2025-09-05T03:33:31.659Z
Learnt from: sharoa119
Repo: fjordllc/bootcamp PR: 9012
File: db/migrate/20250905025850_remove_redundant_index_from_reports.rb:5-5
Timestamp: 2025-09-05T03:33:31.659Z
Learning: reports テーブルには既存の一意制約インデックス index_reports_on_user_id_and_reported_on があるため、同じカラム組み合わせ (user_id, reported_on) の非一意インデックスは冗長であり削除が適切。

Applied to files:

  • test/models/report_test.rb
📚 Learning: 2025-09-05T03:33:31.659Z
Learnt from: sharoa119
Repo: fjordllc/bootcamp PR: 9012
File: db/migrate/20250905025850_remove_redundant_index_from_reports.rb:5-5
Timestamp: 2025-09-05T03:33:31.659Z
Learning: reports テーブルには既存の一意制約インデックス index_reports_on_user_id_and_reported_on があるため、同じカラム組み合わせ (user_id, reported_on) の非一意インデックスは冗長であり削除が適切。一意制約インデックスは非一意インデックスと同等かそれ以上のクエリ性能を提供する。

Applied to files:

  • test/models/report_test.rb
📚 Learning: 2025-07-10T12:59:27.807Z
Learnt from: kitarou888
Repo: fjordllc/bootcamp PR: 8277
File: test/models/searcher_test.rb:326-331
Timestamp: 2025-07-10T12:59:27.807Z
Learning: モデルテストにおいて、1つのクラスメソッドの複数の挙動を検証する場合、機能の異なる側面を同じテストメソッドでテストすることは、包括的なテストとして適切である。特に`only_me`のような機能では、異なる検索条件(空文字と具体的な検索語)を使い分けることで、より広範囲な動作保証が可能となる。

Applied to files:

  • test/models/report_test.rb
📚 Learning: 2025-10-22T06:04:36.036Z
Learnt from: sjabcdefin
Repo: fjordllc/bootcamp PR: 9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.036Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。

Applied to files:

  • test/models/report_test.rb
📚 Learning: 2025-07-07T12:46:01.650Z
Learnt from: masyuko0222
Repo: fjordllc/bootcamp PR: 8277
File: test/models/searcher_test.rb:0-0
Timestamp: 2025-07-07T12:46:01.650Z
Learning: テストにおいて、`result.all? { |condition| }` でコレクションの全要素が条件を満たすことを確認できれば、その条件を満たさない要素が含まれていないことも論理的に保証されるため、追加のアサーションは冗長となる。

Applied to files:

  • test/models/report_test.rb
📚 Learning: 2025-08-25T08:00:11.369Z
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属性の追加など)は他の箇所との整合性を保つため、別途統一して対応する方針である

Applied to files:

  • app/views/application/_unconfirmed_links_open.html.slim
📚 Learning: 2025-11-17T00:46:30.794Z
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 で削除予定。

Applied to files:

  • app/javascript/components/Notifications.jsx
📚 Learning: 2025-07-30T07:26:36.599Z
Learnt from: ryufuta
Repo: fjordllc/bootcamp PR: 8992
File: app/models/correct_answer_notifier.rb:23-23
Timestamp: 2025-07-30T07:26:36.599Z
Learning: Railsの標準的なヘルパーメソッド(例:`question_url`、`user_path`など)が通常の方法で使用されている場合、それらの動作テストは不要である。これらのメソッドはRailsフレームワーク自体でテストされており、アプリケーション固有のロジックではないため。

Applied to files:

  • app/helpers/products_helper.rb
🧬 Code graph analysis (3)
app/controllers/products_controller.rb (3)
app/controllers/products/unchecked_controller.rb (1)
  • before_action (3-25)
app/controllers/products/self_assigned_controller.rb (1)
  • before_action (3-32)
app/controllers/products/unassigned_controller.rb (1)
  • before_action (3-15)
test/system/product/unassigned_test.rb (2)
test/system/product/self_assigned_test.rb (1)
  • test (5-207)
test/supports/product_helper.rb (1)
  • delete_most_unassigned_products! (12-18)
app/controllers/users/reports_controller.rb (1)
app/controllers/reports/unchecked_controller.rb (1)
  • before_action (3-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (8)
app/javascript/components/Notifications.jsx (1)

9-98: LGTM - コンポーネント全体の構成は適切です

通知一覧の表示、ページネーション、フィルタリングのロジックが明確に分離されています。SWRキーの問題を修正すれば、このコンポーネントは正しく動作します。

test/models/report_test.rb (1)

65-75: LGTM - uncheckedスコープのテストが適切に追加されています

テストは必要な検証を行っており、checksが空であることとReport.uncheckedに含まれることの両方を確認しています。コーディングガイドラインに従い、モデルメソッドのテストケースが適切に追加されています。

app/views/products/self_assigned/index.html.slim (1)

18-19: LGTM - 他のproductsビューと一貫したパターンで実装されています

@products.present? の条件付きレンダリングと unconfirmed_links_label(@target) ヘルパーの使用は、products/index.html.slimproducts/unassigned/index.html.slim と同じパターンに従っています。

app/controllers/products_controller.rb (1)

7-7: LGTM - 他の Products 関連コントローラーと一貫したパターンです

set_target メソッドは Products::UnassignedController での @target = 'unassigned'Products::SelfAssignedController でのパラメータベースの @target 設定と同じパターンに従っています。ビューでの unconfirmed_links_label(@target) 呼び出しをサポートするために必要な変更です。

Also applies to: 158-160

app/controllers/users/reports_controller.rb (1)

66-68: LGTM - 過去のレビューフィードバックに従った実装です

set_unchecked_count を専用メソッドとして切り出すことで、set_reports の責務とカウント取得の責務が適切に分離されています。

なお、set_reports メソッド(36-37行目)でも @user.reports.unchecked.not_wip を使用していますが、現時点では共通化よりも可読性を優先する形で問題ありません。

app/views/application/_unconfirmed_links_open.html.slim (1)

1-5: LGTM! 実装が適切です

フォームアクション構造がクリーンで、type="button" 属性も正しく設定されています。以前のレビューコメントで指摘された点が適切に修正されており、問題ありません。

app/helpers/products_helper.rb (1)

4-14: LGTM! ヘルパーメソッドの実装が適切です

ターゲットに応じた日本語ラベルのマッピングが明確で、すべてのケースが適切にカバーされています。以前のレビューで指摘された単体テストも追加済みです。

test/system/product/unassigned_test.rb (1)

24-36: LGTM! テストの実装が適切です

React コンポーネントからバニラ JavaScript への移行に合わせて、テストが適切に更新されています。ウィンドウ操作の検証から、レンダリングされたリンク要素(js-unconfirmed-link クラス)の存在確認に変更されており、新しい実装方針と一致しています。

他の system test(self_assigned_test.rb など)とも一貫性のあるパターンになっています。

@sharoa119
Copy link
Contributor Author

@komagata さん
おはようございます。
修正いたしましたので、再度ご確認のほどよろしくお願いいたします🙇‍♀️

@sharoa119 sharoa119 requested a review from komagata December 22, 2025 02:33
)
links.forEach((link) => {
window.open(link.href, '_target', 'noopener')
window.open(link.href, '_blank', 'noopener')
Copy link
Member

Choose a reason for hiding this comment

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

ここってなんで_blankに変更されてるんでしたっけ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@komagata さん
コメントありがとうございます。
自分なりに調べたところ、_targetは予約されたtargetではなく、
ウィンドウ名として扱われることがわかり、
そうなると、同じタブが再利用される可能性があるということがわかりました。
このunconfirmed-links-open.jsは複数リンクをそれぞれ新しいタブで開く意図だったため、
毎回新しいタブが開く_blank に変更しています。
よろしくお願いいたします🙇‍♀️

Copy link
Member

Choose a reason for hiding this comment

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

@sharoa119 返信ありがとうございます。

_targetは予約されたtargetではなく、

「予約されたtarget」というのはどういう意味でしょうか?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@komagata さん
ブラウザ側であらかじめ意味と挙動が定義されている
特別な target 名、という意味で使っていました。
_blank / _self など)
これらは仕様上それぞれ挙動が固定されており、
それ以外の文字列は単なるウィンドウ名として扱われる挙動になります。
よろしくお願いいたします🙇‍♀️

Copy link
Member

Choose a reason for hiding this comment

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

@sharoa119 なるほどです〜

@@ -1,5 +1,5 @@
- title "#{@user.login_name} 日報"
- set_meta_tags description: "#{@user.login_name}さんの質問一覧ページです"
- set_meta_tags description: "#{@user.login_name}さんの日報一覧ページです"
Copy link
Member

Choose a reason for hiding this comment

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

👍

@sharoa119 sharoa119 requested a review from komagata January 7, 2026 01:47
Copy link
Member

@komagata komagata left a comment

Choose a reason for hiding this comment

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

確認させていただきました。OKです〜🙆‍♂️

)
links.forEach((link) => {
window.open(link.href, '_target', 'noopener')
window.open(link.href, '_blank', 'noopener')
Copy link
Member

Choose a reason for hiding this comment

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

@sharoa119 なるほどです〜

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.

6 participants