Unify play management and admin screens in workspace
This commit is contained in:
@@ -56,8 +56,8 @@ Gameplay capabilities now include:
|
|||||||
- Campaign management owner labels use account display names (no GUID fallback rendering)
|
- Campaign management owner labels use account display names (no GUID fallback rendering)
|
||||||
- Character edit flow supports unlinking from campaigns (owner/GM/admin) and assigning to any existing campaign via expanded campaign options
|
- Character edit flow supports unlinking from campaigns (owner/GM/admin) and assigning to any existing campaign via expanded campaign options
|
||||||
- Campaign management supports character deletion by character owner or admin
|
- Campaign management supports character deletion by character owner or admin
|
||||||
- Shared top header control across workspace and admin views (consistent navigation/logout behavior)
|
- Shared top header control across all authenticated workspace screens (play, campaign management, admin)
|
||||||
- Admin menu navigation now directly targets `Play` or `Campaign Management` workspace screens
|
- Admin user management is integrated into workspace screen toggles (`Play`, `Campaign Management`, `Admin`)
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,5 @@ public enum HomeViewMode
|
|||||||
{
|
{
|
||||||
Loading,
|
Loading,
|
||||||
Anonymous,
|
Anonymous,
|
||||||
Workspace,
|
Workspace
|
||||||
Admin
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,6 @@
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case HomeViewMode.Workspace:
|
case HomeViewMode.Workspace:
|
||||||
<Workspace LoggedOut="OnLoggedOutAsync" AdminRequested="OnAdminRequestedAsync" RequestedScreen="WorkspaceScreenOverride"/>
|
<Workspace LoggedOut="OnLoggedOutAsync"/>
|
||||||
break;
|
|
||||||
|
|
||||||
case HomeViewMode.Admin:
|
|
||||||
<AdminHome LoggedOut="OnLoggedOutAsync" WorkspaceRequested="OnWorkspaceRequestedAsync"/>
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,19 +22,16 @@ public partial class Home
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ = await ApiClient.RequestAsync<MeResponse>("GET", "/api/me");
|
_ = await ApiClient.RequestAsync<MeResponse>("GET", "/api/me");
|
||||||
WorkspaceScreenOverride = null;
|
|
||||||
CurrentView = HomeViewMode.Workspace;
|
CurrentView = HomeViewMode.Workspace;
|
||||||
ClearStatus();
|
ClearStatus();
|
||||||
}
|
}
|
||||||
catch (ApiRequestException ex) when (ex.StatusCode == 401)
|
catch (ApiRequestException ex) when (ex.StatusCode == 401)
|
||||||
{
|
{
|
||||||
WorkspaceScreenOverride = null;
|
|
||||||
CurrentView = HomeViewMode.Anonymous;
|
CurrentView = HomeViewMode.Anonymous;
|
||||||
ClearStatus();
|
ClearStatus();
|
||||||
}
|
}
|
||||||
catch (ApiRequestException ex)
|
catch (ApiRequestException ex)
|
||||||
{
|
{
|
||||||
WorkspaceScreenOverride = null;
|
|
||||||
CurrentView = HomeViewMode.Anonymous;
|
CurrentView = HomeViewMode.Anonymous;
|
||||||
SetStatus(ex.Message, true);
|
SetStatus(ex.Message, true);
|
||||||
}
|
}
|
||||||
@@ -42,22 +39,6 @@ public partial class Home
|
|||||||
|
|
||||||
private Task OnLoggedInAsync()
|
private Task OnLoggedInAsync()
|
||||||
{
|
{
|
||||||
WorkspaceScreenOverride = null;
|
|
||||||
CurrentView = HomeViewMode.Workspace;
|
|
||||||
ClearStatus();
|
|
||||||
return InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task OnAdminRequestedAsync()
|
|
||||||
{
|
|
||||||
CurrentView = HomeViewMode.Admin;
|
|
||||||
ClearStatus();
|
|
||||||
return InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task OnWorkspaceRequestedAsync(string screen)
|
|
||||||
{
|
|
||||||
WorkspaceScreenOverride = NormalizeWorkspaceScreen(screen) ?? "play";
|
|
||||||
CurrentView = HomeViewMode.Workspace;
|
CurrentView = HomeViewMode.Workspace;
|
||||||
ClearStatus();
|
ClearStatus();
|
||||||
return InvokeAsync(StateHasChanged);
|
return InvokeAsync(StateHasChanged);
|
||||||
@@ -65,7 +46,6 @@ public partial class Home
|
|||||||
|
|
||||||
private Task OnLoggedOutAsync(string? message)
|
private Task OnLoggedOutAsync(string? message)
|
||||||
{
|
{
|
||||||
WorkspaceScreenOverride = null;
|
|
||||||
CurrentView = HomeViewMode.Anonymous;
|
CurrentView = HomeViewMode.Anonymous;
|
||||||
if (string.IsNullOrWhiteSpace(message))
|
if (string.IsNullOrWhiteSpace(message))
|
||||||
{
|
{
|
||||||
@@ -90,19 +70,7 @@ public partial class Home
|
|||||||
StatusIsError = false;
|
StatusIsError = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string? NormalizeWorkspaceScreen(string? screen)
|
|
||||||
{
|
|
||||||
if (string.Equals(screen, "management", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return "management";
|
|
||||||
|
|
||||||
if (string.Equals(screen, "play", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return "play";
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HomeViewMode CurrentView { get; set; } = HomeViewMode.Loading;
|
private HomeViewMode CurrentView { get; set; } = HomeViewMode.Loading;
|
||||||
private string? WorkspaceScreenOverride { get; set; }
|
|
||||||
private string? StatusMessage { get; set; }
|
private string? StatusMessage { get; set; }
|
||||||
private bool StatusIsError { get; set; }
|
private bool StatusIsError { get; set; }
|
||||||
private bool HasInitialized { get; set; }
|
private bool HasInitialized { get; set; }
|
||||||
|
|||||||
@@ -76,8 +76,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
}
|
}
|
||||||
|
else if (IsManagementScreen)
|
||||||
@if (IsManagementScreen)
|
|
||||||
{
|
{
|
||||||
<CampaignManagementPanel
|
<CampaignManagementPanel
|
||||||
Campaigns="Campaigns"
|
Campaigns="Campaigns"
|
||||||
@@ -96,6 +95,59 @@
|
|||||||
EditCharacterRequested="OpenEditCharacterModal"
|
EditCharacterRequested="OpenEditCharacterModal"
|
||||||
DeleteCharacterRequested="DeleteCharacterAsync"/>
|
DeleteCharacterRequested="DeleteCharacterAsync"/>
|
||||||
}
|
}
|
||||||
|
else if (IsAdminScreen)
|
||||||
|
{
|
||||||
|
<main class="management-screen">
|
||||||
|
<section class="card">
|
||||||
|
<div class="section-head">
|
||||||
|
<h2>User Management</h2>
|
||||||
|
</div>
|
||||||
|
@if (IsAdminDataLoading)
|
||||||
|
{
|
||||||
|
<p class="empty">Loading users...</p>
|
||||||
|
}
|
||||||
|
else if (!IsCurrentUserAdmin)
|
||||||
|
{
|
||||||
|
<p class="empty">Admin role is required to manage users.</p>
|
||||||
|
}
|
||||||
|
else if (AdminUsers.Count == 0)
|
||||||
|
{
|
||||||
|
<p class="empty">No users found.</p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<ul class="management-list">
|
||||||
|
@foreach (var user in AdminUsers)
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
<strong>@user.Username</strong>
|
||||||
|
<p class="muted">@user.DisplayName</p>
|
||||||
|
<p class="muted">Roles: @(user.Roles.Count == 0 ? "none" : string.Join(", ", user.Roles))</p>
|
||||||
|
</div>
|
||||||
|
<div class="skill-chip-actions">
|
||||||
|
<button type="button"
|
||||||
|
class="chip-button"
|
||||||
|
disabled="@(IsMutating || user.Id == User?.Id)"
|
||||||
|
@onclick="() => ToggleAdminRoleAsync(user)">
|
||||||
|
<span aria-hidden="true" class="emoji">🛡️</span>
|
||||||
|
<span class="sr-only">Toggle admin role for @user.Username</span>
|
||||||
|
</button>
|
||||||
|
<button type="button"
|
||||||
|
class="chip-button"
|
||||||
|
disabled="@(IsMutating || user.Id == User?.Id)"
|
||||||
|
@onclick="() => DeleteUserAsync(user)">
|
||||||
|
<span aria-hidden="true" class="emoji">🗑️</span>
|
||||||
|
<span class="sr-only">Delete user @user.Username</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (Toasts.Count > 0)
|
@if (Toasts.Count > 0)
|
||||||
|
|||||||
@@ -22,18 +22,8 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
|
|
||||||
private async Task InitializeAsync()
|
private async Task InitializeAsync()
|
||||||
{
|
{
|
||||||
var requestedScreen = NormalizeRequestedScreen(RequestedScreen);
|
var storedScreen = await JS.InvokeAsync<string?>("rpgRollerApi.getSessionValue", ScreenSessionKey);
|
||||||
if (requestedScreen is not null)
|
CurrentScreen = NormalizeRequestedScreen(storedScreen) ?? ScreenPlay;
|
||||||
{
|
|
||||||
CurrentScreen = requestedScreen;
|
|
||||||
await JS.InvokeVoidAsync("rpgRollerApi.setSessionValue", ScreenSessionKey, CurrentScreen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var storedScreen = await JS.InvokeAsync<string?>("rpgRollerApi.getSessionValue", ScreenSessionKey);
|
|
||||||
if (string.Equals(storedScreen, "management", StringComparison.OrdinalIgnoreCase))
|
|
||||||
CurrentScreen = "management";
|
|
||||||
}
|
|
||||||
|
|
||||||
var storedPanel = await JS.InvokeAsync<string?>("rpgRollerApi.getSessionValue", MobilePanelSessionKey);
|
var storedPanel = await JS.InvokeAsync<string?>("rpgRollerApi.getSessionValue", MobilePanelSessionKey);
|
||||||
if (string.Equals(storedPanel, "log", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(storedPanel, "log", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -126,11 +116,16 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
|
|
||||||
User = me.User;
|
User = me.User;
|
||||||
ActiveCharacterId = me.ActiveCharacterId;
|
ActiveCharacterId = me.ActiveCharacterId;
|
||||||
|
await EnsureScreenAccessAsync();
|
||||||
|
|
||||||
await ReloadCampaignsAsync(preferredCampaignId ?? me.CurrentCampaignId);
|
await ReloadCampaignsAsync(preferredCampaignId ?? me.CurrentCampaignId);
|
||||||
await ReloadCharacterCampaignOptionsAsync();
|
await ReloadCharacterCampaignOptionsAsync();
|
||||||
await RefreshCampaignScopeAsync();
|
await RefreshCampaignScopeAsync();
|
||||||
await SyncStateEventsAsync();
|
await SyncStateEventsAsync();
|
||||||
|
|
||||||
|
if (IsAdminScreen)
|
||||||
|
await EnsureAdminUsersLoadedAsync();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,9 +229,16 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
|
|
||||||
private async Task SwitchScreenAsync(string screen)
|
private async Task SwitchScreenAsync(string screen)
|
||||||
{
|
{
|
||||||
CurrentScreen = NormalizeRequestedScreen(screen) ?? "play";
|
var targetScreen = NormalizeRequestedScreen(screen) ?? ScreenPlay;
|
||||||
|
if (string.Equals(targetScreen, ScreenAdmin, StringComparison.OrdinalIgnoreCase) && !IsCurrentUserAdmin)
|
||||||
|
targetScreen = ScreenPlay;
|
||||||
|
|
||||||
|
CurrentScreen = targetScreen;
|
||||||
IsScreenMenuOpen = false;
|
IsScreenMenuOpen = false;
|
||||||
await JS.InvokeVoidAsync("rpgRollerApi.setSessionValue", ScreenSessionKey, CurrentScreen);
|
await PersistScreenPreferenceAsync(CurrentScreen);
|
||||||
|
|
||||||
|
if (IsAdminScreen)
|
||||||
|
await EnsureAdminUsersLoadedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SwitchToPlayAsync()
|
private Task SwitchToPlayAsync()
|
||||||
@@ -249,10 +251,112 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
return SwitchScreenAsync("management");
|
return SwitchScreenAsync("management");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OpenAdminAsync()
|
private Task SwitchToAdminAsync()
|
||||||
{
|
{
|
||||||
IsScreenMenuOpen = false;
|
return SwitchScreenAsync(ScreenAdmin);
|
||||||
await AdminRequested.InvokeAsync();
|
}
|
||||||
|
|
||||||
|
private async Task EnsureScreenAccessAsync()
|
||||||
|
{
|
||||||
|
if (IsCurrentUserAdmin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AdminUsers = [];
|
||||||
|
HasLoadedAdminUsers = false;
|
||||||
|
|
||||||
|
if (!IsAdminScreen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CurrentScreen = ScreenPlay;
|
||||||
|
await PersistScreenPreferenceAsync(CurrentScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EnsureAdminUsersLoadedAsync()
|
||||||
|
{
|
||||||
|
if (!IsCurrentUserAdmin || HasLoadedAdminUsers || IsAdminDataLoading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IsAdminDataLoading = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ReloadAdminUsersAsync();
|
||||||
|
}
|
||||||
|
catch (ApiRequestException ex) when (ex.StatusCode == 401)
|
||||||
|
{
|
||||||
|
ClearAuthenticatedState();
|
||||||
|
await StopStateEventsAsync();
|
||||||
|
await LoggedOut.InvokeAsync("Session expired. Please log in again.");
|
||||||
|
}
|
||||||
|
catch (ApiRequestException ex)
|
||||||
|
{
|
||||||
|
SetStatus(ex.Message, true);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsAdminDataLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ReloadAdminUsersAsync()
|
||||||
|
{
|
||||||
|
AdminUsers = (await ApiClient.RequestAsync<IReadOnlyList<AdminUserSummary>>("GET", "/api/admin/users"))
|
||||||
|
.OrderBy(user => user.Username, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
HasLoadedAdminUsers = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ToggleAdminRoleAsync(AdminUserSummary user)
|
||||||
|
{
|
||||||
|
if (IsMutating || User is null || !IsCurrentUserAdmin || user.Id == User.Id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IsMutating = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IReadOnlyList<string> roles = HasAdminRole(user) ? Array.Empty<string>() : [UserRoles.Admin];
|
||||||
|
_ = await ApiClient.RequestAsync<AdminUserSummary>(
|
||||||
|
"PUT",
|
||||||
|
$"/api/admin/users/{user.Id}/roles",
|
||||||
|
new UpdateUserRolesRequest(roles));
|
||||||
|
|
||||||
|
await ReloadAdminUsersAsync();
|
||||||
|
SetStatus("User roles updated.", false);
|
||||||
|
}
|
||||||
|
catch (ApiRequestException ex)
|
||||||
|
{
|
||||||
|
SetStatus(ex.Message, true);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsMutating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteUserAsync(AdminUserSummary user)
|
||||||
|
{
|
||||||
|
if (IsMutating || User is null || !IsCurrentUserAdmin || user.Id == User.Id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var confirmed = await JS.InvokeAsync<bool>("confirm", $"Delete user '{user.Username}'?");
|
||||||
|
if (!confirmed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IsMutating = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ = await ApiClient.RequestAsync<bool>("DELETE", $"/api/admin/users/{user.Id}");
|
||||||
|
await ReloadAdminUsersAsync();
|
||||||
|
SetStatus("User deleted.", false);
|
||||||
|
}
|
||||||
|
catch (ApiRequestException ex)
|
||||||
|
{
|
||||||
|
SetStatus(ex.Message, true);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsMutating = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetMobilePanelAsync(string panel)
|
private async Task SetMobilePanelAsync(string panel)
|
||||||
@@ -533,15 +637,32 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
|
|
||||||
private static string? NormalizeRequestedScreen(string? screen)
|
private static string? NormalizeRequestedScreen(string? screen)
|
||||||
{
|
{
|
||||||
|
if (string.Equals(screen, ScreenAdmin, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return ScreenAdmin;
|
||||||
|
|
||||||
if (string.Equals(screen, "management", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(screen, "management", StringComparison.OrdinalIgnoreCase))
|
||||||
return "management";
|
return "management";
|
||||||
|
|
||||||
if (string.Equals(screen, "play", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(screen, ScreenPlay, StringComparison.OrdinalIgnoreCase))
|
||||||
return "play";
|
return ScreenPlay;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task PersistScreenPreferenceAsync(string screen)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await JS.InvokeVoidAsync("rpgRollerApi.setSessionValue", ScreenSessionKey, screen);
|
||||||
|
}
|
||||||
|
catch (JSDisconnectedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex) when (IsStaticRenderInteropException(ex))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool CanEditSkill(SkillSummary skill)
|
private bool CanEditSkill(SkillSummary skill)
|
||||||
{
|
{
|
||||||
if (SelectedCampaign is null)
|
if (SelectedCampaign is null)
|
||||||
@@ -752,6 +873,9 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
EditCharacterInitialModel = new();
|
EditCharacterInitialModel = new();
|
||||||
CreateCharacterFormVersion = 0;
|
CreateCharacterFormVersion = 0;
|
||||||
EditCharacterFormVersion = 0;
|
EditCharacterFormVersion = 0;
|
||||||
|
AdminUsers = [];
|
||||||
|
HasLoadedAdminUsers = false;
|
||||||
|
IsAdminDataLoading = false;
|
||||||
Toasts.Clear();
|
Toasts.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,6 +932,7 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
private List<CampaignOption> CharacterCampaignOptions { get; set; } = [];
|
private List<CampaignOption> CharacterCampaignOptions { get; set; } = [];
|
||||||
private List<CampaignLogEntry> CampaignLog { get; set; } = [];
|
private List<CampaignLogEntry> CampaignLog { get; set; } = [];
|
||||||
private List<RulesetDefinition> Rulesets { get; set; } = [];
|
private List<RulesetDefinition> Rulesets { get; set; } = [];
|
||||||
|
private List<AdminUserSummary> AdminUsers { get; set; } = [];
|
||||||
private Guid? SelectedCharacterId { get; set; }
|
private Guid? SelectedCharacterId { get; set; }
|
||||||
private RollResult? LastRoll { get; set; }
|
private RollResult? LastRoll { get; set; }
|
||||||
private List<string> KnownUsernames { get; set; } = [];
|
private List<string> KnownUsernames { get; set; } = [];
|
||||||
@@ -815,6 +940,8 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
|
|
||||||
private bool IsMutating { get; set; }
|
private bool IsMutating { get; set; }
|
||||||
private bool IsCampaignDataLoading { get; set; }
|
private bool IsCampaignDataLoading { get; set; }
|
||||||
|
private bool IsAdminDataLoading { get; set; }
|
||||||
|
private bool HasLoadedAdminUsers { get; set; }
|
||||||
private bool HasHealthIssue { get; set; }
|
private bool HasHealthIssue { get; set; }
|
||||||
private string HealthIssueMessage { get; set; } = "Retry to restore the API connection.";
|
private string HealthIssueMessage { get; set; } = "Retry to restore the API connection.";
|
||||||
private List<WorkspaceToast> Toasts { get; } = [];
|
private List<WorkspaceToast> Toasts { get; } = [];
|
||||||
@@ -839,12 +966,6 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<string?> LoggedOut { get; set; }
|
public EventCallback<string?> LoggedOut { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public EventCallback AdminRequested { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string? RequestedScreen { get; set; }
|
|
||||||
|
|
||||||
private string? SelectedCampaignName => SelectedCampaign?.Name;
|
private string? SelectedCampaignName => SelectedCampaign?.Name;
|
||||||
|
|
||||||
private CharacterSummary? SelectedCharacter =>
|
private CharacterSummary? SelectedCharacter =>
|
||||||
@@ -862,14 +983,20 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
private bool IsSelectedCampaignD6 =>
|
private bool IsSelectedCampaignD6 =>
|
||||||
string.Equals(SelectedCampaign?.RulesetId, "d6", StringComparison.OrdinalIgnoreCase);
|
string.Equals(SelectedCampaign?.RulesetId, "d6", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
private static bool HasAdminRole(AdminUserSummary user)
|
||||||
|
{
|
||||||
|
return user.Roles.Contains(UserRoles.Admin, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
private List<SkillSummary> SelectedCharacterSkills =>
|
private List<SkillSummary> SelectedCharacterSkills =>
|
||||||
SelectedCampaign is null || !SelectedCharacterId.HasValue ? [] : SelectedCampaign.Skills.Where(skill => skill.CharacterId == SelectedCharacterId.Value).OrderBy(skill => skill.Name, StringComparer.OrdinalIgnoreCase).ToList();
|
SelectedCampaign is null || !SelectedCharacterId.HasValue ? [] : SelectedCampaign.Skills.Where(skill => skill.CharacterId == SelectedCharacterId.Value).OrderBy(skill => skill.Name, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
|
||||||
private List<SkillGroupSummary> SelectedCharacterSkillGroups =>
|
private List<SkillGroupSummary> SelectedCharacterSkillGroups =>
|
||||||
SelectedCampaign is null || !SelectedCharacterId.HasValue ? [] : SelectedCampaign.SkillGroups.Where(group => group.CharacterId == SelectedCharacterId.Value).OrderBy(group => group.Name, StringComparer.OrdinalIgnoreCase).ToList();
|
SelectedCampaign is null || !SelectedCharacterId.HasValue ? [] : SelectedCampaign.SkillGroups.Where(group => group.CharacterId == SelectedCharacterId.Value).OrderBy(group => group.Name, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
|
||||||
private bool IsPlayScreen => string.Equals(CurrentScreen, "play", StringComparison.OrdinalIgnoreCase);
|
private bool IsPlayScreen => string.Equals(CurrentScreen, ScreenPlay, StringComparison.OrdinalIgnoreCase);
|
||||||
private bool IsManagementScreen => !IsPlayScreen;
|
private bool IsManagementScreen => string.Equals(CurrentScreen, ScreenManagement, StringComparison.OrdinalIgnoreCase);
|
||||||
|
private bool IsAdminScreen => string.Equals(CurrentScreen, ScreenAdmin, StringComparison.OrdinalIgnoreCase);
|
||||||
private IReadOnlyList<AppHeaderMenuItem> HeaderMenuItems
|
private IReadOnlyList<AppHeaderMenuItem> HeaderMenuItems
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -881,7 +1008,7 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (IsCurrentUserAdmin)
|
if (IsCurrentUserAdmin)
|
||||||
items.Add(new AppHeaderMenuItem { Label = "Admin", IsActive = false, OnSelected = OpenAdminAsync });
|
items.Add(new AppHeaderMenuItem { Label = "Admin", IsActive = IsAdminScreen, OnSelected = SwitchToAdminAsync });
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@@ -903,6 +1030,9 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
|
|
||||||
private string AppCssClass => IsPlayScreen ? "rr-app app-play" : "rr-app";
|
private string AppCssClass => IsPlayScreen ? "rr-app app-play" : "rr-app";
|
||||||
|
|
||||||
|
private const string ScreenPlay = "play";
|
||||||
|
private const string ScreenManagement = "management";
|
||||||
|
private const string ScreenAdmin = "admin";
|
||||||
private const string ScreenSessionKey = "screen";
|
private const string ScreenSessionKey = "screen";
|
||||||
private const string CampaignSessionKey = "campaign";
|
private const string CampaignSessionKey = "campaign";
|
||||||
private const string MobilePanelSessionKey = "play-panel";
|
private const string MobilePanelSessionKey = "play-panel";
|
||||||
|
|||||||
Reference in New Issue
Block a user