@page "/reservations" @using System.Security.Claims @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Web @using ZB.MOM.WW.OtOpcUa.Admin.Services @using ZB.MOM.WW.OtOpcUa.Configuration.Entities @attribute [Authorize(Policy = "CanPublish")] @rendermode RenderMode.InteractiveServer @inject ReservationService ReservationSvc

External-ID reservations

Fleet-wide ZTag + SAPID reservation state (decision #124). Releasing a reservation is a FleetAdmin-only audit-logged action — only release when the physical asset is permanently retired and its ID needs to be reused by a different equipment.

Active
@if (_active is null) {

Loading…

} else if (_active.Count == 0) {

No active reservations.

} else {
@foreach (var r in _active) { }
KindValueEquipmentUuidClusterFirst publishedLast published
@r.Kind @r.Value @r.EquipmentUuid @r.ClusterId @r.FirstPublishedAt.ToString("u") by @r.FirstPublishedBy @r.LastPublishedAt.ToString("u")
}
Released (most recent 100)
@if (_released is null) {

Loading…

} else if (_released.Count == 0) {

No released reservations yet.

} else {
@foreach (var r in _released) { }
KindValueReleased atByReason
@r.Kind@r.Value@r.ReleasedAt?.ToString("u")@r.ReleasedBy@r.ReleaseReason
}
@if (_releasing is not null) { } @code { // Admin-008: capture the signed-in operator so the release is attributed correctly in the // ExternalIdReservation.ReleasedBy column and the ConfigAuditLog.Principal column. [CascadingParameter] private Task? AuthState { get; set; } private List? _active; private List? _released; private ExternalIdReservation? _releasing; private string _reason = string.Empty; private bool _busy; private string? _error; protected override async Task OnInitializedAsync() => await ReloadAsync(); private async Task ReloadAsync() { _active = await ReservationSvc.ListActiveAsync(CancellationToken.None); _released = await ReservationSvc.ListReleasedAsync(CancellationToken.None); } private void OpenReleaseDialog(ExternalIdReservation r) { _releasing = r; _reason = string.Empty; _error = null; } private async Task ReleaseAsync() { if (_releasing is null || string.IsNullOrWhiteSpace(_reason)) { _error = "Reason is required"; return; } // Resolve the operator principal. The page is [Authorize(Policy="CanPublish")] so // AuthState will be available with an authenticated user; fall back to "unknown" only // as a defensive last resort (should never happen in practice). var user = AuthState is not null ? (await AuthState).User : null; var operatorName = user?.FindFirstValue(ClaimTypes.Name) ?? user?.FindFirstValue(ClaimTypes.NameIdentifier) ?? "unknown"; _busy = true; try { await ReservationSvc.ReleaseAsync( _releasing.Kind.ToString(), _releasing.Value, _reason, operatorName, CancellationToken.None); _releasing = null; await ReloadAsync(); } catch (Exception ex) { _error = ex.Message; } finally { _busy = false; } } }