@page "/cms/tracks/{Id:int}" @* InteractiveServer: page injects ITrackService in-process; ITokenService reads localStorage via JS interop over the circuit. *@ @rendermode InteractiveServer @using AuthBlocksWeb.HierarchicalAuthorize @using AuthBlocksWeb.Services @using DeepDrftData @using System.Net.Http.Headers @using System.Net.Http.Json @attribute [HierarchicalRoleAuthorize("Admin")] @inject ITrackService TrackService @inject IHttpClientFactory HttpClientFactory @inject ITokenService TokenService @inject ISnackbar Snackbar @inject IDialogService DialogService @inject NavigationManager Nav @inject ILogger Logger Edit Track — DeepDrft CMS Back to tracks @if (_loading) { } else if (_track is null) { Track not found. } else { Edit Track @_track.EntryKey Vault reference — not editable. Delete Save Changes } @code { [Parameter] public int Id { get; set; } private TrackEntity? _track; private TrackEditForm _form = new(); private bool _loading = true; private bool _busy; private bool CanSave => !string.IsNullOrWhiteSpace(_form.TrackName) && !string.IsNullOrWhiteSpace(_form.Artist); protected override async Task OnInitializedAsync() { await LoadAsync(); } private async Task LoadAsync() { _loading = true; var result = await TrackService.GetById(Id); _track = result.Success ? result.Value : null; if (_track is not null) { _form = TrackEditForm.From(_track); } _loading = false; } private async Task SaveAsync() { if (_track is null || !CanSave) return; _busy = true; try { var http = HttpClientFactory.CreateClient("DeepDrft.API"); await AttachBearerAsync(http); var payload = new { TrackName = _form.TrackName, Artist = _form.Artist, Album = string.IsNullOrWhiteSpace(_form.Album) ? null : _form.Album, Genre = string.IsNullOrWhiteSpace(_form.Genre) ? null : _form.Genre, ReleaseDate = _form.ReleaseDate is { } d ? DateOnly.FromDateTime(d) : (DateOnly?)null }; var response = await http.PutAsJsonAsync($"api/cms/track/{Id}", payload); if (response.IsSuccessStatusCode) { Snackbar.Add("Track updated.", Severity.Success); await LoadAsync(); } else { Snackbar.Add($"Save failed: {(int)response.StatusCode} {response.ReasonPhrase}", Severity.Error); } } catch (Exception ex) { Logger.LogError(ex, "Save failed for track {TrackId}", Id); Snackbar.Add("Save failed — please try again.", Severity.Error); } finally { _busy = false; } } // DELETE api/cms/track/{Id} is handled by CmsDeleteController (T3 branch). private async Task ConfirmDelete() { if (_track is null) return; var confirmed = await DialogService.ShowMessageBox( "Delete track", $"Permanently delete \"{_track.TrackName}\" by {_track.Artist}? This cannot be undone.", yesText: "Delete", cancelText: "Cancel"); if (confirmed != true) return; _busy = true; try { var http = HttpClientFactory.CreateClient("DeepDrft.API"); await AttachBearerAsync(http); var response = await http.DeleteAsync($"api/cms/track/{Id}"); if (response.IsSuccessStatusCode) { Snackbar.Add("Track deleted.", Severity.Success); Nav.NavigateTo("/cms/tracks"); } else { Snackbar.Add($"Delete failed: {(int)response.StatusCode} {response.ReasonPhrase}", Severity.Error); _busy = false; } } catch (Exception ex) { Logger.LogError(ex, "Delete failed for track {TrackId}", Id); Snackbar.Add("Delete failed — please try again.", Severity.Error); _busy = false; } } private async Task AttachBearerAsync(HttpClient http) { var token = await TokenService.GetAccessTokenAsync(); if (!string.IsNullOrEmpty(token)) { http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); } } private sealed class TrackEditForm { public string TrackName { get; set; } = string.Empty; public string Artist { get; set; } = string.Empty; public string? Album { get; set; } public string? Genre { get; set; } public DateTime? ReleaseDate { get; set; } public static TrackEditForm From(TrackEntity track) => new() { TrackName = track.TrackName, Artist = track.Artist, Album = track.Album, Genre = track.Genre, ReleaseDate = track.ReleaseDate is { } d ? d.ToDateTime(TimeOnly.MinValue) : null }; } }