feat(ui): event autocomplete + log-bet deep link, steam-move label
- MyBets: add a "Find event" MudAutocomplete over upcoming events (loaded once, filtered client-side) that fills the Event ID; the manual ID field stays as a fallback. Backed by IBetJournalService.GetUpcomingEventOptionsAsync. - Add a "Log bet" CTA on the anomaly detail page that deep-links to /my-bets?eventId=<code>; the journal prefills the Event ID from the query. - Render the new SteamMove anomaly kind with a localized label in the card and detail KindLabel switches (was falling through to the raw enum name). - Localization (en+ru) for all new strings.
This commit is contained in:
@@ -54,6 +54,13 @@
|
||||
data-test="link-back-to-event">
|
||||
@L["Anomaly.Detail.LinkBackToEvent"]
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Outlined"
|
||||
StartIcon="@Icons.Material.Outlined.Receipt"
|
||||
OnClick="@(() => Nav.NavigateTo($"/my-bets?eventId={Uri.EscapeDataString(_detail.Item.EventId.Value)}"))"
|
||||
Class="m-detail-header__export"
|
||||
data-test="log-bet">
|
||||
@L["Action.LogBet"]
|
||||
</MudButton>
|
||||
</aside>
|
||||
</header>
|
||||
|
||||
@@ -99,6 +106,7 @@
|
||||
private string KindLabel(AnomalyKind kind) => kind switch
|
||||
{
|
||||
AnomalyKind.SuspensionFlip => L["Anomaly.Kind.SuspensionFlip"],
|
||||
AnomalyKind.SteamMove => L["Anomaly.Kind.SteamMove"],
|
||||
_ => kind.ToString(),
|
||||
};
|
||||
|
||||
|
||||
@@ -114,6 +114,20 @@
|
||||
|
||||
<article class="m-card m-card--accented m-journal__form-card">
|
||||
<div class="m-journal__form-grid">
|
||||
<div class="m-journal__form-field m-journal__form-field--wide">
|
||||
<label class="m-journal__form-label">@L["Journal.Field.FindEvent"]</label>
|
||||
<MudAutocomplete T="EventOption"
|
||||
SearchFunc="SearchEventsAsync"
|
||||
ToStringFunc="@(o => o is null ? string.Empty : o.Label)"
|
||||
ValueChanged="OnEventSelected"
|
||||
Variant="Variant.Outlined"
|
||||
Clearable="true"
|
||||
ResetValueOnEmptyText="true"
|
||||
Placeholder="@L["Journal.Field.FindEvent.Placeholder"]"
|
||||
data-test="journal-find-event" />
|
||||
<span class="m-journal__form-hint">@L["Journal.Field.FindEvent.Hint"]</span>
|
||||
</div>
|
||||
|
||||
<div class="m-journal__form-field m-journal__form-field--wide">
|
||||
<label class="m-journal__form-label" for="journal-event-id">@L["Journal.Field.EventId"]</label>
|
||||
<MudTextField id="journal-event-id"
|
||||
@@ -718,9 +732,48 @@
|
||||
}
|
||||
private CancellationTokenSource? _loadCts;
|
||||
|
||||
private IReadOnlyList<EventOption> _eventOptions = Array.Empty<EventOption>();
|
||||
|
||||
/// <summary>Optional event code supplied by a "Log bet" deep link (e.g. from an anomaly).</summary>
|
||||
[Parameter, SupplyParameterFromQuery(Name = "eventId")]
|
||||
public string? PrefillEventId { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(PrefillEventId))
|
||||
_form.EventId = PrefillEventId.Trim();
|
||||
|
||||
await LoadAsync();
|
||||
await LoadEventOptionsAsync();
|
||||
}
|
||||
|
||||
private async Task LoadEventOptionsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var options = await Service.GetUpcomingEventOptionsAsync(_loadCts?.Token ?? CancellationToken.None);
|
||||
_eventOptions = options ?? Array.Empty<EventOption>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// The autocomplete is a convenience; failing to populate it must not break the page.
|
||||
Logger.LogWarning(ex, "Journal: failed to load event options for the autocomplete.");
|
||||
_eventOptions = Array.Empty<EventOption>();
|
||||
}
|
||||
}
|
||||
|
||||
private Task<IEnumerable<EventOption>> SearchEventsAsync(string? value, CancellationToken token)
|
||||
{
|
||||
IEnumerable<EventOption> matches = string.IsNullOrWhiteSpace(value)
|
||||
? _eventOptions
|
||||
: _eventOptions.Where(o => o.Label.Contains(value, StringComparison.OrdinalIgnoreCase));
|
||||
return Task.FromResult(matches.Take(20));
|
||||
}
|
||||
|
||||
private void OnEventSelected(EventOption? option)
|
||||
{
|
||||
if (option is not null)
|
||||
_form.EventId = option.Id;
|
||||
}
|
||||
|
||||
private async Task LoadAsync()
|
||||
|
||||
Reference in New Issue
Block a user