Initial commit
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
@using RobotNet10.FleetManager.Shared.DTOs.RobotModel
|
||||
@using RobotNet10.FleetManager.Shared.Enums
|
||||
@using RobotNet10.FleetManager.Client.Services
|
||||
@using RobotNet10.MapEditor.Services.API
|
||||
@using RobotNet10.MapEditor.Shared.DTOs.VehicleType
|
||||
@inject ISnackbar Snackbar
|
||||
@inject MapManagerApiService MapApiService
|
||||
|
||||
<MudDialog>
|
||||
<TitleContent>
|
||||
<MudText Typo="Typo.h6">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Add" Color="Color.Primary" Class="mr-2" />
|
||||
Create Robot Model
|
||||
</MudText>
|
||||
</TitleContent>
|
||||
<DialogContent>
|
||||
<MudStack Spacing="3">
|
||||
<MudTextField @bind-Value="request.ModelName"
|
||||
Label="Model Name *"
|
||||
Variant="Variant.Outlined"
|
||||
ErrorText="@GetValidationError("ModelName")" />
|
||||
|
||||
<MudNumericField T="double" @bind-Value="request.Length"
|
||||
Label="Length (m) *"
|
||||
Variant="Variant.Outlined"
|
||||
Min="0.01"
|
||||
Max="1000"
|
||||
Step="0.01"
|
||||
ErrorText="@GetValidationError("Length")" />
|
||||
|
||||
<MudNumericField T="double" @bind-Value="request.Width"
|
||||
Label="Width (m) *"
|
||||
Variant="Variant.Outlined"
|
||||
Min="0.01"
|
||||
Max="1000"
|
||||
Step="0.01"
|
||||
ErrorText="@GetValidationError("Width")" />
|
||||
|
||||
<MudNumericField T="double" @bind-Value="request.NavigationPointX"
|
||||
Label="Navigation Point X (m) *"
|
||||
Variant="Variant.Outlined"
|
||||
Step="0.01"
|
||||
ErrorText="@GetValidationError("NavigationPointX")" />
|
||||
|
||||
<MudNumericField T="double" @bind-Value="request.NavigationPointY"
|
||||
Label="Navigation Point Y (m) *"
|
||||
Variant="Variant.Outlined"
|
||||
Step="0.01"
|
||||
ErrorText="@GetValidationError("NavigationPointY")" />
|
||||
|
||||
<MudSelect @bind-Value="request.NavigationType"
|
||||
Label="Navigation Type *"
|
||||
Variant="Variant.Outlined"
|
||||
ErrorText="@GetValidationError("NavigationType")">
|
||||
@foreach (NavigationType navType in Enum.GetValues<NavigationType>())
|
||||
{
|
||||
<MudSelectItem Value="@navType">@navType.ToString()</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudSelect @bind-Value="request.VehicleTypeId"
|
||||
Label="Vehicle Type (Optional)"
|
||||
Variant="Variant.Outlined"
|
||||
T="Guid ?"
|
||||
Clearable="true"
|
||||
Disabled="@isLoadingVehicleTypes"
|
||||
ErrorText="@GetValidationError("VehicleTypeId")">
|
||||
@if (isLoadingVehicleTypes)
|
||||
{
|
||||
<MudSelectItem Value="@((Guid?)null)" Disabled="true">Loading vehicle types...</MudSelectItem>
|
||||
}
|
||||
else if (vehicleTypes != null && vehicleTypes.Any())
|
||||
{
|
||||
<MudSelectItem Value="@((Guid?)null)">None</MudSelectItem>
|
||||
@foreach (var vehicleType in vehicleTypes)
|
||||
{
|
||||
<MudSelectItem Value="@((Guid?)vehicleType.Id)">@($"{vehicleType.VehicleTypeId} - {vehicleType.VehicleTypeName}")</MudSelectItem>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudSelectItem Value="@((Guid?)null)" Disabled="true">No vehicle types available</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudDivider />
|
||||
|
||||
<MudText Typo="Typo.subtitle2">Image Upload (Optional)</MudText>
|
||||
<MudFileUpload T="IBrowserFile"
|
||||
@bind-Files="selectedFile"
|
||||
Accept=".png"
|
||||
MaximumFileCount="1">
|
||||
<CustomContent>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.CloudUpload"
|
||||
OnClick="@context.OpenFilePickerAsync">
|
||||
Upload Image (PNG)
|
||||
</MudButton>
|
||||
</CustomContent>
|
||||
</MudFileUpload>
|
||||
|
||||
@if (selectedFile != null)
|
||||
{
|
||||
<MudChip T="string" Color="Color.Success" Icon="@Icons.Material.Filled.AttachFile">
|
||||
@selectedFile.Name (@FormatFileSize(selectedFile.Size))
|
||||
</MudChip>
|
||||
}
|
||||
</MudStack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||
<MudButton Color="Color.Primary"
|
||||
Variant="Variant.Filled"
|
||||
OnClick="Submit"
|
||||
Disabled="@isCreating">
|
||||
@if (isCreating)
|
||||
{
|
||||
<MudProgressCircular Class="mr-2" Size="Size.Small" Indeterminate="true" />
|
||||
<span>Creating...</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Create</span>
|
||||
}
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] private IMudDialogInstance? MudDialog { get; set; }
|
||||
[Parameter] public RobotModelApiService ApiService { get; set; } = null!;
|
||||
|
||||
private CreateRobotModelRequest request = new()
|
||||
{
|
||||
NavigationType = NavigationType.Differential, // Default value
|
||||
ImageWidth = 100, // Default value, will be updated if image is uploaded
|
||||
ImageHeight = 100 // Default value, will be updated if image is uploaded
|
||||
};
|
||||
private IBrowserFile? selectedFile;
|
||||
private Dictionary<string, string> validationErrors = new();
|
||||
private bool isCreating = false;
|
||||
private List<VehicleTypeDto>? vehicleTypes;
|
||||
private bool isLoadingVehicleTypes = false;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadVehicleTypesAsync();
|
||||
}
|
||||
|
||||
private async Task LoadVehicleTypesAsync()
|
||||
{
|
||||
isLoadingVehicleTypes = true;
|
||||
StateHasChanged();
|
||||
try
|
||||
{
|
||||
vehicleTypes = await MapApiService.GetVehicleTypesAsync(isActive: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error loading vehicle types: {ex.Message}", Severity.Warning);
|
||||
vehicleTypes = new List<VehicleTypeDto>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
isLoadingVehicleTypes = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool Validate()
|
||||
{
|
||||
validationErrors.Clear();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.ModelName))
|
||||
{
|
||||
validationErrors["ModelName"] = "Model Name is required";
|
||||
}
|
||||
else if (request.ModelName.Length > 256)
|
||||
{
|
||||
validationErrors["ModelName"] = "Model Name must not exceed 256 characters";
|
||||
}
|
||||
|
||||
if (request.Length <= 0)
|
||||
{
|
||||
validationErrors["Length"] = "Length must be greater than 0";
|
||||
}
|
||||
else if (request.Length > 1000)
|
||||
{
|
||||
validationErrors["Length"] = "Length must not exceed 1000 meters";
|
||||
}
|
||||
|
||||
if (request.Width <= 0)
|
||||
{
|
||||
validationErrors["Width"] = "Width must be greater than 0";
|
||||
}
|
||||
else if (request.Width > 1000)
|
||||
{
|
||||
validationErrors["Width"] = "Width must not exceed 1000 meters";
|
||||
}
|
||||
|
||||
// ImageWidth and ImageHeight are set to default values (100) when request is initialized
|
||||
// They will be updated by server if image is uploaded
|
||||
// No need to validate them here as they're always set to valid values (100)
|
||||
|
||||
// NavigationPointX and NavigationPointY can be 0 or any decimal value
|
||||
// They are required fields but can be 0, so no validation needed here
|
||||
// Server-side validation will handle range checks if needed
|
||||
|
||||
return validationErrors.Count == 0;
|
||||
}
|
||||
|
||||
private string? GetValidationError(string fieldName)
|
||||
{
|
||||
return validationErrors.TryGetValue(fieldName, out var error) ? error : null;
|
||||
}
|
||||
|
||||
private string FormatFileSize(long bytes)
|
||||
{
|
||||
string[] sizes = { "B", "KB", "MB", "GB" };
|
||||
double len = bytes;
|
||||
int order = 0;
|
||||
while (len >= 1024 && order < sizes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
len = len / 1024;
|
||||
}
|
||||
return $"{len:0.##} {sizes[order]}";
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
MudDialog?.Cancel();
|
||||
}
|
||||
|
||||
private async Task Submit()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
var errorMessages = string.Join(", ", validationErrors.Values);
|
||||
Snackbar.Add($"Please fix validation errors: {errorMessages}", Severity.Warning);
|
||||
StateHasChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
isCreating = true;
|
||||
StateHasChanged();
|
||||
|
||||
try
|
||||
{
|
||||
// ImageWidth and ImageHeight are already set to default values (100) in request initialization
|
||||
// If image is provided, server will update these values after extracting dimensions
|
||||
// If no image, default values (100x100) will be used
|
||||
|
||||
// Create robot model first
|
||||
var created = await ApiService.CreateAsync(request);
|
||||
|
||||
// Upload image if provided
|
||||
if (selectedFile != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var stream = selectedFile.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024); // 10MB
|
||||
await ApiService.UploadImageAsync(created.Id, stream, selectedFile.Name);
|
||||
}
|
||||
catch (Exception imageEx)
|
||||
{
|
||||
// Log image upload error but don't fail the entire operation
|
||||
// The robot model was created successfully, image can be uploaded later
|
||||
Snackbar.Add($"Robot model created but image upload failed: {imageEx.Message}", Severity.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
Snackbar.Add($"Robot model '{request.ModelName}' created successfully", Severity.Success);
|
||||
MudDialog?.Close(DialogResult.Ok(created));
|
||||
}
|
||||
catch (HttpRequestException httpEx)
|
||||
{
|
||||
var errorMessage = httpEx.Message;
|
||||
if (httpEx.Data.Contains("Response"))
|
||||
{
|
||||
errorMessage = $"Network error: {httpEx.Message}";
|
||||
}
|
||||
Snackbar.Add($"Error creating robot model: {errorMessage}", Severity.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error creating robot model: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isCreating = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
@using RobotNet10.FleetManager.Shared.DTOs.RobotModel
|
||||
@using RobotNet10.FleetManager.Client.Services
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<MudDialog>
|
||||
<TitleContent>
|
||||
<MudText Typo="Typo.h6">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Delete" Color="Color.Error" Class="mr-2" />
|
||||
Delete Robot Model
|
||||
</MudText>
|
||||
</TitleContent>
|
||||
<DialogContent>
|
||||
<MudStack Spacing="3">
|
||||
<MudText Typo="Typo.body1">
|
||||
Are you sure you want to delete the robot model <strong>@RobotModel.ModelName</strong>?
|
||||
</MudText>
|
||||
|
||||
@if (usageInfo != null)
|
||||
{
|
||||
@if (usageInfo.RobotCount > 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Error">
|
||||
<MudText>This robot model is currently being used by <strong>@usageInfo.RobotCount</strong> robot(s).</MudText>
|
||||
<MudText>You must delete or reassign all robots using this model before you can delete it.</MudText>
|
||||
</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning">
|
||||
<MudText>This action cannot be undone.</MudText>
|
||||
</MudAlert>
|
||||
}
|
||||
}
|
||||
else if (isLoadingUsageInfo)
|
||||
{
|
||||
<MudProgressCircular Size="Size.Small" Indeterminate="true" />
|
||||
}
|
||||
</MudStack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||
<MudButton Color="Color.Error"
|
||||
Variant="Variant.Filled"
|
||||
OnClick="Submit"
|
||||
Disabled="@(isDeleting || (usageInfo != null && !usageInfo.CanDelete))">
|
||||
@if (isDeleting)
|
||||
{
|
||||
<MudProgressCircular Class="mr-2" Size="Size.Small" Indeterminate="true" />
|
||||
<span>Deleting...</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Delete</span>
|
||||
}
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] private IMudDialogInstance? MudDialog { get; set; }
|
||||
[Parameter] public RobotModelApiService ApiService { get; set; } = null!;
|
||||
[Parameter] public RobotModelDto RobotModel { get; set; } = null!;
|
||||
|
||||
private RobotModelUsageInfoDto? usageInfo;
|
||||
private bool isLoadingUsageInfo = true;
|
||||
private bool isDeleting = false;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadUsageInfoAsync();
|
||||
}
|
||||
|
||||
private async Task LoadUsageInfoAsync()
|
||||
{
|
||||
isLoadingUsageInfo = true;
|
||||
try
|
||||
{
|
||||
usageInfo = await ApiService.GetUsageInfoAsync(RobotModel.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error loading usage info: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isLoadingUsageInfo = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
MudDialog?.Cancel();
|
||||
}
|
||||
|
||||
private async Task Submit()
|
||||
{
|
||||
if (usageInfo != null && !usageInfo.CanDelete)
|
||||
{
|
||||
Snackbar.Add("Cannot delete robot model that is in use", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
isDeleting = true;
|
||||
StateHasChanged();
|
||||
|
||||
try
|
||||
{
|
||||
await ApiService.DeleteAsync(RobotModel.Id);
|
||||
Snackbar.Add($"Robot model '{RobotModel.ModelName}' deleted successfully", Severity.Success);
|
||||
MudDialog?.Close(DialogResult.Ok(true));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error deleting robot model: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDeleting = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
@using RobotNet10.FleetManager.Shared.DTOs.RobotModel
|
||||
@using RobotNet10.FleetManager.Shared.Enums
|
||||
@using RobotNet10.FleetManager.Client.Services
|
||||
@using RobotNet10.MapEditor.Services.API
|
||||
@using RobotNet10.MapEditor.Shared.DTOs.VehicleType
|
||||
@inject ISnackbar Snackbar
|
||||
@inject MapManagerApiService MapApiService
|
||||
|
||||
<MudDialog>
|
||||
<TitleContent>
|
||||
<MudText Typo="Typo.h6">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Edit" Color="Color.Primary" Class="mr-2" />
|
||||
Edit Robot Model
|
||||
</MudText>
|
||||
</TitleContent>
|
||||
<DialogContent>
|
||||
<MudStack Spacing="3">
|
||||
<MudTextField @bind-Value="request.ModelName"
|
||||
Label="Model Name"
|
||||
Variant="Variant.Outlined"
|
||||
ErrorText="@GetValidationError("ModelName")" />
|
||||
|
||||
<MudNumericField T="double?" @bind-Value="request.Length"
|
||||
Label="Length (m)"
|
||||
Variant="Variant.Outlined"
|
||||
Min="0.01"
|
||||
Max="1000"
|
||||
Step="0.01"
|
||||
ErrorText="@GetValidationError("Length")" />
|
||||
|
||||
<MudNumericField T="double?" @bind-Value="request.Width"
|
||||
Label="Width (m)"
|
||||
Variant="Variant.Outlined"
|
||||
Min="0.01"
|
||||
Max="1000"
|
||||
Step="0.01"
|
||||
ErrorText="@GetValidationError("Width")" />
|
||||
|
||||
<MudNumericField T="double?" @bind-Value="request.NavigationPointX"
|
||||
Label="Navigation Point X (m)"
|
||||
Variant="Variant.Outlined"
|
||||
Step="0.01"
|
||||
ErrorText="@GetValidationError("NavigationPointX")" />
|
||||
|
||||
<MudNumericField T="double?" @bind-Value="request.NavigationPointY"
|
||||
Label="Navigation Point Y (m)"
|
||||
Variant="Variant.Outlined"
|
||||
Step="0.01"
|
||||
ErrorText="@GetValidationError("NavigationPointY")" />
|
||||
|
||||
<MudSelect @bind-Value="request.NavigationType"
|
||||
Label="Navigation Type"
|
||||
Variant="Variant.Outlined"
|
||||
T="NavigationType?"
|
||||
ErrorText="@GetValidationError("NavigationType")" ReadOnly>
|
||||
@foreach (NavigationType navType in Enum.GetValues<NavigationType>())
|
||||
{
|
||||
<MudSelectItem Value="@((NavigationType?)navType)">@navType.ToString()</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudSelect @bind-Value="request.VehicleTypeId"
|
||||
Label="Vehicle Type (Optional)"
|
||||
Variant="Variant.Outlined"
|
||||
T="Guid?"
|
||||
Clearable="true"
|
||||
Disabled="@isLoadingVehicleTypes"
|
||||
ErrorText="@GetValidationError("VehicleTypeId")">
|
||||
@if (isLoadingVehicleTypes)
|
||||
{
|
||||
<MudSelectItem Value="@((Guid?)null)" Disabled="true">Loading vehicle types...</MudSelectItem>
|
||||
}
|
||||
else if (vehicleTypes != null && vehicleTypes.Any())
|
||||
{
|
||||
<MudSelectItem Value="@((Guid?)null)">None</MudSelectItem>
|
||||
@foreach (var vehicleType in vehicleTypes)
|
||||
{
|
||||
<MudSelectItem Value="@((Guid?)vehicleType.Id)">@($"{vehicleType.VehicleTypeId} - {vehicleType.VehicleTypeName}")</MudSelectItem>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudSelectItem Value="@((Guid?)null)" Disabled="true">No vehicle types available</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudDivider />
|
||||
|
||||
<MudText Typo="Typo.subtitle2">Image Upload (Optional - Leave empty to keep current image)</MudText>
|
||||
<MudFileUpload T="IBrowserFile"
|
||||
@bind-Files="selectedFile"
|
||||
Accept=".png"
|
||||
MaximumFileCount="1">
|
||||
<CustomContent>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.CloudUpload"
|
||||
OnClick="@context.OpenFilePickerAsync">
|
||||
Upload New Image (PNG)
|
||||
</MudButton>
|
||||
</CustomContent>
|
||||
</MudFileUpload>
|
||||
|
||||
@if (selectedFile != null)
|
||||
{
|
||||
<MudChip T="string" Color="Color.Success" Icon="@Icons.Material.Filled.AttachFile">
|
||||
@selectedFile.Name (@FormatFileSize(selectedFile.Size))
|
||||
</MudChip>
|
||||
}
|
||||
</MudStack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||
<MudButton Color="Color.Primary"
|
||||
Variant="Variant.Filled"
|
||||
OnClick="Submit"
|
||||
Disabled="@isUpdating">
|
||||
@if (isUpdating)
|
||||
{
|
||||
<MudProgressCircular Class="mr-2" Size="Size.Small" Indeterminate="true" />
|
||||
<span>Updating...</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Update</span>
|
||||
}
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] private IMudDialogInstance? MudDialog { get; set; }
|
||||
[Parameter] public RobotModelApiService ApiService { get; set; } = null!;
|
||||
[Parameter] public RobotModelDto RobotModel { get; set; } = null!;
|
||||
|
||||
private UpdateRobotModelRequest request = new();
|
||||
private IBrowserFile? selectedFile;
|
||||
private Dictionary<string, string> validationErrors = new();
|
||||
private bool isUpdating = false;
|
||||
private List<VehicleTypeDto>? vehicleTypes;
|
||||
private bool isLoadingVehicleTypes = false;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Pre-fill with existing data
|
||||
request.ModelName = RobotModel.ModelName;
|
||||
request.Length = RobotModel.Length;
|
||||
request.Width = RobotModel.Width;
|
||||
request.NavigationPointX = RobotModel.NavigationPointX;
|
||||
request.NavigationPointY = RobotModel.NavigationPointY;
|
||||
request.NavigationType = RobotModel.NavigationType; // This is nullable, but we set it to the actual value
|
||||
request.VehicleTypeId = RobotModel.VehicleTypeId;
|
||||
|
||||
await LoadVehicleTypesAsync();
|
||||
}
|
||||
|
||||
private async Task LoadVehicleTypesAsync()
|
||||
{
|
||||
isLoadingVehicleTypes = true;
|
||||
StateHasChanged();
|
||||
try
|
||||
{
|
||||
vehicleTypes = await MapApiService.GetVehicleTypesAsync(isActive: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error loading vehicle types: {ex.Message}", Severity.Warning);
|
||||
vehicleTypes = new List<VehicleTypeDto>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
isLoadingVehicleTypes = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool Validate()
|
||||
{
|
||||
validationErrors.Clear();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.ModelName) && request.ModelName.Length > 256)
|
||||
{
|
||||
validationErrors["ModelName"] = "Model Name must be 256 characters or less";
|
||||
}
|
||||
|
||||
if (request.Length.HasValue && request.Length.Value <= 0)
|
||||
{
|
||||
validationErrors["Length"] = "Length must be greater than 0";
|
||||
}
|
||||
|
||||
if (request.Width.HasValue && request.Width.Value <= 0)
|
||||
{
|
||||
validationErrors["Width"] = "Width must be greater than 0";
|
||||
}
|
||||
|
||||
return validationErrors.Count == 0;
|
||||
}
|
||||
|
||||
private string? GetValidationError(string fieldName)
|
||||
{
|
||||
return validationErrors.TryGetValue(fieldName, out var error) ? error : null;
|
||||
}
|
||||
|
||||
private string FormatFileSize(long bytes)
|
||||
{
|
||||
string[] sizes = { "B", "KB", "MB", "GB" };
|
||||
double len = bytes;
|
||||
int order = 0;
|
||||
while (len >= 1024 && order < sizes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
len = len / 1024;
|
||||
}
|
||||
return $"{len:0.##} {sizes[order]}";
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
MudDialog?.Cancel();
|
||||
}
|
||||
|
||||
private async Task Submit()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Snackbar.Add("Please fix validation errors", Severity.Warning);
|
||||
StateHasChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
isUpdating = true;
|
||||
StateHasChanged();
|
||||
|
||||
try
|
||||
{
|
||||
// Update robot model
|
||||
var updated = await ApiService.UpdateAsync(RobotModel.Id, request);
|
||||
|
||||
// Upload new image if provided
|
||||
if (selectedFile != null)
|
||||
{
|
||||
using var stream = selectedFile.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024); // 10MB
|
||||
await ApiService.UploadImageAsync(updated.Id, stream, selectedFile.Name);
|
||||
}
|
||||
|
||||
Snackbar.Add($"Robot model '{updated.ModelName}' updated successfully", Severity.Success);
|
||||
MudDialog?.Close(DialogResult.Ok(updated));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error updating robot model: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isUpdating = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user