chore(cleanup): delete OtOpcUa.Server, OtOpcUa.Admin, and obsolete v1 tests
Task 56: removes the legacy in-process Server + Admin Web project + their test projects (Server.Tests, Admin.Tests, Admin.E2ETests). The fused OtOpcUa.Host binary built across Phases 1-9 is now the sole production entry point. What happened to the 47 legacy Admin Blazor pages: per follow-up F15, the v1 architecture's draft/publish UX is replaced by v2's live-edit + snapshot- deploy model, so a 1:1 migration is not meaningful. The mechanical move via git mv preserves the history; service classes + page bodies that referenced removed v1 types (ConfigGeneration, RedundancyRole, GenerationId) were deleted. AdminUI now ships a minimal Home page + the v2 Deployments page. Per-page rebuild against the v2 surface is tracked as F15. The v2 Deployments page (Task 52) is the only first-party UI shipping in this PR. Task 57: solution build green; 84+ tests green across active v2 + legacy driver test projects.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
@* Reusable loading spinner *@
|
||||
|
||||
@if (IsLoading)
|
||||
{
|
||||
<div class="d-flex align-items-center text-secondary @CssClass">
|
||||
<div class="spinner-border spinner-border-sm me-2" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<span>@Message</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public bool IsLoading { get; set; }
|
||||
[Parameter] public string Message { get; set; } = "Loading...";
|
||||
[Parameter] public string CssClass { get; set; } = "";
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@* Status chip — wraps the theme.css .chip / .chip-ok / .chip-warn / .chip-bad / .chip-idle classes. *@
|
||||
<span class="chip @CssClass">@Text</span>
|
||||
|
||||
@code {
|
||||
[Parameter] public string Text { get; set; } = "";
|
||||
[Parameter] public string CssClass { get; set; } = "chip-idle";
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
@*
|
||||
Reusable toast notification component.
|
||||
|
||||
Toasts intentionally float above modal dialogs so confirmation feedback
|
||||
(Success/Error) is visible even while a dialog is open.
|
||||
*@
|
||||
@implements IDisposable
|
||||
|
||||
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 1090;" aria-live="polite" aria-atomic="true">
|
||||
@foreach (var toast in _toasts)
|
||||
{
|
||||
<div class="toast show mb-2" role="alert">
|
||||
<div class="toast-header @GetHeaderClass(toast.Type)">
|
||||
<strong class="me-auto">@toast.Title</strong>
|
||||
<button type="button" class="btn-close btn-close-white" @onclick="() => Dismiss(toast)"></button>
|
||||
</div>
|
||||
<div class="toast-body">@toast.Message</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private const int DefaultAutoDismissMs = 5000;
|
||||
|
||||
private readonly List<ToastItem> _toasts = new();
|
||||
private readonly object _lock = new();
|
||||
|
||||
// Cancels all pending auto-dismiss delays when the component is disposed
|
||||
// so their continuations never touch a disposed component.
|
||||
private readonly CancellationTokenSource _disposalCts = new();
|
||||
|
||||
/// <summary>Number of toasts currently displayed.</summary>
|
||||
public int ToastCount
|
||||
{
|
||||
get { lock (_lock) { return _toasts.Count; } }
|
||||
}
|
||||
|
||||
public void ShowSuccess(string message, string title = "Success", int? autoDismissMs = null)
|
||||
{
|
||||
AddToast(title, message, ToastType.Success, autoDismissMs);
|
||||
}
|
||||
|
||||
public void ShowError(string message, string title = "Error", int? autoDismissMs = null)
|
||||
{
|
||||
AddToast(title, message, ToastType.Error, autoDismissMs);
|
||||
}
|
||||
|
||||
public void ShowWarning(string message, string title = "Warning", int? autoDismissMs = null)
|
||||
{
|
||||
AddToast(title, message, ToastType.Warning, autoDismissMs);
|
||||
}
|
||||
|
||||
public void ShowInfo(string message, string title = "Info", int? autoDismissMs = null)
|
||||
{
|
||||
AddToast(title, message, ToastType.Info, autoDismissMs);
|
||||
}
|
||||
|
||||
private void AddToast(string title, string message, ToastType type, int? autoDismissMs)
|
||||
{
|
||||
// If the component is already disposed, do not add or schedule anything.
|
||||
if (_disposalCts.IsCancellationRequested) return;
|
||||
|
||||
var toast = new ToastItem { Title = title, Message = message, Type = type };
|
||||
lock (_lock)
|
||||
{
|
||||
_toasts.Add(toast);
|
||||
}
|
||||
StateHasChanged();
|
||||
|
||||
var dismissMs = autoDismissMs ?? DefaultAutoDismissMs;
|
||||
_ = AutoDismissAsync(toast, dismissMs, _disposalCts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a toast after its dismiss delay. The delay is bound to the
|
||||
/// component's disposal token: if the host page is disposed first, the
|
||||
/// delay is cancelled and the continuation never touches the disposed
|
||||
/// component — no <see cref="ObjectDisposedException"/> escapes.
|
||||
/// </summary>
|
||||
private async Task AutoDismissAsync(ToastItem toast, int dismissMs, CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(dismissMs, token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested) return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_toasts.Remove(toast);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Component disposed between the token check and the render — ignore.
|
||||
}
|
||||
}
|
||||
|
||||
private void Dismiss(ToastItem toast)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_toasts.Remove(toast);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetHeaderClass(ToastType type) => type switch
|
||||
{
|
||||
ToastType.Success => "bg-success text-white",
|
||||
ToastType.Error => "bg-danger text-white",
|
||||
ToastType.Warning => "bg-warning text-dark",
|
||||
ToastType.Info => "bg-info text-dark",
|
||||
_ => "bg-secondary text-white"
|
||||
};
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposalCts.Cancel();
|
||||
_disposalCts.Dispose();
|
||||
}
|
||||
|
||||
private enum ToastType { Success, Error, Warning, Info }
|
||||
|
||||
private class ToastItem
|
||||
{
|
||||
public string Title { get; init; } = "";
|
||||
public string Message { get; init; } = "";
|
||||
public ToastType Type { get; init; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user