feat(client): migrate SearchEdit.razor to ISearchApiClient
- Replace ISearchService with ISearchApiClient - Add @using for JdeScoping.Core.ApiContracts and JdeScoping.Client.Extensions - Update LoadSearchAsync to use result.Switch() pattern with ApiResult<T> - Handle CopySearchId, Id, and new search cases with proper error handling - Use ToClient() extension method to convert Core to Client SearchViewModel - Add _errorMessage field and display error alert in UI - Update SubmitSearchInternalAsync and DownloadResultsAsync for consistency - Add FormatValidationErrors helper for ValidationError.FieldErrors
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
@page "/search"
|
||||
@page "/search/{Id:int}"
|
||||
@attribute [Authorize]
|
||||
@inject ISearchService SearchService
|
||||
@using JdeScoping.Core.ApiContracts
|
||||
@using JdeScoping.Client.Extensions
|
||||
@inject ISearchApiClient SearchApi
|
||||
@inject IHubConnectionService HubConnection
|
||||
@inject IFileService FileService
|
||||
@inject AuthStateProvider AuthStateProvider
|
||||
@@ -25,6 +27,12 @@
|
||||
{
|
||||
<LoadingIndicator Message="Loading search..." />
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(_errorMessage))
|
||||
{
|
||||
<RadzenAlert AlertStyle="AlertStyle.Danger" ShowIcon="true" Variant="Variant.Flat" class="rz-mb-4">
|
||||
@_errorMessage
|
||||
</RadzenAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<EditForm Model="@_search" OnValidSubmit="@HandleValidSubmit">
|
||||
@@ -169,6 +177,7 @@ else
|
||||
|
||||
private bool _isLoading = true;
|
||||
private bool _isSubmitting;
|
||||
private string? _errorMessage;
|
||||
|
||||
// Filter visibility flags
|
||||
private bool _showTimespan;
|
||||
@@ -190,25 +199,37 @@ else
|
||||
private async Task LoadSearchAsync()
|
||||
{
|
||||
_isLoading = true;
|
||||
_errorMessage = null;
|
||||
try
|
||||
{
|
||||
if (CopySearchId.HasValue)
|
||||
{
|
||||
var copied = await SearchService.CopySearchAsync(CopySearchId.Value);
|
||||
if (copied != null)
|
||||
{
|
||||
_search = copied;
|
||||
_search.Id = 0;
|
||||
_search.Status = "New";
|
||||
}
|
||||
var result = await SearchApi.CopySearchAsync(CopySearchId.Value);
|
||||
result.Switch(
|
||||
copied =>
|
||||
{
|
||||
_search = copied.ToClient();
|
||||
_search.Id = 0;
|
||||
_search.Status = "New";
|
||||
},
|
||||
notFound => { _errorMessage = "Search to copy not found."; },
|
||||
validation => { _errorMessage = FormatValidationErrors(validation.FieldErrors); },
|
||||
unauthorized => { _errorMessage = "Session expired."; },
|
||||
forbidden => { _errorMessage = "Access denied."; },
|
||||
error => { _errorMessage = error.Message; }
|
||||
);
|
||||
}
|
||||
else if (Id.HasValue && Id.Value > 0)
|
||||
{
|
||||
var loaded = await SearchService.GetSearchAsync(Id.Value);
|
||||
if (loaded != null)
|
||||
{
|
||||
_search = loaded;
|
||||
}
|
||||
var result = await SearchApi.GetSearchAsync(Id.Value);
|
||||
result.Switch(
|
||||
loaded => { _search = loaded.ToClient(); },
|
||||
notFound => { _errorMessage = "Search not found."; },
|
||||
validation => { _errorMessage = FormatValidationErrors(validation.FieldErrors); },
|
||||
unauthorized => { _errorMessage = "Session expired."; },
|
||||
forbidden => { _errorMessage = "Access denied."; },
|
||||
error => { _errorMessage = error.Message; }
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -221,8 +242,11 @@ else
|
||||
};
|
||||
}
|
||||
|
||||
// Detect search type from criteria
|
||||
DetectSearchType();
|
||||
// Detect search type from criteria (only if no error)
|
||||
if (string.IsNullOrEmpty(_errorMessage))
|
||||
{
|
||||
DetectSearchType();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -230,6 +254,12 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatValidationErrors(IReadOnlyDictionary<string, string[]> fieldErrors)
|
||||
{
|
||||
var messages = fieldErrors.SelectMany(kv => kv.Value);
|
||||
return string.Join(" ", messages);
|
||||
}
|
||||
|
||||
private void DetectSearchType()
|
||||
{
|
||||
var criteria = _search.Criteria;
|
||||
@@ -364,15 +394,15 @@ else
|
||||
_isSubmitting = true;
|
||||
try
|
||||
{
|
||||
var id = await SearchService.SaveSearchAsync(_search);
|
||||
if (id.HasValue)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/search/{id}");
|
||||
}
|
||||
else
|
||||
{
|
||||
NotificationService.Notify(NotificationSeverity.Error, "Error", "Failed to submit search.");
|
||||
}
|
||||
var result = await SearchApi.CreateSearchAsync(_search.ToCore());
|
||||
result.Switch(
|
||||
id => { NavigationManager.NavigateTo($"/search/{id}"); },
|
||||
notFound => { NotificationService.Notify(NotificationSeverity.Error, "Error", "Search not found."); },
|
||||
validation => { NotificationService.Notify(NotificationSeverity.Error, "Validation Error", FormatValidationErrors(validation.FieldErrors)); },
|
||||
unauthorized => { NotificationService.Notify(NotificationSeverity.Error, "Error", "Session expired."); },
|
||||
forbidden => { NotificationService.Notify(NotificationSeverity.Error, "Error", "Access denied."); },
|
||||
error => { NotificationService.Notify(NotificationSeverity.Error, "Error", error.Message); }
|
||||
);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -413,17 +443,26 @@ else
|
||||
|
||||
private async Task DownloadResultsAsync()
|
||||
{
|
||||
var results = await SearchService.DownloadResultsAsync(_search.Id);
|
||||
if (results != null && results.Length > 0)
|
||||
{
|
||||
// Trigger download via JS interop
|
||||
await JSRuntime.InvokeVoidAsync("downloadFile", $"search_results_{_search.Id}.xlsx", results);
|
||||
NotificationService.Notify(NotificationSeverity.Success, "Download", "Results downloaded successfully.");
|
||||
}
|
||||
else
|
||||
{
|
||||
NotificationService.Notify(NotificationSeverity.Warning, "Download", "No results available to download.");
|
||||
}
|
||||
var result = await SearchApi.GetResultsAsync(_search.Id);
|
||||
result.Switch(
|
||||
bytes =>
|
||||
{
|
||||
if (bytes.Length > 0)
|
||||
{
|
||||
_ = JSRuntime.InvokeVoidAsync("downloadFile", $"search_results_{_search.Id}.xlsx", bytes);
|
||||
NotificationService.Notify(NotificationSeverity.Success, "Download", "Results downloaded successfully.");
|
||||
}
|
||||
else
|
||||
{
|
||||
NotificationService.Notify(NotificationSeverity.Warning, "Download", "No results available to download.");
|
||||
}
|
||||
},
|
||||
notFound => { NotificationService.Notify(NotificationSeverity.Warning, "Download", "Results not found."); },
|
||||
validation => { NotificationService.Notify(NotificationSeverity.Error, "Error", FormatValidationErrors(validation.FieldErrors)); },
|
||||
unauthorized => { NotificationService.Notify(NotificationSeverity.Error, "Error", "Session expired."); },
|
||||
forbidden => { NotificationService.Notify(NotificationSeverity.Error, "Error", "Access denied."); },
|
||||
error => { NotificationService.Notify(NotificationSeverity.Error, "Error", error.Message); }
|
||||
);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
Reference in New Issue
Block a user