import { useCallback, useEffect, useState } from 'react'; import { api } from '../api/api'; import { ApiError } from '../api/http'; import type { ActiveMission, AvailableMission, ClaimMissionResponse } from '../api/types'; import { useAuth } from '../auth/AuthContext'; import { Countdown } from '../components/Countdown'; import { useToast } from '../components/Toast'; import { difficultyStars, formatDuration, formatMoney, formatPercent, MISSION_TYPE_LABELS, } from '../shared/format'; /** Colore della barra probabilità: verde → oro → rosso. */ function chanceColor(chance: number): string { if (chance >= 0.7) return '#4caf6e'; if (chance >= 0.45) return '#d4a017'; return '#d9534f'; } export function MissionsScreen() { const { setPlayer } = useAuth(); const { toast } = useToast(); const [available, setAvailable] = useState([]); const [active, setActive] = useState([]); const [result, setResult] = useState(null); const [busy, setBusy] = useState(false); // tick fittizio per ri-renderizzare quando un countdown arriva a zero const [, setTick] = useState(0); const reload = useCallback(async () => { const [av, ac] = await Promise.all([api.missionsAvailable(), api.missionsActive()]); setAvailable(av.missions); setActive(ac.missions); }, []); useEffect(() => { void reload(); }, [reload]); async function start(mission: AvailableMission) { setBusy(true); try { await api.startMission(mission.id); toast(`Missione "${mission.title}" avviata`, 'success'); await reload(); } catch (err) { toast(err instanceof ApiError ? err.message : 'Errore imprevisto', 'error'); } finally { setBusy(false); } } async function claim(playerMissionId: string) { setBusy(true); try { const res = await api.claimMission(playerMissionId); setResult(res); setPlayer(res.player); await reload(); } catch (err) { toast(err instanceof ApiError ? err.message : 'Errore imprevisto', 'error'); } finally { setBusy(false); } } async function abandon(mission: ActiveMission) { if (!window.confirm(`Abbandonare "${mission.mission.title}"? Perderai reputazione e la merce già consegnata.`)) { return; } setBusy(true); try { const res = await api.abandonMission(mission.playerMissionId); setPlayer(res.player); toast(`Missione abbandonata (${res.reputationChange} reputazione)`, 'info'); await reload(); } catch (err) { toast(err instanceof ApiError ? err.message : 'Errore imprevisto', 'error'); } finally { setBusy(false); } } const isReady = (m: ActiveMission) => new Date(m.completesAt).getTime() <= Date.now(); return (

Missioni

{active.length > 0 && (

In corso

    {active.map((m) => (
  • {m.mission.title} {isReady(m) ? ( ) : ( setTick((t) => t + 1)} /> )}

    {MISSION_TYPE_LABELS[m.mission.type]} · premio{' '} {formatMoney(m.mission.rewardMoney)} + {m.mission.rewardXp} XP {m.mission.rewardItemName && ` + ${m.mission.rewardItemQuantity}× ${m.mission.rewardItemName}`}

    {!isReady(m) && ( )}
  • ))}
)}

Disponibili in città

{available.length === 0 ? (

Nessuna missione al momento. Riprova tra un minuto.

) : (
    {available.map((mission) => { const locked = !mission.canStart; return (
  • {mission.title} {difficultyStars(mission.difficulty)}
    {MISSION_TYPE_LABELS[mission.type]}

    {mission.description}

    durata {formatDuration(mission.durationSeconds)} · rischio{' '} {formatPercent(mission.risk)}

    successo {formatPercent(mission.estimatedSuccessChance)}
    {mission.requiredItemName && (

    Richiede: {mission.requiredItemQuantity}× {mission.requiredItemName}

    )}
    {formatMoney(mission.rewardMoney)} · {mission.rewardXp} XP {mission.rewardItemName && ` · 🎁 ${mission.rewardItemQuantity}× ${mission.rewardItemName}`} {locked ? ( 🔒 liv. {mission.minLevel} ) : ( )}
  • ); })}
)}
{result && (
setResult(null)}>
e.stopPropagation()}>

{result.success ? '✅ Colpo riuscito!' : '❌ Missione fallita'}

"{result.missionTitle}"

{result.success ? (
  • +{formatMoney(result.rewardMoney)}
  • +{result.rewardXp} XP
  • +{result.reputationChange} reputazione
  • {result.lootItemName && (
  • 🎁 +{result.lootItemQuantity}× {result.lootItemName}
  • )} {result.levelsGained > 0 && (
  • Sei salito al livello {result.player.level}!
  • )}
) : (
    {result.fine > 0 &&
  • 💸 multa: −{formatMoney(result.fine)}
  • }
  • {result.reputationChange} reputazione
  • Probabilità di successo: {formatPercent(result.successChance)}
)}
)}
); }