From 7c3235810beea9e5ed6f8db12d714931ed1f1b6f Mon Sep 17 00:00:00 2001 From: ObayM Date: Tue, 23 Jun 2026 22:17:43 +0300 Subject: [PATCH 1/6] feat(certification): add ability to set bonuses on ship reviews --- .../pages/certification/ships/_index.scss | 13 ++++ .../pages/certification/ships/_show.scss | 71 +++++++++++++++++++ .../admin/certification/ships_controller.rb | 18 ++++- app/models/certification/ship.rb | 3 +- .../admin/certification/ship_policy.rb | 2 + .../admin/certification/ships/index.html.erb | 7 ++ .../admin/certification/ships/show.html.erb | 26 +++++++ config/routes.rb | 1 + ..._stardust_to_certification_ship_reviews.rb | 5 ++ db/schema.rb | 1 + test/models/certification/ship_test.rb | 17 +++++ 11 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20260623190927_add_bonus_stardust_to_certification_ship_reviews.rb diff --git a/app/assets/stylesheets/pages/certification/ships/_index.scss b/app/assets/stylesheets/pages/certification/ships/_index.scss index 8b892b8ed..0d7116506 100644 --- a/app/assets/stylesheets/pages/certification/ships/_index.scss +++ b/app/assets/stylesheets/pages/certification/ships/_index.scss @@ -704,6 +704,19 @@ } } + &__bonus-tag { + display: inline-flex; + align-items: center; + padding: 1px 7px; + border-radius: 999px; + font-size: 0.7rem; + font-weight: 700; + letter-spacing: 0.02em; + white-space: nowrap; + background: rgba(255, 229, 100, 0.15); + color: var(--color-brand-yellow); + } + // --- Empty state ------------------------------------------------------- &__empty { padding: var(--space-xl); diff --git a/app/assets/stylesheets/pages/certification/ships/_show.scss b/app/assets/stylesheets/pages/certification/ships/_show.scss index d3127c7f7..5a082cf98 100644 --- a/app/assets/stylesheets/pages/certification/ships/_show.scss +++ b/app/assets/stylesheets/pages/certification/ships/_show.scss @@ -352,6 +352,77 @@ } } +.bonus-stardust { + border-color: rgba(255, 229, 100, 0.25) !important; + background: rgba(255, 229, 100, 0.04) !important; + + &__current { + margin: 0 0 var(--space-s); + font-size: 0.95rem; + color: var(--color-brand-yellow); + font-weight: 600; + + strong { + font-weight: 800; + } + } + + &__form { + margin-bottom: var(--space-xs); + } + + &__field { + display: flex; + gap: var(--space-xs); + align-items: center; + } + + &__input { + flex: 1; + padding: var(--space-xs) var(--space-s); + font-family: inherit; + font-size: var(--font-size-m); + font-variant-numeric: tabular-nums; + color: var(--color-space-text); + background: var(--color-space-surface, rgba(255, 255, 255, 0.04)); + border: 1px solid rgba(255, 229, 100, 0.3); + border-radius: 8px; + appearance: textfield; + + &:focus-visible { + outline: 2px solid var(--color-brand-yellow); + outline-offset: 1px; + } + + &::-webkit-inner-spin-button, + &::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } + } + + + &__breakdown { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: var(--space-s); + padding-top: var(--space-s); + border-top: 1px solid rgba(255, 229, 100, 0.15); + font-size: 0.85rem; + } + + &__breakdown-label { + color: var(--color-space-text-muted, rgba(255, 255, 255, 0.5)); + } + + &__breakdown-value { + font-weight: 700; + color: var(--color-brand-yellow); + font-variant-numeric: tabular-nums; + } +} + @keyframes ship-review-pop { 0% { transform: scale(1); diff --git a/app/controllers/admin/certification/ships_controller.rb b/app/controllers/admin/certification/ships_controller.rb index d511e0431..3d8c8ab68 100644 --- a/app/controllers/admin/certification/ships_controller.rb +++ b/app/controllers/admin/certification/ships_controller.rb @@ -1,6 +1,6 @@ class Admin::Certification::ShipsController < Admin::Certification::ApplicationController before_action :release_other_claims, only: [ :next ] - before_action :set_ship, only: [ :show, :update, :set_project_type ] + before_action :set_ship, only: [ :show, :update, :set_project_type, :set_bonus_stardust ] before_action :set_submitter_context, only: [ :show, :update ] before_action :set_body_class, only: [ :index, :show, :update, :logs ] @@ -86,6 +86,22 @@ def set_project_type end end + def set_bonus_stardust + authorize @ship + value = params[:bonus_stardust].presence + @ship.bonus_stardust = value.present? ? value.to_f : nil + + # If the ship already has a verdict, recalculate stardust_earned to + # include the updated bonus. + if @ship.stardust_earned.present? && !@ship.pending? + base = @ship.stardust_earned - (@ship.bonus_stardust_was || 0) + @ship.stardust_earned = base + (@ship.bonus_stardust || 0) + end + + @ship.save! + redirect_to admin_certification_ship_path(@ship), notice: "Bonus stardust updated." + end + def update authorize @ship return redirect_to admin_certification_ship_path(@ship), alert: "Ship is no longer pending." unless @ship.pending? diff --git a/app/models/certification/ship.rb b/app/models/certification/ship.rb index ae2ffe335..1f46c293f 100644 --- a/app/models/certification/ship.rb +++ b/app/models/certification/ship.rb @@ -3,6 +3,7 @@ # Table name: certification_ship_reviews # # id :bigint not null, primary key +# bonus_stardust :float # claim_expires_at :datetime # claimed_at :datetime # decided_at :datetime @@ -367,7 +368,7 @@ def assign_stardust_earned .count rank = Certification::Ship.rank_for_reviewer_with_count(reviewer_id, my_count + 1, now: now) multiplier = Certification::Ship.multiplier_for_rank(rank) - self.stardust_earned = REVIEW_BOUNTY * multiplier + self.stardust_earned = (REVIEW_BOUNTY * multiplier) + (bonus_stardust || 0) end def stamp_claimed_at diff --git a/app/policies/admin/certification/ship_policy.rb b/app/policies/admin/certification/ship_policy.rb index b3d4dd684..5e57b643f 100644 --- a/app/policies/admin/certification/ship_policy.rb +++ b/app/policies/admin/certification/ship_policy.rb @@ -13,6 +13,8 @@ def next? = user&.can_review? def set_project_type? = show? + def set_bonus_stardust? = user&.admin? + class Scope < ApplicationPolicy::Scope def resolve return scope.none unless user&.can_review? diff --git a/app/views/admin/certification/ships/index.html.erb b/app/views/admin/certification/ships/index.html.erb index 4e5306bac..63fe90cc3 100644 --- a/app/views/admin/certification/ships/index.html.erb +++ b/app/views/admin/certification/ships/index.html.erb @@ -233,6 +233,9 @@
<%= ship.project&.title || "Deleted Project" %> #<%= ship.id %> + <% if ship.bonus_stardust.present? && ship.bonus_stardust > 0 %> + ✦ +<%= ship.bonus_stardust %> + <% end %>
#<%= ship.id %> + <% if ship.bonus_stardust.present? && ship.bonus_stardust > 0 %> + + ✦ +<%= ship.bonus_stardust %> + <% end %> by <%= owner&.display_name || "—" %> <% if ship.project.project_type.present? %> diff --git a/app/views/admin/certification/ships/show.html.erb b/app/views/admin/certification/ships/show.html.erb index 4a3777649..96b27d2c5 100644 --- a/app/views/admin/certification/ships/show.html.erb +++ b/app/views/admin/certification/ships/show.html.erb @@ -98,6 +98,32 @@