@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 Create Robot Model @foreach (NavigationType navType in Enum.GetValues()) { @navType.ToString() } @if (isLoadingVehicleTypes) { Loading vehicle types... } else if (vehicleTypes != null && vehicleTypes.Any()) { None @foreach (var vehicleType in vehicleTypes) { @($"{vehicleType.VehicleTypeId} - {vehicleType.VehicleTypeName}") } } else { No vehicle types available } Image Upload (Optional) Upload Image (PNG) @if (selectedFile != null) { @selectedFile.Name (@FormatFileSize(selectedFile.Size)) } Cancel @if (isCreating) { Creating... } else { Create } @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 validationErrors = new(); private bool isCreating = false; private List? 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(); } 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(); } } }