-
Notifications
You must be signed in to change notification settings - Fork 75
ペアワークをサポートする機能を実装 #8566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
ペアワークをサポートする機能を実装 #8566
Changes from all commits
78e3b14
da07c0b
9900546
6955c0e
87c51c5
382e964
4a0d067
d891b26
ff95e1a
ccff955
51699d7
84b3d3c
43eea3c
6659101
2f0aaf8
2659d2f
8f9aead
60d157f
e23da83
051f2b0
6e641a3
fc0980f
c311b4e
1d69c4f
d33c12c
0eeff70
71fda62
2733363
4f96461
ee25c4f
b87519c
be1be9f
7403437
8a94823
9f7c6c6
2358c02
c40280b
a20227e
fc76704
1ed0045
0dd9c1e
cce0a1d
4881b76
58b5cb5
226833b
0c43ac1
25ae39e
3de92f4
ecc09ee
9f9224d
9c3f291
a2b627a
7d9d545
f9616cb
155a57b
fc0fabd
13a047f
48fba89
9b9732f
2cbff96
18c69ca
494670d
a9bc903
9613a94
ac8af71
167d0bc
93faf85
19ef03d
7e54872
9c3f913
ca00d14
bb5d15e
f457cf0
77a8df9
9c61d98
c888df7
1706ffb
1548a43
db0de85
4ba7374
5a04b29
e9bb3d1
eb80845
b576943
049e864
b24d59f
186fb25
f17ecf4
bcfc519
516d157
1de0a7b
ad9fc78
345f23c
c59b1c2
8b93a6f
79a9b16
279b1e5
edabe77
6344039
a0a51ec
cbe48e2
0bcf860
7b75250
0a2efa1
86a7323
3b9af7c
b21a6ab
55ac8e3
f2b0914
cc6fcf0
b0a8d3e
dc859ca
51beddb
dd07b1e
822428d
619d425
4abb032
636cf92
feddbbc
9185de2
de5620c
96635a6
c321fd3
c905852
c4f3a5f
457219f
124e418
62d9d97
c3faed2
37a4429
c4e4d9c
036315d
dda6b3f
c350096
1a2831d
b268eca
64b7770
072683d
d5e4cb3
b9ef2a0
4107432
554edfd
9b18177
fc1ee13
fe70410
55b43d3
7e0ed20
8fe2e62
6aa9f94
d5e8e08
b5c4cad
413c8fb
e573047
9888a3b
009c741
d853d59
bcea6b3
1c03b3b
bfac7e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| .page-tabs | ||
| nav.page-tabs | ||
| .container | ||
| ul.page-tabs__items | ||
| - tabs.each do |tab| | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,8 @@ | ||
| li.tab-nav__item | ||
| = link_to link, class: "tab-nav__item-link #{active ? 'is-active' : ''}" do | ||
| = name | ||
| - if count.present? | ||
| = " (#{count})" | ||
| - if badge&.positive? | ||
| .page-tabs__item-count.a-notification-count | ||
| .not-solved-count = badge |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,15 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class SubTabComponent < ViewComponent::Base | ||
| def initialize(name:, link:, active: false) | ||
| def initialize(name:, link:, active: false, count: nil, badge: nil) | ||
| @name = name | ||
| @link = link | ||
| @active = active | ||
| @count = count | ||
| @badge = badge | ||
| end | ||
|
|
||
| private | ||
|
|
||
| attr_reader :name, :link, :active | ||
| attr_reader :name, :link, :active, :count, :badge | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class PairWorksController < ApplicationController | ||
| before_action :set_my_pair_work, only: %i[edit destroy] | ||
| before_action :set_updatable_pair_work, only: %i[update] | ||
|
|
||
| PAGER_NUMBER = 10 | ||
|
|
||
| def index | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 他のところと合わせて1行開けたほうがいいかもです。
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. そうですね、1行開けました! |
||
| @pair_works = PairWork.by_target(params[:target]) | ||
| .with_avatar | ||
| .includes(:practice, :comments, :user) | ||
| .order(:published_at) | ||
| .page(params[:page]) | ||
| .per(PAGER_NUMBER) | ||
| @pair_works_property = PairWork.generate_pair_works_property(params[:target]) | ||
| end | ||
|
|
||
| def show | ||
| @pair_work = PairWork.find(params[:id]) | ||
| @comments = @pair_work.comments.order(:created_at) | ||
| end | ||
|
|
||
| def edit; end | ||
|
|
||
| def new | ||
| @pair_work = PairWork.new | ||
| end | ||
|
|
||
| def create | ||
| @pair_work = PairWork.new(pair_work_params) | ||
| @pair_work.user = current_user | ||
| set_wip | ||
| if @pair_work.save | ||
| ActiveSupport::Notifications.instrument('pair_work.create', pair_work: @pair_work) | ||
| redirect_to Redirection.determin_url(self, @pair_work), notice: @pair_work.generate_notice_message(:create) | ||
| else | ||
|
Comment on lines
+35
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 外部 URL へのリダイレクト脆弱性の可能性 - redirect_to Redirection.determin_url(self, @pair_work), notice: @pair_work.generate_notice_message(:create)
+ redirect_to Redirection.determine_path(self, @pair_work), notice: @pair_work.generate_notice_message(:create)上記のように 絶対 URL ではなくパスを返す メソッドへ置換し、 Also applies to: 40-42 🧰 Tools🪛 Brakeman (7.0.2)[high] 31-31: Possible unprotected redirect (Redirect) 🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain@mousu-a ご指摘ありがとうございます。カスタム実装の 🏁 Script executed: Length of output: 107 🏁 Script executed: Length of output: 444 @mousu-a 実装を確認いたしました。 実装内容: def determin_url
@resource.wip? ? @controller.polymorphic_url(@resource, action: :edit) : @controller.polymorphic_url(@resource)
endこの実装が安全な理由:
Brakemanの警告は、静的解析ツールの性質上、 なお、メソッド名の セキュリティ上の修正は不要ですが、もしBrakemanの警告を回避したい場合は、以下のようにインライン化することも可能です: redirect_to @pair_work.wip? ? edit_pair_work_path(@pair_work) : pair_work_path(@pair_work), notice: @pair_work.generate_notice_message(:create)ただし、現在の実装で十分安全であり、コードの可読性も良いため、変更の必要性は低いと思われます。 🧠 Learnings used |
||
| render :new | ||
| end | ||
| end | ||
|
|
||
| def update | ||
| set_wip | ||
| if @pair_work.update(pair_work_params) | ||
| ActiveSupport::Notifications.instrument('pair_work.update', pair_work: @pair_work) | ||
| redirect_to Redirection.determin_url(self, @pair_work), notice: @pair_work.generate_notice_message(:update) | ||
| else | ||
| render :edit | ||
| end | ||
| end | ||
|
|
||
| def destroy | ||
| @pair_work.destroy | ||
| redirect_to pair_works_url, notice: @pair_work.generate_notice_message(:destroy) | ||
| end | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| private | ||
|
|
||
| def pair_work_params | ||
| params.require(:pair_work).permit(:practice_id, :title, :description, :reserved_at, :buddy_id, :channel, | ||
| schedules_attributes: %i[id proposed_at _destroy]) | ||
| end | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| def set_my_pair_work | ||
| @pair_work = current_user.admin? ? PairWork.find(params[:id]) : current_user.pair_works.find(params[:id]) | ||
| end | ||
|
|
||
| def set_updatable_pair_work | ||
| @pair_work = if PairWork.update_permission?(current_user, params[:pair_work]) | ||
| PairWork.find(params[:id]) | ||
| else | ||
| current_user.pair_works.find(params[:id]) | ||
| end | ||
| end | ||
|
Comment on lines
+68
to
+74
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
def set_updatable_pair_work
- @pair_work = if PairWork.update_permission?(current_user, params[:pair_work])
+ raw_pair_work_params = params.fetch(:pair_work, {})
+ @pair_work = if PairWork.update_permission?(current_user, raw_pair_work_params)
PairWork.find(params[:id])
else
current_user.pair_works.find(params[:id])
end
end🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mousu-a さん、良い質問ですね! 通常のフォーム送信では
実際のリスク評価:
推奨:
現在のコードで問題が発生していなければ、このまま進めても大丈夫だと思います。優先度は低めです。 🧠 Learnings used
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ちょっと過剰な対策だと思うので対応しなくても良いのかなと思います。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mousu-a さん、承知しました!現在の実装で問題なく動作しているのであれば、このままで大丈夫です。ご判断ありがとうございました。 🧠 Learnings used |
||
|
|
||
| def set_wip | ||
| @pair_work.wip = params[:commit] == 'WIP' | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module PageTabs | ||
| module QuestionsAndPairWorksHelper | ||
| def questions_and_pair_works_page_tabs | ||
| tabs = [] | ||
| tabs << { name: 'Q&A', link: questions_path(target: 'not_solved'), badge: Question.unsolved_badge(current_user:, practice_id: params[:practice_id]) } | ||
| tabs << { name: 'ペアワーク', link: pair_works_path(target: 'not_solved'), badge: PairWork.unsolved_badge(current_user:) } | ||
| render PageTabsComponent.new(tabs:, active_tab: question_and_pair_work_active_tab) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def question_and_pair_work_active_tab | ||
| case request.path | ||
| when %r{\A/questions} | ||
| 'Q&A' | ||
| when %r{\A/pair_works} | ||
| 'ペアワーク' | ||
| end | ||
| end | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module PairWorkHelper | ||
| def schedule_dates(date) | ||
| (0..6).map { |i| date.to_date + i.days } | ||
| end | ||
|
|
||
| def sorted_wdays(date) | ||
| max_wday = 6 | ||
| sorted_wdays = [date.wday] | ||
| max_wday.times do | ||
| next sorted_wdays << 0 if sorted_wdays.last == max_wday | ||
|
|
||
| sorted_wdays << sorted_wdays.last + 1 | ||
| end | ||
| sorted_wdays | ||
| end | ||
|
|
||
| def disabled?(target_time, pair_work: nil) | ||
| if pair_work | ||
| target_time < Time.current || pair_work.user_id == current_user.id | ||
| else | ||
| target_time < Time.current | ||
| end | ||
| end | ||
|
|
||
| def learning_time_frame_checked?(target_time, id) | ||
| !disabled?(target_time) && current_user.learning_time_frame_ids.include?(id) | ||
| end | ||
|
|
||
| def schedule_time(day_count, hour_count) | ||
| Time.current.beginning_of_day + day_count.days + hour_count.hours | ||
| end | ||
|
|
||
| def schedule_check_box_id(target_time) | ||
| "schedule_ids_#{target_time.strftime('%Y%m%d%H%M')}" | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module SubTabs | ||
| module PairWorksHelper | ||
| def pair_works_sub_tabs | ||
| tabs = [] | ||
| tabs << { name: 'ペア募集中', link: pair_works_path(target: 'not_solved'), badge: PairWork.unsolved_badge(current_user:) } | ||
| tabs << { name: 'ペア確定', link: pair_works_path(target: 'solved') } | ||
| tabs << { name: '全て', link: pair_works_path } | ||
| render SubTabsComponent.new(tabs:, active_tab: pair_work_active_tab) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def pair_work_active_tab | ||
| case params[:target] | ||
| when 'not_solved' | ||
| 'ペア募集中' | ||
| when 'solved' | ||
| 'ペア確定' | ||
| else | ||
| '全て' | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| @use "../../../config/mixins/text" | ||
|
|
||
| .page-body-header__inner | ||
| display: flex | ||
| align-items: center | ||
| gap: 1rem | ||
| margin-bottom: 1rem | ||
|
|
||
| .page-body-header__title | ||
| +text.text-block(1rem 1.4, 700) | ||
| color: var(--main) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||||||||||||
| @use "../../../config/mixins/break-points" | ||||||||||||||||
| @use "../../../config/mixins/position" | ||||||||||||||||
| @use "../../../config/mixins/size" | ||||||||||||||||
| @use "../../../config/mixins/text" | ||||||||||||||||
|
|
||||||||||||||||
| .pair-work-info | ||||||||||||||||
| display: flex | ||||||||||||||||
| gap: .75rem | ||||||||||||||||
|
Comment on lines
+6
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 絶対配置の基準要素を明示してバッジの位置ズレを防止
.pair-work-info
display: flex
gap: .75rem
+ position: relative📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @machida There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @machida ご指摘いただき、ありがとうございます!
現在の実装で正しく表示されているのであれば、追加の変更は不要ですね。貴重な情報をありがとうございました。 ✏️ Learnings added
🧠 Learnings used |
||||||||||||||||
|
|
||||||||||||||||
| .pair-work-info__user-icon | ||||||||||||||||
| +size.size(3.5rem) | ||||||||||||||||
|
|
||||||||||||||||
| .pair-work-info__end | ||||||||||||||||
| flex: 1 | ||||||||||||||||
|
|
||||||||||||||||
| .pair-badge | ||||||||||||||||
| font-family: serif | ||||||||||||||||
| border: double .1875rem var(--stamp-color) | ||||||||||||||||
| border-radius: .75rem | ||||||||||||||||
| +size.size(4rem 3.5rem) | ||||||||||||||||
| padding: .125rem | ||||||||||||||||
| +position.position(absolute, right 0, top -.125rem, 1) | ||||||||||||||||
| transform: rotate(25deg) | ||||||||||||||||
| +text.text-block(1.125rem 1.1, flex center) | ||||||||||||||||
| font-weight: 800 | ||||||||||||||||
| color: var(--stamp-color) | ||||||||||||||||
| flex-direction: column | ||||||||||||||||
| align-items: center | ||||||||||||||||
| justify-content: center | ||||||||||||||||
| pointer-events: none | ||||||||||||||||
| +break-points.media-breakpoint-down(sm) | ||||||||||||||||
| +position.position(absolute, left 80%, top 0) | ||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| @use "../../../config/mixins/text" | ||
|
|
||
| .pair-work-schedule-dates | ||
| &.is-solved | ||
| .pair-work-schedule-dates__table | ||
| display: none | ||
| input:checked + .pair-work-schedule-dates__table | ||
| display: block | ||
machida marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| .pair-work-schedule-dates__action-items | ||
| display: flex | ||
| justify-content: center | ||
|
|
||
| .pair-work-schedule-dates__action-item | ||
| min-width: 20rem | ||
|
|
||
| .pair-work-schedule-dates__action-item-description | ||
| margin-top: .25rem | ||
| +text.text-block(.75rem 1.4, center) | ||
|
|
||
| .pair-work-schedule-dates__title | ||
| +text.text-block(1rem 1.4, 600) | ||
| margin-bottom: 1rem | ||
|
|
||
| .pair-work-schedule-dates__table | ||
| .pair-work-schedule-dates__action + .pair-work-schedule-dates__table-container & | ||
| margin-top: 1rem | ||
|
|
||
| .pair-work-schedule-dates__cancel | ||
| display: flex | ||
| justify-content: center | ||
| margin-bottom: 1rem | ||
|
|
||
| .pair-work-schedule-dates__cancel-action | ||
| min-width: 20rem | ||
Uh oh!
There was an error while loading. Please reload this page.