diff --git a/DeepDrftCms/DeepDrftCms.csproj b/DeepDrftCms/DeepDrftCms.csproj
index 65b0747..c484fdc 100644
--- a/DeepDrftCms/DeepDrftCms.csproj
+++ b/DeepDrftCms/DeepDrftCms.csproj
@@ -21,6 +21,7 @@
+
diff --git a/DeepDrftCms/Pages/Tracks/TrackList.razor b/DeepDrftCms/Pages/Tracks/TrackList.razor
new file mode 100644
index 0000000..e63ba33
--- /dev/null
+++ b/DeepDrftCms/Pages/Tracks/TrackList.razor
@@ -0,0 +1,135 @@
+@page "/cms/tracks"
+@rendermode InteractiveServer
+@using System.Net
+@using AuthBlocksWeb.HierarchicalAuthorize
+@using DeepDrftModels.Models
+@attribute [HierarchicalRoleAuthorize("Admin")]
+@inject ITrackService TrackService
+@inject IHttpClientFactory HttpClientFactory
+@inject IDialogService DialogService
+@inject ISnackbar Snackbar
+@inject NavigationManager Navigation
+
+Tracks — DeepDrft CMS
+
+
+
+ Tracks
+
+ Add Track
+
+
+
+
+
+ No tracks found.
+
+
+ Loading tracks…
+
+
+ Track Name
+ Artist
+ Album
+ Genre
+ Release Date
+ Entry Key
+ Actions
+
+
+ @context.TrackName
+ @context.Artist
+ @(context.Album ?? "—")
+ @(context.Genre ?? "—")
+ @(context.ReleaseDate?.ToString("yyyy-MM-dd") ?? "—")
+ @context.EntryKey
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@code {
+ private MudTable? _table;
+
+ private async Task> LoadServerData(TableState state, CancellationToken cancellationToken)
+ {
+ var pageNumber = state.Page + 1; // MudTable is 0-based, service is 1-based.
+ var sortColumn = string.IsNullOrEmpty(state.SortLabel) ? "TrackName" : state.SortLabel;
+ var sortDescending = state.SortDirection == SortDirection.Descending;
+
+ var result = await TrackService.GetPaged(pageNumber, state.PageSize, sortColumn, sortDescending);
+
+ if (!result.Success || result.Value is null)
+ {
+ var errorText = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
+ Snackbar.Add($"Failed to load tracks: {errorText}", Severity.Error);
+ return new TableData { Items = Array.Empty(), TotalItems = 0 };
+ }
+
+ var page = result.Value;
+ return new TableData
+ {
+ Items = page.Items,
+ TotalItems = page.TotalCount
+ };
+ }
+
+ private async Task ConfirmAndDelete(TrackEntity track)
+ {
+ var confirmed = await DialogService.ShowMessageBox(
+ title: "Delete track",
+ markupMessage: new MarkupString($"Delete {WebUtility.HtmlEncode(track.TrackName)} by {WebUtility.HtmlEncode(track.Artist)}? This removes both the metadata row and the underlying audio entry."),
+ yesText: "Delete",
+ cancelText: "Cancel");
+
+ if (confirmed != true) return;
+
+ try
+ {
+ var client = HttpClientFactory.CreateClient("DeepDrft.API");
+ var response = await client.DeleteAsync($"api/cms/track/{track.Id}");
+
+ if (response.IsSuccessStatusCode)
+ {
+ Snackbar.Add($"Deleted '{track.TrackName}'.", Severity.Success);
+ if (_table is not null) await _table.ReloadServerData();
+ }
+ else
+ {
+ Snackbar.Add($"Delete failed ({(int)response.StatusCode} {response.ReasonPhrase}).", Severity.Error);
+ }
+ }
+ catch (Exception ex)
+ {
+ Snackbar.Add($"Delete failed: {ex.Message}", Severity.Error);
+ }
+ }
+}
diff --git a/DeepDrftCms/_Imports.razor b/DeepDrftCms/_Imports.razor
index cf3d836..d0b266a 100644
--- a/DeepDrftCms/_Imports.razor
+++ b/DeepDrftCms/_Imports.razor
@@ -8,4 +8,5 @@
@using Microsoft.JSInterop
@using DeepDrftCms
@using DeepDrftModels.Entities
+@using DeepDrftWeb.Services
@using MudBlazor