From e703f41f32633341c019825c8285025c50228e84 Mon Sep 17 00:00:00 2001
From: Sreya Saju <68282466+sreyasaju@users.noreply.github.com>
Date: Thu, 2 Jul 2026 15:36:44 +0300
Subject: [PATCH 1/2] feat: add 'Made by a teen' banner to shop items
- Adds teen_creator_url to ShopItems via migration
- Updates ShopItemCardComponent to render the banner
- Includes styling and tests
Closes #692
---
.../components/_shop-item-card.scss | 28 +++++++++++++++++++
.../shop_item_card_component.html.erb | 8 +++++-
app/components/shop_item_card_component.rb | 9 ++++--
app/models/shop_item.rb | 1 +
app/views/shop/items/_item_card.html.erb | 3 +-
app/views/shop/items/_recently_added.html.erb | 3 +-
...2214_add_teen_creator_url_to_shop_items.rb | 5 ++++
db/schema.rb | 3 +-
test/fixtures/shop_items.yml | 1 +
test/models/shop_item_test.rb | 1 +
10 files changed, 56 insertions(+), 6 deletions(-)
create mode 100644 db/migrate/20260701212214_add_teen_creator_url_to_shop_items.rb
diff --git a/app/assets/stylesheets/components/_shop-item-card.scss b/app/assets/stylesheets/components/_shop-item-card.scss
index a7a6ca28a..e53cdb332 100644
--- a/app/assets/stylesheets/components/_shop-item-card.scss
+++ b/app/assets/stylesheets/components/_shop-item-card.scss
@@ -66,6 +66,24 @@
var(--color-brand-highlight) transparent;
}
}
+
+ &--teen-creator {
+ background: var(--color-brand-orange);
+ color: var(--color-space-bg);
+ cursor: pointer;
+ transition: filter 0.2s ease;
+
+ &::after {
+ border-color: var(--color-brand-orange) transparent
+ var(--color-brand-orange) transparent;
+ }
+
+ &:hover,
+ &:focus-visible {
+ filter: brightness(1.05);
+ outline: none;
+ }
+ }
}
&__star {
@@ -279,4 +297,14 @@
display: none;
}
}
+
+ &--stacked-ribbons {
+ .shop-item-card__ribbon--teen-creator {
+ top: 48px;
+ }
+
+ .shop-item-card__sale-badge {
+ top: 84px;
+ }
+ }
}
diff --git a/app/components/shop_item_card_component.html.erb b/app/components/shop_item_card_component.html.erb
index 1552cdfbc..4403f862a 100644
--- a/app/components/shop_item_card_component.html.erb
+++ b/app/components/shop_item_card_component.html.erb
@@ -1,4 +1,4 @@
-
New
<% end %>
+ <% if teen_creator? %>
+ <%= link_to teen_creator_url, target: "_blank", rel: "noopener", class: "shop-item-card__ribbon shop-item-card__ribbon--teen-creator" do %>
+ Made by a teen!
+ <% end %>
+ <% end %>
+
<% if on_sale %>
<%= sale_percentage %>% OFF
<% end %>
diff --git a/app/components/shop_item_card_component.rb b/app/components/shop_item_card_component.rb
index aab0f0b20..09ebe520e 100644
--- a/app/components/shop_item_card_component.rb
+++ b/app/components/shop_item_card_component.rb
@@ -8,9 +8,9 @@ class ShopItemCardComponent < ViewComponent::Base
# surface the popup in place instead of routing to a separate screen.
OPEN_VERIFY_MODAL = "document.getElementById('idv-verify-modal')?.showModal()".freeze
- attr_reader :item_id, :name, :description, :hours, :price, :image_url, :item_type, :balance, :enabled_regions, :regional_price, :logged_in, :tutorial_spotlight, :cta_mode, :remaining_stock, :limited, :on_sale, :sale_percentage, :original_price, :created_at, :show_bow, :show_time_ago, :purchase_count, :is_new, :enabled_until, :locked_by_achievement, :required_achievement_names, :required_achievement_hints, :mission_locked, :unlocking_mission_names, :wishlisted
+ attr_reader :item_id, :name, :description, :hours, :price, :image_url, :item_type, :balance, :enabled_regions, :regional_price, :logged_in, :tutorial_spotlight, :cta_mode, :remaining_stock, :limited, :on_sale, :sale_percentage, :original_price, :created_at, :show_bow, :show_time_ago, :purchase_count, :is_new, :enabled_until, :locked_by_achievement, :required_achievement_names, :required_achievement_hints, :mission_locked, :unlocking_mission_names, :wishlisted, :teen_creator_url
- def initialize(item_id:, name:, description:, hours:, price:, image_url:, item_type: nil, balance: nil, enabled_regions: [], regional_price: nil, logged_in: true, interactive: true, tutorial_spotlight: false, cta_mode: :order, remaining_stock: nil, limited: false, on_sale: false, sale_percentage: nil, original_price: nil, created_at: nil, show_bow: false, show_time_ago: false, purchase_count: nil, is_new: false, enabled_until: nil, locked_by_achievement: false, required_achievement_names: [], required_achievement_hints: [], mission_locked: false, unlocking_mission_names: [], wishlisted: false)
+ def initialize(item_id:, name:, description:, hours:, price:, image_url:, item_type: nil, balance: nil, enabled_regions: [], regional_price: nil, logged_in: true, interactive: true, tutorial_spotlight: false, cta_mode: :order, remaining_stock: nil, limited: false, on_sale: false, sale_percentage: nil, original_price: nil, created_at: nil, show_bow: false, show_time_ago: false, purchase_count: nil, is_new: false, enabled_until: nil, locked_by_achievement: false, required_achievement_names: [], required_achievement_hints: [], mission_locked: false, unlocking_mission_names: [], wishlisted: false, teen_creator_url: nil)
@item_id = item_id
@name = name
@description = description
@@ -41,6 +41,7 @@ def initialize(item_id:, name:, description:, hours:, price:, image_url:, item_t
@mission_locked = mission_locked
@unlocking_mission_names = unlocking_mission_names
@wishlisted = wishlisted
+ @teen_creator_url = teen_creator_url
end
def lock_overlay_html
@@ -122,4 +123,8 @@ def cta_price_icon
return nil if display_price.to_i.zero?
"icons/stardust.png"
end
+
+ def teen_creator?
+ teen_creator_url.present?
+ end
end
diff --git a/app/models/shop_item.rb b/app/models/shop_item.rb
index ca0b0a9c4..cde109c1e 100644
--- a/app/models/shop_item.rb
+++ b/app/models/shop_item.rb
@@ -53,6 +53,7 @@
# source_region :string
# special :boolean
# stock :integer
+# teen_creator_url :string
# ticket_cost :integer
# type :string
# unlisted :boolean default(FALSE)
diff --git a/app/views/shop/items/_item_card.html.erb b/app/views/shop/items/_item_card.html.erb
index 701289dca..e68f917b3 100644
--- a/app/views/shop/items/_item_card.html.erb
+++ b/app/views/shop/items/_item_card.html.erb
@@ -52,5 +52,6 @@
required_achievement_hints: item_achievement_infos.map(&:description),
mission_locked: item_mission_locked,
unlocking_mission_names: item_unlocking_missions,
- wishlisted: wishlisted_item_ids.include?(item.id)
+ wishlisted: wishlisted_item_ids.include?(item.id),
+ teen_creator_url: item.teen_creator_url
) %>
diff --git a/app/views/shop/items/_recently_added.html.erb b/app/views/shop/items/_recently_added.html.erb
index 5bc15ed18..eddd2d4c3 100644
--- a/app/views/shop/items/_recently_added.html.erb
+++ b/app/views/shop/items/_recently_added.html.erb
@@ -35,7 +35,8 @@
required_achievement_names: item_achievement_infos.map(&:name),
required_achievement_hints: item_achievement_infos.map(&:description),
mission_locked: item_mission_locked,
- unlocking_mission_names: item_unlocking_missions
+ unlocking_mission_names: item_unlocking_missions,
+ teen_creator_url: item.teen_creator_url
) %>
<% end %>
diff --git a/db/migrate/20260701212214_add_teen_creator_url_to_shop_items.rb b/db/migrate/20260701212214_add_teen_creator_url_to_shop_items.rb
new file mode 100644
index 000000000..57e449280
--- /dev/null
+++ b/db/migrate/20260701212214_add_teen_creator_url_to_shop_items.rb
@@ -0,0 +1,5 @@
+class AddTeenCreatorUrlToShopItems < ActiveRecord::Migration[8.1]
+ def change
+ add_column :shop_items, :teen_creator_url, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c433799dd..0cc5839bc 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.1].define(version: 2026_06_30_165531) do
+ActiveRecord::Schema[8.1].define(version: 2026_07_01_212214) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
enable_extension "vector"
@@ -1066,6 +1066,7 @@
t.string "source_region"
t.boolean "special"
t.integer "stock"
+ t.string "teen_creator_url"
t.integer "ticket_cost"
t.string "type"
t.boolean "unlisted", default: false
diff --git a/test/fixtures/shop_items.yml b/test/fixtures/shop_items.yml
index 86b4183ae..9556afd25 100644
--- a/test/fixtures/shop_items.yml
+++ b/test/fixtures/shop_items.yml
@@ -59,6 +59,7 @@
# source_region :string
# special :boolean
# stock :integer
+# teen_creator_url :string
# ticket_cost :integer
# type :string
# unlisted :boolean default(FALSE)
diff --git a/test/models/shop_item_test.rb b/test/models/shop_item_test.rb
index f2bc0ea0b..078a00666 100644
--- a/test/models/shop_item_test.rb
+++ b/test/models/shop_item_test.rb
@@ -53,6 +53,7 @@
# source_region :string
# special :boolean
# stock :integer
+# teen_creator_url :string
# ticket_cost :integer
# type :string
# unlisted :boolean default(FALSE)
From 62872f2c9900053246f10d4f2dcc02da2c447fbd Mon Sep 17 00:00:00 2001
From: Sreya Saju <68282466+sreyasaju@users.noreply.github.com>
Date: Thu, 2 Jul 2026 16:40:31 +0300
Subject: [PATCH 2/2] style: fix scss formatting with prettier
---
app/assets/stylesheets/components/_shop-item-card.scss | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/assets/stylesheets/components/_shop-item-card.scss b/app/assets/stylesheets/components/_shop-item-card.scss
index e53cdb332..24d3210d1 100644
--- a/app/assets/stylesheets/components/_shop-item-card.scss
+++ b/app/assets/stylesheets/components/_shop-item-card.scss
@@ -300,9 +300,9 @@
&--stacked-ribbons {
.shop-item-card__ribbon--teen-creator {
- top: 48px;
+ top: 48px;
}
-
+
.shop-item-card__sale-badge {
top: 84px;
}