diff --git a/Hcs.WebApp/Components/Pages/Objects/Houses.razor b/Hcs.WebApp/Components/Pages/Objects/Houses.razor
new file mode 100644
index 0000000..65a0e26
--- /dev/null
+++ b/Hcs.WebApp/Components/Pages/Objects/Houses.razor
@@ -0,0 +1,203 @@
+@page "/objects/houses"
+
+@using System.IdentityModel.Claims
+@using Hcs.WebApp.BackgroundServices
+@using Hcs.WebApp.Components.Dialogs
+@using Hcs.WebApp.Services
+@using Microsoft.AspNetCore.Authorization
+@using Microsoft.AspNetCore.Identity
+@using Microsoft.EntityFrameworkCore
+
+@implements IDisposable
+
+@attribute [Authorize]
+
+@inject AuthenticationStateProvider AuthenticationStateProvider
+@inject HeadquartersService HeadquartersService
+@inject HouseService HouseService
+@inject DialogService DialogService
+@inject CampaignManagementState CampaignManagementState
+
+Жилищный фонд
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @errorMessage
+
+
+ @*
+
+
+
+
+
+
+
+
+
+
+
+ *@
+
+
+
+
+
+
+
+
+
+
+
+@code {
+ SyncedPageState state;
+ IEnumerable houses;
+ string syncText = "...";
+ bool hasError;
+ string errorMessage;
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ await base.OnAfterRenderAsync(firstRender);
+
+ if (firstRender)
+ {
+ ChangeState(SyncedPageState.Init);
+
+ var finalState = SyncedPageState.Idle;
+ var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
+ if (state.User.IsOperatorOrHigher())
+ {
+ var operationInProgress = await HeadquartersService.HasActiveCampaignAsync(Campaign.CampaignType.ExportHousesData_15_7_0_1);
+ if (operationInProgress)
+ {
+ finalState = SyncedPageState.SyncWaiting;
+ }
+
+ CampaignManagementState.OnCampaignCreated += OnCampaignCreated;
+ CampaignManagementState.OnCampaignEnded += OnCampaignEnded;
+
+ houses = await HouseService.GetAllHousesAsync();
+ }
+
+ ChangeState(finalState);
+ }
+ }
+
+ async Task SyncHousesAsync()
+ {
+ if (state == SyncedPageState.SyncWaiting) return;
+
+ ChangeState(SyncedPageState.SyncWaiting);
+
+ if (await HeadquartersService.HasActiveCampaignAsync(Campaign.CampaignType.ExportHousesData_15_7_0_1))
+ {
+ ChangeState(SyncedPageState.Idle);
+ }
+ else
+ {
+ // TODO: Use user id
+ var campaign = await HeadquartersService.InitiateCampaignAsync(Campaign.CampaignType.ExportHousesData_15_7_0_1, "");
+ CampaignManagementState.EnqueueCampaign(campaign);
+ }
+ }
+
+ async Task RowExpandAsync(House house)
+ {
+ // TODO
+ }
+
+ async Task ShowElementAsync(RegistryElement registryElement)
+ {
+ // await DialogService.OpenAsync(
+ // "Просмотр записи",
+ // new Dictionary()
+ // {
+ // { nameof(ViewRegistryElement.RegistryElement), registryElement }
+ // },
+ // new DialogOptions()
+ // {
+ // Width = "420px",
+ // Height = "600px"
+ // });
+ }
+
+ void ChangeState(SyncedPageState state)
+ {
+ if (this.state == state) return;
+
+ this.state = state;
+
+ SetSyncText();
+ StateHasChanged();
+ }
+
+ void SetSyncText()
+ {
+ switch (state)
+ {
+ case SyncedPageState.Init:
+ syncText = "...";
+ break;
+
+ case SyncedPageState.Loading:
+ case SyncedPageState.Idle:
+ syncText = "Синхронизировать";
+ break;
+
+ case SyncedPageState.SyncWaiting:
+ syncText = "Идет синхронизация...";
+ break;
+ }
+ }
+
+ void OnCampaignCreated(Campaign campaign)
+ {
+ if (campaign.Type == Campaign.CampaignType.ExportHousesData_15_7_0_1)
+ {
+ InvokeAsync(() => ChangeState(SyncedPageState.SyncWaiting));
+ }
+ }
+
+ void OnCampaignEnded(int campaignId, Campaign.CampaignType type, DateTime endedAt, string failureReason)
+ {
+ if (type == Campaign.CampaignType.ExportHousesData_15_7_0_1)
+ {
+ Task.Run(RefreshHouses);
+ }
+ }
+
+ async Task RefreshHouses()
+ {
+ await InvokeAsync(() => ChangeState(SyncedPageState.Loading));
+
+ var refreshedHouses = await HouseService.GetAllHousesAsync();
+
+ await InvokeAsync(() =>
+ {
+ houses = refreshedHouses;
+
+ ChangeState(SyncedPageState.Idle);
+ });
+ }
+
+ public void Dispose()
+ {
+ CampaignManagementState.OnCampaignCreated -= OnCampaignCreated;
+ CampaignManagementState.OnCampaignEnded -= OnCampaignEnded;
+ }
+}
diff --git a/Hcs.WebApp/Data/Hcs/Campaign.cs b/Hcs.WebApp/Data/Hcs/Campaign.cs
index b2f3ae8..ef2dc4d 100644
--- a/Hcs.WebApp/Data/Hcs/Campaign.cs
+++ b/Hcs.WebApp/Data/Hcs/Campaign.cs
@@ -11,7 +11,10 @@ namespace Hcs.WebApp.Data.Hcs
ExportCommonRegistryElements_15_7_0_1,
[Display(Description = "Экспорт частных справочников")]
- ExportPrivateRegistryElements_15_7_0_1
+ ExportPrivateRegistryElements_15_7_0_1,
+
+ [Display(Description = "Экспорт данных домов")]
+ ExportHousesData_15_7_0_1
}
public int Id { get; set; }
diff --git a/Hcs.WebApp/Program.cs b/Hcs.WebApp/Program.cs
index 2b0645b..56dd0dd 100644
--- a/Hcs.WebApp/Program.cs
+++ b/Hcs.WebApp/Program.cs
@@ -69,6 +69,7 @@ builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
+builder.Services.AddScoped();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
diff --git a/Hcs.WebApp/Services/HouseService.cs b/Hcs.WebApp/Services/HouseService.cs
new file mode 100644
index 0000000..03af57f
--- /dev/null
+++ b/Hcs.WebApp/Services/HouseService.cs
@@ -0,0 +1,14 @@
+using Hcs.WebApp.Data.Hcs;
+using Microsoft.EntityFrameworkCore;
+
+namespace Hcs.WebApp.Services
+{
+ public class HouseService(IDbContextFactory factory) : HcsServiceBase(factory)
+ {
+ public async Task> GetAllHousesAsync()
+ {
+ using var context = GetNewContext();
+ return await context.Houses.ToListAsync();
+ }
+ }
+}