diff --git a/Hcs.WebApp/Components/Layout/MainLayout.razor b/Hcs.WebApp/Components/Layout/MainLayout.razor
index 3a84a37..8da0ffa 100644
--- a/Hcs.WebApp/Components/Layout/MainLayout.razor
+++ b/Hcs.WebApp/Components/Layout/MainLayout.razor
@@ -28,6 +28,8 @@
+
+
diff --git a/Hcs.WebApp/Components/Pages/Campaigns.razor b/Hcs.WebApp/Components/Pages/Campaigns.razor
new file mode 100644
index 0000000..9bd1a99
--- /dev/null
+++ b/Hcs.WebApp/Components/Pages/Campaigns.razor
@@ -0,0 +1,131 @@
+@page "/campaigns"
+
+@using Hcs.WebApp.BackgroundServices
+@using Hcs.WebApp.Services
+@using Microsoft.AspNetCore.Authorization
+
+@implements IDisposable
+
+@attribute [Authorize]
+
+@inject AuthenticationStateProvider AuthenticationStateProvider
+@inject HeadquartersService HeadquartersService
+@inject CampaignManagementState CampaignManagementState
+
+Кампании
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@code {
+ enum CampaignsPageState
+ {
+ Init,
+ Loading,
+ Idle
+ }
+
+ CampaignsPageState state;
+ IEnumerable campaigns;
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ await base.OnAfterRenderAsync(firstRender);
+
+ if (firstRender)
+ {
+ ChangeState(CampaignsPageState.Loading);
+
+ var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
+ if (state.User.IsInRole(AppRole.ADMINISTRATOR_TYPE) || state.User.IsInRole(AppRole.OPERATOR_TYPE))
+ {
+ CampaignManagementState.OnCampaignCreated += OnCampaignCreated;
+ CampaignManagementState.OnCampaignEnded += OnCampaignEnded;
+
+ campaigns = await HeadquartersService.GetCampaignsAsync();
+ }
+
+ ChangeState(CampaignsPageState.Idle);
+ }
+ }
+
+ async Task RowExpandAsync(Campaign campaign)
+ {
+ // TODO
+ }
+
+ void ChangeState(CampaignsPageState state)
+ {
+ if (this.state == state) return;
+
+ this.state = state;
+
+ StateHasChanged();
+ }
+
+ void OnCampaignCreated(Campaign campaign)
+ {
+ Task.Run(RefreshCampaigns);
+ }
+
+ void OnCampaignEnded(Campaign campaign)
+ {
+ Task.Run(RefreshCampaigns);
+ }
+
+ async Task RefreshCampaigns()
+ {
+ await InvokeAsync(() => ChangeState(CampaignsPageState.Loading));
+
+ var refreshedCampaigns = await HeadquartersService.GetCampaignsAsync();
+
+ await InvokeAsync(() =>
+ {
+ campaigns = refreshedCampaigns;
+
+ ChangeState(CampaignsPageState.Idle);
+ });
+ }
+
+ public void Dispose()
+ {
+ CampaignManagementState.OnCampaignCreated -= OnCampaignCreated;
+ CampaignManagementState.OnCampaignEnded -= OnCampaignEnded;
+ }
+}
diff --git a/Hcs.WebApp/Components/Pages/Registry/Common.razor b/Hcs.WebApp/Components/Pages/Registry/Common.razor
index 4b466f6..7ebfb27 100644
--- a/Hcs.WebApp/Components/Pages/Registry/Common.razor
+++ b/Hcs.WebApp/Components/Pages/Registry/Common.razor
@@ -72,8 +72,8 @@
OperationWaiting
}
- IEnumerable registries;
CommonPageState state;
+ IEnumerable registries;
string syncText = "...";
bool hasError;
string errorMessage;
diff --git a/Hcs.WebApp/Data/Hcs/Campaign.cs b/Hcs.WebApp/Data/Hcs/Campaign.cs
index 6d44110..c152a6b 100644
--- a/Hcs.WebApp/Data/Hcs/Campaign.cs
+++ b/Hcs.WebApp/Data/Hcs/Campaign.cs
@@ -26,7 +26,7 @@ namespace Hcs.WebApp.Data.Hcs
public string? FailureReason { get; set; }
- public virtual ICollection Operations { get; set; } = [];
+ public virtual ICollection? Operations { get; set; } = null;
[NotMapped]
public bool Completed => EndedAt.HasValue;
diff --git a/Hcs.WebApp/Services/HeadquartersService.cs b/Hcs.WebApp/Services/HeadquartersService.cs
index 741f401..cc04fed 100644
--- a/Hcs.WebApp/Services/HeadquartersService.cs
+++ b/Hcs.WebApp/Services/HeadquartersService.cs
@@ -11,6 +11,12 @@ namespace Hcs.WebApp.Services
return await context.Campaigns.AnyAsync(x => x.Type == type && !x.EndedAt.HasValue);
}
+ public async Task> GetCampaignsAsync()
+ {
+ using var context = GetNewContext();
+ return await context.Campaigns.ToListAsync();
+ }
+
public async Task> GetNotEndedCampaignsAsync()
{
using var context = GetNewContext();