@implements IAsyncDisposable @using MudBlazor @using Microsoft.JSInterop @using Microsoft.AspNetCore.Components.Authorization @using RobotNet10.ScriptEditor.Clients @using RobotNet10.ScriptEditor.Dialogs @using RobotNet10.ScriptEngine.Shared @using RobotNet10.Shared @using RobotNet10.Components @inject InstanceMissionHubClient InstanceMissionClient @inject IDialogService DialogService @inject ISnackbar Snackbar @inject IJSRuntime JSRuntime @inject AuthenticationStateProvider AuthenticationStateProvider
Instance Missions
Mission Name State Score Created At Stopped At Actions @context.MissionName @context.State @($"{(100.0 * context.Score / @context.TotalScore):#.00}%") @context.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss") @(GetStoppedAtString(context))
@if (context.State == ScriptMissionState.Running || context.State == ScriptMissionState.Paused || context.State == ScriptMissionState.Pausing) { } @if (context.State == ScriptMissionState.Running) { } @if (context.State == ScriptMissionState.Paused) { }
No matching records found Loading...
@code { private ElementReference _containerRef; private ElementReference toolbarRef; private MudTable table = default!; private string _tableHeight = "400px"; private string _searchText = ""; private bool _isLoading = false; private int _totalItems = 0; protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); if (firstRender) { await InstanceMissionClient.StartAsync(); await CalculateTableHeight(); } } private async Task CalculateTableHeight() { var rect = await _containerRef.MudGetBoundingClientRectAsync(); var toolbarRect = await toolbarRef.MudGetBoundingClientRectAsync(); _tableHeight = $"{rect.Height - 70 - Math.Max(toolbarRect.Height, 64)}px"; StateHasChanged(); } private async Task> LoadData(TableState state, CancellationToken cancellationToken) { _isLoading = true; StateHasChanged(); try { var request = new SearchRequest( Page: state.Page + 1, // MudTable uses 0-based page, but our API uses 1-based Size: state.PageSize, TxtSearch: _searchText ); var result = await InstanceMissionClient.SearchInstanceMissionsAsync(request); _totalItems = result.Total; return new TableData { Items = result.Items, TotalItems = result.Total }; } catch (Exception ex) { Snackbar.Add($"Error loading missions: {ex.Message}", Severity.Error); return new TableData { Items = [], TotalItems = 0 }; } finally { _isLoading = false; StateHasChanged(); } } private async Task OnSearch() { await table.ReloadServerData(); } private Color GetStateColor(ScriptMissionState state) { return state switch { ScriptMissionState.Running => Color.Success, ScriptMissionState.Paused => Color.Warning, ScriptMissionState.Pausing => Color.Warning, ScriptMissionState.Resuming => Color.Info, ScriptMissionState.Completed => Color.Success, ScriptMissionState.Canceled => Color.Default, ScriptMissionState.Error => Color.Error, _ => Color.Default }; } private async Task HandleViewLog(InstanceMissionDto mission) { var parameters = new DialogParameters { { x => x.MissionId, mission.Id }, { x => x.MissionName, mission.MissionName }, { x => x.State, mission.State }, { x => x.InitialLog, mission.Log } }; var options = new DialogOptions { MaxWidth = MaxWidth.Large, FullWidth = true, CloseButton = true }; await DialogService.ShowAsync($"Mission Log: {mission.MissionName}", parameters, options); } private async Task HandleCancelMission(InstanceMissionDto mission) { var parameters = new DialogParameters { { x => x.MissionName, mission.MissionName } }; var options = new DialogOptions { MaxWidth = MaxWidth.Small, FullWidth = true }; var dialog = await DialogService.ShowAsync("Cancel Mission", parameters, options); var result = await dialog.Result; if (result is not null && !result.Canceled && result.Data is string userReason) { try { // Get current user information var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var userName = authState?.User?.Identity?.Name ?? "Unknown"; // Combine user reason with user information var reason = string.IsNullOrWhiteSpace(userReason) ? $"Canceled by {userName}" : $"Canceled by {userName}: {userReason.Trim()}"; var messageResult = await InstanceMissionClient.CancelMissionAsync(mission.Id, reason); if (messageResult.IsSuccess) { Snackbar.Add(messageResult.Message ?? "Mission canceled", Severity.Success); await table.ReloadServerData(); } else { Snackbar.Add(messageResult.Message ?? "Failed to cancel mission", Severity.Error); } } catch (Exception ex) { Snackbar.Add($"Error canceling mission: {ex.Message}", Severity.Error); } } } private async Task HandlePauseMission(InstanceMissionDto mission) { try { var messageResult = await InstanceMissionClient.PauseMissionAsync(mission.Id); if (messageResult.IsSuccess) { Snackbar.Add(messageResult.Message ?? "Mission paused", Severity.Success); } else { Snackbar.Add(messageResult.Message ?? "Failed to pause mission", Severity.Error); } } catch (Exception ex) { Snackbar.Add($"Error pausing mission: {ex.Message}", Severity.Error); } } private async Task HandleResumeMission(InstanceMissionDto mission) { try { var messageResult = await InstanceMissionClient.ResumeMissionAsync(mission.Id); if (messageResult.IsSuccess) { Snackbar.Add(messageResult.Message ?? "Mission resumed", Severity.Success); } else { Snackbar.Add(messageResult.Message ?? "Failed to resume mission", Severity.Error); } } catch (Exception ex) { Snackbar.Add($"Error resuming mission: {ex.Message}", Severity.Error); } } private string GetStoppedAtString(InstanceMissionDto mission) { if (mission.State == ScriptMissionState.Canceled || mission.State == ScriptMissionState.Completed || mission.State == ScriptMissionState.Error) { return mission.StoppedAt.ToString("yyyy-MM-dd HH:mm:ss"); } else { return "--"; } } public async ValueTask DisposeAsync() { await InstanceMissionClient.StopAsync(); GC.SuppressFinalize(this); } }