feat(fortune-wheel): celebration reveal, spin animation, prize editor add/remove

Player experience:
- Tiered win celebration overlay (WheelWinReveal): quiet message for the
  "nothing" slice, lighter reveal for common prizes, full confetti +
  jackpot glow for rare ones. Rarity classified client-side by type +
  amount (wheelPrizeTier), shared icon rendering (wheelPrizeIcon).
- Three-phase spin motion (wind-back -> overshoot -> settle) with a
  reduced-motion fast path; responsive wheel scaling via ResizeObserver.

Reveal-timing fix:
- The server pushes the refreshed winners list (which already contains the
  just-won prize) the instant it answers the spin, ~5s before the wheel
  stops. useFortuneWheel now buffers that update mid-spin and flushes it in
  finishSpin so the prize is no longer spoiled in the winners panel.
- handleTransitionEnd only reacts to the wheel's own transform transition,
  so a child icon's bubbling transitionend can't advance the spin phase
  machine early.

Prize editor (admin):
- Add/Remove prize buttons in FortuneWheelSettingsView. New rows carry a
  negative temp id collapsed to 0 on the wire (server inserts); removed rows
  are simply omitted (server soft-disables). Requires the matching emulator
  change to WheelManager.savePrize / WheelAdminSavePrizesEvent.

i18n: wheel.win.* and rarevalues.editor.add/remove in en/it/nl.
This commit is contained in:
simoleo89
2026-05-31 10:47:51 +02:00
parent fa71e8eb4a
commit ccebcad8a8
10 changed files with 502 additions and 107 deletions
@@ -642,6 +642,9 @@
'wheel.buy': 'Buy spin',
'wheel.winners': 'Latest winners',
'wheel.winners.empty': 'No winners yet',
'wheel.win.title': 'You won!',
'wheel.win.jackpot': '★ Jackpot ★',
'wheel.win.nothing': 'Better luck next time!',
// ------------------------------------------------------------------------
// Soundboard
@@ -674,6 +677,8 @@
'rarevalues.editor.weight': 'Chance',
'rarevalues.editor.label': 'Label',
'rarevalues.editor.save': 'Save',
'rarevalues.editor.add': '+ Add prize',
'rarevalues.editor.remove': 'Remove prize',
'rarevalues.editor.cat.item': 'Furni (ID)',
'rarevalues.editor.cat.spin': 'Extra spins',
'rarevalues.editor.cat.nothing': 'Nothing',
@@ -642,6 +642,9 @@
'wheel.buy': 'Acquista giro',
'wheel.winners': 'Ultimi vincitori',
'wheel.winners.empty': 'Ancora nessun vincitore',
'wheel.win.title': 'Hai vinto!',
'wheel.win.jackpot': '★ Jackpot ★',
'wheel.win.nothing': 'Sarà per la prossima!',
// ------------------------------------------------------------------------
// Soundboard
@@ -674,6 +677,8 @@
'rarevalues.editor.weight': 'Probabilità',
'rarevalues.editor.label': 'Etichetta',
'rarevalues.editor.save': 'Salva',
'rarevalues.editor.add': '+ Aggiungi premio',
'rarevalues.editor.remove': 'Rimuovi premio',
'rarevalues.editor.cat.item': 'Arredo (ID)',
'rarevalues.editor.cat.spin': 'Giri extra',
'rarevalues.editor.cat.nothing': 'Niente',
@@ -644,6 +644,9 @@
'wheel.buy': 'Draaibeurt kopen',
'wheel.winners': 'Laatste winnaars',
'wheel.winners.empty': 'Nog geen winnaars',
'wheel.win.title': 'Gewonnen!',
'wheel.win.jackpot': '★ Jackpot ★',
'wheel.win.nothing': 'Volgende keer beter!',
// ------------------------------------------------------------------------
// Soundboard
@@ -676,6 +679,8 @@
'rarevalues.editor.weight': 'Kans',
'rarevalues.editor.label': 'Label',
'rarevalues.editor.save': 'Opslaan',
'rarevalues.editor.add': '+ Prijs toevoegen',
'rarevalues.editor.remove': 'Prijs verwijderen',
'rarevalues.editor.cat.item': 'Meubel (ID)',
'rarevalues.editor.cat.spin': 'Extra draaien',
'rarevalues.editor.cat.nothing': 'Niets',