Razor panel for the club career achievements screen. Lists career achievements and their claim buttons, shows season challenges with progress meters, and renders recent season history; has a compact mode that delegates to CareerFinaleView. Claim clicks call the OnClaim callback.
@using Sandbox
@using Sandbox.UI
@using Socer
@using System
@using System.Linq
@inherits Panel
<root class="career-achievements localized-text @(Compact?"compact":"expanded")">
@if(Compact)
{
<CareerFinaleView Career="@Career" />
}
else
{
<label class="career-achievements-title">@T("Достижения клуба","Club achievements")</label>
@if(Career.Achievements.Any(a=>a.RewardAvailable && !Career.CanReceiveTrainingCards(a.TrainingCount)) || Career.SeasonChallenges.Any(c=>c.Earned && !c.Claimed && !Career.CanReceiveTrainingCards(c.TrainingCount)))
{<label class="career-challenge-hint">@T("Для награды нужно место в инвентаре улучшений. Примените или спишите ненужные карточки в «Мой клуб → Улучшения». Награда остаётся доступной.","This reward needs upgrade inventory space. Apply or discard unwanted cards in My club → Upgrades. The reward remains available.")</label>}
<div class="career-achievement-grid">
@foreach(var id in AchievementIds)
{
var achievement=Career.Achievements.FirstOrDefault(a=>a.Id==id);
<div class="career-achievement @(achievement==null?"locked":"earned")"><img src="/ui-icons/ui-bold-nav-cup.svg"/><div><label>@AchievementName(id)</label><span>@(achievement==null?AchievementGoal(id):T($"Сезон {achievement.EarnedSeason}",$"Season {achievement.EarnedSeason}"))</span></div>
@if(achievement?.RewardAvailable==true){<button class="primary claim-career-reward" disabled="@(Career.Pending!=null || !Career.CanReceiveTrainingCards(achievement.TrainingCount))" onclick=@(()=>OnClaim?.Invoke(id))>@Reward(achievement.CoinReward,achievement.TrainingGrade,achievement.TrainingCount)</button>}
else if(achievement!=null){<b>✓</b>}
</div>
}
</div>
<label class="career-achievements-title">@T("Новые вызовы","Next challenges")</label>
@if(!Career.PremierChampion){<label class="career-challenge-hint">@T("После первого титула Премьер-лиги каждый новый сезон получает отдельный вызов. Карьера продолжается, состав и достижения сохраняются.","After your first Premier title, each new season has a separate challenge. Your career continues with your squad and achievements.")</label>}
else if(!Career.SeasonChallenges.Any()){<label class="career-challenge-hint">@T("Первый титул выигран! С нового сезона начнутся новые вызовы.","The first title is yours! New challenges start next season.")</label>}
@foreach(var challenge in Career.SeasonChallenges.OrderByDescending(c=>c.Season).Where(c=>c.Season==Career.Season || c.Earned && !c.Claimed).Take(6))
{
<div class="career-season-challenge @(challenge.Earned?"earned":"")"><div><span>@T($"Сезон {challenge.Season}",$"Season {challenge.Season}")</span><label>@ChallengeName(challenge.Objective)</label><span>@ChallengeGoal(challenge)</span></div><div class="career-challenge-meter"><span>@($"{challenge.Progress}/{challenge.Target}")</span><div><span style="@($"width:{Math.Clamp(challenge.Progress*100/Math.Max(1,challenge.Target),0,100)}%;")"></span></div></div><button class="primary claim-career-reward" disabled="@(!challenge.Earned || challenge.Claimed || Career.Pending!=null || !Career.CanReceiveTrainingCards(challenge.TrainingCount))" onclick=@(()=>OnClaim?.Invoke(challenge.Id))>@(challenge.Claimed?T("Получено","Claimed"):challenge.Earned?Reward(challenge.CoinReward,challenge.TrainingGrade,challenge.TrainingCount):challenge.Resolved?T("Не выполнено","Missed"):T("В процессе","In progress"))</button></div>
}
@if(Career.LeagueHistory.Count>0){<label class="career-achievements-title">@T("История сезонов","Season history")</label><div class="career-season-history">@foreach(var result in Career.LeagueHistory.OrderByDescending(r=>r.Season).Take(8)){<div><label>@T($"Сезон {result.Season} · {League(result.Division)}",$"Season {result.Season} · {League(result.Division)}")</label><b>@($"#{result.Place}")</b><span>@T($"{result.Points} оч. · {result.Wins} В / {result.Draws} Н / {result.Losses} П",$"{result.Points} pts · {result.Wins} W / {result.Draws} D / {result.Losses} L")</span></div>}</div>}
}
</root>
@code {
[Parameter] public ClubCareer Career {get;set;}
[Parameter] public bool Compact {get;set;}
[Parameter] public Action<string> OnClaim {get;set;}
static readonly string[] AchievementIds={"first-league-title","cup-winner","premier-champion","unbeaten-champion","domestic-double"};
static string T(string ru,string en)=>GamePreferences.Russian?ru:en;
static string League(int division)=>T(new[]{"Третья лига","Вторая лига","Первая лига","Национальная лига","Премьер-лига"}[Math.Clamp(division-1,0,4)],new[]{"Third League","Second League","First League","National League","Premier League"}[Math.Clamp(division-1,0,4)]);
static string AchievementName(string id)=>id switch{
"first-league-title"=>T("Первое чемпионство","First league title"),"cup-winner"=>T("Первый кубок","First cup"),
"premier-champion"=>T("Чемпион Премьер-лиги","Premier champion"),"unbeaten-champion"=>T("Непобеждённый чемпион","Unbeaten champion"),
"domestic-double"=>T("Золотой дубль","Domestic double"),_=>id};
static string AchievementGoal(string id)=>id switch{
"first-league-title"=>T("Выиграйте любую лигу.","Win any league."),"cup-winner"=>T("Выиграйте кубок.","Win a cup."),
"premier-champion"=>T("Выиграйте Премьер-лигу.","Win the Premier League."),"unbeaten-champion"=>T("Выиграйте Премьер-лигу без поражений.","Win the Premier League without a loss."),
"domestic-double"=>T("Выиграйте Премьер-лигу и кубок в одном сезоне.","Win the Premier League and a cup in one season."),_=>""};
static string ChallengeName(string id)=>id switch{
"defend-title"=>T("Защитить титул","Defend the title"),"double"=>T("Золотой дубль","Domestic double"),
"strong-season"=>T("Сильный сезон","Strong season"),"clean-sheets"=>T("Надёжная оборона","Clean sheets"),
"return-premier"=>T("Вернуться в Премьер-лигу","Return to the Premier League"),_=>id};
static string ChallengeGoal(CareerSeasonChallenge c)=>c.Objective switch{
"defend-title"=>T("Снова выиграйте Премьер-лигу.","Win the Premier League again."),"double"=>AchievementGoal("domestic-double"),
"strong-season"=>T($"Наберите {c.Target} очков в лиге.",$"Earn {c.Target} league points."),
"clean-sheets"=>T($"Завершите {c.Target} матчей лиги без пропущенных голов.",$"Keep {c.Target} league clean sheets."),
"return-premier"=>T("Финишируйте в первой двойке Национальной лиги.","Finish in the National League's top two."),_=>""};
static string Reward(int coins,int grade,int count)=>T($"Забрать: {coins} ◈ + {count} {Grade(grade)} улучш.",$"Claim: {coins} ◈ + {count} {Grade(grade)} upgrade");
static string Grade(int grade)=>T(new[]{"бронз.","серебр.","золот."}[Math.Clamp(grade-1,0,2)],new[]{"bronze","silver","gold"}[Math.Clamp(grade-1,0,2)]);
protected override int BuildHash()=>HashCode.Combine(Career?.Season,Career?.PremierTitleCount,Career?.ClaimableCareerRewards,Career?.CurrentSeasonChallenge?.Progress,Career?.LeagueHistory?.Count,Compact,GamePreferences.Russian)^HashCode.Combine(Career?.Achievements?.Count,Career?.CurrentSeasonChallenge?.Earned,Career?.CurrentSeasonChallenge?.Resolved,Career?.Pending!=null,Career?.CareerWon,Career?.FinalCup!=null);
}