Mattias Bodlund 2 days ago
parent
commit
0e6ed040d7
40 changed files with 103 additions and 220 deletions
  1. +2
    -10
      AGENTS.md
  2. +4
    -4
      Gemfile.lock
  3. +1
    -15
      app/assets/stylesheets/analytics.css
  4. +4
    -62
      app/assets/stylesheets/application.css
  5. +0
    -9
      app/controllers/game_controller.rb
  6. +0
    -2
      app/javascript/application.js
  7. +0
    -20
      app/models/player.rb
  8. +0
    -23
      app/services/game_analytics.rb
  9. +0
    -24
      app/views/admin/analytics/index.html.erb
  10. +7
    -18
      app/views/game/results.html.erb
  11. +2
    -2
      app/views/layouts/application.html.erb
  12. +1
    -1
      app/views/shared/_dev_jump.html.erb
  13. +0
    -1
      config/importmap.rb
  14. +3
    -1
      config/locales/cs.yml
  15. +3
    -1
      config/locales/da.yml
  16. +3
    -1
      config/locales/de.yml
  17. +3
    -1
      config/locales/en.yml
  18. +3
    -1
      config/locales/es.yml
  19. +3
    -1
      config/locales/fi.yml
  20. +3
    -1
      config/locales/fr-CA.yml
  21. +3
    -1
      config/locales/fr.yml
  22. +3
    -1
      config/locales/hr.yml
  23. +3
    -1
      config/locales/hu.yml
  24. +3
    -1
      config/locales/it.yml
  25. +3
    -1
      config/locales/ja.yml
  26. +3
    -1
      config/locales/ko.yml
  27. +3
    -1
      config/locales/nb.yml
  28. +3
    -1
      config/locales/nl.yml
  29. +3
    -1
      config/locales/pl.yml
  30. +3
    -1
      config/locales/pt.yml
  31. +3
    -1
      config/locales/ro.yml
  32. +3
    -1
      config/locales/sk.yml
  33. +3
    -1
      config/locales/sl.yml
  34. +3
    -1
      config/locales/sr.yml
  35. +3
    -1
      config/locales/sv.yml
  36. +3
    -1
      config/locales/uk.yml
  37. +3
    -1
      config/locales/zh.yml
  38. +0
    -1
      config/routes.rb
  39. +9
    -0
      db/migrate/20260918120000_remove_rating_from_players.rb
  40. +1
    -4
      db/schema.rb

+ 2
- 10
AGENTS.md View File

@ -17,8 +17,7 @@ This project is a Ruby on Rails application developed for the IKEA Foundation. I
the next stage. Mobile only. See below.
- **Scoring:** Answer scores live in `config/question_scores.json`, not in the database. See below.
- **Analytics:** An admin dashboard at `/admin/:locale/analytics` reporting funnel drop-off,
answer distribution, the results-screen thumbs vote and who played — device, language and
country. See below.
answer distribution and who played — device, language and country. See below.
- **Admin Interface:** A backend for managing nodes, assets (Active Storage), users, and translations.
- **Search:** `pg_search` integration for content discovery.
@ -39,8 +38,7 @@ This project is a Ruby on Rails application developed for the IKEA Foundation. I
- **Level 3 (outcomes under a chance):** `good_answer`, `bad_answer`
- `Player`: Tracks session state, `progress` (per-stage `answer_id` / `result_id`), the
cumulative `score`, the `scores` hash keyed by `food_waste`, `emissions`, `income`,
the furthest screen reached (`furthest_step`), the `device` class they played on and
the results-screen thumbs vote (`rating` / `rated_at`).
the furthest screen reached (`furthest_step`) and the `device` class they played on.
- `Asset` & `Attachment`: Handles media and its contextual content (body text, styling) associated with nodes.
- `User`: Admin authentication and roles.
@ -151,12 +149,6 @@ is pinned to the bottom of the panel so the QR rows stay together at the top.
**Not counted here:** the board game QR points at `dice.ikeafoundation.org`, a different app.
Its `utm_content=board_game` scans are recorded there, not in this dashboard.
### Thumbs up / down
`Player#rate!` stores `rating` as `1` / `-1` (`Player::RATINGS`), one per player — voting
again overwrites. The results screen posts to `game#rate` via `rating_controller.js`, which
flips the button state immediately and does not await the response.
## Screens and animation
Every screen lives inside the single `turbo-frame#game`, so there is no page render to hang an


+ 4
- 4
Gemfile.lock View File

@ -134,7 +134,7 @@ GEM
actionpack (>= 6.0.0)
activesupport (>= 6.0.0)
railties (>= 6.0.0)
io-console (0.9.3)
io-console (0.9.4)
irb (1.18.0)
pp (>= 0.6.0)
prism (>= 1.3.0)
@ -186,7 +186,7 @@ GEM
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.3.0)
net-protocol (0.4.0)
timeout
net-smtp (0.5.1)
net-protocol
@ -464,7 +464,7 @@ CHECKSUMS
i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
image_processing (1.14.0) sha256=754cc169c9c262980889bec6bfd325ed1dafad34f85242b5a07b60af004742fb
importmap-rails (2.2.3) sha256=7101be2a4dc97cf1558fb8f573a718404c5f6bcfe94f304bf1f39e444feeb16a
io-console (0.9.3) sha256=f555049461b0afb78a5448b5be8e7c7b48371347cd48abfc22814764160000dd
io-console (0.9.4) sha256=a403304f32928ee85e56d457b4612ca43f2703b561a3258844992bd24085fbb1
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
jbuilder (2.15.1) sha256=2430bec28fb0cebacb5875b1009cf9d8bc3c303ccb810c4c8b062a4b51457637
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
@ -486,7 +486,7 @@ CHECKSUMS
msgpack (1.8.5) sha256=97419eaa7b35dfe3e462c0e6c1a3dfdcbfc8756605c09e46e4c6daef127517ba
net-imap (0.6.7) sha256=b5c9573be975d856de252ee851871724da66aa2d449b2482d5690bd12bc23660
net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3
net-protocol (0.3.0) sha256=ba310c3d4f1cad46bb1ab20336b06669b1ff8f7c568d9cb9342b32a718547472
net-protocol (0.4.0) sha256=f9f90c8c6f64794a45160bf422177a336db12809de3beeeaa26b88fcd2e8c853
net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736
nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f


+ 1
- 15
app/assets/stylesheets/analytics.css View File

@ -223,21 +223,7 @@
}
/* ------------------------------------------------------------ split & stage */
.analytics-split {
display: flex;
height: 14px;
border-radius: 3px;
overflow: hidden;
margin-top: 1.25rem;
background: var(--clr-grey-200);
}
.analytics-split-part {
&.positive { background: var(--analytics-positive); }
&.negative { background: var(--analytics-negative); }
}
/* ------------------------------------------------------------------- stage */
.analytics-stage {
margin-top: 2rem;


+ 4
- 62
app/assets/stylesheets/application.css View File

@ -1456,48 +1456,6 @@ dialog::backdrop {
}
}
.like-container {
margin: 0 1rem 1.5rem 1rem;
& h3 {
font: var(--td-base);
font-weight: 700;
margin: 0 0 0.5rem 0;
}
& > div {
display: flex;
gap: 0.5rem;
}
button {
appearance: none;
border-radius: 50%;
width: 40px;
aspect-ratio: 1;
border: 1px solid var(--clr-black);
background-color: transparent;
display: flex;
justify-content: center;
align-items: center;
&.thumbs-down {
& svg {
rotate: 180deg;
}
}
&.is-selected {
background-color: var(--clr-green);
border-color: var(--clr-green);
& svg {
fill: var(--clr-black);
}
}
}
}
.newsletter-container {
margin: 0 1rem 2rem 1rem;
@ -1512,14 +1470,8 @@ dialog::backdrop {
color: var(--clr-black);
text-decoration: none;
& span:has(svg) {
& .arrow {
background-color: var(--clr-green);
width: 32px;
height: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
}
}
@ -1883,6 +1835,9 @@ dialog::backdrop {
transform: rotate(90deg) translate(345px, -174px) scaleX(-1);
}
}
.newsletter-container a .arrow {
background-color: var(--clr-white);
}
.cta-container {
gap: 1rem;
padding-bottom: 2.5rem;
@ -1891,19 +1846,6 @@ dialog::backdrop {
}
}
}
.like-container {
button {
&.is-selected {
background-color: var(--clr-white);
border-color: var(--clr-white);
& svg {
fill: var(--clr-black);
}
}
}
}
}
.debug-score {


+ 0
- 9
app/controllers/game_controller.rb View File

@ -122,15 +122,6 @@ class GameController < ApplicationController
end
# POST -- thumbs up / down on the results screen. One rating per player;
# tapping the other thumb (or the same one again) just overwrites it.
def rate
current_player.rate!(params[:direction])
head :no_content
end
private


+ 0
- 2
app/javascript/application.js View File

@ -7,7 +7,6 @@ import IntroController from "intro_controller"
import ChanceController from "chance_controller"
import ShareController from "share_controller"
import ModalController from "modal_controller"
import RatingController from "rating_controller"
import StartController from "start_controller"
import AnswerController from "answer_controller"
@ -19,7 +18,6 @@ application.register("intro", IntroController)
application.register("chance", ChanceController)
application.register("share", ShareController)
application.register("modal", ModalController)
application.register("rating", RatingController)
application.register("start", StartController)
application.register("answer", AnswerController)


+ 0
- 20
app/models/player.rb View File

@ -7,8 +7,6 @@ class Player < ApplicationRecord
SCORE_KEYS = %w[food_waste emissions income].freeze
RATINGS = { up: 1, down: -1 }.freeze
# Campaign tags carried in from the printed QR codes. See UtmTracking.
UTM_KEYS = %i[utm_source utm_medium utm_campaign utm_content].freeze
@ -21,11 +19,6 @@ class Player < ApplicationRecord
STEP_RANKS = { "start" => 0, "facts" => 1, "intro" => 2,
"last_save" => 900, "done" => 901, "results" => 902 }.freeze
scope :rated, -> { where.not(rating: nil) }
scope :thumbs_up, -> { where(rating: RATINGS[:up]) }
scope :thumbs_down, -> { where(rating: RATINGS[:down]) }
# Stage screens interleave: stage 1 (10), its result (11), stage 2 (12)...
# Everything before stages sits below 10, everything after above 900.
def self.step_rank(step)
@ -50,19 +43,6 @@ class Player < ApplicationRecord
end
def rate!(direction)
value = RATINGS[direction.to_s.to_sym]
return false unless value
update(rating: value, rated_at: Time.current)
end
def rating_direction
RATINGS.key(rating)
end
# ---------------------------------------------------------------- device


+ 0
- 23
app/services/game_analytics.rb View File

@ -52,29 +52,6 @@ class GameAnalytics
def completion_rate = percent(completed_players, total_players)
# ---------------------------------------------------------------- ratings
def ratings
@ratings ||= begin
counts = players.rated.group(:rating).count
up = counts[Player::RATINGS[:up]].to_i
down = counts[Player::RATINGS[:down]].to_i
{
up: up,
down: down,
total: up + down,
up_share: percent(up, up + down),
down_share: percent(down, up + down),
# Share of people who saw the results screen and bothered to vote. A vote
# proves the player got there, so it also acts as the floor on reach --
# players backfilled from before step tracking stop at "done".
response_rate: percent(up + down, [ reached("results"), up + down ].max)
}
end
end
# ----------------------------------------------------------------- funnel
# One row per screen, with how many players got that far and how many were


+ 0
- 24
app/views/admin/analytics/index.html.erb View File

@ -37,30 +37,6 @@
</section>
<section class="analytics-panel">
<h2>Did you like this game?</h2>
<% ratings = @analytics.ratings %>
<% if ratings[:total].zero? %>
<p class="analytics-empty">No votes yet.</p>
<% else %>
<div class="analytics-cards">
<%= render "card", label: "Thumbs up", value: number_with_delimiter(ratings[:up]),
sub: "#{ratings[:up_share]}%", modifier: "positive" %>
<%= render "card", label: "Thumbs down", value: number_with_delimiter(ratings[:down]),
sub: "#{ratings[:down_share]}%", modifier: "negative" %>
<%= render "card", label: "Votes", value: number_with_delimiter(ratings[:total]),
sub: "#{ratings[:response_rate]}% of players who reached the results" %>
</div>
<div class="analytics-split">
<div class="analytics-split-part positive" style="flex-grow: <%= ratings[:up] %>"></div>
<div class="analytics-split-part negative" style="flex-grow: <%= ratings[:down] %>"></div>
</div>
<% end %>
</section>
<section class="analytics-panel">
<h2>Funnel</h2>
<p class="analytics-note">


+ 7
- 18
app/views/game/results.html.erb View File

@ -64,29 +64,18 @@
</details>
</div>
<div class="like-container" data-controller="rating" data-rating-url-value="<%= rate_path %>">
<%= tag.h3 t("game.did_you_like_this_game") %>
<div>
<% { up: "thumbs-up", down: "thumbs-down" }.each do |direction, css_class| %>
<button class="<%= class_names(css_class, "is-selected" => current_player.rating_direction == direction) %>"
type="button"
aria-label="<%= direction %>"
data-direction="<%= direction %>"
data-rating-target="button"
data-action="rating#rate"><%= svg "ico-thumb" %></button>
<% end %>
</div>
</div>
<div class="newsletter-container">
<%= tag.h3 t("game.join_our_newsletter") %>
<%= tag.p t("game.join_our_newsletter_desc") %>
<%= tag.p t("game.join_our_newsletter_desc") %>
<%= link_to t("game.subscribe_to_newsletter_url"), target: "_blank" do %>
<span class="arrow"></span>
<%= tag.span(t("game.subscribe_to_newsletter")) %>
<% end %>
</div>
<div class="cta-container">
<%= link_to tag.span(t("game.subscribe_to_newsletter")), "https://www.ikeafoundation.org/newsletter", class: "cta cta-link", target: "_blank" %>
<%= link_to tag.span(t("game.learn_more")), t("game.learn_more_url"), class: "cta cta-link", target: "_blank" %>
<%= button_to start_path(skip_facts: true), class: "cta" do %>
<%= tag.span(t("game.save_another_tomato")) %>


+ 2
- 2
app/views/layouts/application.html.erb View File

@ -17,7 +17,7 @@
<link rel="icon" sizes="192x192" href="/ikea-favicon-300x300.png">
<%= stylesheet_link_tag "reset", "application" %>
<%= frontend_javascript_importmap_tags %w[application @hotwired/turbo-rails @hotwired/stimulus language_menu_controller carousel_controller intro_controller chance_controller share_controller modal_controller rating_controller start_controller answer_controller] %>
<%= frontend_javascript_importmap_tags %w[application @hotwired/turbo-rails @hotwired/stimulus language_menu_controller carousel_controller intro_controller chance_controller share_controller modal_controller start_controller answer_controller] %>
</head>
<body id="page">
<%= turbo_frame_tag "game" do %>
@ -46,6 +46,6 @@
<% end %>
<% end %>
<%# render "shared/dev_jump" if Rails.env.development? %>
<%= render "shared/dev_jump" if Rails.env.development? %>
</body>
</html>

+ 1
- 1
app/views/shared/_dev_jump.html.erb View File

@ -39,7 +39,7 @@
<style>
#dev-jump {
position: fixed;
inset: auto auto 0.5rem 0.5rem;
inset: 0.5rem 0.5rem auto auto;
z-index: 2147483647;
margin: 0;
padding: 0;


+ 0
- 1
config/importmap.rb View File

@ -20,6 +20,5 @@ pin "intro_controller", preload: false
pin "chance_controller", preload: false
pin "share_controller", preload: false
pin "modal_controller", preload: false
pin "rating_controller", preload: false
pin "start_controller", preload: false
pin "answer_controller", preload: false

+ 3
- 1
config/locales/cs.yml View File

@ -18,13 +18,15 @@ cs:
cancel: Zrušit
next_stage: Další fáze
drumroll_see_the_result: A je to tady… podívej se na výsledek
did_you_like_this_game: Líbila se ti tato hra?
join_our_newsletter: Odebírej náš newsletter
join_our_newsletter_desc: Rádi bychom tě průběžně informovali a sdíleli s tebou příběhy o našich partnerech a o důležité práci, kterou vykonávají.
sounds_good: "Zní to dobře!"
save_another_tomato: Zachraň další rajče
share: Sdílet hru
subscribe_to_newsletter: Přihlas se k odběru newsletteru
subscribe_to_newsletter_url: https://www.ikeafoundation.org/cs/newsletter
learn_more: Zjistit více
learn_more_url: https://www.ikeafoundation.org/cs/campaign-26
results:
best:
headline: "<strong>Výborně,</strong> rajče je zachráněné!"


+ 3
- 1
config/locales/da.yml View File

@ -19,13 +19,15 @@ da:
cancel: Annuller
next_stage: Næste trin
drumroll_see_the_result: Trommehvirvel... se dit resultat
did_you_like_this_game: Kunne du lide dette spil?
join_our_newsletter: Tilmeld dig vores nyhedsbrev
join_our_newsletter_desc: Vi vil rigtig gerne holde dig opdateret og dele historier om vores partnere og det vigtige arbejde, de udfører.
sounds_good: "Det lyder godt!"
save_another_tomato: Red endnu en tomat
share: Del spillet
subscribe_to_newsletter: Tilmeld dig nyhedsbrevet
subscribe_to_newsletter_url: https://www.ikeafoundation.org/da/newsletter
learn_more: Læs mere
learn_more_url: https://www.ikeafoundation.org/da/campaign-26
results:
best:
headline: "<strong>Godt gået,</strong> du reddede tomaten!"


+ 3
- 1
config/locales/de.yml View File

@ -19,13 +19,15 @@ de:
cancel: Abbrechen
next_stage: Nächste Phase
drumroll_see_the_result: Trommelwirbel … hier ist dein Ergebnis
did_you_like_this_game: Hat dir dieses Spiel gefallen?
join_our_newsletter: Abonniere unseren Newsletter
join_our_newsletter_desc: Wir würden dich gerne auf dem Laufenden halten und Geschichten über unsere Partner und die wichtige Arbeit, die sie leisten, mit dir teilen.
sounds_good: "Klingt gut!"
save_another_tomato: Rette eine weitere Tomate
share: Spiel teilen
subscribe_to_newsletter: Jetzt Newsletter abonnieren
subscribe_to_newsletter_url: https://www.ikeafoundation.org/de/newsletter
learn_more: Mehr erfahren
learn_more_url: https://www.ikeafoundation.org/de/campaign-26
results:
best:
headline: "<strong>Gut gemacht,</strong> du hast die Tomate gerettet!"


+ 3
- 1
config/locales/en.yml View File

@ -22,7 +22,6 @@ en:
cancel: Cancel
next_stage: Next stage
drumroll_see_the_result: Drumroll… see your result
did_you_like_this_game: Did you like this game?
join_our_newsletter: Join our newsletter
join_our_newsletter_desc: We'd love to keep you in the loop and share stories
about our partners and the important work they're doing.
@ -30,6 +29,9 @@ en:
save_another_tomato: Save another tomato
share: Share game
subscribe_to_newsletter: Subscribe to newsletter
subscribe_to_newsletter_url: https://www.ikeafoundation.org/newsletter
learn_more: Read more
learn_more_url: https://www.ikeafoundation.org/campaign-26
results:
best:
headline: "<strong>Well done!</strong> You saved the tomato!"


+ 3
- 1
config/locales/es.yml View File

@ -19,13 +19,15 @@ es:
cancel: Cancelar
next_stage: Siguiente etapa
drumroll_see_the_result: Redoble de tambores... ¡Aquí tienes tu resultado!
did_you_like_this_game: ¿Te ha gustado este juego?
join_our_newsletter: Suscríbete a nuestra newsletter
join_our_newsletter_desc: Nos encantaría mantenerte al día y compartir contigo historias sobre nuestros socios y la importante labor que realizan.
sounds_good: "¡Suena bien!"
save_another_tomato: Salva otro tomate
share: Compartir juego
subscribe_to_newsletter: Suscribirse a la newsletter
subscribe_to_newsletter_url: https://www.ikeafoundation.org/es/newsletter
learn_more: Saber más
learn_more_url: https://www.ikeafoundation.org/es/campaign-26
results:
best:
headline: "<strong>¡Bien hecho,</strong> has salvado el tomate!"


+ 3
- 1
config/locales/fi.yml View File

@ -19,13 +19,15 @@ fi:
cancel: Peruuta
next_stage: Seuraava vaihe
drumroll_see_the_result: Rumpujen pärinää... katso tuloksesi
did_you_like_this_game: Piditkö tästä pelistä?
join_our_newsletter: Ryhdy uutiskirjeemme tilaajaksi
join_our_newsletter_desc: Haluaisimme pitää sinut ajan tasalla ja jakaa tarinoita kumppaneistamme ja heidän tärkeästä työstään.
sounds_good: "Kuulostaa hyvältä!"
save_another_tomato: Pelasta toinen tomaatti
share: Jaa peli
subscribe_to_newsletter: Tilaa uutiskirje
subscribe_to_newsletter_url: https://www.ikeafoundation.org/fi/newsletter
learn_more: Lue lisää
learn_more_url: https://www.ikeafoundation.org/fi/campaign-26
results:
best:
headline: "<strong>Hienoa,</strong> pelastit tomaatin!"


+ 3
- 1
config/locales/fr-CA.yml View File

@ -18,13 +18,15 @@ fr-CA:
cancel: Annuler
next_stage: Étape suivante
drumroll_see_the_result: Roulement de tambour... Découvrez votre résultat
did_you_like_this_game: Ce jeu vous a-t-il plu ?
join_our_newsletter: S’abonner à l’infolettre
join_our_newsletter_desc: Nous serions ravis de vous tenir informés et de partager avec vous des témoignages sur nos partenaires et le travail essentiel qu’ils accomplissent.
sounds_good: "Bonne idée !"
save_another_tomato: Sauver une autre tomate
share: Partager le jeu
subscribe_to_newsletter: S’abonner à l’infolettre
subscribe_to_newsletter_url: https://www.ikeafoundation.org/fr/newsletter
learn_more: En savoir plus
learn_more_url: https://www.ikeafoundation.org/fr/campaign-26
results:
best:
headline: "<strong>Bravo,</strong> vous avez sauvé la tomate !"


+ 3
- 1
config/locales/fr.yml View File

@ -18,13 +18,15 @@ fr:
cancel: Annuler
next_stage: Étape suivante
drumroll_see_the_result: Roulement de tambour... Découvrez votre résultat
did_you_like_this_game: Ce jeu vous a plu ?
join_our_newsletter: S'abonner à la newsletter
join_our_newsletter_desc: Nous serions ravis de continuer à vous donner des nouvelles et de partager avec vous des témoignages sur nos partenaires et le travail essentiel qu'ils accomplissent.
sounds_good: "Bonne idée !"
save_another_tomato: Sauver une autre tomate
share: Partager le jeu
subscribe_to_newsletter: S'abonner à la newsletter
subscribe_to_newsletter_url: https://www.ikeafoundation.org/fr/newsletter
learn_more: En savoir plus
learn_more_url: https://www.ikeafoundation.org/fr/campaign-26
results:
best:
headline: "<strong>Bravo,</strong> vous avez sauvé la tomate !"


+ 3
- 1
config/locales/hr.yml View File

@ -19,13 +19,15 @@ hr:
cancel: Otkaži
next_stage: Sljedeća faza
drumroll_see_the_result: Bubnjevi... pogledaj svoj rezultat
did_you_like_this_game: Je li ti se svidjela ova igra?
join_our_newsletter: Pridruži se našim e-novostima
join_our_newsletter_desc: Veselimo se što ti možemo slati novosti i dijeliti priče o našim partnerima i važnosti njihova rada.
sounds_good: "Zvuči dobro!"
save_another_tomato: Spasi još jednu rajčicu
share: Podijeli igru
subscribe_to_newsletter: Pretplati se na e-novosti
subscribe_to_newsletter_url: https://www.ikeafoundation.org/hr/newsletter
learn_more: Saznaj više
learn_more_url: https://www.ikeafoundation.org/hr/campaign-26
results:
best:
headline: "<strong>Odlično,</strong> spasio/la si rajčicu!"


+ 3
- 1
config/locales/hu.yml View File

@ -19,13 +19,15 @@ hu:
cancel: Mégse
next_stage: Következő szakasz
drumroll_see_the_result: Dobpergés... nézd meg az eredményed!
did_you_like_this_game: Élvezted ezt a játékot?
join_our_newsletter: Legyél te is a hírlevelünk olvasója
join_our_newsletter_desc: Szeretnénk továbbra is tájékoztatni téged a fejleményekről, megosztani veled a partnereink történeteit, és beszámolni a fontos munkáról, amit végeznek.
sounds_good: "Jól hangzik!"
save_another_tomato: Ments meg egy újabb paradicsomot
share: Játék megosztása
subscribe_to_newsletter: Iratkozz fel a hírlevelünkre
subscribe_to_newsletter_url: https://www.ikeafoundation.org/hu/newsletter
learn_more: Tudj meg többet
learn_more_url: https://www.ikeafoundation.org/hu/campaign-26
results:
best:
headline: "<strong>Ügyes voltál,</strong> sikerült megmentened a paradicsomot!"


+ 3
- 1
config/locales/it.yml View File

@ -19,13 +19,15 @@ it:
cancel: Annulla
next_stage: Fase successiva
drumroll_see_the_result: Ecco il tuo risultato!
did_you_like_this_game: Questo gioco è stato di tuo gradimento?
join_our_newsletter: Iscriviti alla nostra newsletter
join_our_newsletter_desc: Ci piacerebbe continuare a informarti sulle nostre iniziative e condividere storie sui nostri partner e sull'importante lavoro che stanno svolgendo.
sounds_good: "Ottima idea!"
save_another_tomato: Salva un altro pomodoro
share: Condividi il gioco
subscribe_to_newsletter: Iscriviti alla newsletter
subscribe_to_newsletter_url: https://www.ikeafoundation.org/it/newsletter
learn_more: Scopri di più
learn_more_url: https://www.ikeafoundation.org/it/campaign-26
results:
best:
headline: "<strong>Benissimo,</strong> hai salvato il pomodoro!"


+ 3
- 1
config/locales/ja.yml View File

@ -19,13 +19,15 @@ ja:
cancel: キャンセル
next_stage: 次のステージへ
drumroll_see_the_result: お待たせしました。さあ、結果をご覧ください
did_you_like_this_game: このゲームは気に入りましたか?
join_our_newsletter: ニュースレターにご登録ください
join_our_newsletter_desc: イケアのパートナーや、その大切な取り組みについて、最新のストーリーをお届けします。
sounds_good: "いいですね!"
save_another_tomato: さらにトマトを救う
share: ゲームをシェア
subscribe_to_newsletter: ニュースレターに登録する
subscribe_to_newsletter_url: https://www.ikeafoundation.org/ja/newsletter
learn_more: 詳しく見る
learn_more_url: https://www.ikeafoundation.org/ja/campaign-26
results:
best:
headline: "<strong>トマトを救うことができました!</strong>"


+ 3
- 1
config/locales/ko.yml View File

@ -18,13 +18,15 @@ ko:
cancel: 취소
next_stage: 다음 단계
drumroll_see_the_result: 두구두구두구… 결과를 확인해 보세요!
did_you_like_this_game: 게임을 재미있게 즐기셨나요?
join_our_newsletter: 뉴스레터를 구독해 보세요
join_our_newsletter_desc: 파트너들의 이야기와 중요한 활동 소식을 계속 전해 드릴게요.
sounds_good: Sounds good!
save_another_tomato: 토마토 더 구하러 가기
share: 게임 공유하기
subscribe_to_newsletter: 뉴스레터 구독하기
subscribe_to_newsletter_url: https://www.ikeafoundation.org/ko/newsletter
learn_more: 더 알아보기
learn_more_url: https://www.ikeafoundation.org/ko/campaign-26
results:
best:
headline: "<strong>축하합니다!</strong> 토마토를 구했어요!"


+ 3
- 1
config/locales/nb.yml View File

@ -19,13 +19,15 @@ nb:
cancel: Avbryt
next_stage: Neste trinn
drumroll_see_the_result: Trommevirvel ... se resultatet ditt
did_you_like_this_game: Likte du dette spillet?
join_our_newsletter: Meld deg på nyhetsbrevet vårt
join_our_newsletter_desc: Vi vil gjerne holde deg oppdatert og dele historier om partnerne våre og det viktige arbeidet de gjør.
sounds_good: "Høres bra ut!"
save_another_tomato: Redd en tomat til
share: Del spill
subscribe_to_newsletter: Abonner på nyhetsbrev
subscribe_to_newsletter_url: https://www.ikeafoundation.org/no/newsletter
learn_more: Les mer
learn_more_url: https://www.ikeafoundation.org/no/campaign-26
results:
best:
headline: "<strong>Bra jobba,</strong> du reddet tomaten!"


+ 3
- 1
config/locales/nl.yml View File

@ -19,13 +19,15 @@ nl:
cancel: Annuleren
next_stage: Volgende fase
drumroll_see_the_result: Tromgeroffel... bekijk je resultaat
did_you_like_this_game: Vond je deze game leuk?
join_our_newsletter: Meld je aan voor onze nieuwsbrief
join_our_newsletter_desc: We houden je graag op de hoogte en delen verhalen over onze partners en het belangrijke werk dat ze doen.
sounds_good: "Klinkt goed!"
save_another_tomato: Red nog een tomaat
share: Spel delen
subscribe_to_newsletter: Aanmelden voor nieuwsbrief
subscribe_to_newsletter_url: https://www.ikeafoundation.org/nl/newsletter
learn_more: Lees meer
learn_more_url: https://www.ikeafoundation.org/nl/campaign-26
results:
best:
headline: "<strong>Goed gedaan,</strong> je hebt de tomaat gered!"


+ 3
- 1
config/locales/pl.yml View File

@ -19,13 +19,15 @@ pl:
cancel: Anuluj
next_stage: Następny etap
drumroll_see_the_result: Czas na werdykt – zobacz, jak Ci poszło!
did_you_like_this_game: Podobała Ci się ta gra?
join_our_newsletter: Dołącz do naszego newslettera
join_our_newsletter_desc: Z miłą chęcią będziemy informować Cię na bieżąco i opowiadać o naszych partnerach oraz o ich ważnej pracy.
sounds_good: "Brzmi dobrze!"
save_another_tomato: Uratuj kolejnego pomidora
share: Poleć grę innym
subscribe_to_newsletter: Subskrybuj newsletter
subscribe_to_newsletter_url: https://www.ikeafoundation.org/pl/newsletter
learn_more: Dowiedz się więcej
learn_more_url: https://www.ikeafoundation.org/pl/campaign-26
results:
best:
headline: "<strong>Dobra robota,</strong> pomidor uratowany!"


+ 3
- 1
config/locales/pt.yml View File

@ -19,13 +19,15 @@ pt:
cancel: Cancelar
next_stage: Etapa seguinte
drumroll_see_the_result: Rufem os tambores... vê o teu resultado
did_you_like_this_game: Gostaste do jogo?
join_our_newsletter: Subscreve a nossa newsletter
join_our_newsletter_desc: Adorávamos manter-te a par e partilhar histórias sobre os nossos parceiros e o trabalho importante que estão a fazer.
sounds_good: "Parece-me bem!"
save_another_tomato: Salvar outro tomate
share: Partilhar o jogo
subscribe_to_newsletter: Subscrever a newsletter
subscribe_to_newsletter_url: https://www.ikeafoundation.org/pt/newsletter
learn_more: Saiba mais
learn_more_url: https://www.ikeafoundation.org/pt/campaign-26
results:
best:
headline: "<strong>Muito bem,</strong> salvaste o tomate!"


+ 3
- 1
config/locales/ro.yml View File

@ -19,13 +19,15 @@ ro:
cancel: Anulează
next_stage: Etapa următoare
drumroll_see_the_result: Puțin suspans... vezi rezultatul
did_you_like_this_game: Ți-a plăcut acest joc?
join_our_newsletter: Abonează-te la newsletterul nostru
join_our_newsletter_desc: Ne-ar plăcea să te ținem la curent și să-ți împărtășim povești despre partenerii noștri și activitatea lor importantă.
sounds_good: "Sună bine!"
save_another_tomato: Salvează altă roșie
share: Distribuie jocul
subscribe_to_newsletter: Abonează-te la newsletter
subscribe_to_newsletter_url: https://www.ikeafoundation.org/ro/newsletter
learn_more: Află mai multe
learn_more_url: https://www.ikeafoundation.org/ro/campaign-26
results:
best:
headline: "<strong>Bravo,</strong> ai salvat roșia!"


+ 3
- 1
config/locales/sk.yml View File

@ -19,13 +19,15 @@ sk:
cancel: Zrušiť
next_stage: Ďalšia fáza
drumroll_see_the_result: Ešte chvíľku… a tu je tvoj výsledok!
did_you_like_this_game: Páčila sa ti táto hra?
join_our_newsletter: Prihlás sa na odber nášho newslettera
join_our_newsletter_desc: Radi ťa budeme informovať o novinkách a podelíme sa s tebou o príbehy našich partnerov a dôležitej práci, ktorú robia.
sounds_good: "Znie to dobre!"
save_another_tomato: Zachrániť ďalšiu paradajku
share: Zdieľať hru
subscribe_to_newsletter: Prihlásiť sa na odber newslettera
subscribe_to_newsletter_url: https://www.ikeafoundation.org/newsletter
learn_more: Zistiť viac
learn_more_url: https://www.ikeafoundation.org/campaign-26
results:
best:
headline: "<strong>Super,</strong> podarilo sa ti zachrániť paradajku!"


+ 3
- 1
config/locales/sl.yml View File

@ -19,13 +19,15 @@ sl:
cancel: Prekliči
next_stage: Naslednja stopnja
drumroll_see_the_result: Bobni ... poglej svoj rezultat
did_you_like_this_game: Ti je bila ta igra všeč?
join_our_newsletter: Spremljaj naše e-novice
join_our_newsletter_desc: Z veseljem te bomo obveščali o novostih in s tabo delili zgodbe o naših partnerjih ter pomembnem delu, ki ga opravljajo.
sounds_good: "Sliši se dobro!"
save_another_tomato: Reši še en paradižnik
share: Deli igro
subscribe_to_newsletter: Prijavi se na e-novice
subscribe_to_newsletter_url: https://www.ikeafoundation.org/newsletter
learn_more: Izvedi več
learn_more_url: https://www.ikeafoundation.org/campaign-26
results:
best:
headline: "<strong>Super,</strong> rešil/-a si paradižnik!"


+ 3
- 1
config/locales/sr.yml View File

@ -19,13 +19,15 @@ sr:
cancel: Otkaži
next_stage: Sledeća faza
drumroll_see_the_result: Ta-da... evo tvog rezultata
did_you_like_this_game: Da li ti se svidela ova igra?
join_our_newsletter: Prijavi se na naš bilten
join_our_newsletter_desc: Voleli bismo da budeš u toku i da delimo priče o našim partnerima i važnom poslu koji obavljaju.
sounds_good: "Zvuči dobro!"
save_another_tomato: Sačuvaj još jedan paradajz
share: Deli igru
subscribe_to_newsletter: Prijavi se na bilten
subscribe_to_newsletter_url: https://www.ikeafoundation.org/sr/newsletter
learn_more: Saznaj više
learn_more_url: https://www.ikeafoundation.org/sr/campaign-26
results:
best:
headline: "<strong>Bravo,</strong> paradajz je sačuvan!"


+ 3
- 1
config/locales/sv.yml View File

@ -19,13 +19,15 @@ sv:
cancel: Avbryt
next_stage: Nästa steg
drumroll_see_the_result: Trumvirvel … se ditt resultat
did_you_like_this_game: Gillade du det här spelet?
join_our_newsletter: Anmäl dig till vårt nyhetsbrev
join_our_newsletter_desc: Vi vill gärna hålla dig uppdaterad och dela berättelser om våra partner och det viktiga arbete de gör.
sounds_good: "Låter bra!"
save_another_tomato: Rädda en tomat till
share: Dela spel
subscribe_to_newsletter: Registrera dig till vårt nyhetsbrev
subscribe_to_newsletter_url: https://www.ikeafoundation.org/sv/newsletter
learn_more: Läs mer
learn_more_url: https://www.ikeafoundation.org/sv/campaign-26
results:
best:
headline: "<strong>Bra jobbat,</strong> du räddade tomaten!"


+ 3
- 1
config/locales/uk.yml View File

@ -19,13 +19,15 @@ uk:
cancel: Скасувати
next_stage: Наступний етап
drumroll_see_the_result: Барабанний дріб… подивіться свій результат
did_you_like_this_game: Вам сподобалася ця гра?
join_our_newsletter: Підпишіться на нашу розсилку
join_our_newsletter_desc: Ми хотіли б тримати вас у курсі подій і ділитися історіями про наших партнерів та їхню важливу діяльність.
sounds_good: "Звучить чудово!"
save_another_tomato: Врятувати ще один помідор
share: Поділитися грою
subscribe_to_newsletter: Підписатися на розсилку
subscribe_to_newsletter_url: https://www.ikeafoundation.org/newsletter
learn_more: Дізнатися більше
learn_more_url: https://www.ikeafoundation.org/campaign-26
results:
best:
headline: "<strong>Чудово,</strong> вам вдалося врятувати помідор!"


+ 3
- 1
config/locales/zh.yml View File

@ -19,13 +19,15 @@ zh:
cancel: 取消
next_stage: 下一关
drumroll_see_the_result: 咚咚咚……来看看你的结果
did_you_like_this_game: 你喜欢这款游戏吗?
join_our_newsletter: 订阅我们的新闻简讯
join_our_newsletter_desc: 我们很乐意与你分享我们合作伙伴的动人故事,深入了解他们的重要工作成果。
sounds_good: "听起来不错!"
save_another_tomato: 再拯救一颗番茄
share: 分享游戏
subscribe_to_newsletter: 订阅新闻简讯
subscribe_to_newsletter_url: https://www.ikeafoundation.org/zh/newsletter
learn_more: 了解更多
learn_more_url: https://www.ikeafoundation.org/zh/campaign-26
results:
best:
headline: "<strong>做得好,</strong> 你拯救了这颗番茄!"


+ 0
- 1
config/routes.rb View File

@ -85,7 +85,6 @@ Rails.application.routes.draw do
get "done", to: "game#done"
get "results", to: "game#results"
post "results/rating", to: "game#rate", as: :rate
get "", to: "game#index", as: :locale_root
end


+ 9
- 0
db/migrate/20260918120000_remove_rating_from_players.rb View File

@ -0,0 +1,9 @@
class RemoveRatingFromPlayers < ActiveRecord::Migration[8.1]
def change
# The results screen no longer asks "did you like this game?", so the vote
# and the admin panel reading it are gone along with these columns.
remove_index :players, :rating
remove_column :players, :rating, :integer
remove_column :players, :rated_at, :datetime
end
end

+ 1
- 4
db/schema.rb View File

@ -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_09_07_120000) do
ActiveRecord::Schema[8.1].define(version: 2026_09_18_120000) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@ -122,8 +122,6 @@ ActiveRecord::Schema[8.1].define(version: 2026_09_07_120000) do
t.boolean "is_done", default: false, null: false
t.string "locale", null: false
t.jsonb "progress", default: {}, null: false
t.datetime "rated_at"
t.integer "rating"
t.integer "score", default: 0, null: false
t.jsonb "scores", default: {}, null: false
t.datetime "updated_at", null: false
@ -137,7 +135,6 @@ ActiveRecord::Schema[8.1].define(version: 2026_09_07_120000) do
t.index ["furthest_step"], name: "index_players_on_furthest_step"
t.index ["is_done"], name: "index_players_on_is_done"
t.index ["locale"], name: "index_players_on_locale"
t.index ["rating"], name: "index_players_on_rating"
t.index ["utm_content"], name: "index_players_on_utm_content"
t.index ["utm_source"], name: "index_players_on_utm_source"
end


Loading…
Cancel
Save