+
+ <% @node.attachments.each do |attachment| %>
+ <%= attachment.body.html_safe %>
+ <% end %>
+
+
+ <% @node.children.viewable.ordered.each do |answer_option| %>
+ <%= button_to answer_option.title, url_for(action: 'pick_bonus', option: answer_option.id), method: :post %>
+ <% end %>
+
+
+
\ No newline at end of file
diff --git a/app/views/stages/result.html.erb b/app/views/stages/result.html.erb
index 38f76f0..4c78f5f 100644
--- a/app/views/stages/result.html.erb
+++ b/app/views/stages/result.html.erb
@@ -3,38 +3,47 @@
content_for :meta_description, @node.page_description
content_for :debug, current_player.inspect
+
+
%>
- <% @node.attachments.each do |attachment| %>
- <%= attachment.body.html_safe %>
- <% end %>
-
+ <%= @node.attachments[(@result == "bad" and current_player.bonus_points >= stage_index) ? 1 : 0]&.body&.html_safe %>
+
- <% case @result %>
- <% when "best", "good" %>
- <%= button_to t("next_stage"), url_for(action: 'next'), method: :post %>
- <% when "bad" %>
- <% if current_player.extra_lives_stage >= stage_index %>
- <%= button_to t("use_extra_life"), url_for(action: 'next'), method: :post %>
- <% else %>
- <% if stage_index > 1 %>
- <%= button_to t("get_extra_life"), url_for(action: 'get_extra_life'), method: :post %>
- <% else %>
+ <% if @node.parent&.bonus? %>
+ <%= button_to t("get_bonus_point"), url_for(action: 'get_bonus_point'), method: :post %>
+ <% else %>
+
+ <% case @result %>
+ <% when "best", "good" %>
+ <%= button_to t("next_stage"), url_for(action: 'next'), method: :post %>
+
+ <% when "bad" %>
+ <% if current_player.bonus_points >= stage_index %>
+ <%= button_to t("use_bonus_point"), url_for(action: 'next'), method: :post %>
+ <% else %>
+ <% if bonus_node %>
+ <%= button_to t("bonus"), url_for(action: 'try_bonus'), method: :post %>
+ <% else %>
+ <%= button_to t("go_again"), go_again_path(), method: :post %>
+ <% end %>
+ <% end %>
+
+ <% when "bonus" %>
+ <%= button_to t("let_my_try"), url_for(action: 'try_bonus'), method: :post %>
+
+ <% when "game_over" %>
+ <% if bonus_node %>
+ <%= button_to t("bonus"), url_for(action: 'try_bonus'), method: :post %>
+ <% else %>
<%= button_to t("go_again"), go_again_path(), method: :post %>
<% end %>
<% end %>
-
- <% when "bonus" %>
- <%= button_to t("bonus"), url_for(action: 'bonus'), method: :post %>
-
- <% when "game_over" %>
- <%= button_to t("go_again"), go_again_path(), method: :post %>
-
- <% end %>
+ <% end %>
\ No newline at end of file
diff --git a/config/locales/en.yml b/config/locales/en.yml
index ccd1a88..48c650f 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -4,13 +4,15 @@ en:
client_name: IKEA Foundation
let_me_try: Let me try!
+ flip_the_card: Flip the card
next_stage: Proceed to the next stage
bonus: Bonus
go_again: Go again
- use_extra_life: Use extra life
- get_extra_life: Get extra life
-
+ use_bonus_point: Use bonus point
+ get_bonus_point: Get bonus point
+ let_my_try: Let me try
+
languages:
en: English
zh: 中文
@@ -211,6 +213,7 @@ en:
game_over: Game Over
good: Good
bonus: Bonus
+ score: Result page
categories:
box: Box
diff --git a/config/routes.rb b/config/routes.rb
index 85d1870..e46fbf2 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -63,9 +63,12 @@ Rails.application.routes.draw do
get "result", to: "stages#result"
post "next", to: "stages#next"
- post "bonus", to: "stages#bonus"
- post "get_extra_life", to: "stages#get_extra_life"
+ post "bonus", to: "stages#try_bonus"
+ get "bonus", to: "stages#bonus"
+ post "pick_bonus(/:option)", to: "stages#pick_bonus"
+
+ post "get_bonus_point", to: "stages#get_bonus_point"
end
get "game_over", to: "stages#game_over"
diff --git a/db/migrate/20250527114853_create_players.rb b/db/migrate/20250527114853_create_players.rb
index 12792f4..cb3f8e4 100644
--- a/db/migrate/20250527114853_create_players.rb
+++ b/db/migrate/20250527114853_create_players.rb
@@ -3,7 +3,7 @@ class CreatePlayers < ActiveRecord::Migration[8.1]
create_table :players do |t|
t.text :locale
t.integer :current_stage, index: true
- t.integer :extra_lives_stage, index: true
+ t.integer :bonus_points, index: true
t.jsonb :progress, default: {}
t.boolean :is_done, default: false, index: true
t.timestamps
diff --git a/db/schema.rb b/db/schema.rb
index 533ab8a..3a39b5a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -115,15 +115,15 @@ ActiveRecord::Schema[8.1].define(version: 2025_05_27_114853) do
end
create_table "players", force: :cascade do |t|
+ t.integer "bonus_points"
t.datetime "created_at", null: false
t.integer "current_stage"
- t.integer "extra_lives_stage"
t.boolean "is_done", default: false
t.text "locale"
t.jsonb "progress", default: {}
t.datetime "updated_at", null: false
+ t.index ["bonus_points"], name: "index_players_on_bonus_points"
t.index ["current_stage"], name: "index_players_on_current_stage"
- t.index ["extra_lives_stage"], name: "index_players_on_extra_lives_stage"
t.index ["is_done"], name: "index_players_on_is_done"
end
diff --git a/lib/tasks/nodes.rake b/lib/tasks/nodes.rake
new file mode 100644
index 0000000..5fdb45c
--- /dev/null
+++ b/lib/tasks/nodes.rake
@@ -0,0 +1,60 @@
+namespace :nodes do
+ desc "Deep copy a node including descendants and attachments. Usage: rake nodes:copy[22]"
+ task :copy, [ :node_id ] => :environment do |t, args|
+ node_id = args[:node_id] || 22
+ begin
+ source = Node.find(node_id)
+ puts "Starting deep copy of Node #{source.id}: \"#{source.title}\"..."
+
+ # We wrap in a transaction to ensure integrity
+ new_root = Node.transaction do
+ copy_node_recursively(source)
+ end
+
+ puts "Successfully created deep copy!"
+ puts "Source ID: #{source.id}"
+ puts "New ID: #{new_root.id}"
+ rescue ActiveRecord::RecordNotFound
+ puts "Error: Node with ID #{node_id} not found."
+ rescue => e
+ puts "Error during copy: #{e.message}"
+ puts e.backtrace.first(5)
+ end
+ end
+
+ def copy_node_recursively(source, parent = nil)
+ # 1. Duplicate the node (this copies attributes including JSONB translations)
+ new_node = source.dup
+
+ # 2. Set parent (Ancestry gem handles the tree logic)
+ # If no parent provided, we default to the same parent as the source (sibling)
+ new_node.parent = parent || source.parent
+
+ # 3. Modify title of the root copy to distinguish it
+ if parent.nil?
+ I18n.available_locales.each do |locale|
+ title = source.title(locale: locale)
+ if title.present?
+ new_node.send(:title=, "#{title} (Copy)", locale: locale)
+ end
+ end
+ end
+
+ # 4. Save to get an ID
+ new_node.save!
+
+ # 5. Duplicate Attachments
+ source.attachments.each do |attachment|
+ new_attachment = attachment.dup
+ new_attachment.attachable_for = new_node
+ new_attachment.save!
+ end
+
+ # 6. Recurse for children
+ source.children.ordered.each do |child|
+ copy_node_recursively(child, new_node)
+ end
+
+ new_node
+ end
+end
diff --git a/lib/tasks/quiz_stats.rake b/lib/tasks/quiz_stats.rake
deleted file mode 100644
index e9c282d..0000000
--- a/lib/tasks/quiz_stats.rake
+++ /dev/null
@@ -1,81 +0,0 @@
-# lib/tasks/quiz_stats.rake
-namespace :quiz do
- desc "Show quiz statistics from Oct 17, 2025"
- task stats: :environment do
- results = QuizResult.where("created_at >= ?", Date.new(2025, 10, 17))
-
- total = results.count
- by_locale = results.group(:locale).count
- scores = results.group(:score).count
- avg_score = results.average(:score)
-
- # Calculate result categories
- questions = Node.at_depth(1).tmpl_article.viewable.ordered.to_a
- questions_count = questions.count
- result_categories = { "People" => 0, "Planet" => 0, "Balanced" => 0 }
-
- results.each do |qr|
- people_score = questions_count - qr.score
- score_diff = people_score - qr.score
-
- category = case
- when score_diff >= 2
- "People"
- when score_diff <= -2
- "Planet"
- else
- "Balanced"
- end
- result_categories[category] += 1
- end
-
- puts "Quiz Results from Oct 17, 2025"
- puts "=" * 40
- puts "Total: #{total}"
-
- puts "\nBy locale:"
- by_locale.sort.each do |locale, count|
- locale_name = I18n.t(locale, scope: 'languages')
- puts " #{locale_name}: #{count}"
- end
-
- puts "\nScore distribution:"
- scores.sort.each do |score, count|
- puts " Score #{score}: #{count}"
- end
-
- puts "\nAverage score: #{avg_score&.round(2)}"
-
- puts "\nResult categories:"
- result_categories.each do |category, count|
- percentage = total > 0 ? (count.to_f / total * 100).round(1) : 0
- puts " #{category}: #{count} (#{percentage}%)"
- end
-
- # Question by question breakdown
-
- puts "\n\nQuestion breakdown:"
- puts "=" * 40
- questions.each_with_index do |question, i|
- answers = Answer.where(node_id: question.id).where("created_at >= ?", Date.new(2025, 10, 17))
- answer_counts = answers.group(:value).count
-
- planet_answer = (i == 0 || i == 3) ? 0 : 1
- people_answer = (i == 0 || i == 3) ? 1 : 0
-
- planet_count = answer_counts[planet_answer] || 0
- people_count = answer_counts[people_answer] || 0
- total_answers = planet_count + people_count
-
- puts "\nQ#{i + 1}: #{question.title}"
- if total_answers > 0
- planet_pct = (planet_count.to_f / total_answers * 100).round(1)
- people_pct = (people_count.to_f / total_answers * 100).round(1)
- puts " Planet: #{planet_count} (#{planet_pct}%)"
- puts " People: #{people_count} (#{people_pct}%)"
- else
- puts " No answers"
- end
- end
- end
-end