Newer
Older
gnexus-tasks / frontend / src / gamification.ts
// Геймификация (ТЗ 3.13): микронаграда за закрытие задачи — конфетти
// и тост «+N XP» с похвалой. Только позитив: ничего не сгорает, серий нет.

import { takeEarnedXp } from './api'
import i18n from './i18n'

// Регистрация toast-провайдера кита (вызывается один раз в App.vue setup)
type ToastApi = { success: (opts: { title: string; text?: string }) => unknown }
let toast: ToastApi | null = null

export function initGamification(t: ToastApi): void {
  toast = t
}

// Похвала — случайная фраза из локали (gamify.praise — массив строк)
function randomPraise(): string {
  const messages = i18n.global.tm('gamify.praise')
  const list = Array.isArray(messages) ? (messages as unknown[]) : []
  const phrase = list.length ? list[Math.floor(Math.random() * list.length)] : ''
  return typeof phrase === 'string' ? phrase : ''
}

// CSS-конфетти: частицы падают сверху с вращением, слой чистится сам
const CONFETTI_COLORS = ['#7aa2f7', '#9ece6a', '#e0af68', '#bb9af7', '#f7768e', '#73daca']

function confetti(): void {
  const layer = document.createElement('div')
  layer.className = 'confetti-layer'
  for (let i = 0; i < 32; i++) {
    const piece = document.createElement('span')
    piece.className = 'confetti-piece'
    piece.style.left = `${5 + Math.random() * 90}%`
    piece.style.background = CONFETTI_COLORS[i % CONFETTI_COLORS.length]
    piece.style.animationDelay = `${Math.random() * 0.4}s`
    piece.style.animationDuration = `${1.1 + Math.random() * 0.9}s`
    piece.style.setProperty('--rot', `${-540 + Math.floor(Math.random() * 1080)}deg`)
    piece.style.setProperty('--drift', `${-60 + Math.floor(Math.random() * 120)}px`)
    layer.appendChild(piece)
  }
  document.body.appendChild(layer)
  window.setTimeout(() => layer.remove(), 2400)
}

// Вызывать после любого действия, которое могло закрыть задачу:
// если бэк начислил XP (заголовок X-Earned-XP) — праздник + тост
export function celebrateEarned(): void {
  const xp = takeEarnedXp()
  if (!xp) return
  confetti()
  toast?.success({ title: `+${xp} XP`, text: randomPraise() })
}