@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);
}
}