Skip to content

提出物、日報のチェック関連vue.jsを削除#8867

Merged
komagata merged 47 commits intomainfrom
chore/remove-vue-check-component
Aug 2, 2025
Merged

提出物、日報のチェック関連vue.jsを削除#8867
komagata merged 47 commits intomainfrom
chore/remove-vue-check-component

Conversation

@su-su-su-su
Copy link
Copy Markdown
Contributor

@su-su-su-su su-su-su-su commented Jun 30, 2025

Issue

概要

app/javascript/check.vue
app/javascript/product_checker.vue
を廃止するために、関係箇所をRailsとJSで動作するように置き換えました。

変更確認方法

  1. chore/remove-vue-check-componentをローカルに取り込む
git fetch origin chore/remove-vue-check-component
git checkoutchore/remove-vue-check-component 
  1. foreman start -f Procfile.devでサーバーを立ち上げます。

  2. komagataでログイン

ダッシュボードでの動作確認

  1. ダッシュボード(http://localhost:3000) で確認
  • 未アサインの提出物で「担当する」ボタンが表示されることを確認
  • 「担当する」ボタンをクリックして担当機能が正常に動作することを確認

課題一覧ページでの動作確認

  1. 未完了提出物ページ(http://localhost:3000/products/unchecked) の確認
  • 未完了の提出物一覧が表示されることを確認
  • 自分が担当している提出物: 「担当から外れる」ボタンが表示され、クリックで担当解除できることを確認
  • 他の人が担当している提出物: 担当者の名前とアバターが表示され、ボタンが表示されないことを確認
  • 未担当の提出物: 「担当する」ボタンが表示され、クリックで担当できることを確認
  1. 未アサイン提出物ページ(http://localhost:3000/products/unassigned) の確認
  • 未アサインの提出物一覧が表示されることを確認
  • 提出物の「担当する」ボタンが正常に動作することを確認
  • 担当後、該当の提出物がこの一覧から消えることを確認
  1. 自分の担当提出物ページ(http://localhost:3000/products/self_assigned) の確認
  • 自分が担当している提出物一覧が表示されることを確認
  • 全ての提出物で「担当から外れる」ボタンが表示されることを確認
  • 「担当から外れる」をクリックして担当解除が正常に動作することを確認
  • 解除後、提出物が一覧から消えることを確認

日報確認機能の動作確認

  1. 未確認の日報ページにアクセス(http://localhost:3000/reports/unchecked)
  • 日報一覧から未確認の日報を選択
  1. 確認処理の動作確認
  • 日報の「確認」ボタンをクリックして確認処理が正常に動作することを確認
  • 確認後、ページ上部に確認済みスタンプが表示されることを確認
  1. sad気分の場合の確認ダイアログ
  • 気分が「sad」かつコメント0件の日報で「確認」ボタンをクリック
  • 「今日の気分は「sad」ですが、コメント無しで確認しますか?」のダイアログが表示されることを確認
  • キャンセル/OK両方の動作を確認

Screenshot

動きは変更前と変わらないため、スクリーンショットはありません。

Summary by CodeRabbit

  • 新機能

    • プロダクトやレポートに対する「チェック」管理機能を追加しました(チェックの作成・解除、担当者の割り当て・解除)。
    • チェック関連のUIを新しい共通パーツに統合し、管理者・メンター向けに操作性を向上しました。
    • プロダクト担当者割り当て用の新しいコントローラーとルーティングを追加しました。
  • 改善

    • プロダクト・レポート詳細画面のUI/UXを整理し、ボタンやリンクの表示を一貫性のあるものに変更しました。
    • チェック・担当者操作時にトースト通知を表示するようになりました。
    • 自己担当・未担当・未チェックの一覧画面での表示を改善し、ページネーションと絞り込みを強化しました。
    • プロダクト詳細テンプレートの記述をセマンティックかつ一貫性のある形に改善しました。
    • チェック確認時の確認ダイアログをJavaScriptで実装し、誤操作防止を強化しました。
    • Vue.js製のチェック関連コンポーネントを削除し、Railsの部分テンプレートとJavaScriptに置き換えました。
  • バグ修正

    • テストにて、ボタンの存在確認方法をより正確なものに修正しました。
  • リファクタリング

    • Vue.js製のチェック関連コンポーネントを廃止し、Railsベースの実装に統一しました。
    • コードの可読性・保守性向上のため、テンプレートやコントローラーの記述を整理しました。

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jun 30, 2025

"""

Walkthrough

この変更は、チェック機能とチェック担当者(チェッカー)管理のUI・ロジックをVue.jsコンポーネントからRailsの部分テンプレートとコントローラに置き換え、関連するルーティング、コントローラ、JavaScript、テストを全面的に調整しています。UI・UXの一部はトースト通知や確認ダイアログで補強されました。

Changes

ファイル群 変更概要
app/components/products/product_component.html.slim
app/components/products/product_component.rb
ビューの構文・表現を整理し、ユーザーアイコンやリンク生成を部分テンプレート化。ユーザーごとのロールクラス取得メソッドを追加。
app/controllers/checks_controller.rb 新たにチェック作成・削除のRESTfulコントローラを追加。
app/controllers/products/checker_assignments_controller.rb チェッカーのアサイン・解除を行うコントローラを新規追加。
app/controllers/products/self_assigned_controller.rb
app/controllers/products/unassigned_controller.rb
app/controllers/products/unchecked_controller.rb
各コントローラのindexアクションでスコープ・ページネーションを明示的に構築。
app/controllers/products_controller.rb indexアクションの実装。
app/javascript/check.js
app/javascript/check.vue
app/javascript/product_checker.vue
チェック・チェッカー管理のVue関連ファイルを全削除。
app/javascript/check_confirmation.js チェック時のコメント未入力確認ダイアログを表示する新JSモジュール追加。
app/javascript/packs/application.js Vue関連のimportを削除し、新JSモジュールをimport。
app/javascript/comment-checkable.js
app/javascript/new-comment.js
チェック・チェッカー操作後にsessionStorage経由でトースト通知を表示、ページリロードを追加。
app/views/products/_product_body.html.slim
app/views/reports/_report_body.html.slim
チェック操作のdivを削除し、共通部分テンプレート(shared/check_actions)呼び出しに統一。
app/views/shared/_check_actions.html.slim チェック・チェッカー管理UIを提供する新部分テンプレートを追加。
app/views/products/self_assigned/index.html.slim react_componentへのprops渡しの構文修正。
config/routes/products.rb
config/routes/reports.rb
チェック関連のRESTfulルーティングとチェッカーアサイン/解除ルートを追加。
test/system/products_test.rb
test/system/notification/assigned_as_checker_test.rb
test/system/user/products_test.rb
test/system/product/checker_test.rb
ボタン存在確認のassertionへ修正、wait_for_javascript_componentsの削除などテストの調整。

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Browser
    participant RailsApp
    participant DB

    User->>Browser: チェック/チェッカー操作ボタンをクリック
    Browser->>RailsApp: チェック/チェッカー操作のHTTPリクエスト送信
    RailsApp->>DB: チェック・チェッカー情報を作成/更新/削除
    DB-->>RailsApp: 結果返却
    RailsApp-->>Browser: リダイレクト or レスポンス
    Browser->>Browser: sessionStorageにフラグ設定 & ページリロード
    Browser->>User: トースト通知表示
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • kitarou888
  • komagata

Poem

うさぎがぴょんと跳ねて言う、
チェックはVueからRailsへビュー!
ボタンを押せばトーストが、
ほっこり知らせてくれるんだ。
コードもテストもスッキリ新装、
みんなでレビュー、さあ出発!
🐇✨
"""

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 RuboCop (1.76.1)
app/components/products/product_component.rb

rubocop-minitest extension supports plugin, specify plugins: rubocop-minitest instead of require: rubocop-minitest in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
rubocop-capybara extension supports plugin, specify plugins: rubocop-capybara instead of require: rubocop-capybara in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
Unable to find gem rubocop-fjord; is the gem installed? Gem::MissingSpecError
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:309:in rescue in gem_config_path' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:293:in gem_config_path'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:84:in block (2 levels) in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:82:in reverse_each'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:82:in block in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:76:in each_pair'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:76:in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader.rb:66:in load_file'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_store.rb:29:in options_config=' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:160:in act_on_options'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:47:in block in run' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:81:in profile_if_needed'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:43:in run' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/exe/rubocop:19:in <top (required)>'
/usr/local/bin/rubocop:25:in load' /usr/local/bin/rubocop:25:in

'

config/routes/reports.rb

rubocop-minitest extension supports plugin, specify plugins: rubocop-minitest instead of require: rubocop-minitest in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
rubocop-capybara extension supports plugin, specify plugins: rubocop-capybara instead of require: rubocop-capybara in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
Unable to find gem rubocop-fjord; is the gem installed? Gem::MissingSpecError
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:309:in rescue in gem_config_path' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:293:in gem_config_path'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:84:in block (2 levels) in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:82:in reverse_each'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:82:in block in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:76:in each_pair'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:76:in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader.rb:66:in load_file'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_store.rb:29:in options_config=' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:160:in act_on_options'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:47:in block in run' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:81:in profile_if_needed'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:43:in run' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/exe/rubocop:19:in <top (required)>'
/usr/local/bin/rubocop:25:in load' /usr/local/bin/rubocop:25:in

'

config/routes/products.rb

rubocop-minitest extension supports plugin, specify plugins: rubocop-minitest instead of require: rubocop-minitest in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
rubocop-capybara extension supports plugin, specify plugins: rubocop-capybara instead of require: rubocop-capybara in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
Unable to find gem rubocop-fjord; is the gem installed? Gem::MissingSpecError
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:309:in rescue in gem_config_path' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:293:in gem_config_path'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:84:in block (2 levels) in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:82:in reverse_each'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:82:in block in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:76:in each_pair'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader_resolver.rb:76:in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_loader.rb:66:in load_file'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/config_store.rb:29:in options_config=' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:160:in act_on_options'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:47:in block in run' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:81:in profile_if_needed'
/var/lib/gems/3.1.0/gems/rubocop-1.77.0/lib/rubocop/cli.rb:43:in run' /var/lib/gems/3.1.0/gems/rubocop-1.77.0/exe/rubocop:19:in <top (required)>'
/usr/local/bin/rubocop:25:in load' /usr/local/bin/rubocop:25:in

'

  • 11 others

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd6bfb and 6a40438.

⛔ Files ignored due to path filters (1)
  • Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (26)
  • app/components/products/product_component.html.slim (3 hunks)
  • app/components/products/product_component.rb (1 hunks)
  • app/controllers/checks_controller.rb (1 hunks)
  • app/controllers/products/checker_assignments_controller.rb (1 hunks)
  • 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 (1 hunks)
  • app/javascript/check.js (0 hunks)
  • app/javascript/check.vue (0 hunks)
  • app/javascript/check_confirmation.js (1 hunks)
  • app/javascript/comment-checkable.js (2 hunks)
  • app/javascript/new-comment.js (2 hunks)
  • app/javascript/packs/application.js (1 hunks)
  • app/javascript/product_checker.vue (0 hunks)
  • app/views/products/_product_body.html.slim (1 hunks)
  • app/views/products/self_assigned/index.html.slim (1 hunks)
  • app/views/reports/_report_body.html.slim (1 hunks)
  • app/views/shared/_check_actions.html.slim (1 hunks)
  • config/routes/products.rb (1 hunks)
  • config/routes/reports.rb (1 hunks)
  • test/components/product/product_component_test.rb (1 hunks)
  • test/system/notification/assigned_as_checker_test.rb (1 hunks)
  • test/system/product/checker_test.rb (1 hunks)
  • test/system/products_test.rb (2 hunks)
  • test/system/user/products_test.rb (1 hunks)
💤 Files with no reviewable changes (3)
  • app/javascript/check.js
  • app/javascript/check.vue
  • app/javascript/product_checker.vue
✅ Files skipped from review due to trivial changes (2)
  • app/javascript/packs/application.js
  • app/controllers/checks_controller.rb
🚧 Files skipped from review as they are similar to previous changes (20)
  • test/system/notification/assigned_as_checker_test.rb
  • test/system/user/products_test.rb
  • app/components/products/product_component.rb
  • test/system/product/checker_test.rb
  • app/controllers/products/unchecked_controller.rb
  • app/controllers/products_controller.rb
  • config/routes/reports.rb
  • app/views/products/_product_body.html.slim
  • app/controllers/products/unassigned_controller.rb
  • app/views/reports/_report_body.html.slim
  • app/views/products/self_assigned/index.html.slim
  • app/javascript/new-comment.js
  • config/routes/products.rb
  • app/javascript/comment-checkable.js
  • app/controllers/products/self_assigned_controller.rb
  • test/system/products_test.rb
  • app/views/shared/_check_actions.html.slim
  • app/javascript/check_confirmation.js
  • app/controllers/products/checker_assignments_controller.rb
  • app/components/products/product_component.html.slim
🧰 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/components/product/product_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/product/product_component_test.rb
🧠 Learnings (2)
📓 Common learnings
Learnt from: ryufuta
PR: fjordllc/bootcamp#8992
File: app/models/correct_answer_notifier.rb:23-23
Timestamp: 2025-07-30T07:26:36.563Z
Learning: PRのスコープを維持することが重要であり、バグ修正PRにおいて、修正対象以外の機能(例:ルーティングテスト)の追加提案は適切ではない。そのような改善が必要な場合は別PRで対応すべきである。
test/components/product/product_component_test.rb (2)

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

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

🧬 Code Graph Analysis (1)
test/components/product/product_component_test.rb (2)
app/models/practice.rb (1)
  • product (139-141)
app/components/products/product_component.rb (1)
  • role_class_for_user (18-20)
⏰ 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 (1)
test/components/product/product_component_test.rb (1)

271-288: 新しいメソッドに対する適切なユニットテストが追加されています。

role_class_for_userメソッドのテストが正しく実装されています:

  • 異なる役割(学生、メンター、管理者)のユーザーに対して適切なCSSクラスが返されることを検証
  • 管理者ユーザーについては動的にprimary_roleを使用する柔軟な実装
  • 既存のテストパターンに従った一貫した構造
  • コーディングガイドラインに従って、追加されたメソッドに対するユニットテストが適切に作成されている
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/remove-vue-check-component

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@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: 3

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

17-24: コメント数の取得ロジックを改善できます

コメント数を取得するセレクタが脆弱です。要素が見つからない場合やinnerHTMLが数値でない場合の処理を強化することをお勧めします。

- const commentCountElement = document.querySelector(
-   'a[href="#comments"] > span'
- )
- let hasComment = false
- if (commentCountElement) {
-   const numberOfComments = parseInt(commentCountElement.innerHTML, 10)
-   hasComment = numberOfComments > 0
- }
+ const commentCountElement = document.querySelector(
+   'a[href="#comments"] > span'
+ )
+ let hasComment = false
+ if (commentCountElement) {
+   const numberOfComments = parseInt(commentCountElement.textContent, 10)
+   hasComment = !isNaN(numberOfComments) && numberOfComments > 0
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d28244 and 6904e64.

📒 Files selected for processing (24)
  • app/components/products/product_component.html.slim (1 hunks)
  • app/components/products/product_component.rb (1 hunks)
  • app/controllers/checks_controller.rb (1 hunks)
  • 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/javascript/check.js (0 hunks)
  • app/javascript/check.vue (0 hunks)
  • app/javascript/check_confirmation.js (1 hunks)
  • app/javascript/packs/application.js (2 hunks)
  • app/javascript/product.vue (0 hunks)
  • app/javascript/product_checker.vue (0 hunks)
  • app/views/products/_product_body.html.slim (1 hunks)
  • app/views/products/_product_list.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/reports/_report_body.html.slim (1 hunks)
  • app/views/shared/_check_actions.html.slim (1 hunks)
  • config/routes/products.rb (1 hunks)
  • config/routes/reports.rb (1 hunks)
  • test/system/products_test.rb (3 hunks)
💤 Files with no reviewable changes (4)
  • app/javascript/product.vue
  • app/javascript/check.js
  • app/javascript/check.vue
  • app/javascript/product_checker.vue
🔇 Additional comments (38)
app/components/products/product_component.rb (1)

18-20: 実装が適切です

既存のrole_classメソッドと一貫性があり、ユーザーのロールに基づくCSSクラス生成の柔軟性を提供しています。コメント一覧でのユーザーアイコン表示などで活用されるものと思われます。

app/javascript/packs/application.js (2)

15-15: Vue コンポーネントから確認ダイアログへの移行

check.jsからcheck_confirmation.jsへの変更は、Vueコンポーネントを削除してシンプルな確認ダイアログに置き換える今回のリファクタリングと整合性があります。


82-82: トースト通知の追加

toast.jsの追加により、ユーザーへのフィードバック機能が強化されます。サーバーサイドレンダリングへの移行に伴い、適切なUXを提供するための変更と考えられます。

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

24-24: Reactコンポーネントからサーバーサイドレンダリングへの移行の検証結果

  • app/controllers/products/unchecked_controller.rb:14 で
    @products = … .page(params[:page]).per(50) が設定されている
  • app/views/products/_product_list.html.slim:99 で
    = paginate @products が呼ばれている

これにより、サーバーサイドでのページネーション実装が正しく組み込まれていることを確認しました。変更を承認します。

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

17-18: サーバーサイドレンダリングとページネーションの実装

Reactコンポーネントからサーバーサイドパーシャルへの移行が適切に実装されています。@products.respond_to?(:current_page)を使った条件付きページネーション表示は堅実なアプローチです。

app/views/reports/_report_body.html.slim (1)

27-27: チェック機能の共通パーシャル化

インラインのVue/Reactコンポーネントから共通パーシャルshared/check_actionsへの移行により、レポートと提出物間でのチェックUI の一貫性と保守性が向上しています。ポリモーフィックな実装に適したパラメータ設計です。

app/controllers/products/unassigned_controller.rb (1)

7-11: LGTM: サーバーサイドレンダリングへの移行に対応した適切な実装

Vue.jsコンポーネントからサーバーサイドレンダリングへの移行に伴い、ページネーション機能が適切に追加されています。1ページあたり50件の表示設定も妥当です。

config/routes/reports.rb (1)

8-10: LGTM: RESTfulなネストルートの適切な実装

reportsリソース配下にchecksリソースをネストし、create/destroyアクションのみに制限する設計は、チェック機能の要件に適合しており、Rails慣例に従った適切な実装です。

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

15-18: LGTM: React コンポーネントからサーバーサイドレンダリングへの適切な移行

Reactコンポーネントの削除とサーバーサイドパーシャルレンダリングへの移行が適切に実装されています。CSSクラスの追加とページネーション機能の統合も正しく行われています。

app/controllers/products/unchecked_controller.rb (1)

10-15: LGTM: 適切なスコープ構築とページネーション実装

checker_idによる条件付きフィルタリングが適切に実装され、スコープ構築とクエリ実行の責任分離も適切です。他のコントローラーとの一貫性も保たれています。

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

15-16: LGTM: 一貫したサーバーサイドレンダリング実装

Reactコンポーネントからパーシャルレンダリングへの移行が適切に実装されており、respond_to?(:current_page)による防御的プログラミングも評価できます。他のビューとの一貫性も保たれています。

config/routes/products.rb (1)

10-16: 新しいルート定義が適切に設計されています

Vue.jsコンポーネントを削除した代わりに追加されたルート定義は、RESTfulな設計に従っており適切です。メンバールート(assign_checker、unassign_checker)とネストしたリソース(checks)の組み合わせにより、担当者の割り当て/解除とチェック機能を適切に分離できています。

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

36-36: 共有パーシャルへの置き換えが適切です

Vue.jsコンポーネントを削除し、shared/check_actionsパーシャルに置き換えたことで、チェック機能のUIロジックが集約され、保守性が向上しています。パラメータ名も明確で、Rails の慣習に従っています。

app/controllers/products_controller.rb (3)

8-13: index アクションの実装が適切です

ページネーション機能が適切に実装されており、50件ずつの表示制限とIDによる並び順も妥当です。Reactコンポーネントからサーバーサイドレンダリングへの移行に必要な変更が正しく行われています。


83-87: 担当者割り当て機能が適切に実装されています

assign_checkerアクションはシンプルで明確な実装になっています。update!を使用することで例外処理も適切に行われ、redirect_backでユーザビリティも考慮されています。


89-93: 担当者解除機能が適切に実装されています

unassign_checkerアクションも同様にシンプルで明確です。checkernilに設定することで担当者を適切に解除し、適切な通知メッセージも表示されます。

test/system/products_test.rb (3)

55-55: ボタンの存在チェックがより正確になりました

assert_textからpage.has_button?への変更により、ボタン要素の存在チェックがより正確になっています。サーバーサイドレンダリングへの移行に合わせた適切な修正です。


372-372: 担当ボタンのテストが改善されました

担当ボタンの存在確認がpage.has_button?を使用することで、より信頼性の高いテストになっています。


380-380: ページネーションのテストが更新されました

ページネーションリンクのセレクターがbuttonからa要素に変更されており、サーバーサイドレンダリングされたページネーションの実装に正しく対応しています。

Also applies to: 402-402

app/controllers/products/self_assigned_controller.rb (3)

6-6: パラメータバリデーションが改善されました

presence_inを使用したパラメータバリデーションにより、コードがより簡潔で読みやすくなりました。不正な値の場合のデフォルト値設定も適切です。


7-11: ページネーション機能が適切に実装されています

プライベートメソッドを使用したプロダクト取得とページネーション機能の組み合わせが適切です。他のコントローラーとの一貫性も保たれています。


20-30: プライベートメソッドの設計が優秀です

build_self_assigned_productsメソッドは単一責任の原則に従って設計されており、case文を使用したターゲット別の処理分岐も明確です。メソッドの分離により保守性が向上しています。

app/javascript/check_confirmation.js (3)

5-9: 早期returnの実装が適切です

ボタンが見つからない場合の早期returnが正しく実装されており、エラーを防いでいます。


28-37: 確認ダイアログの条件ロジックが正確です

sad感情、コメントなし、新規チェックの3つの条件を適切に組み合わせており、ビジネスロジックが正しく実装されています。


11-12: 検証結果:クローンによる競合は発生しません

  • #js-shortcut-check は以下の 3 箇所でのみ使用されています
    • app/views/shared/_check_actions.html.slim(ボタン要素の定義)
    • app/javascript/shortcut.js(ショートカットキー押下時の button.click() 呼び出し)
    • app/javascript/check_confirmation.js(cloneNodereplaceChild→クリック確認用のイベント登録)
  • 他ライブラリや Turbo がこの要素に対して直接イベントリスナーをアタッチしておらず、cloneNode(true) によって削除されるのは、元の要素に紐づくユーザー定義のリスナーのみです。
  • Rails の form_with local: true もデフォルトでは要素単位のイベントを登録しないため、フォーム送信挙動に影響はありません。

以上より、この実装で問題はないと判断しました。

app/controllers/checks_controller.rb (3)

3-4: 適切な認証が実装されています

スタッフのログインが必要な機能に対して適切な認証が実装されています。


9-13: 重複チェックの防止ロジックが優秀です

同一ユーザーによる重複チェックを適切に防止し、わかりやすいメッセージを表示しています。


38-46: ポリモーフィック関係の処理が適切です

find_checkableメソッドでProductとReportの両方を適切に処理し、必要なパラメータが不足している場合の例外処理も実装されています。

app/views/shared/_check_actions.html.slim (4)

1-1: アクセス制御が適切に実装されています

管理者またはメンターのみがチェック機能にアクセスできるよう適切に制限されています。


7-21: 担当者割り当てロジックが優れています

Product特有の担当者割り当て機能が適切に実装されており、条件分岐も論理的です。担当者の表示もユーザーフレンドリーです。


33-35: ボタンのdata属性の使用法が正しいです

JavaScriptでの処理に必要なdata-has_check属性が適切に設定されており、check_confirmation.jsとの連携が取れています。


25-35: ルーティング設定の確認結果
config/routes/reports.rb および config/routes/products.rb にて、それぞれ resources :checks, only: %i[create destroy] がネスト定義されているため、
polymorphic_path([checkable, check]) および polymorphic_path([checkable, :checks]) の使用は問題ありません。
修正は不要です。

app/views/products/_product_list.html.slim (3)

68-73: ユーザーアイコンの部分テンプレート使用が改善されています

ユーザーアイコンの表示で部分テンプレートとcachedオプションを使用しており、パフォーマンスとメンテナンス性が向上しています。


92-98: 合格スタンプの表示が直感的です

チェック済みの提出物に対する合格スタンプの表示が分かりやすく、日付とチェック者の情報も適切に表示されています。


2-2: N+1クエリの懸念は解消されています

app/controllers/products_controller.rb の user_products メソッドで以下のように includes が使われており、関連データが事前読み込みされています。

  • app/controllers/products_controller.rb: user_products メソッド内
    .includes(:practice, :user, :comments, :checks, comments: :user)

これにより、@products.each 内でのユーザー情報、コメント、チェック、練習情報へのアクセス時に N+1 クエリは発生しません。

app/components/products/product_component.html.slim (3)

1-1: メソッド呼び出しへのリファクタリングが優秀です

@product.wip?のような適切なメソッド呼び出しにリファクタリングされており、コードの可読性が向上しています。

Also applies to: 13-13, 30-30


5-5: ユーザーアイコンの部分テンプレート使用が改善されています

ユーザーアイコンの表示を部分テンプレートで統一しており、メンテナンス性が向上しています。


70-70: ヘルパーメソッドの使用が適切です

role_class_for_user(user)ヘルパーメソッドを使用してCSSクラスを動的に決定しており、ロジックの分離が適切に行われています。

Comment on lines +28 to +38
def destroy
@check = Check.find(params[:id])
@checkable = @check.checkable

@check.destroy
redirect_back(fallback_location: @checkable)
end
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

destroyアクションのセキュリティを強化してください

destroyアクションで、現在のユーザーがそのチェックの所有者であることを確認する必要があります。

  def destroy
    @check = Check.find(params[:id])
+   
+   unless @check.user == current_user || current_user.admin?
+     redirect_back(fallback_location: root_path, alert: 'このチェックを削除する権限がありません。')
+     return
+   end
+   
    @checkable = @check.checkable

    @check.destroy
    redirect_back(fallback_location: @checkable)
  end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def destroy
@check = Check.find(params[:id])
@checkable = @check.checkable
@check.destroy
redirect_back(fallback_location: @checkable)
end
def destroy
@check = Check.find(params[:id])
unless @check.user == current_user || current_user.admin?
redirect_back(fallback_location: root_path, alert: 'このチェックを削除する権限がありません。')
return
end
@checkable = @check.checkable
@check.destroy
redirect_back(fallback_location: @checkable)
end
🤖 Prompt for AI Agents
In app/controllers/checks_controller.rb around lines 28 to 34, the destroy
action lacks a check to verify that the current user owns the check before
allowing deletion. Add a condition to confirm that the current user is the owner
of @check before calling destroy. If the user is not the owner, prevent deletion
and handle the response appropriately, such as redirecting with an error message
or raising an authorization error.

Comment on lines +77 to +90
- if current_user.mentor? && product.checks.empty?
.card-list-item__row.is-only-mentor
.card-list-item__assignee
- if product.checker_id.nil?
= form_with url: assign_checker_product_path(product), method: :patch, local: true do |f|
= f.submit '担当する', class: 'a-button is-sm is-secondary is-block'
- elsif product.checker_id == current_user.id
= form_with url: unassign_checker_product_path(product), method: :delete, local: true do |f|
= f.submit '担当から外れる', class: 'a-button is-sm is-warning is-block'
- else
.a-button.is-sm.is-block.card-list-item__assignee-button.is-only-mentor
span.card-list-item__assignee-image
= image_tag product.checker.avatar_url, class: 'a-user-icon', width: 20, height: 20
span.card-list-item__assignee-name = product.checker.login_name
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

担当者割り当てロジックが重複しています

この担当者割り当てのロジックはshared/check_actionsパーシャルと重複しています。コードの重複を避けるため、共通化を検討してください。

- - if current_user.mentor? && product.checks.empty?
-   .card-list-item__row.is-only-mentor
-     .card-list-item__assignee
-       - if product.checker_id.nil?
-         = form_with url: assign_checker_product_path(product), method: :patch, local: true do |f|
-           = f.submit '担当する', class: 'a-button is-sm is-secondary is-block'
-       - elsif product.checker_id == current_user.id
-         = form_with url: unassign_checker_product_path(product), method: :delete, local: true do |f|
-           = f.submit '担当から外れる', class: 'a-button is-sm is-warning is-block'
-       - else
-         .a-button.is-sm.is-block.card-list-item__assignee-button.is-only-mentor
-           span.card-list-item__assignee-image
-             = image_tag product.checker.avatar_url, class: 'a-user-icon', width: 20, height: 20
-           span.card-list-item__assignee-name = product.checker.login_name
+ - if current_user.mentor? && product.checks.empty?
+   = render 'shared/check_actions', checkable: product, checkable_type: 'Product', checkable_label: '提出物', display_only_checker_assignment: true
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- if current_user.mentor? && product.checks.empty?
.card-list-item__row.is-only-mentor
.card-list-item__assignee
- if product.checker_id.nil?
= form_with url: assign_checker_product_path(product), method: :patch, local: true do |f|
= f.submit '担当する', class: 'a-button is-sm is-secondary is-block'
- elsif product.checker_id == current_user.id
= form_with url: unassign_checker_product_path(product), method: :delete, local: true do |f|
= f.submit '担当から外れる', class: 'a-button is-sm is-warning is-block'
- else
.a-button.is-sm.is-block.card-list-item__assignee-button.is-only-mentor
span.card-list-item__assignee-image
= image_tag product.checker.avatar_url, class: 'a-user-icon', width: 20, height: 20
span.card-list-item__assignee-name = product.checker.login_name
- if current_user.mentor? && product.checks.empty?
= render 'shared/check_actions',
checkable: product,
checkable_type: 'Product',
checkable_label: '提出物',
display_only_checker_assignment: true
🤖 Prompt for AI Agents
In app/views/products/_product_list.html.slim lines 77 to 90, the checker
assignment logic duplicates the code found in the shared/check_actions partial.
To fix this, extract the duplicated logic into a single shared partial or helper
and replace the current code with a call to that shared partial, ensuring the UI
and functionality remain consistent while avoiding code duplication.

Copy link
Copy Markdown

@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 (2)
app/javascript/comment-checkable.js (2)

63-64: sessionStorageとページリロードによる通知連携が機能的に正しく実装されています

確認操作成功後にsessionStorageフラグを設定してページをリロードする実装は、new-comment.jsでの通知表示と適切に連携しています。ただし、ページリロードによるアプローチはUXの観点で改善の余地があります。

将来的にはページリロードを避けてDOMの部分更新を行うアプローチを検討することをお勧めします:

-        sessionStorage.setItem('showCheckToast', 'true')
-        location.reload()
+        // DOM更新とトースト通知を直接実行
+        updateCheckUIElements()
+        toast(getCheckToastMessage(checkableType))

88-90: 担当者割り当て後の処理が適切に実装されています

sessionStorageフラグの設定とページリロードにより、担当者割り当て後の通知が適切に処理されています。check操作と同様のパターンで一貫性があります。

check操作と同様に、将来的にはページリロードを避けた実装を検討することをお勧めします。また、alert()の使用も統一されたトースト通知に置き換えることを検討してください:

-        alert('提出物の担当になりました。')
-        
-        const event = new Event('checkerAssigned')
-        window.dispatchEvent(event)
-
-        sessionStorage.setItem('showAssignedToast', 'true')
-
-        location.reload()
+        // DOM更新とトースト通知を直接実行
+        updateCheckerUIElements()
+        toast('担当になりました。')
+        
+        const event = new Event('checkerAssigned')
+        window.dispatchEvent(event)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6173fc5 and bcb3841.

📒 Files selected for processing (3)
  • app/javascript/comment-checkable.js (2 hunks)
  • app/javascript/new-comment.js (2 hunks)
  • app/views/comments/_comments.html.slim (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • app/views/comments/_comments.html.slim
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: tyrrell-IH
PR: fjordllc/bootcamp#8807
File: app/views/welcome/job_support.html.slim:391-392
Timestamp: 2025-06-21T22:30:20.116Z
Learning: このプロジェクトにはRailsの組み込みmarkdownヘルパが利用できないため、markdownのHTML変換には自前でヘルパを実装する必要がある。
🧬 Code Graph Analysis (1)
app/javascript/new-comment.js (1)
app/javascript/vanillaToast.js (1)
  • toast (3-13)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build_and_test
🔇 Additional comments (2)
app/javascript/new-comment.js (2)

11-25: sessionStorageを使用したトースト通知の実装が適切です

ページロード時にsessionStorageのフラグを確認してトースト通知を表示する実装は、Vue コンポーネントからの移行において適切なアプローチです。担当者の割り当てと確認済み操作の両方に対応し、コメント対象の種類に応じて適切なメッセージを表示しています。


206-208: 重複通知を防ぐ条件分岐が適切です

担当者の割り当てや確認済み操作が発生した場合にトースト通知を抑制する条件分岐により、重複した通知を防いでいます。これにより、ユーザーエクスペリエンスが向上します。

@su-su-su-su
Copy link
Copy Markdown
Contributor Author

@kitarou888
お疲れさまです。
お手隙の際にこちらのレビューをお願いできますでしょうか?
ご検討のほどよろしくお願いします🙇

@su-su-su-su su-su-su-su requested a review from kitarou888 July 4, 2025 15:02
@kitarou888
Copy link
Copy Markdown
Contributor

@su-su-su-su
承知しました!
今日明日で対応いたします🙆‍♂️

@kitarou888
Copy link
Copy Markdown
Contributor

@su-su-su-su
コンフリクトが発生しているようですので、解決していただけますか?

Copy link
Copy Markdown
Contributor

@kitarou888 kitarou888 left a comment

Choose a reason for hiding this comment

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

@su-su-su-su
ひととおりチェック完了しました!
動作確認、コードともに問題ありませんでした。

今回の非Vueにおいて必要なDiffなのか、リファクタリングなのかがちょっと判断できなかったため、コメントに残させていただきましたのでご確認ください🙏

span.a-meta__value = "約#{until_next_elapsed_days}時間"

- if @product.comments.size > 0
- if @product.comments.any?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

こちらは今回の移行に必要な変更ですか?それともただのリファクタリングですか?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

エラーが出て色々修正しており、その変更したコードが残ってしまっていたのと思います。
不要なので元のコードに戻しました。

.card-list-item-meta__items
.card-list-item-meta__item
- if @product.user.training_ends_on
.card-list-item-meta
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

この行が追加されていることで以降のネストが1段深くなっていますが、こちらは意図したものでしょうか?

Copy link
Copy Markdown
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 +44 to +37
span.a-meta__label
| 提出
= l(@product.published_at)
span.a-meta__label 提出
span.a-meta__value = l(@product.published_at)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

これと似たdiffが多く見られますが、今回の移行において必要な変更でしょうか?
それともリファクタリングでしょうか?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

こちらも不要なので元のコードに戻しました。

const event = new Event('checkerAssigned')
window.dispatchEvent(event)

sessionStorage.setItem('showAssignedToast', 'true')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ここでsessionStorageを使用されている意図について、教えていただけるとうれしいです。(次のnew-comment.jsについても同様)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Vue.js(非同期)からRailsへの移行によりページリロードが必要となったため、リロードを跨いでtoastの表示状態を保持する目的で使用しています。

select 'komagata', from: 'product_checker_id'
click_button '提出する'
assert_text '担当から外れる'
assert page.has_button?('担当から外れる')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

このdiffは今回のイシューで必要になった変更ですか?それともリファクタリングでしょうか?(他も同様)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

assert_text '担当から外れる' のテストが失敗しており、原因を確認したところassert_textではボタンの文言をうまく認識できていないようでした。

Notification::AssignedAsCheckerTest#test_not_notice_self_assigned_as_checker [test/system/notification/assigned_as_checker_test.rb:41]:
expected to find text "担当から外れる"

assert page.has_button?('担当から外れる')に修正することで、テストが成功しました。
また他のコードを確認するとassert page.has_button?は使われていなかったので、assert_buttonに変更しました。

@su-su-su-su
Copy link
Copy Markdown
Contributor Author

su-su-su-su commented Jul 5, 2025

@kitarou888
ご確認、コメントありがとうございます!

内容確認しました。すぐに対応できず申し訳ありません。
明日と明後日と仕事があるため、火曜日までにはコンフリクトの解決とコメントへの返信をいたします。

少しお時間いただきますが、よろしくお願いいたします🙇

@su-su-su-su su-su-su-su force-pushed the chore/remove-vue-check-component branch from 42112ad to 488660a Compare July 7, 2025 14:48
@su-su-su-su
Copy link
Copy Markdown
Contributor Author

@kitarou888
お疲れ様です。

ご指摘頂いたコンフリクトを解決、コメントに対しての修正を行いました。
お手すきの際にご確認お願いいたします🙇

Copy link
Copy Markdown
Contributor

@kitarou888 kitarou888 left a comment

Choose a reason for hiding this comment

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

@su-su-su-su
とくに問題は見当たりませんでしたのでApproveにしました!

1点だけコメントしましたので、必要に応じて修正をお願いできればと思います。

Comment on lines +28 to +36
if (hasSadEmotion && !hasComment && isNewCheck) {
if (
!window.confirm(
'今日の気分は「sad」ですが、コメント無しで確認しますか?'
)
) {
event.preventDefault()
return false
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ここのところ、

  • if文をネストしている点
  • 条件式の改行が多い点(以下1行にまとめても62文字でした)
if (!window.confirm('今日の気分は「sad」ですが、コメント無しで確認しますか?')) {
  event.preventDefault()
  return false
}

が気になりました。
if文のネストについてはこのように分けている理由があれば教えていただけるとうれしいです。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます。
削除したapp/javascript/check.vueの記述に合わせた形になります。
ご指摘の通りまとめることも可能だと思いますが、どちらが良いでしょうか?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@su-su-su-su
ご判断はお任せします!
が、もし私だったら今回のケースはリファクタリングによってDiffが出るわけではないですので、明らかに汚いコードがあれば直してしまうと思います!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@kitarou888
なるほどですね!
こちらも修正しました!

    if (
      hasSadEmotion &&
      !hasComment &&
      isNewCheck &&
      !window.confirm('今日の気分は「sad」ですが、コメント無しで確認しますか?')
    ) {
      event.preventDefault()
      return false
    }

アドバイスありがとうございました!

@su-su-su-su
Copy link
Copy Markdown
Contributor Author

@kitarou888
ご指摘ありがとうございます!
コメントを返させていただきました。お手すきの際にご確認お願いいたします。

@su-su-su-su su-su-su-su force-pushed the chore/remove-vue-check-component branch from 144be0b to b2f42a3 Compare July 12, 2025 04:50
@su-su-su-su
Copy link
Copy Markdown
Contributor Author

@komagata
メンバーの方からApproveいただきました。

お手すきの際にご確認お願いいたします。

@su-su-su-su su-su-su-su requested a review from komagata July 12, 2025 12:49
@komagata
Copy link
Copy Markdown
Member

Issueのタイトルをわかりやすいもの(日本語)に修正してください。

@su-su-su-su su-su-su-su changed the title Chore/remove vue check component 提出物、日報のチェック関連vue.jsを削除 Jul 13, 2025
@su-su-su-su
Copy link
Copy Markdown
Contributor Author

@komagata
ご指摘ありがとうございます。
修正しました。ご確認お願いいたします。

Comment on lines +12 to +13
patch :assign_checker
delete :unassign_checker
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

極力RESTfulなURL設計だけを使いたい感じです。

DHHはどのようにRailsのコントローラを書くのか | POSTD

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます。
新しくapp/controllers/products/checker_assignments_controller.rbを作成して、RESTfulなルートに修正しました。

@su-su-su-su su-su-su-su force-pushed the chore/remove-vue-check-component branch from b2f42a3 to 6fc6975 Compare July 17, 2025 13:21
Copy link
Copy Markdown

@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: 3

🧹 Nitpick comments (1)
app/controllers/products/checker_assignments_controller.rb (1)

18-21: パラメータの処理方法と認可について検討が必要です。

現在の実装では product_idid の両方をチェックしていますが、以下の点を検討してください:

  1. 両方のパラメータをチェックする理由を明確にする
  2. プロダクトにアクセスする権限があるかの認可チェック
  3. エラーハンドリングの一貫性

以下のような改善を検討してください:

def find_product
  product_id = params[:product_id] || params[:id]
  @product = Product.find(product_id)
+  authorize! :read, @product
+rescue ActiveRecord::RecordNotFound
+  redirect_to products_path, alert: '指定されたプロダクトが見つかりません。'
end

また、パラメータの使い分けについてコメントを追加することを推奨します。

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2f42a3 and 6fc6975.

📒 Files selected for processing (25)
  • app/components/products/product_component.html.slim (3 hunks)
  • app/components/products/product_component.rb (1 hunks)
  • app/controllers/checks_controller.rb (1 hunks)
  • app/controllers/products/checker_assignments_controller.rb (1 hunks)
  • 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/javascript/check.js (0 hunks)
  • app/javascript/check.vue (0 hunks)
  • app/javascript/check_confirmation.js (1 hunks)
  • app/javascript/comment-checkable.js (2 hunks)
  • app/javascript/new-comment.js (2 hunks)
  • app/javascript/packs/application.js (2 hunks)
  • app/javascript/product_checker.vue (0 hunks)
  • app/views/products/_product_body.html.slim (1 hunks)
  • app/views/products/self_assigned/index.html.slim (1 hunks)
  • app/views/reports/_report_body.html.slim (1 hunks)
  • app/views/shared/_check_actions.html.slim (1 hunks)
  • config/routes/products.rb (1 hunks)
  • config/routes/reports.rb (1 hunks)
  • test/system/notification/assigned_as_checker_test.rb (1 hunks)
  • test/system/product/checker_test.rb (1 hunks)
  • test/system/products_test.rb (2 hunks)
  • test/system/user/products_test.rb (1 hunks)
💤 Files with no reviewable changes (3)
  • app/javascript/check.js
  • app/javascript/check.vue
  • app/javascript/product_checker.vue
🚧 Files skipped from review as they are similar to previous changes (21)
  • app/javascript/packs/application.js
  • test/system/user/products_test.rb
  • app/views/products/self_assigned/index.html.slim
  • test/system/notification/assigned_as_checker_test.rb
  • test/system/product/checker_test.rb
  • app/views/reports/_report_body.html.slim
  • app/controllers/products/unassigned_controller.rb
  • app/components/products/product_component.rb
  • app/controllers/products/unchecked_controller.rb
  • app/javascript/new-comment.js
  • app/views/products/_product_body.html.slim
  • app/controllers/products_controller.rb
  • config/routes/reports.rb
  • app/controllers/products/self_assigned_controller.rb
  • test/system/products_test.rb
  • app/javascript/comment-checkable.js
  • app/javascript/check_confirmation.js
  • app/controllers/checks_controller.rb
  • config/routes/products.rb
  • app/views/shared/_check_actions.html.slim
  • app/components/products/product_component.html.slim
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb

Instructions used from:

Sources:
⚙️ CodeRabbit Configuration File

⏰ 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 (1)
app/controllers/products/checker_assignments_controller.rb (1)

3-4: クラス定義とbefore_actionの実装が適切です。

Rails の慣習に従った適切なコントローラ構造になっています。before_action を使用してプロダクトの検索を共通化しているのも良いアプローチです。

@su-su-su-su
Copy link
Copy Markdown
Contributor Author

@coderabbitai
ご指摘ありがとうございます。
確認があります。

app/controllers/products_controller.rb

  def assign_checker
    @product = find_product
    @product.update!(checker: current_user)
    redirect_back(fallback_location: @product, notice: '担当になりました。')
  end

  def unassign_checker
    @product = find_product
    @product.update!(checker: nil)
    redirect_back(fallback_location: @product, notice: '担当から外れました。')
  end


app/controllers/products/checker_assignments_controller.rb
に切り分けています。
元のコードには指摘してもらった認可チェックとバリデーションはありませんでしたので確認してください。

またユーザーが担当者になる権限があるかの認可チェック
既に他の担当者がいる場合の制御
ユーザーが担当者になる資格があるかの検証
についてはapp/components/products/product_component.html.slim

      - if @is_mentor && @product.checks.size == 0
        .card-list-item__row.is-only-mentor
          .card-list-item__assignee
            - if @product.checker_id.nil?
              = form_with url: products_checker_assignments_path, method: :post, local: true do |f|
                = f.hidden_field :product_id, value: @product.id
                = f.submit '担当する', class: 'a-button is-sm is-secondary is-block'
            - elsif @product.checker_id == @current_user_id
              = form_with url: products_checker_assignment_path(id: @product.id), method: :delete, local: true do |f|
                = f.submit '担当から外れる', class: 'a-button is-sm is-warning is-block'
            - else
              .a-button.is-sm.is-block.card-list-item__assignee-button.is-only-mentor
                span.card-list-item__assignee-image
                  = image_tag @product.checker.avatar_url, class: 'a-user-icon', width: 20, height: 20
                span.card-list-item__assignee-name
                  = @product.checker.login_name

でチェックしていますがいかがでしょうか。

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jul 17, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@su-su-su-su ご質問ありがとうございます。確認させていただきます。

コントローラの実装を確認した結果、以下の点が懸念されます:

セキュリティ上の問題点:

  1. コントローラレベルでの認可チェックが不足

    • CheckerAssignmentsControllerにはbefore_actionでの認可チェックがありません
    • ビューレベルでの@is_mentorチェックのみでは不十分です
  2. 直接APIアクセスによるバイパス可能性

    • ビューを経由せずに直接POST/DELETEリクエストを送信することで、認可チェックを回避できる可能性があります

推奨される修正:

class Products::CheckerAssignmentsController < ApplicationController
  before_action :staff_login_required  # または適切な認可メソッド
  before_action :find_product
  before_action :validate_mentor_permission
  
  def create
    # 既存の実装に加えて、二重チェックを追加
    return redirect_back(fallback_location: @product, alert: 'この提出物は既にチェック済みです。') if @product.checks.any?
    
    @product.update!(checker: current_user)
    redirect_back(fallback_location: @product, notice: '担当になりました。')
  end
  
  def destroy
    @product.update!(checker: nil)
    redirect_back(fallback_location: @product, notice: '担当から外れました。')
  end
  
  private
  
  def validate_mentor_permission
    unless current_user.mentor? || current_user.admin?
      redirect_back(fallback_location: root_path, alert: 'メンターまたは管理者のみ担当できます。')
    end
  end
end

ビューでの条件チェックはUIの表示制御のためのものであり、セキュリティの担保はコントローラレベルで行う必要があります。

🔒 セキュリティの原則:「信頼しないクライアント」

  • フロントエンドの制御は簡単に回避できるため、バックエンドで必ず検証する必要があります

🏁 Script executed:

#!/bin/bash
# Check the current implementation of CheckerAssignmentsController
cat app/controllers/products/checker_assignments_controller.rb

Length of output: 584


🏁 Script executed:

#!/bin/bash
# Check if there are any authorization checks or filters in the controller
rg -A 5 -B 5 "before_action|authorize|admin|mentor" app/controllers/products/checker_assignments_controller.rb

Length of output: 374


🏁 Script executed:

#!/bin/bash
# Check the base controller to see if there are any inherited authorization methods
rg -A 10 -B 5 "class.*Controller.*ApplicationController" app/controllers/products/checker_assignments_controller.rb

Length of output: 498


🏁 Script executed:

#!/bin/bash
# Check ApplicationController for any authorization patterns
rg -A 5 -B 5 "admin_or_mentor|staff_login" app/controllers/application_controller.rb

Length of output: 86


実装を確認した結果、以下の重要なセキュリティ問題が確認されました:

🚨 セキュリティ上の重大な問題

現在のCheckerAssignmentsControllerには認可チェックが一切ありません。

class Products::CheckerAssignmentsController < ApplicationController
  before_action :find_product  # 認可チェックなし!

  def create
    @product.update!(checker: current_user)  # 誰でも担当者になれる
    # ...
  end

  def destroy
    @product.update!(checker: nil)  # 誰でも担当者を外せる
    # ...
  end
end

🔓 現在の脆弱性

  1. 任意のユーザーが担当者になれる

    • 学習者でも直接POSTリクエストで担当者になれます
  2. 任意のユーザーが他人の担当を外せる

    • 悪意のあるユーザーが他の担当者を外すことができます
  3. ビューレベルの制御は無効

    • @is_mentorチェックは簡単に回避できます

✅ 必要な修正

class Products::CheckerAssignmentsController < ApplicationController
  before_action :staff_login_required  # 認可チェック追加
  before_action :find_product
  before_action :ensure_mentor_or_admin  # メンター/管理者のみ許可

  def create
    # 既にチェック済みの場合は拒否
    if @product.checks.any?
      redirect_back(fallback_location: @product, alert: 'この提出物は既にチェック済みです。')
      return
    end
    
    @product.update!(checker: current_user)
    redirect_back(fallback_location: @product, notice: '担当になりました。')
  end

  def destroy
    @product.update!(checker: nil)
    redirect_back(fallback_location: @product, notice: '担当から外れました。')
  end

  private

  def find_product
    product_id = params[:product_id] || params[:id]
    @product = Product.find(product_id)
  end

  def ensure_mentor_or_admin
    unless current_user.mentor? || current_user.admin?
      redirect_back(fallback_location: root_path, alert: '権限がありません。')
    end
  end
end

ビューでの条件チェックは表示制御のみで、セキュリティの担保にはなりません。必ずコントローラレベルで認可チェックを実装してください。


🏁 Script executed:

#!/bin/bash
# Check ApplicationController for any authorization methods
cat app/controllers/application_controller.rb | head -50

Length of output: 1525


🏁 Script executed:

#!/bin/bash
# Search for authorization methods in the entire codebase
rg -A 3 -B 3 "def.*admin_or_mentor|def.*staff_login" app/controllers/

Length of output: 2185


🏁 Script executed:

#!/bin/bash
# Check if there are any authorization concerns or modules
find app/controllers/concerns -name "*.rb" -exec grep -l "admin\|mentor\|staff" {} \;

Length of output: 253


🏁 Script executed:

#!/bin/bash
# Check other product controllers for authorization patterns
rg -A 5 -B 5 "before_action.*admin|before_action.*mentor|before_action.*staff" app/controllers/products/

Length of output: 2383


@su-su-su-su 認可システムを確認しました。以下が現状の問題点と解決策です:

🔍 現状の問題確認

既存の認可システムが存在し、他のコントローラでは正しく使用されています:

# 他のプロダクト関連コントローラは正しく認可チェックを実装
class Products::UncheckedController < ApplicationController
  before_action :require_staff_login  # ✅ 正しい実装
end

class Products::UnassignedController < ApplicationController
  before_action :require_staff_login  # ✅ 正しい実装
end

class Products::SelfAssignedController < ApplicationController
  before_action :require_staff_login  # ✅ 正しい実装
end

しかし、CheckerAssignmentsControllerのみ認可チェックがありません:

class Products::CheckerAssignmentsController < ApplicationController
  before_action :find_product  # ❌ 認可チェックなし!
end

🚨 セキュリティ上の重大な脆弱性

  1. 学習者でも担当者になれる

    • 直接POSTリクエストを送信するだけで担当者になれます
  2. 悪意のあるユーザーが他人の担当を外せる

    • DELETEリクエストで任意の担当者を外せます
  3. ビューレベルの制御は回避可能

    • @is_mentorチェックは表示制御のみで、セキュリティには無効です

✅ 必要な修正

class Products::CheckerAssignmentsController < ApplicationController
  before_action :require_staff_login  # 🔒 この1行を追加するだけ
  before_action :find_product

  def create
    @product.update!(checker: current_user)
    redirect_back(fallback_location: @product, notice: '担当になりました。')
  end

  def destroy
    @product.update!(checker: nil)
    redirect_back(fallback_location: @product, notice: '担当から外れました。')
  end

  private

  def find_product
    product_id = params[:product_id] || params[:id]
    @product = Product.find(product_id)
  end
end

たった1行の追加で、他のコントローラと同じレベルのセキュリティが確保されます。

🔐 セキュリティの原則

  • 認可はコントローラレベルで実装:ビューでの条件分岐は表示制御のみ
  • Defense in Depth:複数の層でセキュリティを確保
  • 一貫性:同じ機能レベルのコントローラは同じ認可レベルを適用

この修正により、既存のセキュリティ体系と一貫性を保ちながら、適切な認可制御が実現できます。

@su-su-su-su
Copy link
Copy Markdown
Contributor Author

@komagata
ご指摘頂いた点を修正しました。
ご確認お願いいたします。

@komagata
Copy link
Copy Markdown
Member

@su-su-su-su conflictの修正をお願いします。

@su-su-su-su su-su-su-su force-pushed the chore/remove-vue-check-component branch 2 times, most recently from 0d62a1b to 1758f4a Compare July 23, 2025 09:53
@su-su-su-su
Copy link
Copy Markdown
Contributor Author

@komagata
conflictの修正をしました。
ご確認をお願いいたします。

@su-su-su-su su-su-su-su force-pushed the chore/remove-vue-check-component branch from 2bd6bfb to 6a40438 Compare July 30, 2025 07:40
@su-su-su-su
Copy link
Copy Markdown
Contributor Author

@komagata
ご指摘ありがとうございます。
テストを追加いたしました。
ご確認お願いいたします。

Copy link
Copy Markdown
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です〜🙆‍♂️

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