refactor: remove unused classes and consolidate ViewModels in Core

Remove 9 unused types from Core (duplicate extension classes, TableSpec, ColumnSpec, LotLocation), move ComponentLotViewModel and OperatorViewModel from Client to Core, and refactor DataSync.Dev to use pipeline-based configuration. Fix Login.razor to use UserInfoDto directly.
This commit is contained in:
Joseph Doherty
2026-01-19 00:13:12 -05:00
parent 80057590f4
commit 7e36bb4225
89 changed files with 1049 additions and 2282 deletions
@@ -1,6 +1,6 @@
using System.Net.Http.Json;
using System.Security.Claims;
using JdeScoping.Client.Models;
using JdeScoping.Core.ApiContracts.Auth;
using Microsoft.AspNetCore.Components.Authorization;
namespace JdeScoping.Client.Auth;
@@ -46,14 +46,14 @@ public class AuthStateProvider : AuthenticationStateProvider
/// Validates the current session by calling /api/auth/me.
/// Returns null if not authenticated.
/// </summary>
private async Task<UserInfoViewModel?> ValidateSessionAsync()
private async Task<UserInfoDto?> ValidateSessionAsync()
{
try
{
var response = await _httpClient.GetAsync("api/auth/me");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadFromJsonAsync<UserInfoViewModel>();
return await response.Content.ReadFromJsonAsync<UserInfoDto>();
}
}
catch
@@ -67,7 +67,7 @@ public class AuthStateProvider : AuthenticationStateProvider
/// <summary>
/// Creates an authenticated state from user info.
/// </summary>
private static AuthenticationState CreateAuthState(UserInfoViewModel user)
private static AuthenticationState CreateAuthState(UserInfoDto user)
{
var claims = new List<Claim>
{
@@ -87,7 +87,7 @@ public class AuthStateProvider : AuthenticationStateProvider
/// <summary>
/// Called after successful login to update auth state.
/// </summary>
public async Task MarkUserAsAuthenticated(UserInfoViewModel user)
public async Task MarkUserAsAuthenticated(UserInfoDto user)
{
await _userStorage.SetUserAsync(user);
NotifyAuthenticationStateChanged(Task.FromResult(CreateAuthState(user)));
@@ -1,4 +1,4 @@
using JdeScoping.Client.Models;
using JdeScoping.Core.ApiContracts.Auth;
namespace JdeScoping.Client.Auth;
@@ -12,12 +12,12 @@ public interface IUserStorageService
/// <summary>
/// Gets the stored user info.
/// </summary>
Task<UserInfoViewModel?> GetUserAsync();
Task<UserInfoDto?> GetUserAsync();
/// <summary>
/// Stores the user info.
/// </summary>
Task SetUserAsync(UserInfoViewModel user);
Task SetUserAsync(UserInfoDto user);
/// <summary>
/// Removes the stored user info.
@@ -1,5 +1,5 @@
using System.Text.Json;
using JdeScoping.Client.Models;
using JdeScoping.Core.ApiContracts.Auth;
using Microsoft.JSInterop;
namespace JdeScoping.Client.Auth;
@@ -19,7 +19,7 @@ public class UserStorageService : IUserStorageService
_jsRuntime = jsRuntime;
}
public async Task<UserInfoViewModel?> GetUserAsync()
public async Task<UserInfoDto?> GetUserAsync()
{
try
{
@@ -29,7 +29,7 @@ public class UserStorageService : IUserStorageService
return null;
}
return JsonSerializer.Deserialize<UserInfoViewModel>(json, new JsonSerializerOptions
return JsonSerializer.Deserialize<UserInfoDto>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
@@ -40,7 +40,7 @@ public class UserStorageService : IUserStorageService
}
}
public async Task SetUserAsync(UserInfoViewModel user)
public async Task SetUserAsync(UserInfoDto user)
{
var json = JsonSerializer.Serialize(user);
await _jsRuntime.InvokeVoidAsync("jdeScopingInterop.setSessionStorage", UserKey, json);
@@ -51,16 +51,42 @@
</tbody>
</table>
@if (!string.IsNullOrWhiteSpace(QueryPreview))
@if (Config.Parameters?.Count > 0)
{
<RadzenText TextStyle="TextStyle.Subtitle1" class="rz-mb-2">Parameters</RadzenText>
<ul class="rz-mb-4">
@foreach (var param in Config.Parameters)
{
<li><strong>@param.Name</strong>: @(param.Format ?? "default") (source: @param.Source)</li>
}
</ul>
}
@if (!string.IsNullOrWhiteSpace(Config.Query))
{
<RadzenText TextStyle="TextStyle.Subtitle1" class="rz-mb-2">Query</RadzenText>
<div style="background: #f5f5f5; padding: 0.75rem; border-radius: 4px; font-family: monospace; font-size: 0.875rem;">
@QueryPreview
</div>
@if (!string.IsNullOrWhiteSpace(FullQuery))
<pre style="background: #f8f9fa; padding: 1rem; border-radius: 4px; font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 0.875rem; line-height: 1.5; overflow-x: auto; white-space: pre-wrap; word-break: break-word; max-height: 400px; overflow-y: auto;">@FormatSql(Config.Query)</pre>
}
@if (Config.PreScripts?.Count > 0)
{
<RadzenText TextStyle="TextStyle.Subtitle1" class="rz-mt-4 rz-mb-2">Pre-Scripts (@Config.PreScripts.Count)</RadzenText>
@for (int i = 0; i < Config.PreScripts.Count; i++)
{
<RadzenButton Text="View Full Query" Icon="open_in_new" ButtonStyle="ButtonStyle.Light" Size="ButtonSize.Small"
Click="@(() => OnViewQuery.InvokeAsync(FullQuery))" class="rz-mt-2" />
var script = Config.PreScripts[i];
<RadzenText TextStyle="TextStyle.Body2" class="rz-mb-1"><strong>Script @(i + 1):</strong></RadzenText>
<pre style="background: #fff3cd; padding: 0.75rem; border-radius: 4px; font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 0.8rem; line-height: 1.4; overflow-x: auto; white-space: pre-wrap; word-break: break-word; max-height: 200px; overflow-y: auto; margin-bottom: 0.5rem;">@FormatSql(script)</pre>
}
}
@if (Config.PostScripts?.Count > 0)
{
<RadzenText TextStyle="TextStyle.Subtitle1" class="rz-mt-4 rz-mb-2">Post-Scripts (@Config.PostScripts.Count)</RadzenText>
@for (int i = 0; i < Config.PostScripts.Count; i++)
{
var script = Config.PostScripts[i];
<RadzenText TextStyle="TextStyle.Body2" class="rz-mb-1"><strong>Script @(i + 1):</strong></RadzenText>
<pre style="background: #d1ecf1; padding: 0.75rem; border-radius: 4px; font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 0.8rem; line-height: 1.4; overflow-x: auto; white-space: pre-wrap; word-break: break-word; max-height: 200px; overflow-y: auto; margin-bottom: 0.5rem;">@FormatSql(script)</pre>
}
}
}
@@ -69,9 +95,6 @@
@code {
[Parameter] public UpdateTypes ScheduleType { get; set; }
[Parameter] public PipelineScheduleDto? Config { get; set; }
[Parameter] public string? QueryPreview { get; set; }
[Parameter] public string? FullQuery { get; set; }
[Parameter] public EventCallback<string> OnViewQuery { get; set; }
private static string GetScheduleTypeName(UpdateTypes type) => type switch
{
@@ -89,4 +112,50 @@
return $"{minutes / 60} hour(s) ({minutes} min)";
return $"{minutes} minutes";
}
private static string FormatSql(string? sql)
{
if (string.IsNullOrWhiteSpace(sql))
return "";
// Format SELECT columns - put each column on its own line
var result = FormatSelectColumns(sql);
// Add line breaks before major clauses
result = result
.Replace(" FROM ", "\nFROM ")
.Replace(" WHERE ", "\nWHERE ")
.Replace(" AND ", "\n AND ")
.Replace(" OR ", "\n OR ")
.Replace(" LEFT ", "\nLEFT ")
.Replace(" RIGHT ", "\nRIGHT ")
.Replace(" INNER ", "\nINNER ")
.Replace(" OUTER ", "\nOUTER ")
.Replace(" JOIN ", " JOIN\n ")
.Replace(" ORDER BY ", "\nORDER BY ")
.Replace(" GROUP BY ", "\nGROUP BY ")
.Replace(" HAVING ", "\nHAVING ");
return result.Trim();
}
private static string FormatSelectColumns(string sql)
{
var selectIndex = sql.IndexOf("SELECT", StringComparison.OrdinalIgnoreCase);
var fromIndex = sql.IndexOf(" FROM ", StringComparison.OrdinalIgnoreCase);
if (selectIndex < 0 || fromIndex < 0 || fromIndex <= selectIndex)
return sql;
var beforeSelect = sql[..selectIndex];
var selectKeyword = sql.Substring(selectIndex, 6);
var columnsStart = selectIndex + 6;
var columns = sql[columnsStart..fromIndex];
var afterColumns = sql[fromIndex..];
var columnList = columns.Split(',');
var formattedColumns = string.Join(",\n ", columnList.Select(c => c.Trim()));
return $"{beforeSelect}{selectKeyword} {formattedColumns}{afterColumns}";
}
}
@@ -90,9 +90,11 @@
if (string.IsNullOrWhiteSpace(sql))
return "";
// Basic SQL formatting - add line breaks before major clauses
return sql
.Replace(" SELECT ", "\nSELECT ")
// Format SELECT columns - put each column on its own line
var result = FormatSelectColumns(sql);
// Add line breaks before major clauses
result = result
.Replace(" FROM ", "\nFROM ")
.Replace(" WHERE ", "\nWHERE ")
.Replace(" AND ", "\n AND ")
@@ -104,8 +106,31 @@
.Replace(" JOIN ", " JOIN\n ")
.Replace(" ORDER BY ", "\nORDER BY ")
.Replace(" GROUP BY ", "\nGROUP BY ")
.Replace(" HAVING ", "\nHAVING ")
.Trim();
.Replace(" HAVING ", "\nHAVING ");
return result.Trim();
}
private static string FormatSelectColumns(string sql)
{
// Find SELECT and FROM positions (case-insensitive)
var selectIndex = sql.IndexOf("SELECT", StringComparison.OrdinalIgnoreCase);
var fromIndex = sql.IndexOf(" FROM ", StringComparison.OrdinalIgnoreCase);
if (selectIndex < 0 || fromIndex < 0 || fromIndex <= selectIndex)
return sql;
var beforeSelect = sql[..selectIndex];
var selectKeyword = sql.Substring(selectIndex, 6); // "SELECT"
var columnsStart = selectIndex + 6;
var columns = sql[columnsStart..fromIndex];
var afterColumns = sql[fromIndex..];
// Split columns by comma and rejoin with newlines
var columnList = columns.Split(',');
var formattedColumns = string.Join(",\n ", columnList.Select(c => c.Trim()));
return $"{beforeSelect}{selectKeyword} {formattedColumns}{afterColumns}";
}
private async Task CopyToClipboard()
@@ -1,6 +1,7 @@
using JdeScoping.Client.Models;
using JdeScoping.Core.Models.Enums;
using JdeScoping.Core.Models.Search;
using JdeScoping.Core.ViewModels;
using CoreSearch = JdeScoping.Core.ViewModels.SearchViewModel;
using CoreItem = JdeScoping.Core.ViewModels.ItemViewModel;
using CoreWorkOrder = JdeScoping.Core.ViewModels.WorkOrderViewModel;
@@ -9,6 +10,7 @@ using CoreWorkCenter = JdeScoping.Core.ViewModels.WorkCenterViewModel;
using CoreLot = JdeScoping.Core.ViewModels.LotViewModel;
using CorePartOp = JdeScoping.Core.ViewModels.PartOperationViewModel;
using CoreJdeUser = JdeScoping.Core.ViewModels.JdeUserViewModel;
using SearchViewModel = JdeScoping.Client.Models.SearchViewModel;
namespace JdeScoping.Client.Extensions;
@@ -1,21 +0,0 @@
namespace JdeScoping.Client.Models;
/// <summary>
/// View model for component lot filter.
/// </summary>
public class ComponentLotViewModel
{
public string LotNumber { get; set; } = string.Empty;
public string ItemNumber { get; set; } = string.Empty;
public override bool Equals(object? obj)
{
if (obj is ComponentLotViewModel other)
{
return LotNumber == other.LotNumber && ItemNumber == other.ItemNumber;
}
return false;
}
public override int GetHashCode() => HashCode.Combine(LotNumber, ItemNumber);
}
@@ -1,22 +0,0 @@
namespace JdeScoping.Client.Models;
/// <summary>
/// View model for operator filter.
/// </summary>
public class OperatorViewModel
{
public int AddressNumber { get; set; }
public string UserId { get; set; } = string.Empty;
public string FullName { get; set; } = string.Empty;
public override bool Equals(object? obj)
{
if (obj is OperatorViewModel other)
{
return UserId == other.UserId;
}
return false;
}
public override int GetHashCode() => UserId.GetHashCode();
}
@@ -1,15 +0,0 @@
namespace JdeScoping.Client.Models;
/// <summary>
/// SignalR message for search status updates.
/// </summary>
public record SearchUpdate
{
public int Id { get; init; }
public string Name { get; init; } = string.Empty;
public string UserName { get; init; } = string.Empty;
public string Status { get; init; } = string.Empty;
public DateTime? SubmitDt { get; init; }
public DateTime? StartDt { get; init; }
public DateTime? EndDt { get; init; }
}
@@ -1,10 +0,0 @@
namespace JdeScoping.Client.Models;
/// <summary>
/// SignalR message for processor status updates.
/// </summary>
public record StatusUpdate
{
public string Message { get; init; } = string.Empty;
public DateTime? Timestamp { get; init; }
}
@@ -1,38 +0,0 @@
namespace JdeScoping.Client.Models;
/// <summary>
/// Client-side view model for authenticated user information.
/// Mirrors the server-side UserInfo model returned by /api/auth/login and /api/auth/me.
/// </summary>
public class UserInfoViewModel
{
/// <summary>
/// User's login username.
/// </summary>
public string Username { get; set; } = string.Empty;
/// <summary>
/// User's first name.
/// </summary>
public string FirstName { get; set; } = string.Empty;
/// <summary>
/// User's last name.
/// </summary>
public string LastName { get; set; } = string.Empty;
/// <summary>
/// User's display name (computed on server, provided here for convenience).
/// </summary>
public string DisplayName { get; set; } = string.Empty;
/// <summary>
/// User's organization title.
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// User's email address.
/// </summary>
public string EmailAddress { get; set; } = string.Empty;
}
@@ -6,17 +6,17 @@ namespace JdeScoping.Client.Models;
/// </summary>
public class ValidCombination
{
public int Id { get; init; }
public string Name { get; init; } = string.Empty;
public bool Timespan { get; init; }
public bool WorkOrder { get; init; }
public bool ItemNumber { get; init; }
public bool ProfitCenter { get; init; }
public bool WorkCenter { get; init; }
public bool ComponentLot { get; init; }
public bool Operator { get; init; }
public bool ItemOperationMis { get; init; }
public bool ExtractMis { get; init; }
public int Id { get; private init; }
public string Name { get; private init; } = string.Empty;
public bool Timespan { get; private init; }
public bool WorkOrder { get; private init; }
public bool ItemNumber { get; private init; }
public bool ProfitCenter { get; private init; }
public bool WorkCenter { get; private init; }
public bool ComponentLot { get; private init; }
public bool Operator { get; private init; }
public bool ItemOperationMis { get; private init; }
public bool ExtractMis { get; private init; }
/// <summary>
/// Checks if the given filter flags match this combination.
@@ -48,7 +48,7 @@ public class ValidCombination
/// </summary>
public static IReadOnlyList<ValidCombination> GetAll() =>
[
new ValidCombination
new()
{
Id = 10,
Name = "Work Order",
@@ -62,7 +62,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 20,
Name = "Component Lot",
@@ -76,7 +76,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 30,
Name = "Time Span + Profit Center",
@@ -90,7 +90,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 40,
Name = "Time Span + Work Center",
@@ -104,7 +104,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 50,
Name = "Time Span + Operator",
@@ -118,7 +118,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 60,
Name = "Time Span + Profit Center + Item Number",
@@ -132,7 +132,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 70,
Name = "Time Span + Profit Center + Item/Operation/MIS",
@@ -146,7 +146,7 @@ public class ValidCombination
ItemOperationMis = true,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 80,
Name = "Time Span + Profit Center + Work Order + Item/Operation/MIS",
@@ -160,7 +160,7 @@ public class ValidCombination
ItemOperationMis = true,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 90,
Name = "Time Span + Profit Center + Extract MIS",
@@ -174,7 +174,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = true
},
new ValidCombination
new()
{
Id = 100,
Name = "Time Span + Work Center + Item Number",
@@ -188,7 +188,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 110,
Name = "Time Span + Work Center + Extract MIS",
@@ -202,7 +202,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = true
},
new ValidCombination
new()
{
Id = 120,
Name = "Time Span + Work Center + Item/Operation/MIS",
@@ -216,7 +216,7 @@ public class ValidCombination
ItemOperationMis = true,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 130,
Name = "Time Span + Work Center + Work Order + Item/Operation/MIS",
@@ -230,7 +230,7 @@ public class ValidCombination
ItemOperationMis = true,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 140,
Name = "Time Span + Item Number",
@@ -244,7 +244,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 150,
Name = "Time Span + Work Center + Operator",
@@ -258,7 +258,7 @@ public class ValidCombination
ItemOperationMis = false,
ExtractMis = false
},
new ValidCombination
new()
{
Id = 160,
Name = "Time Span + Profit Center + Operator",
@@ -107,7 +107,7 @@ else if (_config is not null)
<!-- Common Pipeline Info -->
<RadzenRow Gap="1rem" class="rz-mb-4">
<!-- Source Card -->
<RadzenColumn Size="4">
<RadzenColumn Size="6">
<RadzenCard Style="height: 100%;">
<RadzenText TextStyle="TextStyle.H6" class="rz-mb-2">Source</RadzenText>
<p><strong>Connection:</strong>
@@ -127,21 +127,11 @@ else if (_config is not null)
break;
}
</p>
@if (_config.Source.Parameters.Count > 0)
{
<p><strong>Parameters:</strong></p>
<ul>
@foreach (var param in _config.Source.Parameters)
{
<li>@param.Name (@(param.Format ?? "default"))</li>
}
</ul>
}
</RadzenCard>
</RadzenColumn>
<!-- Destination Card -->
<RadzenColumn Size="4">
<RadzenColumn Size="6">
<RadzenCard Style="height: 100%;">
<RadzenText TextStyle="TextStyle.H6" class="rz-mb-2">Destination</RadzenText>
<p><strong>Table:</strong> @_config.Destination.Table</p>
@@ -151,67 +141,36 @@ else if (_config is not null)
</p>
@if (_config.Destination.MatchColumns?.Count > 0)
{
<p><strong>Match Columns:</strong> @string.Join(", ", _config.Destination.MatchColumns)</p>
<p><strong>Match Columns:</strong></p>
<ul>
@foreach (var col in _config.Destination.MatchColumns)
{
<li>@col</li>
}
</ul>
}
@if (_config.Destination.ExcludeFromUpdate?.Count > 0)
{
<p><strong>Exclude:</strong> @string.Join(", ", _config.Destination.ExcludeFromUpdate)</p>
}
</RadzenCard>
</RadzenColumn>
<!-- Scripts Card -->
<RadzenColumn Size="4">
<RadzenCard Style="height: 100%;">
<RadzenText TextStyle="TextStyle.H6" class="rz-mb-2">Scripts</RadzenText>
<p><strong>Pre-Scripts:</strong> @_config.PreScriptCount</p>
<p><strong>Post-Scripts:</strong> @_config.PostScriptCount</p>
@if (_config.PreScripts?.Count > 0)
{
<RadzenText TextStyle="TextStyle.Subtitle2" class="rz-mt-2">Pre-Scripts:</RadzenText>
@for (int i = 0; i < _config.PreScripts.Count; i++)
{
var script = _config.PreScripts[i];
var index = i + 1;
<div>
<RadzenButton Text="@($"Script {index}")" Icon="code" ButtonStyle="ButtonStyle.Light" Size="ButtonSize.Small"
Click="@(() => ShowSqlModal($"Pre-Script {index}", script))" />
</div>
}
}
@if (_config.PostScripts?.Count > 0)
{
<RadzenText TextStyle="TextStyle.Subtitle2" class="rz-mt-2">Post-Scripts:</RadzenText>
@for (int i = 0; i < _config.PostScripts.Count; i++)
{
var script = _config.PostScripts[i];
var index = i + 1;
<div>
<RadzenButton Text="@($"Script {index}")" Icon="code" ButtonStyle="ButtonStyle.Light" Size="ButtonSize.Small"
Click="@(() => ShowSqlModal($"Post-Script {index}", script))" />
</div>
}
<p><strong>Exclude:</strong></p>
<ul>
@foreach (var col in _config.Destination.ExcludeFromUpdate)
{
<li>@col</li>
}
</ul>
}
</RadzenCard>
</RadzenColumn>
</RadzenRow>
<!-- Schedule Sections -->
<PipelineScheduleSection ScheduleType="UpdateTypes.Mass" Config="@_config.Schedules.Mass"
QueryPreview="@_config.Source.MassQueryPreview" FullQuery="@_config.Source.MassQuery"
OnViewQuery="@(q => ShowSqlModal("Mass Query", q))" />
<PipelineScheduleSection ScheduleType="UpdateTypes.Mass" Config="@_config.Schedules.Mass" />
<PipelineScheduleSection ScheduleType="UpdateTypes.Daily" Config="@_config.Schedules.Daily"
QueryPreview="@_config.Source.QueryPreview" FullQuery="@_config.Source.Query"
OnViewQuery="@(q => ShowSqlModal("Daily Query", q))" />
<PipelineScheduleSection ScheduleType="UpdateTypes.Daily" Config="@_config.Schedules.Daily" />
<PipelineScheduleSection ScheduleType="UpdateTypes.Hourly" Config="@_config.Schedules.Hourly"
QueryPreview="@_config.Source.QueryPreview" FullQuery="@_config.Source.Query"
OnViewQuery="@(q => ShowSqlModal("Hourly Query", q))" />
<PipelineScheduleSection ScheduleType="UpdateTypes.Hourly" Config="@_config.Schedules.Hourly" />
}
<SqlQueryModal @bind-Visible="_showSqlModal" Title="@_sqlModalTitle" Sql="@_sqlModalContent" />
@code {
private List<string> _pipelineNames = [];
private string? _selectedPipeline;
@@ -220,10 +179,6 @@ else if (_config is not null)
private List<PipelineScheduleStatusDto> _statuses = [];
private List<PipelineExecutionDto> _executions = [];
private bool _showSqlModal;
private string? _sqlModalTitle;
private string? _sqlModalContent;
protected override async Task OnInitializedAsync()
{
var result = await PipelineApi.GetPipelineNamesAsync();
@@ -271,13 +226,6 @@ else if (_config is not null)
}
}
private void ShowSqlModal(string title, string sql)
{
_sqlModalTitle = $"{title} - {_selectedPipeline}";
_sqlModalContent = sql;
_showSqlModal = true;
}
private static string FormatDuration(TimeSpan? duration)
{
if (!duration.HasValue) return "-";
+1 -10
View File
@@ -74,16 +74,7 @@
if (loginResult.Success && loginResult.User is not null)
{
// Notify auth state provider of successful login
var userViewModel = new JdeScoping.Client.Models.UserInfoViewModel
{
Username = loginResult.User.Username,
FirstName = loginResult.User.FirstName,
LastName = loginResult.User.LastName,
DisplayName = loginResult.User.DisplayName,
EmailAddress = loginResult.User.EmailAddress,
Title = loginResult.User.Title
};
_ = AuthStateProvider.MarkUserAsAuthenticated(userViewModel);
_ = AuthStateProvider.MarkUserAsAuthenticated(loginResult.User);
var returnUrl = string.IsNullOrEmpty(ReturnUrl) ? "/" : ReturnUrl;
NavigationManager.NavigateTo(returnUrl);
@@ -315,7 +315,7 @@ else
await HubConnection.StartAsync();
}
private void HandleSearchUpdate(SearchUpdate update)
private void HandleSearchUpdate(SearchUpdateDto update)
{
if (update.Id == _search.Id)
{
@@ -120,7 +120,7 @@ else
}
}
private void HandleSearchUpdate(SearchUpdate update)
private void HandleSearchUpdate(SearchUpdateDto update)
{
InvokeAsync(() =>
{
@@ -162,7 +162,7 @@ else
});
}
private void HandleStatusUpdate(StatusUpdate update)
private void HandleStatusUpdate(StatusUpdateDto update)
{
InvokeAsync(() =>
{
@@ -93,7 +93,7 @@ else
await HubConnection.StartAsync();
}
private void HandleSearchUpdate(SearchUpdate update)
private void HandleSearchUpdate(SearchUpdateDto update)
{
InvokeAsync(() =>
{
@@ -1,6 +1,6 @@
using JdeScoping.Core.ApiContracts;
using JdeScoping.Core.ApiContracts.Auth;
using JdeScoping.Core.ApiContracts.Results;
using JdeScoping.Core.Models;
using JdeScoping.Core.Models.Auth;
namespace JdeScoping.Client.Services;
@@ -21,6 +21,6 @@ public class AuthApiClient : ApiClientBase, IAuthApiClient
public Task<ApiResult<Unit>> LogoutAsync(CancellationToken ct = default)
=> PostAsync<Unit>(ApiRoutes.Auth.Logout, ct);
public Task<ApiResult<UserInfo>> GetCurrentUserAsync(CancellationToken ct = default)
=> GetAsync<UserInfo>(ApiRoutes.Auth.Me, ct);
public Task<ApiResult<UserInfoDto>> GetCurrentUserAsync(CancellationToken ct = default)
=> GetAsync<UserInfoDto>(ApiRoutes.Auth.Me, ct);
}
@@ -1,6 +1,5 @@
using System.Net.Http.Json;
using JdeScoping.Client.Auth;
using JdeScoping.Client.Models;
using JdeScoping.Core.Models.Auth;
namespace JdeScoping.Client.Services;
@@ -43,17 +42,8 @@ public class AuthService : IAuthService
if (result.Success && result.User is not null)
{
// Notify auth state provider of the login
var userViewModel = new UserInfoViewModel
{
Username = result.User.Username,
FirstName = result.User.FirstName,
LastName = result.User.LastName,
DisplayName = result.User.DisplayName,
EmailAddress = result.User.EmailAddress,
Title = result.User.Title
};
await _authStateProvider.MarkUserAsAuthenticated(userViewModel);
// LoginResultModel.User is already UserInfoDto - pass directly
await _authStateProvider.MarkUserAsAuthenticated(result.User);
}
return result;
@@ -1,4 +1,4 @@
using JdeScoping.Client.Models;
using JdeScoping.Core.ApiContracts.SignalR;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
@@ -13,8 +13,8 @@ public class HubConnectionService : IHubConnectionService, IAsyncDisposable
private readonly NavigationManager _navigationManager;
private HubConnection? _hubConnection;
public event Action<SearchUpdate>? OnSearchUpdate;
public event Action<StatusUpdate>? OnStatusUpdate;
public event Action<SearchUpdateDto>? OnSearchUpdate;
public event Action<StatusUpdateDto>? OnStatusUpdate;
public bool IsConnected => _hubConnection?.State == HubConnectionState.Connected;
@@ -43,12 +43,12 @@ public class HubConnectionService : IHubConnectionService, IAsyncDisposable
])
.Build();
_hubConnection.On<SearchUpdate>("searchUpdate", update =>
_hubConnection.On<SearchUpdateDto>("searchUpdate", update =>
{
OnSearchUpdate?.Invoke(update);
});
_hubConnection.On<StatusUpdate>("statusUpdate", update =>
_hubConnection.On<StatusUpdateDto>("statusUpdate", update =>
{
OnStatusUpdate?.Invoke(update);
});
@@ -92,7 +92,7 @@ public class HubConnectionService : IHubConnectionService, IAsyncDisposable
}
}
public async Task<StatusUpdate?> GetCachedStatusAsync()
public async Task<StatusUpdateDto?> GetCachedStatusAsync()
{
if (_hubConnection == null || _hubConnection.State != HubConnectionState.Connected)
{
@@ -101,7 +101,7 @@ public class HubConnectionService : IHubConnectionService, IAsyncDisposable
try
{
return await _hubConnection.InvokeAsync<StatusUpdate>("GetCachedStatus");
return await _hubConnection.InvokeAsync<StatusUpdateDto>("GetCachedStatus");
}
catch (Exception ex)
{
@@ -1,4 +1,4 @@
using JdeScoping.Client.Models;
using JdeScoping.Core.ApiContracts.SignalR;
namespace JdeScoping.Client.Services;
@@ -10,12 +10,12 @@ public interface IHubConnectionService
/// <summary>
/// Event fired when a search update is received.
/// </summary>
event Action<SearchUpdate>? OnSearchUpdate;
event Action<SearchUpdateDto>? OnSearchUpdate;
/// <summary>
/// Event fired when a processor status update is received.
/// </summary>
event Action<StatusUpdate>? OnStatusUpdate;
event Action<StatusUpdateDto>? OnStatusUpdate;
/// <summary>
/// Starts the SignalR connection.
@@ -30,7 +30,7 @@ public interface IHubConnectionService
/// <summary>
/// Gets the cached processor status from the server.
/// </summary>
Task<StatusUpdate?> GetCachedStatusAsync();
Task<StatusUpdateDto?> GetCachedStatusAsync();
/// <summary>
/// Gets the current connection state.
+2
View File
@@ -21,5 +21,7 @@
@using JdeScoping.Client.Models
@using JdeScoping.Client.Pages
@using JdeScoping.Client.Services
@using JdeScoping.Core.ApiContracts.Auth
@using JdeScoping.Core.ApiContracts.SignalR
@using JdeScoping.Core.ViewModels
@using ClientSearchViewModel = JdeScoping.Client.Models.SearchViewModel