Skip to content

Commit

Permalink
Allow some badges to be awarded multiple times (forem#20876)
Browse files Browse the repository at this point in the history
* Add migration, update badgeachievements model and admin view to set flag on badges

* Show multiple badges on profile

* update factory

* badge model validation

* safe navigation to badge?

* add null: false to migratrion
  • Loading branch information
PhilipHow committed Apr 23, 2024
1 parent c44a2dc commit 3ceb9a1
Show file tree
Hide file tree
Showing 12 changed files with 57 additions and 7 deletions.
13 changes: 13 additions & 0 deletions app/assets/stylesheets/views/profile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,16 @@
border-bottom: unset;
}
}

.badge-indicator {
font-size: var(--fs-s);
color: var(--base-0);
width: var(--su-6);
height: var(--su-6);
line-height: var(--su-6);
position: absolute;
background-color: var(--accent-brand);
right: var(--su-1);
bottom: -5px;
border-radius: 100%;
}
2 changes: 1 addition & 1 deletion app/controllers/admin/badges_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def update
private

def badge_params
params.require(:badge).permit(:title, :description, :badge_image, :credits_awarded)
params.require(:badge).permit(:title, :description, :badge_image, :credits_awarded, :allow_multiple_awards)
end
end
end
1 change: 1 addition & 0 deletions app/models/badge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Badge < ApplicationRecord
validates :description, presence: true
validates :slug, presence: true
validates :title, presence: true, uniqueness: true
validates :allow_multiple_awards, inclusion: { in: [true, false] }

before_validation :generate_slug

Expand Down
6 changes: 5 additions & 1 deletion app/models/badge_achievement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class BadgeAchievement < ApplicationRecord

counter_culture :user, column_name: "badge_achievements_count"

validates :badge_id, uniqueness: { scope: :user_id }
validates :badge_id, uniqueness: { scope: :user_id, if: :single_award_badge? }

before_validation :render_rewarding_context_message_html
after_create :award_credits
Expand Down Expand Up @@ -53,4 +53,8 @@ def award_credits

Credit.add_to(user, badge.credits_awarded)
end

def single_award_badge?
badge&.allow_multiple_awards == false
end
end
8 changes: 8 additions & 0 deletions app/views/admin/badges/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@
<%= form.text_field :credits_awarded, class: "crayons-textfield" %>
</div>

<div class="crayons-field crayons-field--checkbox ">
<%= form.check_box :allow_multiple_awards, class: "crayons-checkbox" %>
<%= form.label :allow_multiple_awards, class: "crayons-field__label" do %>
Allow multiple awards
<p class="crayons-field__description">Allows this badge to be awarded multiple times to the same user</p>
<% end %>
</div>

<div>
<%= submit_tag "Update badge", class: "c-btn c-btn--primary" %>
</div>
Expand Down
9 changes: 9 additions & 0 deletions app/views/admin/badges/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@
<%= form.text_field :credits_awarded, class: "crayons-textfield" %>
</div>

<div class="crayons-field crayons-field--checkbox ">
<%= form.check_box :allow_multiple_awards, class: "crayons-checkbox" %>
<%= form.label :allow_multiple_awards, class: "crayons-field__label" do %>
Allow multiple awards
<p class="crayons-field__description">Allows this badge to be awarded multiple times to the same user</p>
<% end %>
</div>


<div>
<%= submit_tag "Create Badge", class: "c-btn c-btn--primary" %>
</div>
Expand Down
8 changes: 5 additions & 3 deletions app/views/users/_badges_area.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@

<div class="crayons-card__body">
<div class="grid gap-4 grid-cols-3 s:grid-cols-4 m:grid-cols-2 align-center items-center js-profile-badges">
<% achievements.each_with_index do |achievement, i| %>
<% achievements.group_by(&:badge_id).each_with_index do |(badge_id, badge_achievements), i| %>
<% achievement = badge_achievements.first %>
<div role="button" onclick="window.Forem.showModal({size: 'medium', showHeader: false, contentSelector: '#badge-<%= achievement.badge_id %>', overlay: true})"
title="<%= achievement.badge_title %>"
class="js-profile-badge <% if i >= limit %>hidden<% end %>">
class="js-profile-badge <% if i >= limit %>hidden<% end %> relative">
<img src="<%= optimized_image_url(achievement.badge_image_url, width: 180) %>"
alt="<%= achievement.badge_title %>"
class="mx-auto max-w-75 h-auto align-middle"
style="object-fit: contain; cursor: pointer; width: 100%; aspect-ratio: 1 / 1; transform: rotate(<%= rand(-10..10) %>deg);"
style="object-fit: contain; cursor: pointer; width: 100%; aspect-ratio: 1 / 1;"
loading="lazy" />
<% if badge_achievements.length > 1 %><div class="badge-indicator" title="<%= t("views.badges.indicator", user: @user.name, number: badge_achievements.length) %>"><%= badge_achievements.length %></div><% end %>
</div>

<div id="badge-<%= achievement.badge_id %>" class="hidden">
Expand Down
1 change: 1 addition & 0 deletions config/locales/views/misc/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ en:
desc: Receive badges for being awesome! Click on a badge to see how you can earn it.
icon_close: Close
gotit: Got it
indicator: "%{user} earned this badge %{number} times."
campaign:
subtitle: Stories (%{count})
view: See all posts
Expand Down
1 change: 1 addition & 0 deletions config/locales/views/misc/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ fr:
desc: Receive badges for being awesome! Click on a badge to see how you can earn it.
icon_close: Close
gotit: Got it
indicator: "%{user} a obtenu ce badge %{number} fois."
campaign:
subtitle: Stories (%{count})
view: See all posts
Expand Down
10 changes: 10 additions & 0 deletions db/migrate/20240419201108_add_allow_multiple_awards_to_badges.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class AddAllowMultipleAwardsToBadges < ActiveRecord::Migration[7.0]
disable_ddl_transaction!

def change
add_column :badges, :allow_multiple_awards, :boolean, null: false, default: false

# remove unique index
remove_index :badge_achievements, column: [:badge_id, :user_id], algorithm: :concurrently if index_exists?(:badge_achievements, [:badge_id, :user_id])
end
end
4 changes: 2 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2024_04_08_101038) do
ActiveRecord::Schema[7.0].define(version: 2024_04_19_201108) do
# These are extensions that must be enabled in order to support this database
enable_extension "citext"
enable_extension "ltree"
Expand Down Expand Up @@ -207,11 +207,11 @@
t.text "rewarding_context_message_markdown"
t.datetime "updated_at", precision: nil, null: false
t.bigint "user_id", null: false
t.index ["badge_id", "user_id"], name: "index_badge_achievements_on_badge_id_and_user_id", unique: true
t.index ["user_id", "badge_id"], name: "index_badge_achievements_on_user_id_and_badge_id"
end

create_table "badges", force: :cascade do |t|
t.boolean "allow_multiple_awards", default: false, null: false
t.string "badge_image"
t.datetime "created_at", precision: nil, null: false
t.integer "credits_awarded", default: 0, null: false
Expand Down
1 change: 1 addition & 0 deletions spec/factories/badges.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
sequence(:title) { |n| "#{Faker::Book.title}-#{n}" }
description { Faker::Lorem.sentence }
badge_image { Rack::Test::UploadedFile.new(image_path, "image/jpeg") }
allow_multiple_awards { false }
end
end

0 comments on commit 3ceb9a1

Please sign in to comment.