Files
jdescopingtool/NEW/src/JdeScoping.Client/Pages/SearchQueue.razor
T
Joseph Doherty 7e36bb4225 refactor: remove unused classes and consolidate ViewModels in Core
Remove 9 unused types from Core (duplicate extension classes, TableSpec, ColumnSpec, LotLocation), move ComponentLotViewModel and OperatorViewModel from Client to Core, and refactor DataSync.Dev to use pipeline-based configuration. Fix Login.razor to use UserInfoDto directly.
2026-01-19 00:13:12 -05:00

190 lines
7.0 KiB
Plaintext

@page "/search/queue"
@attribute [Authorize]
@using JdeScoping.Core.ApiContracts
@using JdeScoping.Client.Extensions
@inject ISearchApiClient SearchApi
@inject IHubConnectionService HubConnection
@implements IDisposable
<PageTitle>Search Queue - JDE Scoping Tool</PageTitle>
<RadzenText TextStyle="TextStyle.H4" class="rz-mb-4">Search Queue</RadzenText>
<!-- Processor Status Panel -->
<RadzenCard class="rz-mb-4">
<RadzenText TextStyle="TextStyle.H6" class="rz-mb-3">Search Processor Status</RadzenText>
<RadzenRow Gap="1rem">
<RadzenColumn Size="8">
<RadzenFormField Text="Status Message" Style="width: 100%;">
<RadzenTextBox Value="@_statusMessage" ReadOnly="true" Style="width: 100%;" />
</RadzenFormField>
</RadzenColumn>
<RadzenColumn Size="4">
<RadzenFormField Text="Last Update Timestamp" Style="width: 100%;">
<RadzenTextBox Value="@_statusUpdateDt" ReadOnly="true" Style="width: 100%;" />
</RadzenFormField>
</RadzenColumn>
</RadzenRow>
</RadzenCard>
@if (_isLoading)
{
<LoadingIndicator Message="Loading queue..." />
}
else if (!string.IsNullOrEmpty(_errorMessage))
{
<RadzenAlert AlertStyle="AlertStyle.Danger" ShowIcon="true" Variant="Variant.Flat" class="rz-mb-4">
@_errorMessage
</RadzenAlert>
}
else
{
<RadzenDataGrid @ref="_grid" Data="@_searches" TItem="ClientSearchViewModel" AllowSorting="true" AllowPaging="true" PageSize="20"
PagerHorizontalAlign="HorizontalAlign.Center" AllowColumnResize="true">
<Columns>
<RadzenDataGridColumn TItem="ClientSearchViewModel" Property="UserName" Title="Owner" Width="150px" />
<RadzenDataGridColumn TItem="ClientSearchViewModel" Property="Name" Title="Name" />
<RadzenDataGridColumn TItem="ClientSearchViewModel" Property="SubmitDT" Title="Submitted" Width="180px">
<Template Context="search">
@(search.SubmitDt?.ToString("MM/dd/yyyy hh:mm:ss tt") ?? "")
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn TItem="ClientSearchViewModel" Property="StartDT" Title="Started" Width="180px">
<Template Context="search">
@(search.StartDt?.ToString("MM/dd/yyyy hh:mm:ss tt") ?? "")
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn TItem="ClientSearchViewModel" Property="EndDT" Title="Ended" Width="180px">
<Template Context="search">
@(search.EndDt?.ToString("MM/dd/yyyy hh:mm:ss tt") ?? "")
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn TItem="ClientSearchViewModel" Property="Status" Title="Status" Width="100px">
<Template Context="search">
<RadzenBadge BadgeStyle="@GetBadgeStyle(search.Status)" Text="@search.Status" />
</Template>
</RadzenDataGridColumn>
</Columns>
</RadzenDataGrid>
}
@code {
private List<ClientSearchViewModel> _searches = [];
private RadzenDataGrid<ClientSearchViewModel>? _grid;
private bool _isLoading = true;
private string? _errorMessage;
private string _statusMessage = "";
private string _statusUpdateDt = "";
protected override async Task OnInitializedAsync()
{
await LoadQueueAsync();
await SetupSignalRAsync();
}
private async Task LoadQueueAsync()
{
_isLoading = true;
_errorMessage = null;
try
{
var result = await SearchApi.GetQueuedSearchesAsync();
result.Switch(
searches => { _searches = searches.ToClientList(); },
notFound => { _errorMessage = "Queue not found."; _searches = []; },
validation => { _errorMessage = string.Join("; ", validation.FieldErrors.SelectMany(e => e.Value)); },
unauthorized => { _errorMessage = "Session expired. Please login again."; },
forbidden => { _errorMessage = "Access denied."; },
error => { _errorMessage = error.Message; }
);
}
finally
{
_isLoading = false;
}
}
private async Task SetupSignalRAsync()
{
HubConnection.OnSearchUpdate += HandleSearchUpdate;
HubConnection.OnStatusUpdate += HandleStatusUpdate;
await HubConnection.StartAsync();
// Get cached status
var cachedStatus = await HubConnection.GetCachedStatusAsync();
if (cachedStatus != null)
{
HandleStatusUpdate(cachedStatus);
}
}
private void HandleSearchUpdate(SearchUpdateDto update)
{
InvokeAsync(() =>
{
if (update.Status == "Ended" || update.Status == "Error")
{
// Remove completed/errored searches from queue
var existing = _searches.FirstOrDefault(s => s.Id == update.Id);
if (existing != null)
{
_searches.Remove(existing);
}
}
else
{
// Update or add the search
var existing = _searches.FirstOrDefault(s => s.Id == update.Id);
if (existing != null)
{
existing.Status = update.Status;
existing.SubmitDt = update.SubmitDt;
existing.StartDt = update.StartDt;
existing.EndDt = update.EndDt;
}
else
{
_searches.Add(new ClientSearchViewModel
{
Id = update.Id,
Name = update.Name,
UserName = update.UserName,
Status = update.Status,
SubmitDt = update.SubmitDt,
StartDt = update.StartDt,
EndDt = update.EndDt
});
}
}
StateHasChanged();
});
}
private void HandleStatusUpdate(StatusUpdateDto update)
{
InvokeAsync(() =>
{
_statusMessage = update.Message;
_statusUpdateDt = update.Timestamp?.ToString("MM/dd/yyyy hh:mm:ss tt") ?? "";
StateHasChanged();
});
}
private static BadgeStyle GetBadgeStyle(string status) => status switch
{
"Error" => BadgeStyle.Danger,
"Ended" => BadgeStyle.Success,
"Running" => BadgeStyle.Info,
"Queued" => BadgeStyle.Warning,
_ => BadgeStyle.Light
};
public void Dispose()
{
HubConnection.OnSearchUpdate -= HandleSearchUpdate;
HubConnection.OnStatusUpdate -= HandleStatusUpdate;
}
}