From bd555607af3d3a1ce54d12dc4f614bf51c7bfb73 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Mon, 6 Apr 2026 19:50:03 -0400 Subject: [PATCH 01/37] Adds initial Dashboard index page Initializes the Dashboard Razor Page as the foundation for the advanced search functionality. Part of issue #954. --- FMS/Pages/Dashboard/Index.cshtml | 4 ++++ FMS/Pages/Dashboard/Index.cshtml.cs | 12 ++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 FMS/Pages/Dashboard/Index.cshtml create mode 100644 FMS/Pages/Dashboard/Index.cshtml.cs diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml new file mode 100644 index 00000000..92fbca44 --- /dev/null +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -0,0 +1,4 @@ +@page +@model FMS.Pages.Dashboard.IndexModel +@{ +} diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs new file mode 100644 index 00000000..1a959174 --- /dev/null +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace FMS.Pages.Dashboard +{ + public class IndexModel : PageModel + { + public void OnGet() + { + } + } +} From f7bdd32d90bde059f70ae73117b2f6df127a9df7 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 7 Apr 2026 11:39:05 -0400 Subject: [PATCH 02/37] Redirects authenticated users to the Dashboard page Updates the sign-in logic to redirect users with assigned roles to the Dashboard instead of the Facilities list. Adds initial content to the Dashboard index page and removes the unused HSI menu partial. Part of issue #954. --- FMS/FMS.csproj | 12 ++++++++++++ FMS/Pages/Dashboard/Index.cshtml | 6 ++++++ FMS/Pages/Index.cshtml.cs | 10 ++++++++++ 3 files changed, 28 insertions(+) diff --git a/FMS/FMS.csproj b/FMS/FMS.csproj index 02770824..cd0721b1 100644 --- a/FMS/FMS.csproj +++ b/FMS/FMS.csproj @@ -7,6 +7,18 @@ 2023.4.13 + + + + + + + + + + + + Always diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 92fbca44..67ad8caf 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -1,4 +1,10 @@ @page +@using FMS.Pages.Dashboard @model FMS.Pages.Dashboard.IndexModel + @{ + ViewData["Title"] = $"User Dashboard"; } +

@ViewData["Title"]

+ +

User Dashboard Page

diff --git a/FMS/Pages/Index.cshtml.cs b/FMS/Pages/Index.cshtml.cs index d5c5d136..e4967c93 100644 --- a/FMS/Pages/Index.cshtml.cs +++ b/FMS/Pages/Index.cshtml.cs @@ -18,6 +18,16 @@ public IActionResult OnGet() { if (_signInManager.IsSignedIn(User)) { + if(User.Identity.IsAuthenticated) + { + foreach(var role in UserRoles.AllRoles) + { + if(User.IsInRole(role)) + { + return LocalRedirect("~/Dashboard"); + } + } + } return LocalRedirect("~/Facilities"); } From e87e0a26de85c3d584d6d53bc1c8a8fb388218ec Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 21 Apr 2026 10:59:23 -0400 Subject: [PATCH 03/37] Removes unused HSI menu partial Deletes the unused `_HSIMenuPartial.cshtml` file to clean up the codebase. Part of issue #954. --- FMS/Pages/Shared/_HSIMenuPartial.cshtml | 28 ------------------------- 1 file changed, 28 deletions(-) delete mode 100644 FMS/Pages/Shared/_HSIMenuPartial.cshtml diff --git a/FMS/Pages/Shared/_HSIMenuPartial.cshtml b/FMS/Pages/Shared/_HSIMenuPartial.cshtml deleted file mode 100644 index cbb80600..00000000 --- a/FMS/Pages/Shared/_HSIMenuPartial.cshtml +++ /dev/null @@ -1,28 +0,0 @@ - \ No newline at end of file From 7430eb46094238309ceafe1b36557d02332456b3 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Fri, 15 May 2026 19:40:49 -0400 Subject: [PATCH 04/37] Adds UserProgram entity and refines Site Summary display logic Introduces the `UserProgram` entity to enable tracking of user associations with specific organizational units or programs, a foundational step for advanced search capabilities on the dashboard. Also, adjusts the conditional logic in `SiteSummaryHelper` to ensure drinking water well information is displayed only when relevant to the groundwater score. Part of issue #954. --- FMS.Domain/Entities/Users/UserProgram.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 FMS.Domain/Entities/Users/UserProgram.cs diff --git a/FMS.Domain/Entities/Users/UserProgram.cs b/FMS.Domain/Entities/Users/UserProgram.cs new file mode 100644 index 00000000..89560447 --- /dev/null +++ b/FMS.Domain/Entities/Users/UserProgram.cs @@ -0,0 +1,20 @@ +using FMS.Domain.Entities.Base; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Domain.Entities.Users +{ + public class UserProgram : BaseActiveModel + { + public UserProgram() { } + + public UserProgram(int id) { } + + public Guid UserId { get; set; } + + public Guid OrgUnitId { get; set; } + + public string Position { get; set; } + } +} From 3ba22126f35562db1b8674001201ead015254c9d Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Wed, 20 May 2026 09:44:46 -0400 Subject: [PATCH 05/37] Adds structured Dashboard UI and user program settings Replaces the placeholder Dashboard page with a more comprehensive layout including collapsible sections for current facilities, events, and data maintenance. The associated backend logic is prepared to fetch user-specific data. Also introduces an `IXmlSerializable Settings` property to the `UserProgram` entity, enabling the storage of user-specific configurations for their associated programs, which can be leveraged by the dashboard. Part of issue #954. --- FMS.Domain/Entities/Users/UserProgram.cs | 3 ++ FMS/Pages/Dashboard/Index.cshtml | 69 ++++++++++++++++++++++-- FMS/Pages/Dashboard/Index.cshtml.cs | 25 ++++++++- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/FMS.Domain/Entities/Users/UserProgram.cs b/FMS.Domain/Entities/Users/UserProgram.cs index 89560447..b2f42e1a 100644 --- a/FMS.Domain/Entities/Users/UserProgram.cs +++ b/FMS.Domain/Entities/Users/UserProgram.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Xml.Serialization; namespace FMS.Domain.Entities.Users { @@ -16,5 +17,7 @@ public UserProgram(int id) { } public Guid OrgUnitId { get; set; } public string Position { get; set; } + + public IXmlSerializable Settings { get; set; } } } diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 67ad8caf..2f915512 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -3,8 +3,71 @@ @model FMS.Pages.Dashboard.IndexModel @{ - ViewData["Title"] = $"User Dashboard"; + ViewData["Title"] = $"{Model.UserName}'s Dashboard"; } -

@ViewData["Title"]

+

@ViewData["Title"]

-

User Dashboard Page

+
+
+
+

+ +

+
+
+
+
+
+

Facility Info

+
+
+
+
+
+
+
+
+ +
+
+
+

+ +

+
+
+
+
+

Event Info

+
+
+
+
+
+
+
+ +
+
+
+

+ +

+
+
+
+
+

Data Maintenance Info

+
+
+
+
+
+
+
\ No newline at end of file diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index 1a959174..a1844f1c 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -1,12 +1,33 @@ +using FMS.Domain.Entities; +using FMS.Domain.Repositories; +using FMS.Domain.Services; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace FMS.Pages.Dashboard { - public class IndexModel : PageModel + public class IndexModel( + IAuthorizationService authorization, + IUserService userService, + IFacilityRepository _repository) : PageModel { - public void OnGet() + public UserView CurrentUser { get; private set; } + + public string UserName { get; set; } + + public IList UserFacilities { get; set; } + + public async Task OnGetAsync() { + if (User.Identity is not { IsAuthenticated: true }) + return Challenge(); + + UserName = await userService.GetCurrentUserAsync() is UserView user ? user.DisplayName : "User"; + + //UserFacilities = await _repository.GetFacilitiesForUserAsync(user.Id); + + return Page(); } } } From 5e224a432d75060c19bb2215230f4ec307f9ac9f Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 26 May 2026 14:57:11 -0400 Subject: [PATCH 06/37] Adds dashboard reporting fields to EventType and standardizes map search parameters Introduces `ReportEligible` and `Points` to the `EventType` entity to support future dashboard reporting and scoring functionalities. Refactors the facility map page to use a single `[BindProperty]` `Spec` object for both displaying and exporting map search results. This simplifies parameter handling and ensures consistency between the on-screen view and exported data. Part of issue #954. --- FMS.Domain/Entities/EventType.cs | 4 ++++ FMS/Pages/Facilities/Map.cshtml.cs | 12 +++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/FMS.Domain/Entities/EventType.cs b/FMS.Domain/Entities/EventType.cs index a0f436e8..eb39dd79 100644 --- a/FMS.Domain/Entities/EventType.cs +++ b/FMS.Domain/Entities/EventType.cs @@ -21,6 +21,10 @@ public EventType(EventTypeEditDto eventType) public string Name { get; set; } + public bool ReportEligible { get; set; } = false; + + public int Points { get; set; } = 0; + public void TrimAll() { Name = Name?.Trim(); diff --git a/FMS/Pages/Facilities/Map.cshtml.cs b/FMS/Pages/Facilities/Map.cshtml.cs index 982a26f2..497552c3 100644 --- a/FMS/Pages/Facilities/Map.cshtml.cs +++ b/FMS/Pages/Facilities/Map.cshtml.cs @@ -13,6 +13,7 @@ public class MapModel : PageModel private readonly ISelectListHelper _listHelper; // "Spec" is the Facility DTO bound to the HTML Page elements + [BindProperty] public FacilityMapSpec Spec { get; set; } // List of facilities returned from the search @@ -116,14 +117,7 @@ public async Task OnGetSearchAsync(FacilityMapSpec spec) ShowNone = true; } - ExportSpec = new FacilityMapSpec() - { - Latitude = spec.Latitude, - Longitude = spec.Longitude, - ShowDeleted = spec.ShowDeleted, - Radius = spec.Radius, - FacilityTypeId = spec.FacilityTypeId - }; + Spec = spec; } else @@ -140,7 +134,7 @@ public async Task OnPostAsync() { var fileName = $"FMS_Map_export{DateTime.Now:yyyy-MM-dd.HH-mm-ss.FFF}.xlsx"; - IReadOnlyList facilityMapSummaries = await _repository.GetFacilityListAsync(ExportSpec); + IReadOnlyList facilityMapSummaries = await _repository.GetFacilityListAsync(Spec); var facilityMapDetail = from p in facilityMapSummaries select new FacilityMapSummaryDtoScalar(p); return File(facilityMapDetail.ExportExcelAsByteArray(ExportHelper.ReportType.Map), "application/vnd.ms.excel", fileName); } From b8c8cf09f56095380ecb7bc8c97058bc1aba0562 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 26 May 2026 16:27:45 -0400 Subject: [PATCH 07/37] Add UserPosition entity and refactor UserProgram to use it Introduces the `UserPosition` entity along with its DTOs, repository, and database context integration, enabling standardized management of user roles and positions. Refactors the `UserProgram` entity to replace the free-text `Position` string with a `PositionId` (Guid), establishing a foreign key relationship to the new `UserPosition` entity. This provides structured data foundational for advanced search capabilities on the dashboard. Part of issue #954. --- .../Dto/UserPosition/UserPositionCreateDto.cs | 14 ++ .../Dto/UserPosition/UserPositionEditDto.cs | 30 +++++ .../Dto/UserPosition/UserPostionSummaryDto.cs | 20 +++ FMS.Domain/Entities/UserPosition.cs | 25 ++++ FMS.Domain/Entities/Users/UserProgram.cs | 4 +- .../Repositories/IUserPositionRepository.cs | 24 ++++ FMS.Infrastructure/Contexts/FmsDbContext.cs | 3 +- .../Repositories/ParcelTypeRepository.cs | 8 +- .../Repositories/UserPositionRepository.cs | 127 ++++++++++++++++++ .../Maintenance/ParcelType/Add.cshtml.cs | 2 +- .../Maintenance/ParcelType/Edit.cshtml.cs | 2 +- .../Maintenance/ParcelType/Index.cshtml.cs | 2 +- FMS/Startup.cs | 1 + .../ParcelTypeRepositoryTests.cs | 2 +- 14 files changed, 253 insertions(+), 11 deletions(-) create mode 100644 FMS.Domain/Dto/UserPosition/UserPositionCreateDto.cs create mode 100644 FMS.Domain/Dto/UserPosition/UserPositionEditDto.cs create mode 100644 FMS.Domain/Dto/UserPosition/UserPostionSummaryDto.cs create mode 100644 FMS.Domain/Entities/UserPosition.cs create mode 100644 FMS.Domain/Repositories/IUserPositionRepository.cs create mode 100644 FMS.Infrastructure/Repositories/UserPositionRepository.cs diff --git a/FMS.Domain/Dto/UserPosition/UserPositionCreateDto.cs b/FMS.Domain/Dto/UserPosition/UserPositionCreateDto.cs new file mode 100644 index 00000000..2f8dcfa8 --- /dev/null +++ b/FMS.Domain/Dto/UserPosition/UserPositionCreateDto.cs @@ -0,0 +1,14 @@ +namespace FMS.Domain.Dto +{ + public class UserPositionCreateDto + { + [Display(Name = "User Position")] + [Required(ErrorMessage = "User Position Name is required.")] + public string Name { get; set; } + + public void TrimAll() + { + Name = Name?.Trim(); + } + } +} diff --git a/FMS.Domain/Dto/UserPosition/UserPositionEditDto.cs b/FMS.Domain/Dto/UserPosition/UserPositionEditDto.cs new file mode 100644 index 00000000..f5a5b553 --- /dev/null +++ b/FMS.Domain/Dto/UserPosition/UserPositionEditDto.cs @@ -0,0 +1,30 @@ +using FMS.Domain.Entities; + +namespace FMS.Domain.Dto +{ + public class UserPositionEditDto + { + public UserPositionEditDto() { } + + public UserPositionEditDto(UserPosition userPosition) + { + Id = userPosition.Id; + Name = userPosition.Name; + Active = userPosition.Active; + } + + public Guid Id { get; set; } + + [Display(Name = "User Position")] + [Required(ErrorMessage = "User Position Name is required.")] + public string Name { get; set; } + + [Display(Name = "Is Active")] + public bool Active { get; set; } + + public void TrimAll() + { + Name = Name?.Trim(); + } + } +} diff --git a/FMS.Domain/Dto/UserPosition/UserPostionSummaryDto.cs b/FMS.Domain/Dto/UserPosition/UserPostionSummaryDto.cs new file mode 100644 index 00000000..562797bc --- /dev/null +++ b/FMS.Domain/Dto/UserPosition/UserPostionSummaryDto.cs @@ -0,0 +1,20 @@ +using FMS.Domain.Entities; + +namespace FMS.Domain.Dto +{ + public class UserPositionSummaryDto + { + public UserPositionSummaryDto(UserPosition userPosition) + { + Id = userPosition.Id; + Active = userPosition.Active; + Name = userPosition.Name; + } + + public Guid Id { get; } + + public bool Active { get; } + + public string Name { get; set; } + } +} diff --git a/FMS.Domain/Entities/UserPosition.cs b/FMS.Domain/Entities/UserPosition.cs new file mode 100644 index 00000000..baea2ff7 --- /dev/null +++ b/FMS.Domain/Entities/UserPosition.cs @@ -0,0 +1,25 @@ +using FMS.Domain.Dto; +using FMS.Domain.Entities.Base; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Domain.Entities +{ + public class UserPosition : BaseActiveModel, INamedModel + { + public UserPosition() { } + + public UserPosition(UserPositionCreateDto userPosition) + { + Name = userPosition.Name; + } + + public string Name { get; set; } + + public void TrimAll() + { + Name = Name?.Trim(); + } + } +} diff --git a/FMS.Domain/Entities/Users/UserProgram.cs b/FMS.Domain/Entities/Users/UserProgram.cs index b2f42e1a..44d11d8c 100644 --- a/FMS.Domain/Entities/Users/UserProgram.cs +++ b/FMS.Domain/Entities/Users/UserProgram.cs @@ -14,9 +14,9 @@ public UserProgram(int id) { } public Guid UserId { get; set; } - public Guid OrgUnitId { get; set; } + public OrganizationalUnit OrgUnit { get; set; } - public string Position { get; set; } + public Guid PositionId { get; set; } public IXmlSerializable Settings { get; set; } } diff --git a/FMS.Domain/Repositories/IUserPositionRepository.cs b/FMS.Domain/Repositories/IUserPositionRepository.cs new file mode 100644 index 00000000..5ec58757 --- /dev/null +++ b/FMS.Domain/Repositories/IUserPositionRepository.cs @@ -0,0 +1,24 @@ +using FMS.Domain.Dto; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Domain.Repositories +{ + public interface IUserPositionRepository : IDisposable + { + Task UserPositionExistsAsync(Guid id); + + Task UserPositionNameExistsAsync(string name, Guid? ignoreId = null); + + Task GetUserPositionAsync(Guid id); + + Task> GetUserPositionListAsync(); + + Task CreateUserPositionAsync(UserPositionCreateDto userPosition); + + Task UpdateUserPositionAsync(Guid Id, UserPositionEditDto userPositionUpdates); + + Task UpdateUserPositionStatusAsync(Guid id, bool active); + } +} diff --git a/FMS.Infrastructure/Contexts/FmsDbContext.cs b/FMS.Infrastructure/Contexts/FmsDbContext.cs index fa236a1b..ead2ade2 100644 --- a/FMS.Infrastructure/Contexts/FmsDbContext.cs +++ b/FMS.Infrastructure/Contexts/FmsDbContext.cs @@ -55,7 +55,8 @@ public FmsDbContext(DbContextOptions options, public DbSet GroundwaterScores { get; set; } public DbSet Substances { get; set; } public DbSet Statuses { get; set; } - + public DbSet UserPrograms { get; set; } + public DbSet UserPositions { get; set; } // The "Counties" table is only used to add County data to the database for database-side use. diff --git a/FMS.Infrastructure/Repositories/ParcelTypeRepository.cs b/FMS.Infrastructure/Repositories/ParcelTypeRepository.cs index 18f3e5e5..259255db 100644 --- a/FMS.Infrastructure/Repositories/ParcelTypeRepository.cs +++ b/FMS.Infrastructure/Repositories/ParcelTypeRepository.cs @@ -12,11 +12,11 @@ public class ParcelTypeRepository : IParcelTypeRepository private readonly FmsDbContext _context; public ParcelTypeRepository(FmsDbContext context) => _context = context; - public Task ParcelTypeExistsAsync(Guid id) => - _context.ParcelTypes.AnyAsync(e => e.Id == id); + public async Task ParcelTypeExistsAsync(Guid id) => + await _context.ParcelTypes.AnyAsync(e => e.Id == id); - public Task ParcelTypeNameExistsAsync(string name, Guid? ignoreId = null) => - _context.ParcelTypes.AnyAsync(e => e.Name == name && (!ignoreId.HasValue || e.Id != ignoreId.Value)); + public async Task ParcelTypeNameExistsAsync(string name, Guid? ignoreId = null) => + await _context.ParcelTypes.AnyAsync(e => e.Name == name && (!ignoreId.HasValue || e.Id != ignoreId.Value)); public async Task GetParcelTypeAsync(Guid id) { diff --git a/FMS.Infrastructure/Repositories/UserPositionRepository.cs b/FMS.Infrastructure/Repositories/UserPositionRepository.cs new file mode 100644 index 00000000..f2cb002e --- /dev/null +++ b/FMS.Infrastructure/Repositories/UserPositionRepository.cs @@ -0,0 +1,127 @@ +using FMS.Domain.Dto; +using FMS.Domain.Entities; +using FMS.Domain.Repositories; +using FMS.Domain.Utils; +using FMS.Infrastructure.Contexts; +using Microsoft.EntityFrameworkCore; + +namespace FMS.Infrastructure.Repositories +{ + public class UserPositionRepository : IUserPositionRepository + { + private readonly FmsDbContext _context; + public UserPositionRepository(FmsDbContext context) => _context = context; + + public async Task UserPositionExistsAsync(Guid id) => + await _context.UserPositions.AnyAsync(e => e.Id == id); + + public async Task UserPositionNameExistsAsync(string name, Guid? ignoreId = null) => + await _context.UserPositions.AnyAsync(e => e.Name == name && (!ignoreId.HasValue || e.Id != ignoreId.Value)); + + public async Task GetUserPositionAsync(Guid id){ + var userPosition = await _context.UserPositions.AsNoTracking() + .SingleOrDefaultAsync(e => e.Id == id); + + return userPosition == null ? null : new UserPositionEditDto(userPosition); + } + + public async Task> GetUserPositionListAsync() => + await _context.UserPositions.AsNoTracking() + .OrderByDescending(e => e.Active) + .ThenBy(e => e.Name) + .Select(e => new UserPositionSummaryDto(e)) + .ToListAsync(); + + public async Task CreateUserPositionAsync(UserPositionCreateDto userPosition) + { + Prevent.Null(userPosition, nameof(userPosition)); + Prevent.NullOrEmpty(userPosition.Name, nameof(userPosition.Name)); + + return await CreateUserPositionInternalAsync(userPosition); + } + + private async Task CreateUserPositionInternalAsync(UserPositionCreateDto userPosition) + { + if (await UserPositionNameExistsAsync(userPosition.Name)) + { + throw new ArgumentException($"User Position Name: {userPosition.Name} Already Exists."); + } + + var newUserPosition = new UserPosition(userPosition); + + await _context.UserPositions.AddAsync(newUserPosition); + await _context.SaveChangesAsync(); + return newUserPosition.Id; + } + + public async Task UpdateUserPositionAsync(Guid Id, UserPositionEditDto userPositionUpdates) + { + Prevent.Null(userPositionUpdates, nameof(userPositionUpdates)); + Prevent.NullOrEmpty(userPositionUpdates.Name, nameof(userPositionUpdates.Name)); + + if (!await UserPositionExistsAsync(Id)) + { + throw new ArgumentException($"User Position with Id {Id} does not exist."); + } + + if (await UserPositionNameExistsAsync(userPositionUpdates.Name, Id)) + { + throw new ArgumentException($"User Position Name: {userPositionUpdates.Name} Already Exists."); + } + var existingUserPosition = await _context.UserPositions.FindAsync(Id); + + existingUserPosition.Name = userPositionUpdates.Name; + + await _context.SaveChangesAsync(); + } + + public async Task UpdateUserPositionStatusAsync(Guid id, bool active) + { + if (!await UserPositionExistsAsync(id)) + { + throw new ArgumentException($"User Position with Id {id} does not exist."); + } + var existingUserPosition = await _context.UserPositions.FindAsync(id); + + existingUserPosition.Active = active; + + await _context.SaveChangesAsync(); + } + + + #region IDisposable Support + + private bool _disposedValue; + + protected virtual void Dispose(bool disposing) + { + if (_disposedValue) return; + + if (disposing) + { + // dispose managed state (managed objects) + _context.Dispose(); + } + + // free unmanaged resources (unmanaged objects) and override finalizer + // set large fields to null + _disposedValue = true; + } + + // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + ~UserPositionRepository() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + #endregion + } +} diff --git a/FMS/Pages/Maintenance/ParcelType/Add.cshtml.cs b/FMS/Pages/Maintenance/ParcelType/Add.cshtml.cs index 2d457dd4..e57b8609 100644 --- a/FMS/Pages/Maintenance/ParcelType/Add.cshtml.cs +++ b/FMS/Pages/Maintenance/ParcelType/Add.cshtml.cs @@ -15,7 +15,7 @@ public class AddModel : PageModel public AddModel(IParcelTypeRepository repository) => _repository = repository; [BindProperty] - public ParcelTypeCreateDto ParcelType { get; set; } + public UserPositionCreateDto ParcelType { get; set; } public void OnGet() { diff --git a/FMS/Pages/Maintenance/ParcelType/Edit.cshtml.cs b/FMS/Pages/Maintenance/ParcelType/Edit.cshtml.cs index fa965d52..736d969f 100644 --- a/FMS/Pages/Maintenance/ParcelType/Edit.cshtml.cs +++ b/FMS/Pages/Maintenance/ParcelType/Edit.cshtml.cs @@ -16,7 +16,7 @@ public class EditModel : PageModel public EditModel(IParcelTypeRepository repository) => _repository = repository; [BindProperty] - public ParcelTypeEditDto ParcelType { get; set; } + public UserPositionEditDto ParcelType { get; set; } [BindProperty] public Guid Id { get; set; } diff --git a/FMS/Pages/Maintenance/ParcelType/Index.cshtml.cs b/FMS/Pages/Maintenance/ParcelType/Index.cshtml.cs index 847da110..f6c7510e 100644 --- a/FMS/Pages/Maintenance/ParcelType/Index.cshtml.cs +++ b/FMS/Pages/Maintenance/ParcelType/Index.cshtml.cs @@ -15,7 +15,7 @@ public class IndexModel : PageModel private readonly IParcelTypeRepository _repository; public IndexModel(IParcelTypeRepository repository) => _repository = repository; - public IReadOnlyList ParcelTypes { get; private set; } + public IReadOnlyList ParcelTypes { get; private set; } public DisplayMessage DisplayMessage { get; private set; } public async Task OnGetAsync() diff --git a/FMS/Startup.cs b/FMS/Startup.cs index e49651d5..18cd0ee6 100644 --- a/FMS/Startup.cs +++ b/FMS/Startup.cs @@ -86,6 +86,7 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // Set up database services.AddHostedService(); diff --git a/tests/FMS.Infrastructure.Tests/ParcelTypeRepositoryTests.cs b/tests/FMS.Infrastructure.Tests/ParcelTypeRepositoryTests.cs index a611c95f..fb9df39b 100644 --- a/tests/FMS.Infrastructure.Tests/ParcelTypeRepositoryTests.cs +++ b/tests/FMS.Infrastructure.Tests/ParcelTypeRepositoryTests.cs @@ -99,7 +99,7 @@ public async Task GetParcelTypeListAsync_ReturnsAllParcelTypes() [Test] public async Task CreateParcelTypeAsync_CreateNewParcelType_WhenDataIsValid() { - var dto = new ParcelTypeCreateDto { Name = "VALID_PACRCELTYPE" }; + var dto = new UserPositionCreateDto { Name = "VALID_PACRCELTYPE" }; var newId = await _repository.CreateParcelTypeAsync(dto); var createdParcelType = await _context.ParcelTypes.FindAsync(newId); From 734e3db91d43d6660c2fb583019d7ef90bd5b46b Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 26 May 2026 19:39:32 -0400 Subject: [PATCH 08/37] Add standalone UserProgram entity and rename user relationship entity Introduces the `UserProgram` entity as a distinct, top-level domain concept, complete with its DTOs and dedicated repository for managing available programs. The existing `UserProgram` entity under `FMS.Domain/Entities/Users/` is renamed `UserInfo` and updated to hold explicit references to the new `UserProgram` and `UserPosition` entities. This clarifies the relationship between users, programs, and positions by separating the definition of a program from a user's specific association. Also corrects a typo in `UserPositionSummaryDto.cs`. Part of issue #954. --- ...ummaryDto.cs => UserPositionSummaryDto.cs} | 0 .../Dto/UserProgram/UserProgramCreateDto.cs | 14 ++ .../Dto/UserProgram/UserProgramEditDto.cs | 30 ++++ .../Dto/UserProgram/UserProgramSummaryDto.cs | 20 +++ FMS.Domain/Entities/UserProgram.cs | 22 +++ .../Users/{UserProgram.cs => UserInfo.cs} | 13 +- .../Repositories/IUserPositionRepository.cs | 3 - .../Repositories/IUserProgramRepository.cs | 21 +++ FMS.Infrastructure/Contexts/FmsDbContext.cs | 1 + .../Repositories/UserProgramRepository.cs | 128 ++++++++++++++++++ 10 files changed, 242 insertions(+), 10 deletions(-) rename FMS.Domain/Dto/UserPosition/{UserPostionSummaryDto.cs => UserPositionSummaryDto.cs} (100%) create mode 100644 FMS.Domain/Dto/UserProgram/UserProgramCreateDto.cs create mode 100644 FMS.Domain/Dto/UserProgram/UserProgramEditDto.cs create mode 100644 FMS.Domain/Dto/UserProgram/UserProgramSummaryDto.cs create mode 100644 FMS.Domain/Entities/UserProgram.cs rename FMS.Domain/Entities/Users/{UserProgram.cs => UserInfo.cs} (54%) create mode 100644 FMS.Domain/Repositories/IUserProgramRepository.cs create mode 100644 FMS.Infrastructure/Repositories/UserProgramRepository.cs diff --git a/FMS.Domain/Dto/UserPosition/UserPostionSummaryDto.cs b/FMS.Domain/Dto/UserPosition/UserPositionSummaryDto.cs similarity index 100% rename from FMS.Domain/Dto/UserPosition/UserPostionSummaryDto.cs rename to FMS.Domain/Dto/UserPosition/UserPositionSummaryDto.cs diff --git a/FMS.Domain/Dto/UserProgram/UserProgramCreateDto.cs b/FMS.Domain/Dto/UserProgram/UserProgramCreateDto.cs new file mode 100644 index 00000000..c329ecb7 --- /dev/null +++ b/FMS.Domain/Dto/UserProgram/UserProgramCreateDto.cs @@ -0,0 +1,14 @@ +namespace FMS.Domain.Dto +{ + public class UserProgramCreateDto + { + [Display(Name = "User Position")] + [Required(ErrorMessage = "User Position Name is required.")] + public string Name { get; set; } + + public void TrimAll() + { + Name = Name?.Trim(); + } + } +} diff --git a/FMS.Domain/Dto/UserProgram/UserProgramEditDto.cs b/FMS.Domain/Dto/UserProgram/UserProgramEditDto.cs new file mode 100644 index 00000000..c0bdafa3 --- /dev/null +++ b/FMS.Domain/Dto/UserProgram/UserProgramEditDto.cs @@ -0,0 +1,30 @@ +using FMS.Domain.Entities; + +namespace FMS.Domain.Dto +{ + public class UserProgramEditDto + { + public UserProgramEditDto() { } + + public UserProgramEditDto(UserProgram userProgram) + { + Id = userProgram.Id; + Name = userProgram.Name; + Active = userProgram.Active; + } + + public Guid Id { get; set; } + + [Display(Name = "User Program")] + [Required(ErrorMessage = "User Program Name is required.")] + public string Name { get; set; } + + [Display(Name = "Is Active")] + public bool Active { get; set; } + + public void TrimAll() + { + Name = Name?.Trim(); + } + } +} diff --git a/FMS.Domain/Dto/UserProgram/UserProgramSummaryDto.cs b/FMS.Domain/Dto/UserProgram/UserProgramSummaryDto.cs new file mode 100644 index 00000000..ecc029d0 --- /dev/null +++ b/FMS.Domain/Dto/UserProgram/UserProgramSummaryDto.cs @@ -0,0 +1,20 @@ +using FMS.Domain.Entities; + +namespace FMS.Domain.Dto +{ + public class UserProgramSummaryDto + { + public UserProgramSummaryDto(UserProgram userProgram) + { + Id = userProgram.Id; + Active = userProgram.Active; + Name = userProgram.Name; + } + + public Guid Id { get; } + + public bool Active { get; } + + public string Name { get; set; } + } +} diff --git a/FMS.Domain/Entities/UserProgram.cs b/FMS.Domain/Entities/UserProgram.cs new file mode 100644 index 00000000..e4667415 --- /dev/null +++ b/FMS.Domain/Entities/UserProgram.cs @@ -0,0 +1,22 @@ +using FMS.Domain.Dto; +using FMS.Domain.Entities.Base; + +namespace FMS.Domain.Entities +{ + public class UserProgram : BaseActiveModel, INamedModel + { + public UserProgram() { } + + public UserProgram(UserProgramCreateDto userProgram) + { + Name = userProgram.Name; + } + + public string Name { get; set; } + + public void TrimAll() + { + Name = Name?.Trim(); + } + } +} diff --git a/FMS.Domain/Entities/Users/UserProgram.cs b/FMS.Domain/Entities/Users/UserInfo.cs similarity index 54% rename from FMS.Domain/Entities/Users/UserProgram.cs rename to FMS.Domain/Entities/Users/UserInfo.cs index 44d11d8c..140611ee 100644 --- a/FMS.Domain/Entities/Users/UserProgram.cs +++ b/FMS.Domain/Entities/Users/UserInfo.cs @@ -1,22 +1,21 @@ using FMS.Domain.Entities.Base; -using System; -using System.Collections.Generic; -using System.Text; using System.Xml.Serialization; namespace FMS.Domain.Entities.Users { - public class UserProgram : BaseActiveModel + public class UserInfo : BaseActiveModel { - public UserProgram() { } + public UserInfo() { } - public UserProgram(int id) { } + public UserInfo(int id) { } public Guid UserId { get; set; } + public UserProgram UserProgram { get; set; } + public OrganizationalUnit OrgUnit { get; set; } - public Guid PositionId { get; set; } + public UserPosition Position { get; set; } public IXmlSerializable Settings { get; set; } } diff --git a/FMS.Domain/Repositories/IUserPositionRepository.cs b/FMS.Domain/Repositories/IUserPositionRepository.cs index 5ec58757..74550a11 100644 --- a/FMS.Domain/Repositories/IUserPositionRepository.cs +++ b/FMS.Domain/Repositories/IUserPositionRepository.cs @@ -1,7 +1,4 @@ using FMS.Domain.Dto; -using System; -using System.Collections.Generic; -using System.Text; namespace FMS.Domain.Repositories { diff --git a/FMS.Domain/Repositories/IUserProgramRepository.cs b/FMS.Domain/Repositories/IUserProgramRepository.cs new file mode 100644 index 00000000..eb535004 --- /dev/null +++ b/FMS.Domain/Repositories/IUserProgramRepository.cs @@ -0,0 +1,21 @@ +using FMS.Domain.Dto; + +namespace FMS.Domain.Repositories +{ + public interface IUserProgramRepository : IDisposable + { + Task UserProgramExistsAsync(Guid id); + + Task UserProgramNameExistsAsync(string name, Guid? ignoreId = null); + + Task GetUserProgramAsync(Guid id); + + Task> GetUserProgramListAsync(); + + Task CreateUserProgramAsync(UserProgramCreateDto userProgram); + + Task UpdateUserProgramAsync(Guid Id, UserProgramEditDto userProgramUpdates); + + Task UpdateUserProgramStatusAsync(Guid id, bool active); + } +} diff --git a/FMS.Infrastructure/Contexts/FmsDbContext.cs b/FMS.Infrastructure/Contexts/FmsDbContext.cs index ead2ade2..9cc15542 100644 --- a/FMS.Infrastructure/Contexts/FmsDbContext.cs +++ b/FMS.Infrastructure/Contexts/FmsDbContext.cs @@ -57,6 +57,7 @@ public FmsDbContext(DbContextOptions options, public DbSet Statuses { get; set; } public DbSet UserPrograms { get; set; } public DbSet UserPositions { get; set; } + public DbSet UserInfo { get; set; } // The "Counties" table is only used to add County data to the database for database-side use. diff --git a/FMS.Infrastructure/Repositories/UserProgramRepository.cs b/FMS.Infrastructure/Repositories/UserProgramRepository.cs new file mode 100644 index 00000000..7e7410bf --- /dev/null +++ b/FMS.Infrastructure/Repositories/UserProgramRepository.cs @@ -0,0 +1,128 @@ +using FMS.Domain.Dto; +using FMS.Domain.Entities; +using FMS.Domain.Repositories; +using FMS.Domain.Utils; +using FMS.Infrastructure.Contexts; +using Microsoft.EntityFrameworkCore; + +namespace FMS.Infrastructure.Repositories +{ + public class UserProgramRepository : IUserProgramRepository + { + private readonly FmsDbContext _context; + public UserProgramRepository(FmsDbContext context) => _context = context; + + public async Task UserProgramExistsAsync(Guid id) => + await _context.UserPrograms.AnyAsync(e => e.Id == id); + + public async Task UserProgramNameExistsAsync(string name, Guid? ignoreId = null) => + await _context.UserPrograms.AnyAsync(e => e.Name == name && (!ignoreId.HasValue || e.Id != ignoreId.Value)); + + public async Task GetUserProgramAsync(Guid id) + { + var userProgram = await _context.UserPrograms.AsNoTracking() + .SingleOrDefaultAsync(e => e.Id == id); + + return userProgram == null ? null : new UserProgramEditDto(userProgram); + } + + public async Task> GetUserProgramListAsync() => + await _context.UserPrograms.AsNoTracking() + .OrderByDescending(e => e.Active) + .ThenBy(e => e.Name) + .Select(e => new UserProgramSummaryDto(e)) + .ToListAsync(); + + public async Task CreateUserProgramAsync(UserProgramCreateDto userProgram) + { + Prevent.Null(userProgram, nameof(userProgram)); + Prevent.NullOrEmpty(userProgram.Name, nameof(userProgram.Name)); + + return await CreateUserProgramInternalAsync(userProgram); + } + + private async Task CreateUserProgramInternalAsync(UserProgramCreateDto userProgram) + { + if (await UserProgramNameExistsAsync(userProgram.Name)) + { + throw new ArgumentException($"User Program Name: {userProgram.Name} Already Exists."); + } + + var newUserProgram = new UserProgram(userProgram); + + await _context.UserPrograms.AddAsync(newUserProgram); + await _context.SaveChangesAsync(); + return newUserProgram.Id; + } + + public async Task UpdateUserProgramAsync(Guid Id, UserProgramEditDto userProgramUpdates) + { + Prevent.Null(userProgramUpdates, nameof(userProgramUpdates)); + Prevent.NullOrEmpty(userProgramUpdates.Name, nameof(userProgramUpdates.Name)); + + if (!await UserProgramExistsAsync(Id)) + { + throw new ArgumentException($"User Program with Id {Id} does not exist."); + } + + if (await UserProgramNameExistsAsync(userProgramUpdates.Name, Id)) + { + throw new ArgumentException($"User Program Name: {userProgramUpdates.Name} Already Exists."); + } + var existingUserProgram = await _context.UserPrograms.FindAsync(Id); + + existingUserProgram.Name = userProgramUpdates.Name; + + await _context.SaveChangesAsync(); + } + + public async Task UpdateUserProgramStatusAsync(Guid id, bool active) + { + if (!await UserProgramExistsAsync(id)) + { + throw new ArgumentException($"User Program with Id {id} does not exist."); + } + var existingUserProgram = await _context.UserPrograms.FindAsync(id); + + existingUserProgram.Active = active; + + await _context.SaveChangesAsync(); + } + + + #region IDisposable Support + + private bool _disposedValue; + + protected virtual void Dispose(bool disposing) + { + if (_disposedValue) return; + + if (disposing) + { + // dispose managed state (managed objects) + _context.Dispose(); + } + + // free unmanaged resources (unmanaged objects) and override finalizer + // set large fields to null + _disposedValue = true; + } + + // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + ~UserProgramRepository() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + #endregion + } +} From ef32b180a6b1218ebc923a8fd847898b5fc33352 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 26 May 2026 20:02:10 -0400 Subject: [PATCH 09/37] Enable maintenance UI for UserProgram and UserPosition entities Introduces basic Razor Pages and navigation links for managing `UserProgram` and `UserPosition` entities, facilitating their independent maintenance. The `IUserProgramRepository` is also registered for dependency injection. Corrects an error in `ParcelType` maintenance pages where `UserPosition` DTOs were mistakenly used instead of `ParcelType` DTOs. The recently introduced `UserInfo` entity definition and its `DbSet` entry are temporarily commented out from the domain and infrastructure layers. This indicates a pause or re-evaluation of its design and role in managing user-specific program and position relationships. Part of issue #954. --- FMS.Domain/Entities/Users/UserInfo.cs | 26 +++++++++---------- FMS.Infrastructure/Contexts/FmsDbContext.cs | 2 +- FMS.sln | 2 +- FMS/Pages/Maintenance/Index.cshtml | 6 +++++ FMS/Pages/Maintenance/MaintenanceOptions.cs | 2 ++ .../Maintenance/ParcelType/Add.cshtml.cs | 2 +- .../Maintenance/ParcelType/Edit.cshtml.cs | 2 +- .../Maintenance/ParcelType/Index.cshtml.cs | 2 +- FMS/Pages/Maintenance/UserPosition/Add.cshtml | 4 +++ .../Maintenance/UserPosition/Add.cshtml.cs | 12 +++++++++ .../Maintenance/UserPosition/Edit.cshtml | 4 +++ .../Maintenance/UserPosition/Edit.cshtml.cs | 12 +++++++++ .../Maintenance/UserPosition/Index.cshtml | 4 +++ .../Maintenance/UserPosition/Index.cshtml.cs | 12 +++++++++ FMS/Pages/Maintenance/UserProgram/Add.cshtml | 4 +++ .../Maintenance/UserProgram/Add.cshtml.cs | 12 +++++++++ FMS/Pages/Maintenance/UserProgram/Edit.cshtml | 4 +++ .../Maintenance/UserProgram/Edit.cshtml.cs | 12 +++++++++ .../Maintenance/UserProgram/Index.cshtml | 4 +++ .../Maintenance/UserProgram/Index.cshtml.cs | 12 +++++++++ FMS/Startup.cs | 2 ++ 21 files changed, 123 insertions(+), 19 deletions(-) create mode 100644 FMS/Pages/Maintenance/UserPosition/Add.cshtml create mode 100644 FMS/Pages/Maintenance/UserPosition/Add.cshtml.cs create mode 100644 FMS/Pages/Maintenance/UserPosition/Edit.cshtml create mode 100644 FMS/Pages/Maintenance/UserPosition/Edit.cshtml.cs create mode 100644 FMS/Pages/Maintenance/UserPosition/Index.cshtml create mode 100644 FMS/Pages/Maintenance/UserPosition/Index.cshtml.cs create mode 100644 FMS/Pages/Maintenance/UserProgram/Add.cshtml create mode 100644 FMS/Pages/Maintenance/UserProgram/Add.cshtml.cs create mode 100644 FMS/Pages/Maintenance/UserProgram/Edit.cshtml create mode 100644 FMS/Pages/Maintenance/UserProgram/Edit.cshtml.cs create mode 100644 FMS/Pages/Maintenance/UserProgram/Index.cshtml create mode 100644 FMS/Pages/Maintenance/UserProgram/Index.cshtml.cs diff --git a/FMS.Domain/Entities/Users/UserInfo.cs b/FMS.Domain/Entities/Users/UserInfo.cs index 140611ee..eeda56dd 100644 --- a/FMS.Domain/Entities/Users/UserInfo.cs +++ b/FMS.Domain/Entities/Users/UserInfo.cs @@ -1,22 +1,20 @@ using FMS.Domain.Entities.Base; using System.Xml.Serialization; -namespace FMS.Domain.Entities.Users -{ - public class UserInfo : BaseActiveModel - { - public UserInfo() { } +//namespace FMS.Domain.Entities.Users +//{ +// //public class UserInfo : BaseActiveModel +// //{ +// // public UserInfo() { } - public UserInfo(int id) { } +// // public UserInfo(int id) { } - public Guid UserId { get; set; } +// // public Guid UserId { get; set; } - public UserProgram UserProgram { get; set; } +// // public UserProgram UserProgram { get; set; } - public OrganizationalUnit OrgUnit { get; set; } +// // public OrganizationalUnit OrgUnit { get; set; } - public UserPosition Position { get; set; } - - public IXmlSerializable Settings { get; set; } - } -} +// // public UserPosition Position { get; set; } +// //} +//} diff --git a/FMS.Infrastructure/Contexts/FmsDbContext.cs b/FMS.Infrastructure/Contexts/FmsDbContext.cs index 9cc15542..b343d047 100644 --- a/FMS.Infrastructure/Contexts/FmsDbContext.cs +++ b/FMS.Infrastructure/Contexts/FmsDbContext.cs @@ -57,7 +57,7 @@ public FmsDbContext(DbContextOptions options, public DbSet Statuses { get; set; } public DbSet UserPrograms { get; set; } public DbSet UserPositions { get; set; } - public DbSet UserInfo { get; set; } + //public DbSet UserInfo { get; set; } // The "Counties" table is only used to add County data to the database for database-side use. diff --git a/FMS.sln b/FMS.sln index 707b971d..57bb305f 100644 --- a/FMS.sln +++ b/FMS.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 -VisualStudioVersion = 18.5.11709.299 stable +VisualStudioVersion = 18.5.11709.299 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FMS", "FMS\FMS.csproj", "{67EEBCCE-ACDD-435B-926B-1A2620D248C5}" ProjectSection(ProjectDependencies) = postProject diff --git a/FMS/Pages/Maintenance/Index.cshtml b/FMS/Pages/Maintenance/Index.cshtml index 399a3341..b8b6d46f 100644 --- a/FMS/Pages/Maintenance/Index.cshtml +++ b/FMS/Pages/Maintenance/Index.cshtml @@ -24,6 +24,12 @@
  • @MaintenanceOptions.OrganizationalUnit
  • +
  • + @MaintenanceOptions.UserProgram +
  • +
  • + @MaintenanceOptions.UserPosition +
  • HSI drop-down menu list

    diff --git a/FMS/Pages/Maintenance/MaintenanceOptions.cs b/FMS/Pages/Maintenance/MaintenanceOptions.cs index 80069b6a..99fa8d24 100644 --- a/FMS/Pages/Maintenance/MaintenanceOptions.cs +++ b/FMS/Pages/Maintenance/MaintenanceOptions.cs @@ -24,5 +24,7 @@ public static class MaintenanceOptions public const string GapsAssessment = "GAPS Assessment"; public const string LocationClass = "Location Class"; public const string EventContractor = "Event Contractor"; + public const string UserProgram = "User Program"; + public const string UserPosition = "User Position"; } } \ No newline at end of file diff --git a/FMS/Pages/Maintenance/ParcelType/Add.cshtml.cs b/FMS/Pages/Maintenance/ParcelType/Add.cshtml.cs index e57b8609..2d457dd4 100644 --- a/FMS/Pages/Maintenance/ParcelType/Add.cshtml.cs +++ b/FMS/Pages/Maintenance/ParcelType/Add.cshtml.cs @@ -15,7 +15,7 @@ public class AddModel : PageModel public AddModel(IParcelTypeRepository repository) => _repository = repository; [BindProperty] - public UserPositionCreateDto ParcelType { get; set; } + public ParcelTypeCreateDto ParcelType { get; set; } public void OnGet() { diff --git a/FMS/Pages/Maintenance/ParcelType/Edit.cshtml.cs b/FMS/Pages/Maintenance/ParcelType/Edit.cshtml.cs index 736d969f..fa965d52 100644 --- a/FMS/Pages/Maintenance/ParcelType/Edit.cshtml.cs +++ b/FMS/Pages/Maintenance/ParcelType/Edit.cshtml.cs @@ -16,7 +16,7 @@ public class EditModel : PageModel public EditModel(IParcelTypeRepository repository) => _repository = repository; [BindProperty] - public UserPositionEditDto ParcelType { get; set; } + public ParcelTypeEditDto ParcelType { get; set; } [BindProperty] public Guid Id { get; set; } diff --git a/FMS/Pages/Maintenance/ParcelType/Index.cshtml.cs b/FMS/Pages/Maintenance/ParcelType/Index.cshtml.cs index f6c7510e..847da110 100644 --- a/FMS/Pages/Maintenance/ParcelType/Index.cshtml.cs +++ b/FMS/Pages/Maintenance/ParcelType/Index.cshtml.cs @@ -15,7 +15,7 @@ public class IndexModel : PageModel private readonly IParcelTypeRepository _repository; public IndexModel(IParcelTypeRepository repository) => _repository = repository; - public IReadOnlyList ParcelTypes { get; private set; } + public IReadOnlyList ParcelTypes { get; private set; } public DisplayMessage DisplayMessage { get; private set; } public async Task OnGetAsync() diff --git a/FMS/Pages/Maintenance/UserPosition/Add.cshtml b/FMS/Pages/Maintenance/UserPosition/Add.cshtml new file mode 100644 index 00000000..62fc2d72 --- /dev/null +++ b/FMS/Pages/Maintenance/UserPosition/Add.cshtml @@ -0,0 +1,4 @@ +@page +@model FMS.Pages.Maintenance.UserPosition.AddModel +@{ +} diff --git a/FMS/Pages/Maintenance/UserPosition/Add.cshtml.cs b/FMS/Pages/Maintenance/UserPosition/Add.cshtml.cs new file mode 100644 index 00000000..f0cc7e8b --- /dev/null +++ b/FMS/Pages/Maintenance/UserPosition/Add.cshtml.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace FMS.Pages.Maintenance.UserPosition +{ + public class AddModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/FMS/Pages/Maintenance/UserPosition/Edit.cshtml b/FMS/Pages/Maintenance/UserPosition/Edit.cshtml new file mode 100644 index 00000000..3c804ed3 --- /dev/null +++ b/FMS/Pages/Maintenance/UserPosition/Edit.cshtml @@ -0,0 +1,4 @@ +@page +@model FMS.Pages.Maintenance.UserPosition.EditModel +@{ +} diff --git a/FMS/Pages/Maintenance/UserPosition/Edit.cshtml.cs b/FMS/Pages/Maintenance/UserPosition/Edit.cshtml.cs new file mode 100644 index 00000000..8c7c71f8 --- /dev/null +++ b/FMS/Pages/Maintenance/UserPosition/Edit.cshtml.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace FMS.Pages.Maintenance.UserPosition +{ + public class EditModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/FMS/Pages/Maintenance/UserPosition/Index.cshtml b/FMS/Pages/Maintenance/UserPosition/Index.cshtml new file mode 100644 index 00000000..8e63721f --- /dev/null +++ b/FMS/Pages/Maintenance/UserPosition/Index.cshtml @@ -0,0 +1,4 @@ +@page +@model FMS.Pages.Maintenance.UserPosition.IndexModel +@{ +} diff --git a/FMS/Pages/Maintenance/UserPosition/Index.cshtml.cs b/FMS/Pages/Maintenance/UserPosition/Index.cshtml.cs new file mode 100644 index 00000000..c497cc58 --- /dev/null +++ b/FMS/Pages/Maintenance/UserPosition/Index.cshtml.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace FMS.Pages.Maintenance.UserPosition +{ + public class IndexModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/FMS/Pages/Maintenance/UserProgram/Add.cshtml b/FMS/Pages/Maintenance/UserProgram/Add.cshtml new file mode 100644 index 00000000..18725515 --- /dev/null +++ b/FMS/Pages/Maintenance/UserProgram/Add.cshtml @@ -0,0 +1,4 @@ +@page +@model FMS.Pages.Maintenance.UserProgram.AddModel +@{ +} diff --git a/FMS/Pages/Maintenance/UserProgram/Add.cshtml.cs b/FMS/Pages/Maintenance/UserProgram/Add.cshtml.cs new file mode 100644 index 00000000..ed4a4515 --- /dev/null +++ b/FMS/Pages/Maintenance/UserProgram/Add.cshtml.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace FMS.Pages.Maintenance.UserProgram +{ + public class AddModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/FMS/Pages/Maintenance/UserProgram/Edit.cshtml b/FMS/Pages/Maintenance/UserProgram/Edit.cshtml new file mode 100644 index 00000000..27a8a993 --- /dev/null +++ b/FMS/Pages/Maintenance/UserProgram/Edit.cshtml @@ -0,0 +1,4 @@ +@page +@model FMS.Pages.Maintenance.UserProgram.EditModel +@{ +} diff --git a/FMS/Pages/Maintenance/UserProgram/Edit.cshtml.cs b/FMS/Pages/Maintenance/UserProgram/Edit.cshtml.cs new file mode 100644 index 00000000..e2557124 --- /dev/null +++ b/FMS/Pages/Maintenance/UserProgram/Edit.cshtml.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace FMS.Pages.Maintenance.UserProgram +{ + public class EditModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/FMS/Pages/Maintenance/UserProgram/Index.cshtml b/FMS/Pages/Maintenance/UserProgram/Index.cshtml new file mode 100644 index 00000000..64047694 --- /dev/null +++ b/FMS/Pages/Maintenance/UserProgram/Index.cshtml @@ -0,0 +1,4 @@ +@page +@model FMS.Pages.Maintenance.UserProgram.IndexModel +@{ +} diff --git a/FMS/Pages/Maintenance/UserProgram/Index.cshtml.cs b/FMS/Pages/Maintenance/UserProgram/Index.cshtml.cs new file mode 100644 index 00000000..95e60665 --- /dev/null +++ b/FMS/Pages/Maintenance/UserProgram/Index.cshtml.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace FMS.Pages.Maintenance.UserProgram +{ + public class IndexModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/FMS/Startup.cs b/FMS/Startup.cs index 18cd0ee6..a2b64fdc 100644 --- a/FMS/Startup.cs +++ b/FMS/Startup.cs @@ -87,6 +87,8 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + //services.AddScoped(); + services.AddScoped(); // Set up database services.AddHostedService(); From bd7b619b664c5679ee8597c229ea693dd82223a1 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Thu, 28 May 2026 11:23:17 -0400 Subject: [PATCH 10/37] Enable maintenance UI for UserProgram and UserPosition entities The `UserInfo` entity, previously temporarily commented out, is now fully removed from the domain. This commit also applies the database migration to persist the `UserProgram` and `UserPosition` entities, along with the new `Points` and `ReportEligible` fields for `EventType`, which reflect earlier domain model changes. Finally, the complete Razor Pages for `UserPosition` maintenance are implemented, providing user interface for CRUD and status management. Part of issue #954. --- FMS.Domain/Entities/Users/UserInfo.cs | 20 - ...7133503_UserProgramAndPosition.Designer.cs | 3280 +++++++++++++++++ .../20260527133503_UserProgramAndPosition.cs | 81 + .../Migrations/FmsDbContextModelSnapshot.cs | 64 + .../Maintenance/UserPosition/Index.cshtml | 45 + .../Maintenance/UserPosition/Index.cshtml.cs | 60 +- 6 files changed, 3529 insertions(+), 21 deletions(-) delete mode 100644 FMS.Domain/Entities/Users/UserInfo.cs create mode 100644 FMS.Infrastructure/Migrations/20260527133503_UserProgramAndPosition.Designer.cs create mode 100644 FMS.Infrastructure/Migrations/20260527133503_UserProgramAndPosition.cs diff --git a/FMS.Domain/Entities/Users/UserInfo.cs b/FMS.Domain/Entities/Users/UserInfo.cs deleted file mode 100644 index eeda56dd..00000000 --- a/FMS.Domain/Entities/Users/UserInfo.cs +++ /dev/null @@ -1,20 +0,0 @@ -using FMS.Domain.Entities.Base; -using System.Xml.Serialization; - -//namespace FMS.Domain.Entities.Users -//{ -// //public class UserInfo : BaseActiveModel -// //{ -// // public UserInfo() { } - -// // public UserInfo(int id) { } - -// // public Guid UserId { get; set; } - -// // public UserProgram UserProgram { get; set; } - -// // public OrganizationalUnit OrgUnit { get; set; } - -// // public UserPosition Position { get; set; } -// //} -//} diff --git a/FMS.Infrastructure/Migrations/20260527133503_UserProgramAndPosition.Designer.cs b/FMS.Infrastructure/Migrations/20260527133503_UserProgramAndPosition.Designer.cs new file mode 100644 index 00000000..d968151a --- /dev/null +++ b/FMS.Infrastructure/Migrations/20260527133503_UserProgramAndPosition.Designer.cs @@ -0,0 +1,3280 @@ +// +using System; +using FMS.Infrastructure.Contexts; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace FMS.Infrastructure.Migrations +{ + [DbContext(typeof(FmsDbContext))] + [Migration("20260527133503_UserProgramAndPosition")] + partial class UserProgramAndPosition + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("FMS.Domain.Entities.AbandonedInactive", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AbandonedInactives"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ActionTaken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ActionsTaken"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.AllowedActionTaken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActionTakenId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("CompletionDateRequired") + .HasColumnType("bit"); + + b.Property("DueDateRequired") + .HasColumnType("bit"); + + b.Property("EventTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("StartDateRequired") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ActionTakenId"); + + b.HasIndex("EventTypeId"); + + b.ToTable("AllowedActionsTaken"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.BudgetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Code") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("ProjectNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("BudgetCodes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Cabinet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FirstFileLabel") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("Cabinets"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Chemical", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("CasNo") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemicalName") + .HasColumnType("nvarchar(max)"); + + b.Property("CommonName") + .HasColumnType("nvarchar(max)"); + + b.Property("FinalRc") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("MCLs") + .HasColumnType("nvarchar(max)"); + + b.Property("RQ") + .HasColumnType("nvarchar(max)"); + + b.Property("ToxValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Chemicals"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ComplianceOfficer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FamilyName") + .HasColumnType("nvarchar(max)"); + + b.Property("GivenName") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ComplianceOfficers"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("Company") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactTitle") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("FamilyName") + .HasColumnType("nvarchar(max)"); + + b.Property("GivenName") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("PostalCode") + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ContactTypeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Contacts"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ContactType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ContactTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.County", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Counties"); + + b.HasData( + new + { + Id = 131, + Name = "Appling" + }, + new + { + Id = 122, + Name = "Atkinson" + }, + new + { + Id = 110, + Name = "Bacon" + }, + new + { + Id = 105, + Name = "Baker" + }, + new + { + Id = 213, + Name = "Baldwin" + }, + new + { + Id = 179, + Name = "Banks" + }, + new + { + Id = 217, + Name = "Barrow" + }, + new + { + Id = 249, + Name = "Bartow" + }, + new + { + Id = 211, + Name = "Ben Hill" + }, + new + { + Id = 194, + Name = "Berrien" + }, + new + { + Id = 259, + Name = "Bibb" + }, + new + { + Id = 145, + Name = "Bleckley" + }, + new + { + Id = 108, + Name = "Brantley" + }, + new + { + Id = 158, + Name = "Brooks" + }, + new + { + Id = 111, + Name = "Bryan" + }, + new + { + Id = 159, + Name = "Bulloch" + }, + new + { + Id = 203, + Name = "Burke" + }, + new + { + Id = 188, + Name = "Butts" + }, + new + { + Id = 112, + Name = "Calhoun" + }, + new + { + Id = 169, + Name = "Camden" + }, + new + { + Id = 113, + Name = "Candler" + }, + new + { + Id = 241, + Name = "Carroll" + }, + new + { + Id = 225, + Name = "Catoosa" + }, + new + { + Id = 114, + Name = "Charlton" + }, + new + { + Id = 242, + Name = "Chatham" + }, + new + { + Id = 137, + Name = "Chattahoochee" + }, + new + { + Id = 174, + Name = "Chattooga" + }, + new + { + Id = 243, + Name = "Cherokee" + }, + new + { + Id = 252, + Name = "Clarke" + }, + new + { + Id = 104, + Name = "Clay" + }, + new + { + Id = 204, + Name = "Clayton" + }, + new + { + Id = 178, + Name = "Clinch" + }, + new + { + Id = 245, + Name = "Cobb" + }, + new + { + Id = 146, + Name = "Coffee" + }, + new + { + Id = 227, + Name = "Colquitt" + }, + new + { + Id = 232, + Name = "Columbia" + }, + new + { + Id = 212, + Name = "Cook" + }, + new + { + Id = 250, + Name = "Coweta" + }, + new + { + Id = 115, + Name = "Crawford" + }, + new + { + Id = 220, + Name = "Crisp" + }, + new + { + Id = 170, + Name = "Dade" + }, + new + { + Id = 180, + Name = "Dawson" + }, + new + { + Id = 244, + Name = "Decatur" + }, + new + { + Id = 218, + Name = "DeKalb" + }, + new + { + Id = 175, + Name = "Dodge" + }, + new + { + Id = 189, + Name = "Dooly" + }, + new + { + Id = 257, + Name = "Dougherty" + }, + new + { + Id = 253, + Name = "Douglas" + }, + new + { + Id = 195, + Name = "Early" + }, + new + { + Id = 103, + Name = "Echols" + }, + new + { + Id = 123, + Name = "Effingham" + }, + new + { + Id = 200, + Name = "Elbert" + }, + new + { + Id = 198, + Name = "Emanuel" + }, + new + { + Id = 109, + Name = "Evans" + }, + new + { + Id = 176, + Name = "Fannin" + }, + new + { + Id = 235, + Name = "Fayette" + }, + new + { + Id = 246, + Name = "Floyd" + }, + new + { + Id = 238, + Name = "Forsyth" + }, + new + { + Id = 201, + Name = "Franklin" + }, + new + { + Id = 261, + Name = "Fulton" + }, + new + { + Id = 165, + Name = "Gilmer" + }, + new + { + Id = 124, + Name = "Glascock" + }, + new + { + Id = 216, + Name = "Glynn" + }, + new + { + Id = 230, + Name = "Gordon" + }, + new + { + Id = 208, + Name = "Grady" + }, + new + { + Id = 171, + Name = "Greene" + }, + new + { + Id = 226, + Name = "Gwinnett" + }, + new + { + Id = 214, + Name = "Habersham" + }, + new + { + Id = 256, + Name = "Hall" + }, + new + { + Id = 107, + Name = "Hancock" + }, + new + { + Id = 209, + Name = "Haralson" + }, + new + { + Id = 138, + Name = "Harris" + }, + new + { + Id = 190, + Name = "Hart" + }, + new + { + Id = 152, + Name = "Heard" + }, + new + { + Id = 248, + Name = "Henry" + }, + new + { + Id = 239, + Name = "Houston" + }, + new + { + Id = 139, + Name = "Irwin" + }, + new + { + Id = 222, + Name = "Jackson" + }, + new + { + Id = 147, + Name = "Jasper" + }, + new + { + Id = 160, + Name = "Jeff Davis" + }, + new + { + Id = 199, + Name = "Jefferson" + }, + new + { + Id = 153, + Name = "Jenkins" + }, + new + { + Id = 140, + Name = "Johnson" + }, + new + { + Id = 161, + Name = "Jones" + }, + new + { + Id = 181, + Name = "Lamar" + }, + new + { + Id = 141, + Name = "Lanier" + }, + new + { + Id = 223, + Name = "Laurens" + }, + new + { + Id = 172, + Name = "Lee" + }, + new + { + Id = 148, + Name = "Liberty" + }, + new + { + Id = 125, + Name = "Lincoln" + }, + new + { + Id = 102, + Name = "Long" + }, + new + { + Id = 254, + Name = "Lowndes" + }, + new + { + Id = 192, + Name = "Lumpkin" + }, + new + { + Id = 182, + Name = "Macon" + }, + new + { + Id = 166, + Name = "Madison" + }, + new + { + Id = 133, + Name = "Marion" + }, + new + { + Id = 210, + Name = "McDuffie" + }, + new + { + Id = 126, + Name = "McIntosh" + }, + new + { + Id = 196, + Name = "Meriwether" + }, + new + { + Id = 154, + Name = "Miller" + }, + new + { + Id = 215, + Name = "Mitchell" + }, + new + { + Id = 184, + Name = "Monroe" + }, + new + { + Id = 134, + Name = "Montgomery" + }, + new + { + Id = 197, + Name = "Morgan" + }, + new + { + Id = 205, + Name = "Murray" + }, + new + { + Id = 258, + Name = "Muscogee" + }, + new + { + Id = 231, + Name = "Newton" + }, + new + { + Id = 191, + Name = "Oconee" + }, + new + { + Id = 127, + Name = "Oglethorpe" + }, + new + { + Id = 219, + Name = "Paulding" + }, + new + { + Id = 221, + Name = "Peach" + }, + new + { + Id = 185, + Name = "Pickens" + }, + new + { + Id = 116, + Name = "Pierce" + }, + new + { + Id = 149, + Name = "Pike" + }, + new + { + Id = 233, + Name = "Polk" + }, + new + { + Id = 155, + Name = "Pulaski" + }, + new + { + Id = 183, + Name = "Putnam" + }, + new + { + Id = 100, + Name = "Quitman" + }, + new + { + Id = 167, + Name = "Rabun" + }, + new + { + Id = 156, + Name = "Randolph" + }, + new + { + Id = 260, + Name = "Richmond" + }, + new + { + Id = 251, + Name = "Rockdale" + }, + new + { + Id = 135, + Name = "Schley" + }, + new + { + Id = 157, + Name = "Screven" + }, + new + { + Id = 150, + Name = "Seminole" + }, + new + { + Id = 240, + Name = "Spalding" + }, + new + { + Id = 229, + Name = "Stephens" + }, + new + { + Id = 117, + Name = "Stewart" + }, + new + { + Id = 236, + Name = "Sumter" + }, + new + { + Id = 128, + Name = "Talbot" + }, + new + { + Id = 118, + Name = "Taliaferro" + }, + new + { + Id = 136, + Name = "Tattnall" + }, + new + { + Id = 142, + Name = "Taylor" + }, + new + { + Id = 164, + Name = "Telfair" + }, + new + { + Id = 173, + Name = "Terrell" + }, + new + { + Id = 234, + Name = "Thomas" + }, + new + { + Id = 237, + Name = "Tift" + }, + new + { + Id = 151, + Name = "Toombs" + }, + new + { + Id = 129, + Name = "Towns" + }, + new + { + Id = 101, + Name = "Treutlen" + }, + new + { + Id = 247, + Name = "Troup" + }, + new + { + Id = 186, + Name = "Turner" + }, + new + { + Id = 130, + Name = "Twiggs" + }, + new + { + Id = 168, + Name = "Union" + }, + new + { + Id = 202, + Name = "Upson" + }, + new + { + Id = 224, + Name = "Walker" + }, + new + { + Id = 228, + Name = "Walton" + }, + new + { + Id = 177, + Name = "Ware" + }, + new + { + Id = 162, + Name = "Warren" + }, + new + { + Id = 206, + Name = "Washington" + }, + new + { + Id = 143, + Name = "Wayne" + }, + new + { + Id = 120, + Name = "Webster" + }, + new + { + Id = 106, + Name = "Wheeler" + }, + new + { + Id = 193, + Name = "White" + }, + new + { + Id = 255, + Name = "Whitfield" + }, + new + { + Id = 144, + Name = "Wilcox" + }, + new + { + Id = 163, + Name = "Wilkes" + }, + new + { + Id = 207, + Name = "Wilkinson" + }, + new + { + Id = 187, + Name = "Worth" + }, + new + { + Id = 99, + Name = "OUT OF STATE" + }); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActionTakenId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comment") + .HasColumnType("nvarchar(max)"); + + b.Property("CompletionDate") + .HasColumnType("date"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("EventAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("EventContractorId") + .HasColumnType("uniqueidentifier"); + + b.Property("EventTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ActionTakenId"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("EventContractorId"); + + b.HasIndex("EventTypeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.EventContractor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("EventContractors"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.EventType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("ReportEligible") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("EventTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("AdditionalDataRequested") + .HasColumnType("bit"); + + b.Property("Address") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("BudgetCodeId") + .HasColumnType("uniqueidentifier"); + + b.Property("City") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("CountyId") + .HasColumnType("int"); + + b.Property("DeferredOnSiteScoring") + .HasColumnType("bit"); + + b.Property("DeterminationLetterDate") + .HasColumnType("date"); + + b.Property("FacilityNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileId") + .HasColumnType("uniqueidentifier"); + + b.Property("HSInumber") + .HasMaxLength(5) + .HasColumnType("nvarchar(5)"); + + b.Property("HasERecord") + .HasColumnType("bit"); + + b.Property("HistoricalComplianceOfficer") + .HasColumnType("nvarchar(max)"); + + b.Property("HistoricalUnit") + .HasColumnType("nvarchar(max)"); + + b.Property("ImageChecked") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("IsRetained") + .HasColumnType("bit"); + + b.Property("Latitude") + .HasColumnType("decimal(8, 6)"); + + b.Property("Location") + .HasColumnType("nvarchar(max)"); + + b.Property("Longitude") + .HasColumnType("decimal(9, 6)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationalUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("PreRQSMcleanup") + .HasColumnType("bit"); + + b.Property("RNDateReceived") + .HasColumnType("date"); + + b.Property("State") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("VRPReferral") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("Active"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Active"), new[] { "FileId" }); + + b.HasIndex("BudgetCodeId"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("CountyId"); + + b.HasIndex("FacilityStatusId"); + + b.HasIndex("FacilityTypeId"); + + b.HasIndex("FileId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("FileId"), new[] { "Active", "FacilityNumber", "FacilityTypeId", "OrganizationalUnitId", "BudgetCodeId", "Name", "ComplianceOfficerId", "FacilityStatusId", "Location", "Address", "City", "State", "PostalCode", "Latitude", "Longitude", "CountyId", "IsRetained", "AdditionalDataRequested", "Comments", "DeferredOnSiteScoring", "DeterminationLetterDate", "HSInumber", "HasERecord", "HistoricalComplianceOfficer", "HistoricalUnit", "ImageChecked", "PreRQSMcleanup", "RNDateReceived", "VRPReferral" }); + + b.HasIndex("OrganizationalUnitId"); + + b.HasIndex("Active", "FileId"); + + b.HasIndex("Active", "FacilityTypeId", "Address"); + + b.HasIndex("Active", "FacilityTypeId", "Address", "City"); + + b.ToTable("Facilities"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FacilityStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FacilityStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FacilityType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("FacilityTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.File", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FileLabel") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FileLabel") + .IsUnique() + .HasFilter("[FileLabel] IS NOT NULL"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FundingSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FundingSources"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GapsAssessment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GapsAssessments"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("A") + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("B1") + .HasColumnType("int"); + + b.Property("B2") + .HasColumnType("int"); + + b.Property("C") + .HasColumnType("int"); + + b.Property("CASNO") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemName") + .HasColumnType("nvarchar(max)"); + + b.Property("D2") + .HasColumnType("int"); + + b.Property("D3") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("E1") + .HasColumnType("int"); + + b.Property("E2") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("GWScore") + .HasColumnType("decimal(18,2)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Other") + .HasColumnType("nvarchar(max)"); + + b.Property("SubstanceId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("SubstanceId"); + + b.ToTable("GroundwaterScores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GroundwaterStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.HsrpFacilityProperties", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("BrownfieldDate") + .HasColumnType("date"); + + b.Property("BrownfieldTerminated") + .HasColumnType("bit"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("DateDeListed") + .HasColumnType("date"); + + b.Property("DateListed") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationalUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("VRPDate") + .HasColumnType("date"); + + b.Property("VRPTerminated") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("OrganizationalUnitId"); + + b.ToTable("HsrpFacilityProperties"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LocationClassId") + .HasColumnType("uniqueidentifier"); + + b.Property("MapType") + .HasColumnType("nvarchar(max)"); + + b.Property("MapZoom") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("LocationClassId"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.LocationClass", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("LocationClasses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OnsiteScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("A") + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("B") + .HasColumnType("int"); + + b.Property("C") + .HasColumnType("int"); + + b.Property("CASNO") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemName1D") + .HasColumnType("nvarchar(max)"); + + b.Property("D2") + .HasColumnType("int"); + + b.Property("D3") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("E1") + .HasColumnType("int"); + + b.Property("E2") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("OnsiteScoreValue") + .HasColumnType("decimal(18,2)"); + + b.Property("Other1D") + .HasColumnType("nvarchar(max)"); + + b.Property("SubstanceId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("SubstanceId"); + + b.ToTable("OnsiteScores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OrganizationalUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OrganizationalUnits"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OverallStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OverallStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Parcel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Acres") + .HasColumnType("float"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("DeListDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ListDate") + .HasColumnType("date"); + + b.Property("ParcelNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("ParcelTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("SubListParcelName") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("ParcelTypeId"); + + b.ToTable("Parcels"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ParcelType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ParcelTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Phone", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("ContactId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Number") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneType") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ContactId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Phones"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.RetentionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("BoxNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("ConsignmentNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("EndYear") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("RetentionSchedule") + .HasColumnType("nvarchar(max)"); + + b.Property("ShelfNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("FacilityId"), new[] { "Active", "StartYear", "EndYear", "ConsignmentNumber", "BoxNumber", "ShelfNumber", "RetentionSchedule" }); + + b.ToTable("RetentionRecords"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Score", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ScoredDate") + .HasColumnType("date"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UseComments") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.SoilStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SoilStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.SourceStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SourceStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Status", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AbandonedInactiveId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("CostEstimate") + .HasColumnType("decimal(18,2)"); + + b.Property("CostEstimateDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("FinancialAssurance") + .HasColumnType("bit"); + + b.Property("FundingSourceId") + .HasColumnType("uniqueidentifier"); + + b.Property("GAPSAssessmentId") + .HasColumnType("uniqueidentifier"); + + b.Property("GAPSModelDate") + .HasColumnType("date"); + + b.Property("GAPSNoOfUnknowns") + .HasColumnType("int"); + + b.Property("GAPSScore") + .HasColumnType("int"); + + b.Property("GroundwaterDate") + .HasColumnType("date"); + + b.Property("GroundwaterStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("ISWQS") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LandFill") + .HasColumnType("bit"); + + b.Property("Lien") + .HasColumnType("bit"); + + b.Property("OverallDate") + .HasColumnType("date"); + + b.Property("OverallStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("ReportComments") + .HasColumnType("nvarchar(max)"); + + b.Property("SoilDate") + .HasColumnType("date"); + + b.Property("SoilStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("SolidWastePermitNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("SourceDate") + .HasColumnType("date"); + + b.Property("SourceStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("UEC") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("AbandonedInactiveId"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("FundingSourceId"); + + b.HasIndex("GAPSAssessmentId"); + + b.HasIndex("GroundwaterStatusId"); + + b.HasIndex("OverallStatusId"); + + b.HasIndex("SoilStatusId"); + + b.HasIndex("SourceStatusId"); + + b.ToTable("Statuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Substance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("ChemicalId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("Groundwater") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Soil") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UseForGroundwaterScoring") + .HasColumnType("bit"); + + b.Property("UseForSoilScoring") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ChemicalId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Substances"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.UserPosition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("UserPositions"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.UserProgram", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("UserPrograms"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Users.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FamilyName") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("GivenName") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("MostRecentLogin") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ObjectId") + .HasMaxLength(36) + .HasColumnType("nvarchar(36)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("ProfileUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("ObjectId") + .IsUnique() + .HasFilter("[ObjectId] IS NOT NULL"); + + b.ToTable("AppUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityPasskeyData", b => + { + b.Property("AttestationObject") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("ClientDataJson") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("IsBackedUp") + .HasColumnType("bit"); + + b.Property("IsBackupEligible") + .HasColumnType("bit"); + + b.Property("IsUserVerified") + .HasColumnType("bit"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("SignCount") + .HasColumnType("bigint"); + + b.PrimitiveCollection("Transports") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.ToTable("IdentityPasskeyData"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AppRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AppRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AppUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AppUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AppUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AppUserTokens", (string)null); + }); + + modelBuilder.Entity("FMS.Domain.Entities.AllowedActionTaken", b => + { + b.HasOne("FMS.Domain.Entities.ActionTaken", "ActionTaken") + .WithMany() + .HasForeignKey("ActionTakenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.EventType", "EventType") + .WithMany() + .HasForeignKey("EventTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ActionTaken"); + + b.Navigation("EventType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.HasOne("FMS.Domain.Entities.ContactType", "ContactType") + .WithMany() + .HasForeignKey("ContactTypeId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Contacts") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ContactType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Event", b => + { + b.HasOne("FMS.Domain.Entities.ActionTaken", "ActionTaken") + .WithMany() + .HasForeignKey("ActionTakenId"); + + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.EventContractor", "EventContractor") + .WithMany() + .HasForeignKey("EventContractorId"); + + b.HasOne("FMS.Domain.Entities.EventType", "EventType") + .WithMany() + .HasForeignKey("EventTypeId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Events") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ActionTaken"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("EventContractor"); + + b.Navigation("EventType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.HasOne("FMS.Domain.Entities.BudgetCode", "BudgetCode") + .WithMany() + .HasForeignKey("BudgetCodeId"); + + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.County", "County") + .WithMany() + .HasForeignKey("CountyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.FacilityStatus", "FacilityStatus") + .WithMany() + .HasForeignKey("FacilityStatusId"); + + b.HasOne("FMS.Domain.Entities.FacilityType", "FacilityType") + .WithMany() + .HasForeignKey("FacilityTypeId"); + + b.HasOne("FMS.Domain.Entities.File", "File") + .WithMany("Facilities") + .HasForeignKey("FileId"); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "OrganizationalUnit") + .WithMany() + .HasForeignKey("OrganizationalUnitId"); + + b.Navigation("BudgetCode"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("County"); + + b.Navigation("FacilityStatus"); + + b.Navigation("FacilityType"); + + b.Navigation("File"); + + b.Navigation("OrganizationalUnit"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterScore", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("GroundwaterScoreDetails") + .HasForeignKey("FMS.Domain.Entities.GroundwaterScore", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Substance", "Substance") + .WithMany() + .HasForeignKey("SubstanceId"); + + b.Navigation("Substance"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.HsrpFacilityProperties", b => + { + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("HsrpFacilityProperties") + .HasForeignKey("FMS.Domain.Entities.HsrpFacilityProperties", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "OrganizationalUnit") + .WithMany() + .HasForeignKey("OrganizationalUnitId"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("OrganizationalUnit"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Location", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("LocationDetails") + .HasForeignKey("FMS.Domain.Entities.Location", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.LocationClass", "LocationClass") + .WithMany() + .HasForeignKey("LocationClassId"); + + b.Navigation("LocationClass"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OnsiteScore", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("OnsiteScoreDetails") + .HasForeignKey("FMS.Domain.Entities.OnsiteScore", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Substance", "Substance") + .WithMany() + .HasForeignKey("SubstanceId"); + + b.Navigation("Substance"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Parcel", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Parcels") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.ParcelType", "ParcelType") + .WithMany() + .HasForeignKey("ParcelTypeId"); + + b.Navigation("ParcelType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Phone", b => + { + b.HasOne("FMS.Domain.Entities.Contact", null) + .WithMany("Phones") + .HasForeignKey("ContactId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Phones") + .HasForeignKey("FacilityId"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.RetentionRecord", b => + { + b.HasOne("FMS.Domain.Entities.Facility", "Facility") + .WithMany("RetentionRecords") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Score", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("ScoreDetails") + .HasForeignKey("FMS.Domain.Entities.Score", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Status", b => + { + b.HasOne("FMS.Domain.Entities.AbandonedInactive", "AbandonedInactive") + .WithMany() + .HasForeignKey("AbandonedInactiveId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("StatusDetails") + .HasForeignKey("FMS.Domain.Entities.Status", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.FundingSource", "FundingSource") + .WithMany() + .HasForeignKey("FundingSourceId"); + + b.HasOne("FMS.Domain.Entities.GapsAssessment", "GAPSAssessment") + .WithMany() + .HasForeignKey("GAPSAssessmentId"); + + b.HasOne("FMS.Domain.Entities.GroundwaterStatus", "GroundwaterStatus") + .WithMany() + .HasForeignKey("GroundwaterStatusId"); + + b.HasOne("FMS.Domain.Entities.OverallStatus", "OverallStatus") + .WithMany() + .HasForeignKey("OverallStatusId"); + + b.HasOne("FMS.Domain.Entities.SoilStatus", "SoilStatus") + .WithMany() + .HasForeignKey("SoilStatusId"); + + b.HasOne("FMS.Domain.Entities.SourceStatus", "SourceStatus") + .WithMany() + .HasForeignKey("SourceStatusId"); + + b.Navigation("AbandonedInactive"); + + b.Navigation("FundingSource"); + + b.Navigation("GAPSAssessment"); + + b.Navigation("GroundwaterStatus"); + + b.Navigation("OverallStatus"); + + b.Navigation("SoilStatus"); + + b.Navigation("SourceStatus"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Substance", b => + { + b.HasOne("FMS.Domain.Entities.Chemical", "Chemical") + .WithMany() + .HasForeignKey("ChemicalId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Substances") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Chemical"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.Navigation("Phones"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.Navigation("Contacts"); + + b.Navigation("Events"); + + b.Navigation("GroundwaterScoreDetails"); + + b.Navigation("HsrpFacilityProperties"); + + b.Navigation("LocationDetails"); + + b.Navigation("OnsiteScoreDetails"); + + b.Navigation("Parcels"); + + b.Navigation("Phones"); + + b.Navigation("RetentionRecords"); + + b.Navigation("ScoreDetails"); + + b.Navigation("StatusDetails"); + + b.Navigation("Substances"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.File", b => + { + b.Navigation("Facilities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FMS.Infrastructure/Migrations/20260527133503_UserProgramAndPosition.cs b/FMS.Infrastructure/Migrations/20260527133503_UserProgramAndPosition.cs new file mode 100644 index 00000000..41938487 --- /dev/null +++ b/FMS.Infrastructure/Migrations/20260527133503_UserProgramAndPosition.cs @@ -0,0 +1,81 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace FMS.Infrastructure.Migrations +{ + /// + public partial class UserProgramAndPosition : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Points", + table: "EventTypes", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "ReportEligible", + table: "EventTypes", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateTable( + name: "UserPositions", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: true), + InsertDateTime = table.Column(type: "datetimeoffset", nullable: true), + InsertUser = table.Column(type: "nvarchar(max)", nullable: true), + UpdateDateTime = table.Column(type: "datetimeoffset", nullable: true), + UpdateUser = table.Column(type: "nvarchar(max)", nullable: true), + Active = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserPositions", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "UserPrograms", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: true), + InsertDateTime = table.Column(type: "datetimeoffset", nullable: true), + InsertUser = table.Column(type: "nvarchar(max)", nullable: true), + UpdateDateTime = table.Column(type: "datetimeoffset", nullable: true), + UpdateUser = table.Column(type: "nvarchar(max)", nullable: true), + Active = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserPrograms", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UserPositions"); + + migrationBuilder.DropTable( + name: "UserPrograms"); + + migrationBuilder.DropColumn( + name: "Points", + table: "EventTypes"); + + migrationBuilder.DropColumn( + name: "ReportEligible", + table: "EventTypes"); + } + } +} diff --git a/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs b/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs index d7aadf7a..26311356 100644 --- a/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs +++ b/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs @@ -1330,6 +1330,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("nvarchar(max)"); + b.Property("Points") + .HasColumnType("int"); + + b.Property("ReportEligible") + .HasColumnType("bit"); + b.Property("UpdateDateTime") .HasColumnType("datetimeoffset"); @@ -2471,6 +2477,64 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Substances"); }); + modelBuilder.Entity("FMS.Domain.Entities.UserPosition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("UserPositions"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.UserProgram", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("UserPrograms"); + }); + modelBuilder.Entity("FMS.Domain.Entities.Users.ApplicationUser", b => { b.Property("Id") diff --git a/FMS/Pages/Maintenance/UserPosition/Index.cshtml b/FMS/Pages/Maintenance/UserPosition/Index.cshtml index 8e63721f..115eb261 100644 --- a/FMS/Pages/Maintenance/UserPosition/Index.cshtml +++ b/FMS/Pages/Maintenance/UserPosition/Index.cshtml @@ -1,4 +1,49 @@ @page +@using FMS.Pages.Maintenance @model FMS.Pages.Maintenance.UserPosition.IndexModel +@section Scripts { + +} + @{ + ViewData["Title"] = $"Site Maintenance: {MaintenanceOptions.UserPosition}"; } + +← Site Maintenance +

    + @MaintenanceOptions.UserPosition Create New +

    + + +
    + + + + + + + + + @foreach (var item in Model.UserPositions) + { + + + + + } + +
    NameActions
    + @Html.DisplayFor(m => item.Name, "StringOrNone") + @Html.DisplayFor(m => item.Active, "BoolRemoved") + + Edit + @if (item.Active) + { + + } + else + { + + } +
    +
    \ No newline at end of file diff --git a/FMS/Pages/Maintenance/UserPosition/Index.cshtml.cs b/FMS/Pages/Maintenance/UserPosition/Index.cshtml.cs index c497cc58..a2688db6 100644 --- a/FMS/Pages/Maintenance/UserPosition/Index.cshtml.cs +++ b/FMS/Pages/Maintenance/UserPosition/Index.cshtml.cs @@ -1,12 +1,70 @@ +using FMS.Domain.Dto; +using FMS.Domain.Entities; +using FMS.Domain.Entities.Users; +using FMS.Domain.Repositories; +using FMS.Platform.Extensions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.EntityFrameworkCore; namespace FMS.Pages.Maintenance.UserPosition { + [Authorize(Roles = UserRoles.SiteMaintenance)] public class IndexModel : PageModel { - public void OnGet() + private readonly IUserPositionRepository _repository; + public IndexModel(IUserPositionRepository repository) => _repository = repository; + + public IReadOnlyList UserPositions { get; private set; } + public DisplayMessage DisplayMessage { get; private set; } + + public async Task OnGetAsync() + { + UserPositions = await _repository.GetUserPositionListAsync(); + DisplayMessage = TempData?.GetDisplayMessage(); + return Page(); + } + + public async Task OnPostAsync(Guid? itemId) { + if (itemId == null) + { + return BadRequest(); + } + + if (!ModelState.IsValid) + { + return Page(); + } + + var userPosition = await _repository.GetUserPositionAsync(itemId.Value); + + if (userPosition == null) + { + return NotFound(); + } + + try + { + await _repository.UpdateUserPositionStatusAsync(itemId.Value, !userPosition.Active); + } + catch (DbUpdateConcurrencyException) + { + if (!await _repository.UserPositionExistsAsync(itemId.Value)) + { + return NotFound(); + } + + throw; + } + + TempData?.SetDisplayMessage(Context.Success, + userPosition.Active + ? $"{MaintenanceOptions.UserPosition} \"{userPosition.Name}\" successfully removed from list." + : $"{MaintenanceOptions.UserPosition} \"{userPosition.Name}\" successfully restored."); + + return RedirectToPage("./Index"); } } } From ab356bab8eaebd860f45eaf3c9e5cb89c1534d7a Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Fri, 29 May 2026 18:04:26 -0400 Subject: [PATCH 11/37] Implement description field for UserProgram and UserPosition entities Adds a `Description` property to `UserProgram` and `UserPosition` entities, their DTOs, and associated repository methods. This includes a database migration to persist the new field, validation for unique descriptions, and full integration into the `Add`, `Edit`, and `Index` Razor Pages for comprehensive maintenance. This provides more context and detail for these records. Also adds a display attribute to `SoilStatusSummaryDto.Description`. Part of issue #954. --- .../Dto/SoilStatus/SoilStatusSummaryDto.cs | 1 + .../Dto/UserPosition/UserPositionCreateDto.cs | 5 + .../Dto/UserPosition/UserPositionEditDto.cs | 6 + .../UserPosition/UserPositionSummaryDto.cs | 6 + .../Dto/UserProgram/UserProgramCreateDto.cs | 9 +- .../Dto/UserProgram/UserProgramEditDto.cs | 6 + .../Dto/UserProgram/UserProgramSummaryDto.cs | 6 + FMS.Domain/Entities/UserPosition.cs | 4 + FMS.Domain/Entities/UserProgram.cs | 4 + .../Repositories/IUserPositionRepository.cs | 2 + .../Repositories/IUserProgramRepository.cs | 2 + FMS.Infrastructure/Contexts/FmsDbContext.cs | 2 +- ...iptionToUserProgramAndPosition.Designer.cs | 3286 +++++++++++++++++ ..._AddDescriptionToUserProgramAndPosition.cs | 38 + .../Migrations/FmsDbContextModelSnapshot.cs | 6 + .../Repositories/UserPositionRepository.cs | 9 + .../Repositories/UserProgramRepository.cs | 9 + FMS/Pages/Maintenance/UserPosition/Add.cshtml | 32 + .../Maintenance/UserPosition/Add.cshtml.cs | 46 + .../Maintenance/UserPosition/Edit.cshtml | 43 +- .../Maintenance/UserPosition/Edit.cshtml.cs | 77 +- .../Maintenance/UserPosition/Index.cshtml | 8 +- FMS/Pages/Maintenance/UserProgram/Add.cshtml | 32 + .../Maintenance/UserProgram/Add.cshtml.cs | 46 + FMS/Pages/Maintenance/UserProgram/Edit.cshtml | 43 +- .../Maintenance/UserProgram/Edit.cshtml.cs | 77 +- .../Maintenance/UserProgram/Index.cshtml | 49 + .../Maintenance/UserProgram/Index.cshtml.cs | 62 +- 28 files changed, 3905 insertions(+), 11 deletions(-) create mode 100644 FMS.Infrastructure/Migrations/20260529212605_AddDescriptionToUserProgramAndPosition.Designer.cs create mode 100644 FMS.Infrastructure/Migrations/20260529212605_AddDescriptionToUserProgramAndPosition.cs diff --git a/FMS.Domain/Dto/SoilStatus/SoilStatusSummaryDto.cs b/FMS.Domain/Dto/SoilStatus/SoilStatusSummaryDto.cs index 6de3de30..1ad5d91c 100644 --- a/FMS.Domain/Dto/SoilStatus/SoilStatusSummaryDto.cs +++ b/FMS.Domain/Dto/SoilStatus/SoilStatusSummaryDto.cs @@ -21,6 +21,7 @@ public SoilStatusSummaryDto(SoilStatus soilStatus) [Display(Name = "Soil Status")] public string Name { get; set; } + [Display(Name = "Description")] public string Description { get; set; } //public string DisplayName => $"{Name} ({Description})"; diff --git a/FMS.Domain/Dto/UserPosition/UserPositionCreateDto.cs b/FMS.Domain/Dto/UserPosition/UserPositionCreateDto.cs index 2f8dcfa8..d2a1451c 100644 --- a/FMS.Domain/Dto/UserPosition/UserPositionCreateDto.cs +++ b/FMS.Domain/Dto/UserPosition/UserPositionCreateDto.cs @@ -6,9 +6,14 @@ public class UserPositionCreateDto [Required(ErrorMessage = "User Position Name is required.")] public string Name { get; set; } + [Display(Name = "Description")] + [Required(ErrorMessage = "Description is required.")] + public string Description { get; set; } + public void TrimAll() { Name = Name?.Trim(); + Description = Description?.Trim(); } } } diff --git a/FMS.Domain/Dto/UserPosition/UserPositionEditDto.cs b/FMS.Domain/Dto/UserPosition/UserPositionEditDto.cs index f5a5b553..0146e64f 100644 --- a/FMS.Domain/Dto/UserPosition/UserPositionEditDto.cs +++ b/FMS.Domain/Dto/UserPosition/UserPositionEditDto.cs @@ -10,6 +10,7 @@ public UserPositionEditDto(UserPosition userPosition) { Id = userPosition.Id; Name = userPosition.Name; + Description = userPosition.Description; Active = userPosition.Active; } @@ -19,12 +20,17 @@ public UserPositionEditDto(UserPosition userPosition) [Required(ErrorMessage = "User Position Name is required.")] public string Name { get; set; } + [Display(Name = "Description")] + [Required(ErrorMessage = "Description is required.")] + public string Description { get; set; } + [Display(Name = "Is Active")] public bool Active { get; set; } public void TrimAll() { Name = Name?.Trim(); + Description = Description?.Trim(); } } } diff --git a/FMS.Domain/Dto/UserPosition/UserPositionSummaryDto.cs b/FMS.Domain/Dto/UserPosition/UserPositionSummaryDto.cs index 562797bc..2a7bf021 100644 --- a/FMS.Domain/Dto/UserPosition/UserPositionSummaryDto.cs +++ b/FMS.Domain/Dto/UserPosition/UserPositionSummaryDto.cs @@ -1,4 +1,5 @@ using FMS.Domain.Entities; +using System.ComponentModel; namespace FMS.Domain.Dto { @@ -9,12 +10,17 @@ public UserPositionSummaryDto(UserPosition userPosition) Id = userPosition.Id; Active = userPosition.Active; Name = userPosition.Name; + Description = userPosition.Description; } public Guid Id { get; } public bool Active { get; } + [DisplayName("User Position")] public string Name { get; set; } + + [DisplayName("Description")] + public string Description { get; set; } } } diff --git a/FMS.Domain/Dto/UserProgram/UserProgramCreateDto.cs b/FMS.Domain/Dto/UserProgram/UserProgramCreateDto.cs index c329ecb7..7accbbfb 100644 --- a/FMS.Domain/Dto/UserProgram/UserProgramCreateDto.cs +++ b/FMS.Domain/Dto/UserProgram/UserProgramCreateDto.cs @@ -2,13 +2,18 @@ { public class UserProgramCreateDto { - [Display(Name = "User Position")] - [Required(ErrorMessage = "User Position Name is required.")] + [Display(Name = "User Program")] + [Required(ErrorMessage = "User Program Name is required.")] public string Name { get; set; } + [Display(Name = "Description")] + [Required(ErrorMessage = "Description is required.")] + public string Description { get; set; } + public void TrimAll() { Name = Name?.Trim(); + Description = Description?.Trim(); } } } diff --git a/FMS.Domain/Dto/UserProgram/UserProgramEditDto.cs b/FMS.Domain/Dto/UserProgram/UserProgramEditDto.cs index c0bdafa3..cf39bfcb 100644 --- a/FMS.Domain/Dto/UserProgram/UserProgramEditDto.cs +++ b/FMS.Domain/Dto/UserProgram/UserProgramEditDto.cs @@ -10,6 +10,7 @@ public UserProgramEditDto(UserProgram userProgram) { Id = userProgram.Id; Name = userProgram.Name; + Description = userProgram.Description; Active = userProgram.Active; } @@ -19,12 +20,17 @@ public UserProgramEditDto(UserProgram userProgram) [Required(ErrorMessage = "User Program Name is required.")] public string Name { get; set; } + [Display(Name = "Description")] + [Required(ErrorMessage = "Description is required.")] + public string Description { get; set; } + [Display(Name = "Is Active")] public bool Active { get; set; } public void TrimAll() { Name = Name?.Trim(); + Description = Description?.Trim(); } } } diff --git a/FMS.Domain/Dto/UserProgram/UserProgramSummaryDto.cs b/FMS.Domain/Dto/UserProgram/UserProgramSummaryDto.cs index ecc029d0..c4f7fd06 100644 --- a/FMS.Domain/Dto/UserProgram/UserProgramSummaryDto.cs +++ b/FMS.Domain/Dto/UserProgram/UserProgramSummaryDto.cs @@ -1,4 +1,5 @@ using FMS.Domain.Entities; +using System.ComponentModel; namespace FMS.Domain.Dto { @@ -9,12 +10,17 @@ public UserProgramSummaryDto(UserProgram userProgram) Id = userProgram.Id; Active = userProgram.Active; Name = userProgram.Name; + Description = userProgram.Description; } public Guid Id { get; } public bool Active { get; } + [DisplayName("User Program")] public string Name { get; set; } + + [DisplayName("Description")] + public string Description { get; set; } } } diff --git a/FMS.Domain/Entities/UserPosition.cs b/FMS.Domain/Entities/UserPosition.cs index baea2ff7..82351dfd 100644 --- a/FMS.Domain/Entities/UserPosition.cs +++ b/FMS.Domain/Entities/UserPosition.cs @@ -13,13 +13,17 @@ public UserPosition() { } public UserPosition(UserPositionCreateDto userPosition) { Name = userPosition.Name; + Description = userPosition.Description; } public string Name { get; set; } + public string Description { get; set; } + public void TrimAll() { Name = Name?.Trim(); + Description = Description?.Trim(); } } } diff --git a/FMS.Domain/Entities/UserProgram.cs b/FMS.Domain/Entities/UserProgram.cs index e4667415..e2d8f431 100644 --- a/FMS.Domain/Entities/UserProgram.cs +++ b/FMS.Domain/Entities/UserProgram.cs @@ -10,13 +10,17 @@ public UserProgram() { } public UserProgram(UserProgramCreateDto userProgram) { Name = userProgram.Name; + Description = userProgram.Description; } public string Name { get; set; } + public string Description { get; set; } + public void TrimAll() { Name = Name?.Trim(); + Description = Description?.Trim(); } } } diff --git a/FMS.Domain/Repositories/IUserPositionRepository.cs b/FMS.Domain/Repositories/IUserPositionRepository.cs index 74550a11..7dcb0c9f 100644 --- a/FMS.Domain/Repositories/IUserPositionRepository.cs +++ b/FMS.Domain/Repositories/IUserPositionRepository.cs @@ -8,6 +8,8 @@ public interface IUserPositionRepository : IDisposable Task UserPositionNameExistsAsync(string name, Guid? ignoreId = null); + Task UserPositionDescriptionExistsAsync(string description, Guid? ignoreId = null); + Task GetUserPositionAsync(Guid id); Task> GetUserPositionListAsync(); diff --git a/FMS.Domain/Repositories/IUserProgramRepository.cs b/FMS.Domain/Repositories/IUserProgramRepository.cs index eb535004..6ce17c90 100644 --- a/FMS.Domain/Repositories/IUserProgramRepository.cs +++ b/FMS.Domain/Repositories/IUserProgramRepository.cs @@ -8,6 +8,8 @@ public interface IUserProgramRepository : IDisposable Task UserProgramNameExistsAsync(string name, Guid? ignoreId = null); + Task UserProgramDescriptionExistsAsync(string description, Guid? ignoreId = null); + Task GetUserProgramAsync(Guid id); Task> GetUserProgramListAsync(); diff --git a/FMS.Infrastructure/Contexts/FmsDbContext.cs b/FMS.Infrastructure/Contexts/FmsDbContext.cs index b343d047..b63b58f8 100644 --- a/FMS.Infrastructure/Contexts/FmsDbContext.cs +++ b/FMS.Infrastructure/Contexts/FmsDbContext.cs @@ -57,8 +57,8 @@ public FmsDbContext(DbContextOptions options, public DbSet Statuses { get; set; } public DbSet UserPrograms { get; set; } public DbSet UserPositions { get; set; } - //public DbSet UserInfo { get; set; } + //public DbSet UserInfo { get; set; } // The "Counties" table is only used to add County data to the database for database-side use. // Counties are stored in memory and never accessed from the database, but other entities diff --git a/FMS.Infrastructure/Migrations/20260529212605_AddDescriptionToUserProgramAndPosition.Designer.cs b/FMS.Infrastructure/Migrations/20260529212605_AddDescriptionToUserProgramAndPosition.Designer.cs new file mode 100644 index 00000000..7200c34d --- /dev/null +++ b/FMS.Infrastructure/Migrations/20260529212605_AddDescriptionToUserProgramAndPosition.Designer.cs @@ -0,0 +1,3286 @@ +// +using System; +using FMS.Infrastructure.Contexts; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace FMS.Infrastructure.Migrations +{ + [DbContext(typeof(FmsDbContext))] + [Migration("20260529212605_AddDescriptionToUserProgramAndPosition")] + partial class AddDescriptionToUserProgramAndPosition + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("FMS.Domain.Entities.AbandonedInactive", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AbandonedInactives"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ActionTaken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ActionsTaken"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.AllowedActionTaken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActionTakenId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("CompletionDateRequired") + .HasColumnType("bit"); + + b.Property("DueDateRequired") + .HasColumnType("bit"); + + b.Property("EventTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("StartDateRequired") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ActionTakenId"); + + b.HasIndex("EventTypeId"); + + b.ToTable("AllowedActionsTaken"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.BudgetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Code") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("ProjectNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("BudgetCodes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Cabinet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FirstFileLabel") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("Cabinets"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Chemical", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("CasNo") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemicalName") + .HasColumnType("nvarchar(max)"); + + b.Property("CommonName") + .HasColumnType("nvarchar(max)"); + + b.Property("FinalRc") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("MCLs") + .HasColumnType("nvarchar(max)"); + + b.Property("RQ") + .HasColumnType("nvarchar(max)"); + + b.Property("ToxValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Chemicals"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ComplianceOfficer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FamilyName") + .HasColumnType("nvarchar(max)"); + + b.Property("GivenName") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ComplianceOfficers"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("Company") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactTitle") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("FamilyName") + .HasColumnType("nvarchar(max)"); + + b.Property("GivenName") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("PostalCode") + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ContactTypeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Contacts"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ContactType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ContactTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.County", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Counties"); + + b.HasData( + new + { + Id = 131, + Name = "Appling" + }, + new + { + Id = 122, + Name = "Atkinson" + }, + new + { + Id = 110, + Name = "Bacon" + }, + new + { + Id = 105, + Name = "Baker" + }, + new + { + Id = 213, + Name = "Baldwin" + }, + new + { + Id = 179, + Name = "Banks" + }, + new + { + Id = 217, + Name = "Barrow" + }, + new + { + Id = 249, + Name = "Bartow" + }, + new + { + Id = 211, + Name = "Ben Hill" + }, + new + { + Id = 194, + Name = "Berrien" + }, + new + { + Id = 259, + Name = "Bibb" + }, + new + { + Id = 145, + Name = "Bleckley" + }, + new + { + Id = 108, + Name = "Brantley" + }, + new + { + Id = 158, + Name = "Brooks" + }, + new + { + Id = 111, + Name = "Bryan" + }, + new + { + Id = 159, + Name = "Bulloch" + }, + new + { + Id = 203, + Name = "Burke" + }, + new + { + Id = 188, + Name = "Butts" + }, + new + { + Id = 112, + Name = "Calhoun" + }, + new + { + Id = 169, + Name = "Camden" + }, + new + { + Id = 113, + Name = "Candler" + }, + new + { + Id = 241, + Name = "Carroll" + }, + new + { + Id = 225, + Name = "Catoosa" + }, + new + { + Id = 114, + Name = "Charlton" + }, + new + { + Id = 242, + Name = "Chatham" + }, + new + { + Id = 137, + Name = "Chattahoochee" + }, + new + { + Id = 174, + Name = "Chattooga" + }, + new + { + Id = 243, + Name = "Cherokee" + }, + new + { + Id = 252, + Name = "Clarke" + }, + new + { + Id = 104, + Name = "Clay" + }, + new + { + Id = 204, + Name = "Clayton" + }, + new + { + Id = 178, + Name = "Clinch" + }, + new + { + Id = 245, + Name = "Cobb" + }, + new + { + Id = 146, + Name = "Coffee" + }, + new + { + Id = 227, + Name = "Colquitt" + }, + new + { + Id = 232, + Name = "Columbia" + }, + new + { + Id = 212, + Name = "Cook" + }, + new + { + Id = 250, + Name = "Coweta" + }, + new + { + Id = 115, + Name = "Crawford" + }, + new + { + Id = 220, + Name = "Crisp" + }, + new + { + Id = 170, + Name = "Dade" + }, + new + { + Id = 180, + Name = "Dawson" + }, + new + { + Id = 244, + Name = "Decatur" + }, + new + { + Id = 218, + Name = "DeKalb" + }, + new + { + Id = 175, + Name = "Dodge" + }, + new + { + Id = 189, + Name = "Dooly" + }, + new + { + Id = 257, + Name = "Dougherty" + }, + new + { + Id = 253, + Name = "Douglas" + }, + new + { + Id = 195, + Name = "Early" + }, + new + { + Id = 103, + Name = "Echols" + }, + new + { + Id = 123, + Name = "Effingham" + }, + new + { + Id = 200, + Name = "Elbert" + }, + new + { + Id = 198, + Name = "Emanuel" + }, + new + { + Id = 109, + Name = "Evans" + }, + new + { + Id = 176, + Name = "Fannin" + }, + new + { + Id = 235, + Name = "Fayette" + }, + new + { + Id = 246, + Name = "Floyd" + }, + new + { + Id = 238, + Name = "Forsyth" + }, + new + { + Id = 201, + Name = "Franklin" + }, + new + { + Id = 261, + Name = "Fulton" + }, + new + { + Id = 165, + Name = "Gilmer" + }, + new + { + Id = 124, + Name = "Glascock" + }, + new + { + Id = 216, + Name = "Glynn" + }, + new + { + Id = 230, + Name = "Gordon" + }, + new + { + Id = 208, + Name = "Grady" + }, + new + { + Id = 171, + Name = "Greene" + }, + new + { + Id = 226, + Name = "Gwinnett" + }, + new + { + Id = 214, + Name = "Habersham" + }, + new + { + Id = 256, + Name = "Hall" + }, + new + { + Id = 107, + Name = "Hancock" + }, + new + { + Id = 209, + Name = "Haralson" + }, + new + { + Id = 138, + Name = "Harris" + }, + new + { + Id = 190, + Name = "Hart" + }, + new + { + Id = 152, + Name = "Heard" + }, + new + { + Id = 248, + Name = "Henry" + }, + new + { + Id = 239, + Name = "Houston" + }, + new + { + Id = 139, + Name = "Irwin" + }, + new + { + Id = 222, + Name = "Jackson" + }, + new + { + Id = 147, + Name = "Jasper" + }, + new + { + Id = 160, + Name = "Jeff Davis" + }, + new + { + Id = 199, + Name = "Jefferson" + }, + new + { + Id = 153, + Name = "Jenkins" + }, + new + { + Id = 140, + Name = "Johnson" + }, + new + { + Id = 161, + Name = "Jones" + }, + new + { + Id = 181, + Name = "Lamar" + }, + new + { + Id = 141, + Name = "Lanier" + }, + new + { + Id = 223, + Name = "Laurens" + }, + new + { + Id = 172, + Name = "Lee" + }, + new + { + Id = 148, + Name = "Liberty" + }, + new + { + Id = 125, + Name = "Lincoln" + }, + new + { + Id = 102, + Name = "Long" + }, + new + { + Id = 254, + Name = "Lowndes" + }, + new + { + Id = 192, + Name = "Lumpkin" + }, + new + { + Id = 182, + Name = "Macon" + }, + new + { + Id = 166, + Name = "Madison" + }, + new + { + Id = 133, + Name = "Marion" + }, + new + { + Id = 210, + Name = "McDuffie" + }, + new + { + Id = 126, + Name = "McIntosh" + }, + new + { + Id = 196, + Name = "Meriwether" + }, + new + { + Id = 154, + Name = "Miller" + }, + new + { + Id = 215, + Name = "Mitchell" + }, + new + { + Id = 184, + Name = "Monroe" + }, + new + { + Id = 134, + Name = "Montgomery" + }, + new + { + Id = 197, + Name = "Morgan" + }, + new + { + Id = 205, + Name = "Murray" + }, + new + { + Id = 258, + Name = "Muscogee" + }, + new + { + Id = 231, + Name = "Newton" + }, + new + { + Id = 191, + Name = "Oconee" + }, + new + { + Id = 127, + Name = "Oglethorpe" + }, + new + { + Id = 219, + Name = "Paulding" + }, + new + { + Id = 221, + Name = "Peach" + }, + new + { + Id = 185, + Name = "Pickens" + }, + new + { + Id = 116, + Name = "Pierce" + }, + new + { + Id = 149, + Name = "Pike" + }, + new + { + Id = 233, + Name = "Polk" + }, + new + { + Id = 155, + Name = "Pulaski" + }, + new + { + Id = 183, + Name = "Putnam" + }, + new + { + Id = 100, + Name = "Quitman" + }, + new + { + Id = 167, + Name = "Rabun" + }, + new + { + Id = 156, + Name = "Randolph" + }, + new + { + Id = 260, + Name = "Richmond" + }, + new + { + Id = 251, + Name = "Rockdale" + }, + new + { + Id = 135, + Name = "Schley" + }, + new + { + Id = 157, + Name = "Screven" + }, + new + { + Id = 150, + Name = "Seminole" + }, + new + { + Id = 240, + Name = "Spalding" + }, + new + { + Id = 229, + Name = "Stephens" + }, + new + { + Id = 117, + Name = "Stewart" + }, + new + { + Id = 236, + Name = "Sumter" + }, + new + { + Id = 128, + Name = "Talbot" + }, + new + { + Id = 118, + Name = "Taliaferro" + }, + new + { + Id = 136, + Name = "Tattnall" + }, + new + { + Id = 142, + Name = "Taylor" + }, + new + { + Id = 164, + Name = "Telfair" + }, + new + { + Id = 173, + Name = "Terrell" + }, + new + { + Id = 234, + Name = "Thomas" + }, + new + { + Id = 237, + Name = "Tift" + }, + new + { + Id = 151, + Name = "Toombs" + }, + new + { + Id = 129, + Name = "Towns" + }, + new + { + Id = 101, + Name = "Treutlen" + }, + new + { + Id = 247, + Name = "Troup" + }, + new + { + Id = 186, + Name = "Turner" + }, + new + { + Id = 130, + Name = "Twiggs" + }, + new + { + Id = 168, + Name = "Union" + }, + new + { + Id = 202, + Name = "Upson" + }, + new + { + Id = 224, + Name = "Walker" + }, + new + { + Id = 228, + Name = "Walton" + }, + new + { + Id = 177, + Name = "Ware" + }, + new + { + Id = 162, + Name = "Warren" + }, + new + { + Id = 206, + Name = "Washington" + }, + new + { + Id = 143, + Name = "Wayne" + }, + new + { + Id = 120, + Name = "Webster" + }, + new + { + Id = 106, + Name = "Wheeler" + }, + new + { + Id = 193, + Name = "White" + }, + new + { + Id = 255, + Name = "Whitfield" + }, + new + { + Id = 144, + Name = "Wilcox" + }, + new + { + Id = 163, + Name = "Wilkes" + }, + new + { + Id = 207, + Name = "Wilkinson" + }, + new + { + Id = 187, + Name = "Worth" + }, + new + { + Id = 99, + Name = "OUT OF STATE" + }); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActionTakenId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comment") + .HasColumnType("nvarchar(max)"); + + b.Property("CompletionDate") + .HasColumnType("date"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("EventAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("EventContractorId") + .HasColumnType("uniqueidentifier"); + + b.Property("EventTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ActionTakenId"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("EventContractorId"); + + b.HasIndex("EventTypeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.EventContractor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("EventContractors"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.EventType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("ReportEligible") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("EventTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("AdditionalDataRequested") + .HasColumnType("bit"); + + b.Property("Address") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("BudgetCodeId") + .HasColumnType("uniqueidentifier"); + + b.Property("City") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("CountyId") + .HasColumnType("int"); + + b.Property("DeferredOnSiteScoring") + .HasColumnType("bit"); + + b.Property("DeterminationLetterDate") + .HasColumnType("date"); + + b.Property("FacilityNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileId") + .HasColumnType("uniqueidentifier"); + + b.Property("HSInumber") + .HasMaxLength(5) + .HasColumnType("nvarchar(5)"); + + b.Property("HasERecord") + .HasColumnType("bit"); + + b.Property("HistoricalComplianceOfficer") + .HasColumnType("nvarchar(max)"); + + b.Property("HistoricalUnit") + .HasColumnType("nvarchar(max)"); + + b.Property("ImageChecked") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("IsRetained") + .HasColumnType("bit"); + + b.Property("Latitude") + .HasColumnType("decimal(8, 6)"); + + b.Property("Location") + .HasColumnType("nvarchar(max)"); + + b.Property("Longitude") + .HasColumnType("decimal(9, 6)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationalUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("PreRQSMcleanup") + .HasColumnType("bit"); + + b.Property("RNDateReceived") + .HasColumnType("date"); + + b.Property("State") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("VRPReferral") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("Active"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Active"), new[] { "FileId" }); + + b.HasIndex("BudgetCodeId"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("CountyId"); + + b.HasIndex("FacilityStatusId"); + + b.HasIndex("FacilityTypeId"); + + b.HasIndex("FileId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("FileId"), new[] { "Active", "FacilityNumber", "FacilityTypeId", "OrganizationalUnitId", "BudgetCodeId", "Name", "ComplianceOfficerId", "FacilityStatusId", "Location", "Address", "City", "State", "PostalCode", "Latitude", "Longitude", "CountyId", "IsRetained", "AdditionalDataRequested", "Comments", "DeferredOnSiteScoring", "DeterminationLetterDate", "HSInumber", "HasERecord", "HistoricalComplianceOfficer", "HistoricalUnit", "ImageChecked", "PreRQSMcleanup", "RNDateReceived", "VRPReferral" }); + + b.HasIndex("OrganizationalUnitId"); + + b.HasIndex("Active", "FileId"); + + b.HasIndex("Active", "FacilityTypeId", "Address"); + + b.HasIndex("Active", "FacilityTypeId", "Address", "City"); + + b.ToTable("Facilities"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FacilityStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FacilityStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FacilityType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("FacilityTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.File", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FileLabel") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FileLabel") + .IsUnique() + .HasFilter("[FileLabel] IS NOT NULL"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FundingSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FundingSources"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GapsAssessment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GapsAssessments"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("A") + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("B1") + .HasColumnType("int"); + + b.Property("B2") + .HasColumnType("int"); + + b.Property("C") + .HasColumnType("int"); + + b.Property("CASNO") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemName") + .HasColumnType("nvarchar(max)"); + + b.Property("D2") + .HasColumnType("int"); + + b.Property("D3") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("E1") + .HasColumnType("int"); + + b.Property("E2") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("GWScore") + .HasColumnType("decimal(18,2)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Other") + .HasColumnType("nvarchar(max)"); + + b.Property("SubstanceId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("SubstanceId"); + + b.ToTable("GroundwaterScores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GroundwaterStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.HsrpFacilityProperties", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("BrownfieldDate") + .HasColumnType("date"); + + b.Property("BrownfieldTerminated") + .HasColumnType("bit"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("DateDeListed") + .HasColumnType("date"); + + b.Property("DateListed") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationalUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("VRPDate") + .HasColumnType("date"); + + b.Property("VRPTerminated") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("OrganizationalUnitId"); + + b.ToTable("HsrpFacilityProperties"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LocationClassId") + .HasColumnType("uniqueidentifier"); + + b.Property("MapType") + .HasColumnType("nvarchar(max)"); + + b.Property("MapZoom") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("LocationClassId"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.LocationClass", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("LocationClasses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OnsiteScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("A") + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("B") + .HasColumnType("int"); + + b.Property("C") + .HasColumnType("int"); + + b.Property("CASNO") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemName1D") + .HasColumnType("nvarchar(max)"); + + b.Property("D2") + .HasColumnType("int"); + + b.Property("D3") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("E1") + .HasColumnType("int"); + + b.Property("E2") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("OnsiteScoreValue") + .HasColumnType("decimal(18,2)"); + + b.Property("Other1D") + .HasColumnType("nvarchar(max)"); + + b.Property("SubstanceId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("SubstanceId"); + + b.ToTable("OnsiteScores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OrganizationalUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OrganizationalUnits"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OverallStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OverallStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Parcel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Acres") + .HasColumnType("float"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("DeListDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ListDate") + .HasColumnType("date"); + + b.Property("ParcelNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("ParcelTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("SubListParcelName") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("ParcelTypeId"); + + b.ToTable("Parcels"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ParcelType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ParcelTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Phone", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("ContactId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Number") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneType") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ContactId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Phones"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.RetentionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("BoxNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("ConsignmentNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("EndYear") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("RetentionSchedule") + .HasColumnType("nvarchar(max)"); + + b.Property("ShelfNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("FacilityId"), new[] { "Active", "StartYear", "EndYear", "ConsignmentNumber", "BoxNumber", "ShelfNumber", "RetentionSchedule" }); + + b.ToTable("RetentionRecords"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Score", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ScoredDate") + .HasColumnType("date"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UseComments") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.SoilStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SoilStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.SourceStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SourceStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Status", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AbandonedInactiveId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("CostEstimate") + .HasColumnType("decimal(18,2)"); + + b.Property("CostEstimateDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("FinancialAssurance") + .HasColumnType("bit"); + + b.Property("FundingSourceId") + .HasColumnType("uniqueidentifier"); + + b.Property("GAPSAssessmentId") + .HasColumnType("uniqueidentifier"); + + b.Property("GAPSModelDate") + .HasColumnType("date"); + + b.Property("GAPSNoOfUnknowns") + .HasColumnType("int"); + + b.Property("GAPSScore") + .HasColumnType("int"); + + b.Property("GroundwaterDate") + .HasColumnType("date"); + + b.Property("GroundwaterStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("ISWQS") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LandFill") + .HasColumnType("bit"); + + b.Property("Lien") + .HasColumnType("bit"); + + b.Property("OverallDate") + .HasColumnType("date"); + + b.Property("OverallStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("ReportComments") + .HasColumnType("nvarchar(max)"); + + b.Property("SoilDate") + .HasColumnType("date"); + + b.Property("SoilStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("SolidWastePermitNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("SourceDate") + .HasColumnType("date"); + + b.Property("SourceStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("UEC") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("AbandonedInactiveId"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("FundingSourceId"); + + b.HasIndex("GAPSAssessmentId"); + + b.HasIndex("GroundwaterStatusId"); + + b.HasIndex("OverallStatusId"); + + b.HasIndex("SoilStatusId"); + + b.HasIndex("SourceStatusId"); + + b.ToTable("Statuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Substance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("ChemicalId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("Groundwater") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Soil") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UseForGroundwaterScoring") + .HasColumnType("bit"); + + b.Property("UseForSoilScoring") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ChemicalId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Substances"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.UserPosition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("UserPositions"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.UserProgram", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("UserPrograms"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Users.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FamilyName") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("GivenName") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("MostRecentLogin") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ObjectId") + .HasMaxLength(36) + .HasColumnType("nvarchar(36)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("ProfileUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("ObjectId") + .IsUnique() + .HasFilter("[ObjectId] IS NOT NULL"); + + b.ToTable("AppUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityPasskeyData", b => + { + b.Property("AttestationObject") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("ClientDataJson") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("IsBackedUp") + .HasColumnType("bit"); + + b.Property("IsBackupEligible") + .HasColumnType("bit"); + + b.Property("IsUserVerified") + .HasColumnType("bit"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("SignCount") + .HasColumnType("bigint"); + + b.PrimitiveCollection("Transports") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.ToTable("IdentityPasskeyData"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AppRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AppRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AppUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AppUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AppUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AppUserTokens", (string)null); + }); + + modelBuilder.Entity("FMS.Domain.Entities.AllowedActionTaken", b => + { + b.HasOne("FMS.Domain.Entities.ActionTaken", "ActionTaken") + .WithMany() + .HasForeignKey("ActionTakenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.EventType", "EventType") + .WithMany() + .HasForeignKey("EventTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ActionTaken"); + + b.Navigation("EventType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.HasOne("FMS.Domain.Entities.ContactType", "ContactType") + .WithMany() + .HasForeignKey("ContactTypeId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Contacts") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ContactType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Event", b => + { + b.HasOne("FMS.Domain.Entities.ActionTaken", "ActionTaken") + .WithMany() + .HasForeignKey("ActionTakenId"); + + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.EventContractor", "EventContractor") + .WithMany() + .HasForeignKey("EventContractorId"); + + b.HasOne("FMS.Domain.Entities.EventType", "EventType") + .WithMany() + .HasForeignKey("EventTypeId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Events") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ActionTaken"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("EventContractor"); + + b.Navigation("EventType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.HasOne("FMS.Domain.Entities.BudgetCode", "BudgetCode") + .WithMany() + .HasForeignKey("BudgetCodeId"); + + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.County", "County") + .WithMany() + .HasForeignKey("CountyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.FacilityStatus", "FacilityStatus") + .WithMany() + .HasForeignKey("FacilityStatusId"); + + b.HasOne("FMS.Domain.Entities.FacilityType", "FacilityType") + .WithMany() + .HasForeignKey("FacilityTypeId"); + + b.HasOne("FMS.Domain.Entities.File", "File") + .WithMany("Facilities") + .HasForeignKey("FileId"); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "OrganizationalUnit") + .WithMany() + .HasForeignKey("OrganizationalUnitId"); + + b.Navigation("BudgetCode"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("County"); + + b.Navigation("FacilityStatus"); + + b.Navigation("FacilityType"); + + b.Navigation("File"); + + b.Navigation("OrganizationalUnit"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterScore", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("GroundwaterScoreDetails") + .HasForeignKey("FMS.Domain.Entities.GroundwaterScore", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Substance", "Substance") + .WithMany() + .HasForeignKey("SubstanceId"); + + b.Navigation("Substance"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.HsrpFacilityProperties", b => + { + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("HsrpFacilityProperties") + .HasForeignKey("FMS.Domain.Entities.HsrpFacilityProperties", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "OrganizationalUnit") + .WithMany() + .HasForeignKey("OrganizationalUnitId"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("OrganizationalUnit"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Location", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("LocationDetails") + .HasForeignKey("FMS.Domain.Entities.Location", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.LocationClass", "LocationClass") + .WithMany() + .HasForeignKey("LocationClassId"); + + b.Navigation("LocationClass"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OnsiteScore", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("OnsiteScoreDetails") + .HasForeignKey("FMS.Domain.Entities.OnsiteScore", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Substance", "Substance") + .WithMany() + .HasForeignKey("SubstanceId"); + + b.Navigation("Substance"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Parcel", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Parcels") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.ParcelType", "ParcelType") + .WithMany() + .HasForeignKey("ParcelTypeId"); + + b.Navigation("ParcelType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Phone", b => + { + b.HasOne("FMS.Domain.Entities.Contact", null) + .WithMany("Phones") + .HasForeignKey("ContactId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Phones") + .HasForeignKey("FacilityId"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.RetentionRecord", b => + { + b.HasOne("FMS.Domain.Entities.Facility", "Facility") + .WithMany("RetentionRecords") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Score", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("ScoreDetails") + .HasForeignKey("FMS.Domain.Entities.Score", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Status", b => + { + b.HasOne("FMS.Domain.Entities.AbandonedInactive", "AbandonedInactive") + .WithMany() + .HasForeignKey("AbandonedInactiveId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("StatusDetails") + .HasForeignKey("FMS.Domain.Entities.Status", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.FundingSource", "FundingSource") + .WithMany() + .HasForeignKey("FundingSourceId"); + + b.HasOne("FMS.Domain.Entities.GapsAssessment", "GAPSAssessment") + .WithMany() + .HasForeignKey("GAPSAssessmentId"); + + b.HasOne("FMS.Domain.Entities.GroundwaterStatus", "GroundwaterStatus") + .WithMany() + .HasForeignKey("GroundwaterStatusId"); + + b.HasOne("FMS.Domain.Entities.OverallStatus", "OverallStatus") + .WithMany() + .HasForeignKey("OverallStatusId"); + + b.HasOne("FMS.Domain.Entities.SoilStatus", "SoilStatus") + .WithMany() + .HasForeignKey("SoilStatusId"); + + b.HasOne("FMS.Domain.Entities.SourceStatus", "SourceStatus") + .WithMany() + .HasForeignKey("SourceStatusId"); + + b.Navigation("AbandonedInactive"); + + b.Navigation("FundingSource"); + + b.Navigation("GAPSAssessment"); + + b.Navigation("GroundwaterStatus"); + + b.Navigation("OverallStatus"); + + b.Navigation("SoilStatus"); + + b.Navigation("SourceStatus"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Substance", b => + { + b.HasOne("FMS.Domain.Entities.Chemical", "Chemical") + .WithMany() + .HasForeignKey("ChemicalId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Substances") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Chemical"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.Navigation("Phones"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.Navigation("Contacts"); + + b.Navigation("Events"); + + b.Navigation("GroundwaterScoreDetails"); + + b.Navigation("HsrpFacilityProperties"); + + b.Navigation("LocationDetails"); + + b.Navigation("OnsiteScoreDetails"); + + b.Navigation("Parcels"); + + b.Navigation("Phones"); + + b.Navigation("RetentionRecords"); + + b.Navigation("ScoreDetails"); + + b.Navigation("StatusDetails"); + + b.Navigation("Substances"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.File", b => + { + b.Navigation("Facilities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FMS.Infrastructure/Migrations/20260529212605_AddDescriptionToUserProgramAndPosition.cs b/FMS.Infrastructure/Migrations/20260529212605_AddDescriptionToUserProgramAndPosition.cs new file mode 100644 index 00000000..c122991d --- /dev/null +++ b/FMS.Infrastructure/Migrations/20260529212605_AddDescriptionToUserProgramAndPosition.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace FMS.Infrastructure.Migrations +{ + /// + public partial class AddDescriptionToUserProgramAndPosition : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Description", + table: "UserPrograms", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.AddColumn( + name: "Description", + table: "UserPositions", + type: "nvarchar(max)", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Description", + table: "UserPrograms"); + + migrationBuilder.DropColumn( + name: "Description", + table: "UserPositions"); + } + } +} diff --git a/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs b/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs index 26311356..c214ba20 100644 --- a/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs +++ b/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs @@ -2486,6 +2486,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Active") .HasColumnType("bit"); + b.Property("Description") + .HasColumnType("nvarchar(max)"); + b.Property("InsertDateTime") .HasColumnType("datetimeoffset"); @@ -2515,6 +2518,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Active") .HasColumnType("bit"); + b.Property("Description") + .HasColumnType("nvarchar(max)"); + b.Property("InsertDateTime") .HasColumnType("datetimeoffset"); diff --git a/FMS.Infrastructure/Repositories/UserPositionRepository.cs b/FMS.Infrastructure/Repositories/UserPositionRepository.cs index f2cb002e..f2ee8a00 100644 --- a/FMS.Infrastructure/Repositories/UserPositionRepository.cs +++ b/FMS.Infrastructure/Repositories/UserPositionRepository.cs @@ -18,6 +18,9 @@ public async Task UserPositionExistsAsync(Guid id) => public async Task UserPositionNameExistsAsync(string name, Guid? ignoreId = null) => await _context.UserPositions.AnyAsync(e => e.Name == name && (!ignoreId.HasValue || e.Id != ignoreId.Value)); + public async Task UserPositionDescriptionExistsAsync(string description, Guid? ignoreId = null) => + await _context.UserPositions.AnyAsync(e => e.Description == description && (!ignoreId.HasValue || e.Id != ignoreId.Value)); + public async Task GetUserPositionAsync(Guid id){ var userPosition = await _context.UserPositions.AsNoTracking() .SingleOrDefaultAsync(e => e.Id == id); @@ -47,6 +50,11 @@ private async Task CreateUserPositionInternalAsync(UserPositionCreateDto u throw new ArgumentException($"User Position Name: {userPosition.Name} Already Exists."); } + if (await UserPositionDescriptionExistsAsync(userPosition.Description)) + { + throw new ArgumentException($"User Position Description: {userPosition.Description} Already Exists."); + } + var newUserPosition = new UserPosition(userPosition); await _context.UserPositions.AddAsync(newUserPosition); @@ -71,6 +79,7 @@ public async Task UpdateUserPositionAsync(Guid Id, UserPositionEditDto userPosit var existingUserPosition = await _context.UserPositions.FindAsync(Id); existingUserPosition.Name = userPositionUpdates.Name; + existingUserPosition.Description = userPositionUpdates.Description; await _context.SaveChangesAsync(); } diff --git a/FMS.Infrastructure/Repositories/UserProgramRepository.cs b/FMS.Infrastructure/Repositories/UserProgramRepository.cs index 7e7410bf..2e3de3a6 100644 --- a/FMS.Infrastructure/Repositories/UserProgramRepository.cs +++ b/FMS.Infrastructure/Repositories/UserProgramRepository.cs @@ -18,6 +18,9 @@ public async Task UserProgramExistsAsync(Guid id) => public async Task UserProgramNameExistsAsync(string name, Guid? ignoreId = null) => await _context.UserPrograms.AnyAsync(e => e.Name == name && (!ignoreId.HasValue || e.Id != ignoreId.Value)); + public async Task UserProgramDescriptionExistsAsync(string description, Guid? ignoreId = null) => + await _context.UserPrograms.AnyAsync(e => e.Description == description && (!ignoreId.HasValue || e.Id != ignoreId.Value)); + public async Task GetUserProgramAsync(Guid id) { var userProgram = await _context.UserPrograms.AsNoTracking() @@ -48,6 +51,11 @@ private async Task CreateUserProgramInternalAsync(UserProgramCreateDto use throw new ArgumentException($"User Program Name: {userProgram.Name} Already Exists."); } + if (await UserProgramDescriptionExistsAsync(userProgram.Description)) + { + throw new ArgumentException($"User Program Description: {userProgram.Description} Already Exists."); + } + var newUserProgram = new UserProgram(userProgram); await _context.UserPrograms.AddAsync(newUserProgram); @@ -72,6 +80,7 @@ public async Task UpdateUserProgramAsync(Guid Id, UserProgramEditDto userProgram var existingUserProgram = await _context.UserPrograms.FindAsync(Id); existingUserProgram.Name = userProgramUpdates.Name; + existingUserProgram.Description = userProgramUpdates.Description; await _context.SaveChangesAsync(); } diff --git a/FMS/Pages/Maintenance/UserPosition/Add.cshtml b/FMS/Pages/Maintenance/UserPosition/Add.cshtml index 62fc2d72..31b8fb06 100644 --- a/FMS/Pages/Maintenance/UserPosition/Add.cshtml +++ b/FMS/Pages/Maintenance/UserPosition/Add.cshtml @@ -1,4 +1,36 @@ @page +@using FMS.Pages.Maintenance @model FMS.Pages.Maintenance.UserPosition.AddModel + +@section Scripts { + +} + @{ + ViewData["Title"] = $"Add {MaintenanceOptions.UserPosition}"; } +

    @ViewData["Title"]

    +
    +
    + +
    +
    + + + +
    +
    + + + +
    +
    +
    +

    * denotes a required field

    + + + Cancel + +
    +
    \ No newline at end of file diff --git a/FMS/Pages/Maintenance/UserPosition/Add.cshtml.cs b/FMS/Pages/Maintenance/UserPosition/Add.cshtml.cs index f0cc7e8b..93292819 100644 --- a/FMS/Pages/Maintenance/UserPosition/Add.cshtml.cs +++ b/FMS/Pages/Maintenance/UserPosition/Add.cshtml.cs @@ -1,12 +1,58 @@ +using FMS.Domain.Dto; +using FMS.Domain.Entities.Users; +using FMS.Domain.Repositories; +using FMS.Platform.Extensions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace FMS.Pages.Maintenance.UserPosition { + [Authorize(Roles = UserRoles.SiteMaintenance)] public class AddModel : PageModel { + private readonly IUserPositionRepository _repository; + public AddModel(IUserPositionRepository repository) => _repository = repository; + + [BindProperty] + public UserPositionCreateDto UserPosition { get; set; } + public void OnGet() { + // Method intentionally left empty. + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + UserPosition.TrimAll(); + + if (await _repository.UserPositionNameExistsAsync(UserPosition.Name)) + { + ModelState.AddModelError("UserPosition.Name", "Name entered already exists."); + } + + if (await _repository.UserPositionDescriptionExistsAsync(UserPosition.Description)) + { + ModelState.AddModelError("UserPosition.Description", "Description entered already exists."); + } + + if (!ModelState.IsValid) + { + return Page(); + } + + await _repository.CreateUserPositionAsync(UserPosition); + + TempData?.SetDisplayMessage(Context.Success, + $"{MaintenanceOptions.UserPosition} \"{UserPosition.Name}\" successfully created."); + + return RedirectToPage("./Index", "select", + new { MaintenanceSelection = MaintenanceOptions.UserPosition }); } } } diff --git a/FMS/Pages/Maintenance/UserPosition/Edit.cshtml b/FMS/Pages/Maintenance/UserPosition/Edit.cshtml index 3c804ed3..db5f3b21 100644 --- a/FMS/Pages/Maintenance/UserPosition/Edit.cshtml +++ b/FMS/Pages/Maintenance/UserPosition/Edit.cshtml @@ -1,4 +1,45 @@ -@page +@page "{id:Guid?}" +@using FMS.Pages.Maintenance @model FMS.Pages.Maintenance.UserPosition.EditModel + +@section Scripts { + +} + @{ + ViewData["Title"] = $"Edit {MaintenanceOptions.UserPosition}: {Model.UserPosition.Name}"; } +

    + @ViewData["Title"] + @if (!Model.UserPosition.Active) + { + (Removed) + } +

    + +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    +
    +

    * denotes a required field

    + + + Cancel + +
    +
    diff --git a/FMS/Pages/Maintenance/UserPosition/Edit.cshtml.cs b/FMS/Pages/Maintenance/UserPosition/Edit.cshtml.cs index 8c7c71f8..197d37f0 100644 --- a/FMS/Pages/Maintenance/UserPosition/Edit.cshtml.cs +++ b/FMS/Pages/Maintenance/UserPosition/Edit.cshtml.cs @@ -1,12 +1,87 @@ +using FMS.Domain.Dto; +using FMS.Domain.Entities.Users; +using FMS.Domain.Repositories; +using FMS.Platform.Extensions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.EntityFrameworkCore; namespace FMS.Pages.Maintenance.UserPosition { + [Authorize(Roles = UserRoles.SiteMaintenance)] public class EditModel : PageModel { - public void OnGet() + private readonly IUserPositionRepository _repository; + public EditModel(IUserPositionRepository repository) => _repository = repository; + + [BindProperty] + public UserPositionEditDto UserPosition { get; set; } + + [BindProperty] + public Guid Id { get; set; } + + public async Task OnGetAsync(Guid? id) + { + if (id == null) + { + return NotFound(); + } + + Id = id.Value; + UserPosition = await _repository.GetUserPositionAsync(id.Value); + + if (UserPosition == null) + { + return NotFound(); + } + + return Page(); + } + + public async Task OnPostAsync() { + if (!ModelState.IsValid) + { + return Page(); + } + + UserPosition.TrimAll(); + + // If editing UserPosition, make sure the new Name doesn't already exist before trying to save. + if (await _repository.UserPositionNameExistsAsync(UserPosition.Name, Id)) + { + ModelState.AddModelError("UserPosition.Name", "Name entered already exists."); + } + + if (await _repository.UserPositionDescriptionExistsAsync(UserPosition.Description, Id)) + { + ModelState.AddModelError("UserPosition.Description", "Description entered already exists."); + } + + if (!ModelState.IsValid) + { + return Page(); + } + + try + { + await _repository.UpdateUserPositionAsync(Id, UserPosition); + } + catch (DbUpdateConcurrencyException) + { + if (!await _repository.UserPositionExistsAsync(Id)) + { + return NotFound(); + } + + throw; + } + + TempData?.SetDisplayMessage(Context.Success, + $"{MaintenanceOptions.UserPosition} \"{UserPosition.Name}\" successfully updated."); + + return RedirectToPage("./Index"); } } } diff --git a/FMS/Pages/Maintenance/UserPosition/Index.cshtml b/FMS/Pages/Maintenance/UserPosition/Index.cshtml index 115eb261..9a9ebaa5 100644 --- a/FMS/Pages/Maintenance/UserPosition/Index.cshtml +++ b/FMS/Pages/Maintenance/UserPosition/Index.cshtml @@ -19,8 +19,9 @@ - - + + + @@ -31,6 +32,9 @@ @Html.DisplayFor(m => item.Name, "StringOrNone") @Html.DisplayFor(m => item.Active, "BoolRemoved") + -
    NameActionsNameDescriptionActions
    + @Html.DisplayFor(m => item.Description, "StringOrNone") + Edit @if (item.Active) diff --git a/FMS/Pages/Maintenance/UserProgram/Add.cshtml b/FMS/Pages/Maintenance/UserProgram/Add.cshtml index 18725515..f8a1f8a6 100644 --- a/FMS/Pages/Maintenance/UserProgram/Add.cshtml +++ b/FMS/Pages/Maintenance/UserProgram/Add.cshtml @@ -1,4 +1,36 @@ @page +@using FMS.Pages.Maintenance @model FMS.Pages.Maintenance.UserProgram.AddModel + +@section Scripts { + +} + @{ + ViewData["Title"] = $"Add {MaintenanceOptions.UserProgram}"; } +

    @ViewData["Title"]

    +
    +
    + +
    +
    + + + +
    +
    + + + +
    +
    +
    +

    * denotes a required field

    + + + Cancel + +
    +
    \ No newline at end of file diff --git a/FMS/Pages/Maintenance/UserProgram/Add.cshtml.cs b/FMS/Pages/Maintenance/UserProgram/Add.cshtml.cs index ed4a4515..4cb40ff8 100644 --- a/FMS/Pages/Maintenance/UserProgram/Add.cshtml.cs +++ b/FMS/Pages/Maintenance/UserProgram/Add.cshtml.cs @@ -1,12 +1,58 @@ +using FMS.Domain.Dto; +using FMS.Domain.Entities.Users; +using FMS.Domain.Repositories; +using FMS.Platform.Extensions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace FMS.Pages.Maintenance.UserProgram { + [Authorize(Roles = UserRoles.SiteMaintenance)] public class AddModel : PageModel { + private readonly IUserProgramRepository _repository; + public AddModel(IUserProgramRepository repository) => _repository = repository; + + [BindProperty] + public UserProgramCreateDto UserProgram { get; set; } + public void OnGet() { + // Method intentionally left empty. + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + UserProgram.TrimAll(); + + if (await _repository.UserProgramNameExistsAsync(UserProgram.Name)) + { + ModelState.AddModelError("UserProgram.Name", "Name entered already exists."); + } + + if (await _repository.UserProgramDescriptionExistsAsync(UserProgram.Description)) + { + ModelState.AddModelError("UserProgram.Description", "Description entered already exists."); + } + + if (!ModelState.IsValid) + { + return Page(); + } + + await _repository.CreateUserProgramAsync(UserProgram); + + TempData?.SetDisplayMessage(Context.Success, + $"{MaintenanceOptions.UserProgram} \"{UserProgram.Name}\" successfully created."); + + return RedirectToPage("./Index", "select", + new { MaintenanceSelection = MaintenanceOptions.UserProgram }); } } } diff --git a/FMS/Pages/Maintenance/UserProgram/Edit.cshtml b/FMS/Pages/Maintenance/UserProgram/Edit.cshtml index 27a8a993..bfe6da5b 100644 --- a/FMS/Pages/Maintenance/UserProgram/Edit.cshtml +++ b/FMS/Pages/Maintenance/UserProgram/Edit.cshtml @@ -1,4 +1,45 @@ -@page +@page "{id:Guid?}" +@using FMS.Pages.Maintenance @model FMS.Pages.Maintenance.UserProgram.EditModel + +@section Scripts { + +} + @{ + ViewData["Title"] = $"Edit {MaintenanceOptions.UserProgram}: {Model.UserProgram.Name}"; } +

    + @ViewData["Title"] + @if (!Model.UserProgram.Active) + { + (Removed) + } +

    + +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    +
    +

    * denotes a required field

    + + + Cancel + +
    +
    diff --git a/FMS/Pages/Maintenance/UserProgram/Edit.cshtml.cs b/FMS/Pages/Maintenance/UserProgram/Edit.cshtml.cs index e2557124..37e8687e 100644 --- a/FMS/Pages/Maintenance/UserProgram/Edit.cshtml.cs +++ b/FMS/Pages/Maintenance/UserProgram/Edit.cshtml.cs @@ -1,12 +1,87 @@ +using FMS.Domain.Dto; +using FMS.Domain.Entities.Users; +using FMS.Domain.Repositories; +using FMS.Platform.Extensions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.EntityFrameworkCore; namespace FMS.Pages.Maintenance.UserProgram { + [Authorize(Roles = UserRoles.SiteMaintenance)] public class EditModel : PageModel { - public void OnGet() + private readonly IUserProgramRepository _repository; + public EditModel(IUserProgramRepository repository) => _repository = repository; + + [BindProperty] + public UserProgramEditDto UserProgram { get; set; } + + [BindProperty] + public Guid Id { get; set; } + + public async Task OnGetAsync(Guid? id) + { + if (id == null) + { + return NotFound(); + } + + Id = id.Value; + UserProgram = await _repository.GetUserProgramAsync(id.Value); + + if (UserProgram == null) + { + return NotFound(); + } + + return Page(); + } + + public async Task OnPostAsync() { + if (!ModelState.IsValid) + { + return Page(); + } + + UserProgram.TrimAll(); + + // If editing UserProgram, make sure the new Name doesn't already exist before trying to save. + if (await _repository.UserProgramNameExistsAsync(UserProgram.Name, Id)) + { + ModelState.AddModelError("UserProgram.Name", "Name entered already exists."); + } + + if (await _repository.UserProgramDescriptionExistsAsync(UserProgram.Description, Id)) + { + ModelState.AddModelError("UserProgram.Description", "Description entered already exists."); + } + + if (!ModelState.IsValid) + { + return Page(); + } + + try + { + await _repository.UpdateUserProgramAsync(Id, UserProgram); + } + catch (DbUpdateConcurrencyException) + { + if (!await _repository.UserProgramExistsAsync(Id)) + { + return NotFound(); + } + + throw; + } + + TempData?.SetDisplayMessage(Context.Success, + $"{MaintenanceOptions.UserProgram} \"{UserProgram.Name}\" successfully updated."); + + return RedirectToPage("./Index"); } } } diff --git a/FMS/Pages/Maintenance/UserProgram/Index.cshtml b/FMS/Pages/Maintenance/UserProgram/Index.cshtml index 64047694..f20c2a78 100644 --- a/FMS/Pages/Maintenance/UserProgram/Index.cshtml +++ b/FMS/Pages/Maintenance/UserProgram/Index.cshtml @@ -1,4 +1,53 @@ @page +@using FMS.Pages.Maintenance @model FMS.Pages.Maintenance.UserProgram.IndexModel +@section Scripts { + +} + @{ + ViewData["Title"] = $"Site Maintenance: {MaintenanceOptions.UserProgram}"; } + +← Site Maintenance +

    + @MaintenanceOptions.UserProgram Create New +

    + + +
    + + + + + + + + + + @foreach (var item in Model.UserPrograms) + { + + + + + + } + +
    NameDescriptionActions
    + @Html.DisplayFor(m => item.Name, "StringOrNone") + @Html.DisplayFor(m => item.Active, "BoolRemoved") + + @Html.DisplayFor(m => item.Description, "StringOrNone") + + Edit + @if (item.Active) + { + + } + else + { + + } +
    +
    \ No newline at end of file diff --git a/FMS/Pages/Maintenance/UserProgram/Index.cshtml.cs b/FMS/Pages/Maintenance/UserProgram/Index.cshtml.cs index 95e60665..bb0d2990 100644 --- a/FMS/Pages/Maintenance/UserProgram/Index.cshtml.cs +++ b/FMS/Pages/Maintenance/UserProgram/Index.cshtml.cs @@ -1,12 +1,70 @@ +using FMS.Domain.Dto; +using FMS.Domain.Entities; +using FMS.Domain.Entities.Users; +using FMS.Domain.Repositories; +using FMS.Platform.Extensions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.EntityFrameworkCore; namespace FMS.Pages.Maintenance.UserProgram { + [Authorize(Roles = UserRoles.SiteMaintenance)] public class IndexModel : PageModel { - public void OnGet() + private readonly IUserProgramRepository _repository; + public IndexModel(IUserProgramRepository repository) => _repository = repository; + + public IReadOnlyList UserPrograms { get; private set; } + public DisplayMessage DisplayMessage { get; private set; } + + public async Task OnGetAsync() + { + UserPrograms = await _repository.GetUserProgramListAsync(); + DisplayMessage = TempData?.GetDisplayMessage(); + return Page(); + } + + public async Task OnPostAsync(Guid? itemId) { + if (itemId == null) + { + return BadRequest(); + } + + if (!ModelState.IsValid) + { + return Page(); + } + + var userProgram = await _repository.GetUserProgramAsync(itemId.Value); + + if (userProgram == null) + { + return NotFound(); + } + + try + { + await _repository.UpdateUserProgramStatusAsync(itemId.Value, !userProgram.Active); + } + catch (DbUpdateConcurrencyException) + { + if (!await _repository.UserProgramExistsAsync(itemId.Value)) + { + return NotFound(); + } + + throw; + } + + TempData?.SetDisplayMessage(Context.Success, + userProgram.Active + ? $"{MaintenanceOptions.UserProgram} \"{userProgram.Name}\" successfully removed from list." + : $"{MaintenanceOptions.UserProgram} \"{userProgram.Name}\" successfully restored."); + + return RedirectToPage("./Index"); } } -} +} \ No newline at end of file From 012e0353692156447cfd4503aa9a3f8f0c29229a Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Mon, 1 Jun 2026 19:19:43 -0400 Subject: [PATCH 12/37] Add to Userview --- FMS.Domain/Entities/Users/ApplicationUser.cs | 6 ++++++ FMS.Domain/Services/UserView.cs | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/FMS.Domain/Entities/Users/ApplicationUser.cs b/FMS.Domain/Entities/Users/ApplicationUser.cs index 8b0bc941..1642e63a 100644 --- a/FMS.Domain/Entities/Users/ApplicationUser.cs +++ b/FMS.Domain/Entities/Users/ApplicationUser.cs @@ -25,5 +25,11 @@ public class ApplicationUser : IdentityUser public DateTimeOffset? AccountUpdatedAt { get; set; } public DateTimeOffset? ProfileUpdatedAt { get; set; } public DateTimeOffset? MostRecentLogin { get; set; } + + public UserProgram UserProgram { get; set; } + + public OrganizationalUnit UserUnit { get; set; } + + public UserPosition UserPosition { get; set; } } } diff --git a/FMS.Domain/Services/UserView.cs b/FMS.Domain/Services/UserView.cs index 97137318..96ba584a 100644 --- a/FMS.Domain/Services/UserView.cs +++ b/FMS.Domain/Services/UserView.cs @@ -1,4 +1,5 @@ -using FMS.Domain.Entities.Users; +using FMS.Domain.Entities; +using FMS.Domain.Entities.Users; namespace FMS.Domain.Services { @@ -10,12 +11,18 @@ public UserView(ApplicationUser user) GivenName = user.GivenName; FamilyName = user.FamilyName; Email = user.Email; + UserProgram = user.UserProgram; + UserUnit = user.UserUnit; + UserPosition = user.UserPosition; } public Guid Id { get; } private string GivenName { get; } private string FamilyName { get; } public string Email { get; } + public UserProgram UserProgram { get; } + public OrganizationalUnit UserUnit { get; } + public UserPosition UserPosition { get; } public string DisplayName => string.Join(" ", new[] { GivenName, FamilyName }.Where(s => !string.IsNullOrEmpty(s))); From 98e411b0a47ae8cc72889f79339d3092dcdd113c Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Mon, 1 Jun 2026 19:31:09 -0400 Subject: [PATCH 13/37] EF Migration for User --- ...AddProgramUnitAndPostionToUser.Designer.cs | 3322 +++++++++++++++++ ...01232755_AddProgramUnitAndPostionToUser.cs | 109 + .../Migrations/FmsDbContextModelSnapshot.cs | 42 +- FMS/Pages/Users/Details.cshtml | 4 + 4 files changed, 3476 insertions(+), 1 deletion(-) create mode 100644 FMS.Infrastructure/Migrations/20260601232755_AddProgramUnitAndPostionToUser.Designer.cs create mode 100644 FMS.Infrastructure/Migrations/20260601232755_AddProgramUnitAndPostionToUser.cs diff --git a/FMS.Infrastructure/Migrations/20260601232755_AddProgramUnitAndPostionToUser.Designer.cs b/FMS.Infrastructure/Migrations/20260601232755_AddProgramUnitAndPostionToUser.Designer.cs new file mode 100644 index 00000000..dbf14b1a --- /dev/null +++ b/FMS.Infrastructure/Migrations/20260601232755_AddProgramUnitAndPostionToUser.Designer.cs @@ -0,0 +1,3322 @@ +// +using System; +using FMS.Infrastructure.Contexts; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace FMS.Infrastructure.Migrations +{ + [DbContext(typeof(FmsDbContext))] + [Migration("20260601232755_AddProgramUnitAndPostionToUser")] + partial class AddProgramUnitAndPostionToUser + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("FMS.Domain.Entities.AbandonedInactive", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AbandonedInactives"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ActionTaken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ActionsTaken"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.AllowedActionTaken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActionTakenId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("CompletionDateRequired") + .HasColumnType("bit"); + + b.Property("DueDateRequired") + .HasColumnType("bit"); + + b.Property("EventTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("StartDateRequired") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ActionTakenId"); + + b.HasIndex("EventTypeId"); + + b.ToTable("AllowedActionsTaken"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.BudgetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Code") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("ProjectNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("BudgetCodes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Cabinet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FirstFileLabel") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("Cabinets"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Chemical", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("CasNo") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemicalName") + .HasColumnType("nvarchar(max)"); + + b.Property("CommonName") + .HasColumnType("nvarchar(max)"); + + b.Property("FinalRc") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("MCLs") + .HasColumnType("nvarchar(max)"); + + b.Property("RQ") + .HasColumnType("nvarchar(max)"); + + b.Property("ToxValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Chemicals"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ComplianceOfficer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FamilyName") + .HasColumnType("nvarchar(max)"); + + b.Property("GivenName") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ComplianceOfficers"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("Company") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactTitle") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("FamilyName") + .HasColumnType("nvarchar(max)"); + + b.Property("GivenName") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("PostalCode") + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ContactTypeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Contacts"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ContactType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ContactTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.County", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Counties"); + + b.HasData( + new + { + Id = 131, + Name = "Appling" + }, + new + { + Id = 122, + Name = "Atkinson" + }, + new + { + Id = 110, + Name = "Bacon" + }, + new + { + Id = 105, + Name = "Baker" + }, + new + { + Id = 213, + Name = "Baldwin" + }, + new + { + Id = 179, + Name = "Banks" + }, + new + { + Id = 217, + Name = "Barrow" + }, + new + { + Id = 249, + Name = "Bartow" + }, + new + { + Id = 211, + Name = "Ben Hill" + }, + new + { + Id = 194, + Name = "Berrien" + }, + new + { + Id = 259, + Name = "Bibb" + }, + new + { + Id = 145, + Name = "Bleckley" + }, + new + { + Id = 108, + Name = "Brantley" + }, + new + { + Id = 158, + Name = "Brooks" + }, + new + { + Id = 111, + Name = "Bryan" + }, + new + { + Id = 159, + Name = "Bulloch" + }, + new + { + Id = 203, + Name = "Burke" + }, + new + { + Id = 188, + Name = "Butts" + }, + new + { + Id = 112, + Name = "Calhoun" + }, + new + { + Id = 169, + Name = "Camden" + }, + new + { + Id = 113, + Name = "Candler" + }, + new + { + Id = 241, + Name = "Carroll" + }, + new + { + Id = 225, + Name = "Catoosa" + }, + new + { + Id = 114, + Name = "Charlton" + }, + new + { + Id = 242, + Name = "Chatham" + }, + new + { + Id = 137, + Name = "Chattahoochee" + }, + new + { + Id = 174, + Name = "Chattooga" + }, + new + { + Id = 243, + Name = "Cherokee" + }, + new + { + Id = 252, + Name = "Clarke" + }, + new + { + Id = 104, + Name = "Clay" + }, + new + { + Id = 204, + Name = "Clayton" + }, + new + { + Id = 178, + Name = "Clinch" + }, + new + { + Id = 245, + Name = "Cobb" + }, + new + { + Id = 146, + Name = "Coffee" + }, + new + { + Id = 227, + Name = "Colquitt" + }, + new + { + Id = 232, + Name = "Columbia" + }, + new + { + Id = 212, + Name = "Cook" + }, + new + { + Id = 250, + Name = "Coweta" + }, + new + { + Id = 115, + Name = "Crawford" + }, + new + { + Id = 220, + Name = "Crisp" + }, + new + { + Id = 170, + Name = "Dade" + }, + new + { + Id = 180, + Name = "Dawson" + }, + new + { + Id = 244, + Name = "Decatur" + }, + new + { + Id = 218, + Name = "DeKalb" + }, + new + { + Id = 175, + Name = "Dodge" + }, + new + { + Id = 189, + Name = "Dooly" + }, + new + { + Id = 257, + Name = "Dougherty" + }, + new + { + Id = 253, + Name = "Douglas" + }, + new + { + Id = 195, + Name = "Early" + }, + new + { + Id = 103, + Name = "Echols" + }, + new + { + Id = 123, + Name = "Effingham" + }, + new + { + Id = 200, + Name = "Elbert" + }, + new + { + Id = 198, + Name = "Emanuel" + }, + new + { + Id = 109, + Name = "Evans" + }, + new + { + Id = 176, + Name = "Fannin" + }, + new + { + Id = 235, + Name = "Fayette" + }, + new + { + Id = 246, + Name = "Floyd" + }, + new + { + Id = 238, + Name = "Forsyth" + }, + new + { + Id = 201, + Name = "Franklin" + }, + new + { + Id = 261, + Name = "Fulton" + }, + new + { + Id = 165, + Name = "Gilmer" + }, + new + { + Id = 124, + Name = "Glascock" + }, + new + { + Id = 216, + Name = "Glynn" + }, + new + { + Id = 230, + Name = "Gordon" + }, + new + { + Id = 208, + Name = "Grady" + }, + new + { + Id = 171, + Name = "Greene" + }, + new + { + Id = 226, + Name = "Gwinnett" + }, + new + { + Id = 214, + Name = "Habersham" + }, + new + { + Id = 256, + Name = "Hall" + }, + new + { + Id = 107, + Name = "Hancock" + }, + new + { + Id = 209, + Name = "Haralson" + }, + new + { + Id = 138, + Name = "Harris" + }, + new + { + Id = 190, + Name = "Hart" + }, + new + { + Id = 152, + Name = "Heard" + }, + new + { + Id = 248, + Name = "Henry" + }, + new + { + Id = 239, + Name = "Houston" + }, + new + { + Id = 139, + Name = "Irwin" + }, + new + { + Id = 222, + Name = "Jackson" + }, + new + { + Id = 147, + Name = "Jasper" + }, + new + { + Id = 160, + Name = "Jeff Davis" + }, + new + { + Id = 199, + Name = "Jefferson" + }, + new + { + Id = 153, + Name = "Jenkins" + }, + new + { + Id = 140, + Name = "Johnson" + }, + new + { + Id = 161, + Name = "Jones" + }, + new + { + Id = 181, + Name = "Lamar" + }, + new + { + Id = 141, + Name = "Lanier" + }, + new + { + Id = 223, + Name = "Laurens" + }, + new + { + Id = 172, + Name = "Lee" + }, + new + { + Id = 148, + Name = "Liberty" + }, + new + { + Id = 125, + Name = "Lincoln" + }, + new + { + Id = 102, + Name = "Long" + }, + new + { + Id = 254, + Name = "Lowndes" + }, + new + { + Id = 192, + Name = "Lumpkin" + }, + new + { + Id = 182, + Name = "Macon" + }, + new + { + Id = 166, + Name = "Madison" + }, + new + { + Id = 133, + Name = "Marion" + }, + new + { + Id = 210, + Name = "McDuffie" + }, + new + { + Id = 126, + Name = "McIntosh" + }, + new + { + Id = 196, + Name = "Meriwether" + }, + new + { + Id = 154, + Name = "Miller" + }, + new + { + Id = 215, + Name = "Mitchell" + }, + new + { + Id = 184, + Name = "Monroe" + }, + new + { + Id = 134, + Name = "Montgomery" + }, + new + { + Id = 197, + Name = "Morgan" + }, + new + { + Id = 205, + Name = "Murray" + }, + new + { + Id = 258, + Name = "Muscogee" + }, + new + { + Id = 231, + Name = "Newton" + }, + new + { + Id = 191, + Name = "Oconee" + }, + new + { + Id = 127, + Name = "Oglethorpe" + }, + new + { + Id = 219, + Name = "Paulding" + }, + new + { + Id = 221, + Name = "Peach" + }, + new + { + Id = 185, + Name = "Pickens" + }, + new + { + Id = 116, + Name = "Pierce" + }, + new + { + Id = 149, + Name = "Pike" + }, + new + { + Id = 233, + Name = "Polk" + }, + new + { + Id = 155, + Name = "Pulaski" + }, + new + { + Id = 183, + Name = "Putnam" + }, + new + { + Id = 100, + Name = "Quitman" + }, + new + { + Id = 167, + Name = "Rabun" + }, + new + { + Id = 156, + Name = "Randolph" + }, + new + { + Id = 260, + Name = "Richmond" + }, + new + { + Id = 251, + Name = "Rockdale" + }, + new + { + Id = 135, + Name = "Schley" + }, + new + { + Id = 157, + Name = "Screven" + }, + new + { + Id = 150, + Name = "Seminole" + }, + new + { + Id = 240, + Name = "Spalding" + }, + new + { + Id = 229, + Name = "Stephens" + }, + new + { + Id = 117, + Name = "Stewart" + }, + new + { + Id = 236, + Name = "Sumter" + }, + new + { + Id = 128, + Name = "Talbot" + }, + new + { + Id = 118, + Name = "Taliaferro" + }, + new + { + Id = 136, + Name = "Tattnall" + }, + new + { + Id = 142, + Name = "Taylor" + }, + new + { + Id = 164, + Name = "Telfair" + }, + new + { + Id = 173, + Name = "Terrell" + }, + new + { + Id = 234, + Name = "Thomas" + }, + new + { + Id = 237, + Name = "Tift" + }, + new + { + Id = 151, + Name = "Toombs" + }, + new + { + Id = 129, + Name = "Towns" + }, + new + { + Id = 101, + Name = "Treutlen" + }, + new + { + Id = 247, + Name = "Troup" + }, + new + { + Id = 186, + Name = "Turner" + }, + new + { + Id = 130, + Name = "Twiggs" + }, + new + { + Id = 168, + Name = "Union" + }, + new + { + Id = 202, + Name = "Upson" + }, + new + { + Id = 224, + Name = "Walker" + }, + new + { + Id = 228, + Name = "Walton" + }, + new + { + Id = 177, + Name = "Ware" + }, + new + { + Id = 162, + Name = "Warren" + }, + new + { + Id = 206, + Name = "Washington" + }, + new + { + Id = 143, + Name = "Wayne" + }, + new + { + Id = 120, + Name = "Webster" + }, + new + { + Id = 106, + Name = "Wheeler" + }, + new + { + Id = 193, + Name = "White" + }, + new + { + Id = 255, + Name = "Whitfield" + }, + new + { + Id = 144, + Name = "Wilcox" + }, + new + { + Id = 163, + Name = "Wilkes" + }, + new + { + Id = 207, + Name = "Wilkinson" + }, + new + { + Id = 187, + Name = "Worth" + }, + new + { + Id = 99, + Name = "OUT OF STATE" + }); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActionTakenId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comment") + .HasColumnType("nvarchar(max)"); + + b.Property("CompletionDate") + .HasColumnType("date"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("EventAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("EventContractorId") + .HasColumnType("uniqueidentifier"); + + b.Property("EventTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ActionTakenId"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("EventContractorId"); + + b.HasIndex("EventTypeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.EventContractor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("EventContractors"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.EventType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("ReportEligible") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("EventTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("AdditionalDataRequested") + .HasColumnType("bit"); + + b.Property("Address") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("BudgetCodeId") + .HasColumnType("uniqueidentifier"); + + b.Property("City") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("CountyId") + .HasColumnType("int"); + + b.Property("DeferredOnSiteScoring") + .HasColumnType("bit"); + + b.Property("DeterminationLetterDate") + .HasColumnType("date"); + + b.Property("FacilityNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileId") + .HasColumnType("uniqueidentifier"); + + b.Property("HSInumber") + .HasMaxLength(5) + .HasColumnType("nvarchar(5)"); + + b.Property("HasERecord") + .HasColumnType("bit"); + + b.Property("HistoricalComplianceOfficer") + .HasColumnType("nvarchar(max)"); + + b.Property("HistoricalUnit") + .HasColumnType("nvarchar(max)"); + + b.Property("ImageChecked") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("IsRetained") + .HasColumnType("bit"); + + b.Property("Latitude") + .HasColumnType("decimal(8, 6)"); + + b.Property("Location") + .HasColumnType("nvarchar(max)"); + + b.Property("Longitude") + .HasColumnType("decimal(9, 6)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationalUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("PreRQSMcleanup") + .HasColumnType("bit"); + + b.Property("RNDateReceived") + .HasColumnType("date"); + + b.Property("State") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("VRPReferral") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("Active"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Active"), new[] { "FileId" }); + + b.HasIndex("BudgetCodeId"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("CountyId"); + + b.HasIndex("FacilityStatusId"); + + b.HasIndex("FacilityTypeId"); + + b.HasIndex("FileId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("FileId"), new[] { "Active", "FacilityNumber", "FacilityTypeId", "OrganizationalUnitId", "BudgetCodeId", "Name", "ComplianceOfficerId", "FacilityStatusId", "Location", "Address", "City", "State", "PostalCode", "Latitude", "Longitude", "CountyId", "IsRetained", "AdditionalDataRequested", "Comments", "DeferredOnSiteScoring", "DeterminationLetterDate", "HSInumber", "HasERecord", "HistoricalComplianceOfficer", "HistoricalUnit", "ImageChecked", "PreRQSMcleanup", "RNDateReceived", "VRPReferral" }); + + b.HasIndex("OrganizationalUnitId"); + + b.HasIndex("Active", "FileId"); + + b.HasIndex("Active", "FacilityTypeId", "Address"); + + b.HasIndex("Active", "FacilityTypeId", "Address", "City"); + + b.ToTable("Facilities"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FacilityStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FacilityStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FacilityType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("FacilityTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.File", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FileLabel") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FileLabel") + .IsUnique() + .HasFilter("[FileLabel] IS NOT NULL"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FundingSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FundingSources"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GapsAssessment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GapsAssessments"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("A") + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("B1") + .HasColumnType("int"); + + b.Property("B2") + .HasColumnType("int"); + + b.Property("C") + .HasColumnType("int"); + + b.Property("CASNO") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemName") + .HasColumnType("nvarchar(max)"); + + b.Property("D2") + .HasColumnType("int"); + + b.Property("D3") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("E1") + .HasColumnType("int"); + + b.Property("E2") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("GWScore") + .HasColumnType("decimal(18,2)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Other") + .HasColumnType("nvarchar(max)"); + + b.Property("SubstanceId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("SubstanceId"); + + b.ToTable("GroundwaterScores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GroundwaterStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.HsrpFacilityProperties", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("BrownfieldDate") + .HasColumnType("date"); + + b.Property("BrownfieldTerminated") + .HasColumnType("bit"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("DateDeListed") + .HasColumnType("date"); + + b.Property("DateListed") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationalUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("VRPDate") + .HasColumnType("date"); + + b.Property("VRPTerminated") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("OrganizationalUnitId"); + + b.ToTable("HsrpFacilityProperties"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LocationClassId") + .HasColumnType("uniqueidentifier"); + + b.Property("MapType") + .HasColumnType("nvarchar(max)"); + + b.Property("MapZoom") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("LocationClassId"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.LocationClass", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("LocationClasses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OnsiteScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("A") + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("B") + .HasColumnType("int"); + + b.Property("C") + .HasColumnType("int"); + + b.Property("CASNO") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemName1D") + .HasColumnType("nvarchar(max)"); + + b.Property("D2") + .HasColumnType("int"); + + b.Property("D3") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("E1") + .HasColumnType("int"); + + b.Property("E2") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("OnsiteScoreValue") + .HasColumnType("decimal(18,2)"); + + b.Property("Other1D") + .HasColumnType("nvarchar(max)"); + + b.Property("SubstanceId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("SubstanceId"); + + b.ToTable("OnsiteScores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OrganizationalUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OrganizationalUnits"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OverallStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OverallStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Parcel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Acres") + .HasColumnType("float"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("DeListDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ListDate") + .HasColumnType("date"); + + b.Property("ParcelNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("ParcelTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("SubListParcelName") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("ParcelTypeId"); + + b.ToTable("Parcels"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ParcelType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ParcelTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Phone", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("ContactId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Number") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneType") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ContactId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Phones"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.RetentionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("BoxNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("ConsignmentNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("EndYear") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("RetentionSchedule") + .HasColumnType("nvarchar(max)"); + + b.Property("ShelfNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("FacilityId"), new[] { "Active", "StartYear", "EndYear", "ConsignmentNumber", "BoxNumber", "ShelfNumber", "RetentionSchedule" }); + + b.ToTable("RetentionRecords"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Score", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ScoredDate") + .HasColumnType("date"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UseComments") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.SoilStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SoilStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.SourceStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SourceStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Status", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AbandonedInactiveId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("CostEstimate") + .HasColumnType("decimal(18,2)"); + + b.Property("CostEstimateDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("FinancialAssurance") + .HasColumnType("bit"); + + b.Property("FundingSourceId") + .HasColumnType("uniqueidentifier"); + + b.Property("GAPSAssessmentId") + .HasColumnType("uniqueidentifier"); + + b.Property("GAPSModelDate") + .HasColumnType("date"); + + b.Property("GAPSNoOfUnknowns") + .HasColumnType("int"); + + b.Property("GAPSScore") + .HasColumnType("int"); + + b.Property("GroundwaterDate") + .HasColumnType("date"); + + b.Property("GroundwaterStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("ISWQS") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LandFill") + .HasColumnType("bit"); + + b.Property("Lien") + .HasColumnType("bit"); + + b.Property("OverallDate") + .HasColumnType("date"); + + b.Property("OverallStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("ReportComments") + .HasColumnType("nvarchar(max)"); + + b.Property("SoilDate") + .HasColumnType("date"); + + b.Property("SoilStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("SolidWastePermitNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("SourceDate") + .HasColumnType("date"); + + b.Property("SourceStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("UEC") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("AbandonedInactiveId"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("FundingSourceId"); + + b.HasIndex("GAPSAssessmentId"); + + b.HasIndex("GroundwaterStatusId"); + + b.HasIndex("OverallStatusId"); + + b.HasIndex("SoilStatusId"); + + b.HasIndex("SourceStatusId"); + + b.ToTable("Statuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Substance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("ChemicalId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("Groundwater") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Soil") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UseForGroundwaterScoring") + .HasColumnType("bit"); + + b.Property("UseForSoilScoring") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ChemicalId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Substances"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.UserPosition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("UserPositions"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.UserProgram", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("UserPrograms"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Users.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FamilyName") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("GivenName") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("MostRecentLogin") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ObjectId") + .HasMaxLength(36) + .HasColumnType("nvarchar(36)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("ProfileUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("UserPositionId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserProgramId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserUnitId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("ObjectId") + .IsUnique() + .HasFilter("[ObjectId] IS NOT NULL"); + + b.HasIndex("UserPositionId"); + + b.HasIndex("UserProgramId"); + + b.HasIndex("UserUnitId"); + + b.ToTable("AppUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityPasskeyData", b => + { + b.Property("AttestationObject") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("ClientDataJson") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("IsBackedUp") + .HasColumnType("bit"); + + b.Property("IsBackupEligible") + .HasColumnType("bit"); + + b.Property("IsUserVerified") + .HasColumnType("bit"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("SignCount") + .HasColumnType("bigint"); + + b.PrimitiveCollection("Transports") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.ToTable("IdentityPasskeyData"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AppRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AppRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AppUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AppUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AppUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AppUserTokens", (string)null); + }); + + modelBuilder.Entity("FMS.Domain.Entities.AllowedActionTaken", b => + { + b.HasOne("FMS.Domain.Entities.ActionTaken", "ActionTaken") + .WithMany() + .HasForeignKey("ActionTakenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.EventType", "EventType") + .WithMany() + .HasForeignKey("EventTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ActionTaken"); + + b.Navigation("EventType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.HasOne("FMS.Domain.Entities.ContactType", "ContactType") + .WithMany() + .HasForeignKey("ContactTypeId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Contacts") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ContactType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Event", b => + { + b.HasOne("FMS.Domain.Entities.ActionTaken", "ActionTaken") + .WithMany() + .HasForeignKey("ActionTakenId"); + + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.EventContractor", "EventContractor") + .WithMany() + .HasForeignKey("EventContractorId"); + + b.HasOne("FMS.Domain.Entities.EventType", "EventType") + .WithMany() + .HasForeignKey("EventTypeId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Events") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ActionTaken"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("EventContractor"); + + b.Navigation("EventType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.HasOne("FMS.Domain.Entities.BudgetCode", "BudgetCode") + .WithMany() + .HasForeignKey("BudgetCodeId"); + + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.County", "County") + .WithMany() + .HasForeignKey("CountyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.FacilityStatus", "FacilityStatus") + .WithMany() + .HasForeignKey("FacilityStatusId"); + + b.HasOne("FMS.Domain.Entities.FacilityType", "FacilityType") + .WithMany() + .HasForeignKey("FacilityTypeId"); + + b.HasOne("FMS.Domain.Entities.File", "File") + .WithMany("Facilities") + .HasForeignKey("FileId"); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "OrganizationalUnit") + .WithMany() + .HasForeignKey("OrganizationalUnitId"); + + b.Navigation("BudgetCode"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("County"); + + b.Navigation("FacilityStatus"); + + b.Navigation("FacilityType"); + + b.Navigation("File"); + + b.Navigation("OrganizationalUnit"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterScore", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("GroundwaterScoreDetails") + .HasForeignKey("FMS.Domain.Entities.GroundwaterScore", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Substance", "Substance") + .WithMany() + .HasForeignKey("SubstanceId"); + + b.Navigation("Substance"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.HsrpFacilityProperties", b => + { + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("HsrpFacilityProperties") + .HasForeignKey("FMS.Domain.Entities.HsrpFacilityProperties", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "OrganizationalUnit") + .WithMany() + .HasForeignKey("OrganizationalUnitId"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("OrganizationalUnit"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Location", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("LocationDetails") + .HasForeignKey("FMS.Domain.Entities.Location", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.LocationClass", "LocationClass") + .WithMany() + .HasForeignKey("LocationClassId"); + + b.Navigation("LocationClass"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OnsiteScore", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("OnsiteScoreDetails") + .HasForeignKey("FMS.Domain.Entities.OnsiteScore", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Substance", "Substance") + .WithMany() + .HasForeignKey("SubstanceId"); + + b.Navigation("Substance"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Parcel", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Parcels") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.ParcelType", "ParcelType") + .WithMany() + .HasForeignKey("ParcelTypeId"); + + b.Navigation("ParcelType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Phone", b => + { + b.HasOne("FMS.Domain.Entities.Contact", null) + .WithMany("Phones") + .HasForeignKey("ContactId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Phones") + .HasForeignKey("FacilityId"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.RetentionRecord", b => + { + b.HasOne("FMS.Domain.Entities.Facility", "Facility") + .WithMany("RetentionRecords") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Score", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("ScoreDetails") + .HasForeignKey("FMS.Domain.Entities.Score", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Status", b => + { + b.HasOne("FMS.Domain.Entities.AbandonedInactive", "AbandonedInactive") + .WithMany() + .HasForeignKey("AbandonedInactiveId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("StatusDetails") + .HasForeignKey("FMS.Domain.Entities.Status", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.FundingSource", "FundingSource") + .WithMany() + .HasForeignKey("FundingSourceId"); + + b.HasOne("FMS.Domain.Entities.GapsAssessment", "GAPSAssessment") + .WithMany() + .HasForeignKey("GAPSAssessmentId"); + + b.HasOne("FMS.Domain.Entities.GroundwaterStatus", "GroundwaterStatus") + .WithMany() + .HasForeignKey("GroundwaterStatusId"); + + b.HasOne("FMS.Domain.Entities.OverallStatus", "OverallStatus") + .WithMany() + .HasForeignKey("OverallStatusId"); + + b.HasOne("FMS.Domain.Entities.SoilStatus", "SoilStatus") + .WithMany() + .HasForeignKey("SoilStatusId"); + + b.HasOne("FMS.Domain.Entities.SourceStatus", "SourceStatus") + .WithMany() + .HasForeignKey("SourceStatusId"); + + b.Navigation("AbandonedInactive"); + + b.Navigation("FundingSource"); + + b.Navigation("GAPSAssessment"); + + b.Navigation("GroundwaterStatus"); + + b.Navigation("OverallStatus"); + + b.Navigation("SoilStatus"); + + b.Navigation("SourceStatus"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Substance", b => + { + b.HasOne("FMS.Domain.Entities.Chemical", "Chemical") + .WithMany() + .HasForeignKey("ChemicalId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Substances") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Chemical"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Users.ApplicationUser", b => + { + b.HasOne("FMS.Domain.Entities.UserPosition", "UserPosition") + .WithMany() + .HasForeignKey("UserPositionId"); + + b.HasOne("FMS.Domain.Entities.UserProgram", "UserProgram") + .WithMany() + .HasForeignKey("UserProgramId"); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "UserUnit") + .WithMany() + .HasForeignKey("UserUnitId"); + + b.Navigation("UserPosition"); + + b.Navigation("UserProgram"); + + b.Navigation("UserUnit"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.Navigation("Phones"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.Navigation("Contacts"); + + b.Navigation("Events"); + + b.Navigation("GroundwaterScoreDetails"); + + b.Navigation("HsrpFacilityProperties"); + + b.Navigation("LocationDetails"); + + b.Navigation("OnsiteScoreDetails"); + + b.Navigation("Parcels"); + + b.Navigation("Phones"); + + b.Navigation("RetentionRecords"); + + b.Navigation("ScoreDetails"); + + b.Navigation("StatusDetails"); + + b.Navigation("Substances"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.File", b => + { + b.Navigation("Facilities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FMS.Infrastructure/Migrations/20260601232755_AddProgramUnitAndPostionToUser.cs b/FMS.Infrastructure/Migrations/20260601232755_AddProgramUnitAndPostionToUser.cs new file mode 100644 index 00000000..010664c8 --- /dev/null +++ b/FMS.Infrastructure/Migrations/20260601232755_AddProgramUnitAndPostionToUser.cs @@ -0,0 +1,109 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace FMS.Infrastructure.Migrations +{ + /// + public partial class AddProgramUnitAndPostionToUser : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UserPositionId", + table: "AppUsers", + type: "uniqueidentifier", + nullable: true); + + migrationBuilder.AddColumn( + name: "UserProgramId", + table: "AppUsers", + type: "uniqueidentifier", + nullable: true); + + migrationBuilder.AddColumn( + name: "UserUnitId", + table: "AppUsers", + type: "uniqueidentifier", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_AppUsers_UserPositionId", + table: "AppUsers", + column: "UserPositionId"); + + migrationBuilder.CreateIndex( + name: "IX_AppUsers_UserProgramId", + table: "AppUsers", + column: "UserProgramId"); + + migrationBuilder.CreateIndex( + name: "IX_AppUsers_UserUnitId", + table: "AppUsers", + column: "UserUnitId"); + + migrationBuilder.AddForeignKey( + name: "FK_AppUsers_OrganizationalUnits_UserUnitId", + table: "AppUsers", + column: "UserUnitId", + principalTable: "OrganizationalUnits", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_AppUsers_UserPositions_UserPositionId", + table: "AppUsers", + column: "UserPositionId", + principalTable: "UserPositions", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_AppUsers_UserPrograms_UserProgramId", + table: "AppUsers", + column: "UserProgramId", + principalTable: "UserPrograms", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AppUsers_OrganizationalUnits_UserUnitId", + table: "AppUsers"); + + migrationBuilder.DropForeignKey( + name: "FK_AppUsers_UserPositions_UserPositionId", + table: "AppUsers"); + + migrationBuilder.DropForeignKey( + name: "FK_AppUsers_UserPrograms_UserProgramId", + table: "AppUsers"); + + migrationBuilder.DropIndex( + name: "IX_AppUsers_UserPositionId", + table: "AppUsers"); + + migrationBuilder.DropIndex( + name: "IX_AppUsers_UserProgramId", + table: "AppUsers"); + + migrationBuilder.DropIndex( + name: "IX_AppUsers_UserUnitId", + table: "AppUsers"); + + migrationBuilder.DropColumn( + name: "UserPositionId", + table: "AppUsers"); + + migrationBuilder.DropColumn( + name: "UserProgramId", + table: "AppUsers"); + + migrationBuilder.DropColumn( + name: "UserUnitId", + table: "AppUsers"); + } + } +} diff --git a/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs b/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs index c214ba20..73090930 100644 --- a/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs +++ b/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using FMS.Infrastructure.Contexts; using Microsoft.EntityFrameworkCore; @@ -2629,6 +2629,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(256) .HasColumnType("nvarchar(256)"); + b.Property("UserPositionId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserProgramId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserUnitId") + .HasColumnType("uniqueidentifier"); + b.HasKey("Id"); b.HasIndex("NormalizedEmail") @@ -2639,6 +2648,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasDatabaseName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); + b.HasIndex("ObjectId") + .IsUnique() + .HasFilter("[ObjectId] IS NOT NULL"); + + b.HasIndex("UserPositionId"); + + b.HasIndex("UserProgramId"); + + b.HasIndex("UserUnitId"); + b.ToTable("AppUsers", (string)null); }); @@ -3191,6 +3210,27 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Chemical"); }); + modelBuilder.Entity("FMS.Domain.Entities.Users.ApplicationUser", b => + { + b.HasOne("FMS.Domain.Entities.UserPosition", "UserPosition") + .WithMany() + .HasForeignKey("UserPositionId"); + + b.HasOne("FMS.Domain.Entities.UserProgram", "UserProgram") + .WithMany() + .HasForeignKey("UserProgramId"); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "UserUnit") + .WithMany() + .HasForeignKey("UserUnitId"); + + b.Navigation("UserPosition"); + + b.Navigation("UserProgram"); + + b.Navigation("UserUnit"); + }); + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) diff --git a/FMS/Pages/Users/Details.cshtml b/FMS/Pages/Users/Details.cshtml index 114c6979..4682c27f 100644 --- a/FMS/Pages/Users/Details.cshtml +++ b/FMS/Pages/Users/Details.cshtml @@ -20,6 +20,10 @@ { Edit Roles } + @if (User.Equals(Model.Id) || User.IsInRole(UserRoles.UserMaintenance)) + { + Edit Program/Unit + }

    @Model.Email

    From cf1d78b84869304d19636446c3ecbe976247ec44 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 2 Jun 2026 15:59:59 -0400 Subject: [PATCH 14/37] Add User Dropdowns --- FMS.Domain/Entities/UserPosition.cs | 3 -- .../Repositories/IItemsListRepository.cs | 4 ++ FMS.Domain/Services/IUserService.cs | 5 ++- .../Repositories/ItemsListRepository.cs | 36 +++++++++++++++ FMS.Infrastructure/Services/UserService.cs | 18 +++++++- FMS/Helpers/SelectListHelper.cs | 4 ++ FMS/Pages/Account/Index.cshtml | 38 ++++++++++++++++ FMS/Pages/Account/Index.cshtml.cs | 45 ++++++++++++++++++- 8 files changed, 147 insertions(+), 6 deletions(-) diff --git a/FMS.Domain/Entities/UserPosition.cs b/FMS.Domain/Entities/UserPosition.cs index 82351dfd..5febc52e 100644 --- a/FMS.Domain/Entities/UserPosition.cs +++ b/FMS.Domain/Entities/UserPosition.cs @@ -1,8 +1,5 @@ using FMS.Domain.Dto; using FMS.Domain.Entities.Base; -using System; -using System.Collections.Generic; -using System.Text; namespace FMS.Domain.Entities { diff --git a/FMS.Domain/Repositories/IItemsListRepository.cs b/FMS.Domain/Repositories/IItemsListRepository.cs index fa469d0c..736d232f 100644 --- a/FMS.Domain/Repositories/IItemsListRepository.cs +++ b/FMS.Domain/Repositories/IItemsListRepository.cs @@ -32,6 +32,8 @@ public interface IItemsListRepository : IDisposable Task> GetSoilStatusesListAsync(bool includeInactive = false); Task> GetSourceStatusesListAsync(bool includeInactive = false); Task> GetGapsAssessmentListAsync(bool includeInactive = false); + Task> GetUserProgramListAsync(bool includeInactive = false); + Task> GetUserPositionListAsync(bool includeInactive = false); #endregion @@ -57,6 +59,8 @@ public interface IItemsListRepository : IDisposable Task GetSoilStatusNameAsync(Guid? id); Task GetSourceStatusNameAsync(Guid? id); Task GetGAPSAssessmentNameAsync(Guid? id); + Task GetUserProgramNameAsync(Guid? id); + Task GetUserPositionNameAsync(Guid? id); #endregion } diff --git a/FMS.Domain/Services/IUserService.cs b/FMS.Domain/Services/IUserService.cs index 59233c30..6ca45ced 100644 --- a/FMS.Domain/Services/IUserService.cs +++ b/FMS.Domain/Services/IUserService.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Identity; +using FMS.Domain.Entities; +using Microsoft.AspNetCore.Identity; namespace FMS.Domain.Services { @@ -16,6 +17,8 @@ public interface IUserService public Task> GetUserRolesAsync(Guid id); public Task UpdateUserRolesAsync(Guid id, Dictionary roleSettings); + public Task UpdateUserDesignationsAsync(Guid id, UserProgram userProgram, OrganizationalUnit userUnit, UserPosition userPosition); + // User search public Task> GetUsersAsync(string nameFilter, string emailFilter, string role); } diff --git a/FMS.Infrastructure/Repositories/ItemsListRepository.cs b/FMS.Infrastructure/Repositories/ItemsListRepository.cs index 787bc5ef..a3d9069b 100644 --- a/FMS.Infrastructure/Repositories/ItemsListRepository.cs +++ b/FMS.Infrastructure/Repositories/ItemsListRepository.cs @@ -154,6 +154,20 @@ await _context.GapsAssessments.AsNoTracking() .Select(e => new ListItem() { Id = e.Id, Name = e.Name }) .ToListAsync(); + public async Task> GetUserProgramListAsync(bool includeInactive = false) => + await _context.UserPrograms.AsNoTracking() + .Where(e => e.Active || includeInactive) + .OrderBy(e => e.Name) + .Select(e => new ListItem() { Id = e.Id, Name = e.Name }) + .ToListAsync(); + + public async Task> GetUserPositionListAsync(bool includeInactive = false) => + await _context.UserPositions.AsNoTracking() + .Where(e => e.Active || includeInactive) + .OrderBy(e => e.Name) + .Select(e => new ListItem() { Id = e.Id, Name = e.Name }) + .ToListAsync(); + #endregion #region "Get single ListItem names" @@ -378,6 +392,28 @@ public async Task GetGAPSAssessmentNameAsync(Guid? id) return null; } + public async Task GetUserProgramNameAsync(Guid? id) + { + if (id.HasValue) + { + var item = await _context.UserPrograms.AsNoTracking() + .SingleOrDefaultAsync(e => e.Id == id); + return item?.Name; + } + return null; + } + + public async Task GetUserPositionNameAsync(Guid? id) + { + if (id.HasValue) + { + var item = await _context.UserPositions.AsNoTracking() + .SingleOrDefaultAsync(e => e.Id == id); + return item?.Name; + } + return null; + } + #endregion #region IDisposable Support diff --git a/FMS.Infrastructure/Services/UserService.cs b/FMS.Infrastructure/Services/UserService.cs index e9666b00..2cc97874 100644 --- a/FMS.Infrastructure/Services/UserService.cs +++ b/FMS.Infrastructure/Services/UserService.cs @@ -1,4 +1,5 @@ -using FMS.Domain.Entities.Users; +using FMS.Domain.Entities; +using FMS.Domain.Entities.Users; using FMS.Domain.Services; using FMS.Infrastructure.Contexts; using Microsoft.AspNetCore.Http; @@ -89,6 +90,21 @@ public async Task UpdateUserRolesAsync(Guid id, Dictionary UpdateUserDesignationsAsync(Guid id, UserProgram userProgram, OrganizationalUnit userUnit, UserPosition userPosition) + { + var user = await _userManager.FindByIdAsync(id.ToString()); + if (user == null) + { + return IdentityResult.Failed(_userManager.ErrorDescriber.DefaultError()); + } + + user.UserProgram = userProgram; + user.UserUnit = userUnit; + user.UserPosition = userPosition; + + return await _userManager.UpdateAsync(user); + } + // User search private Task> GetUsersAsync(string nameFilter, string emailFilter) => _context.Users.AsNoTracking() diff --git a/FMS/Helpers/SelectListHelper.cs b/FMS/Helpers/SelectListHelper.cs index 1556a958..c3ffc523 100644 --- a/FMS/Helpers/SelectListHelper.cs +++ b/FMS/Helpers/SelectListHelper.cs @@ -28,6 +28,8 @@ public interface ISelectListHelper Task SoilStatusesSelectListAsync(bool includeInactive = false); Task SourceStatusesSelectListAsync(bool includeInactive = false); Task GAPSAssessmentSelectListAsync(bool includeInactive = false); + Task UserProgramsSelectListAsync(bool includeInactive = false); + Task UserPositionsSelectListAsync(bool includeInactive = false); } public class SelectListHelper : ISelectListHelper @@ -75,6 +77,8 @@ public async Task LocationClassesSelectListAsync(bool includeInactiv public async Task SoilStatusesSelectListAsync(bool includeInactive = false) => (await _listRepository.GetSoilStatusesListAsync(includeInactive)).ToSelectList(); public async Task SourceStatusesSelectListAsync(bool includeInactive = false) => (await _listRepository.GetSourceStatusesListAsync(includeInactive)).ToSelectList(); public async Task GAPSAssessmentSelectListAsync(bool includeInactive = false) => (await _listRepository.GetGapsAssessmentListAsync(includeInactive)).ToSelectList(); + public async Task UserProgramsSelectListAsync(bool includeInactive = false) => (await _listRepository.GetUserProgramListAsync(includeInactive)).ToSelectList(); + public async Task UserPositionsSelectListAsync(bool includeInactive = false) => (await _listRepository.GetUserPositionListAsync(includeInactive)).ToSelectList(); } /// diff --git a/FMS/Pages/Account/Index.cshtml b/FMS/Pages/Account/Index.cshtml index eebeb4bb..df597fa8 100644 --- a/FMS/Pages/Account/Index.cshtml +++ b/FMS/Pages/Account/Index.cshtml @@ -5,12 +5,50 @@ ViewData["Title"] = "Account Profile"; } +@section Scripts { + +} +

    @ViewData["Title"]

    +

    @Model.CurrentUser.DisplayName

    @Model.CurrentUser.Email

    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    + + + Clear +
    +
    +
    +
    + @if (Model.Roles != null && Model.Roles.Count > 0) {

    Roles

    diff --git a/FMS/Pages/Account/Index.cshtml.cs b/FMS/Pages/Account/Index.cshtml.cs index 797f01c7..71a94d10 100644 --- a/FMS/Pages/Account/Index.cshtml.cs +++ b/FMS/Pages/Account/Index.cshtml.cs @@ -1,23 +1,66 @@ +using FMS.Domain.Entities; using FMS.Domain.Services; +using FMS.Platform.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc.Rendering; namespace FMS.Pages.Account { public class IndexModel : PageModel { private readonly IUserService _userService; - public IndexModel(IUserService userService) => _userService = userService; + private readonly ISelectListHelper _listHelper; + public IndexModel(IUserService userService, ISelectListHelper listHelper) + { + _userService = userService; + _listHelper = listHelper; + } public UserView CurrentUser { get; private set; } public IList Roles { get; private set; } + public SelectList UserUnits { get; private set; } + + public SelectList UserPrograms { get; private set; } + + public SelectList UserPositions { get; private set; } + + public DisplayMessage Message { get; private set; } + public async Task OnGetAsync() { CurrentUser = await _userService.GetCurrentUserAsync() ?? throw new Exception("Current user not found"); Roles = await _userService.GetCurrentUserRolesAsync(); + await PopulateSelectsAsync(); return Page(); } + + public async Task OnGetUserDesignationAsync() + { + CurrentUser = await _userService.GetCurrentUserAsync() + ?? throw new Exception("Current user not found"); + try + { + await _userService.UpdateUserDesignationsAsync(CurrentUser.Id, CurrentUser.UserProgram, CurrentUser.UserUnit, CurrentUser.UserPosition); + } + catch (Exception ex) + { + TempData?.SetDisplayMessage(Context.Danger, $"Unable to update user designations: {ex.Message}"); + await PopulateSelectsAsync(); + return Page(); + } + + TempData?.SetDisplayMessage(Context.Success, $"User designations successfully updated."); + return Page(); + } + + private async Task PopulateSelectsAsync() + { + UserPrograms = await _listHelper.UserProgramsSelectListAsync(); + UserUnits = await _listHelper.OrganizationalUnitsSelectListAsync(false, ["Remedial Sites 1", "Remedial Sites 2", "Remedial Sites 3", "DOD Facilities", "NPL Unit", "Treatment & Storage", "SW Env. Monitoring Compliance", "Voluntary Remediation"]); + UserPositions = await _listHelper.UserPositionsSelectListAsync(); + } } } \ No newline at end of file From 177ac9303df7e55fd03972c639f5deb8bfc7d6c9 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 2 Jun 2026 18:12:58 -0400 Subject: [PATCH 15/37] Unify user designation editing to use IDs and a single dedicated page Refactors `IUserService.UpdateUserDesignationsAsync` to accept `Guid?` for UserProgram, OrganizationalUnit, and UserPosition. This allows for more robust updates and the ability to clear existing designations by passing `null`. Consolidates the user designation editing UI into the `/Account/Index` Razor Page, which can now handle updates for both the current user and other users (when an optional `id` parameter is provided). This centralizes the management of user programs, units, and positions. Adds `AutoInclude` for `ApplicationUser` navigation properties (`UserProgram`, `UserUnit`, `UserPosition`) in the `FmsDbContext` to streamline data retrieval. Part of issue #954. --- FMS.Domain/Services/IUserService.cs | 2 +- FMS.Infrastructure/Contexts/FmsDbContext.cs | 7 ++- FMS.Infrastructure/Services/UserService.cs | 19 ++++-- FMS/Pages/Account/Index.cshtml | 24 ++++---- FMS/Pages/Account/Index.cshtml.cs | 67 +++++++++++++++++---- FMS/Pages/Users/Details.cshtml | 2 +- 6 files changed, 89 insertions(+), 32 deletions(-) diff --git a/FMS.Domain/Services/IUserService.cs b/FMS.Domain/Services/IUserService.cs index 6ca45ced..4334d559 100644 --- a/FMS.Domain/Services/IUserService.cs +++ b/FMS.Domain/Services/IUserService.cs @@ -17,7 +17,7 @@ public interface IUserService public Task> GetUserRolesAsync(Guid id); public Task UpdateUserRolesAsync(Guid id, Dictionary roleSettings); - public Task UpdateUserDesignationsAsync(Guid id, UserProgram userProgram, OrganizationalUnit userUnit, UserPosition userPosition); + public Task UpdateUserDesignationsAsync(Guid id, Guid? userProgramId, Guid? userUnitId, Guid? userPositionId); // User search public Task> GetUsersAsync(string nameFilter, string emailFilter, string role); diff --git a/FMS.Infrastructure/Contexts/FmsDbContext.cs b/FMS.Infrastructure/Contexts/FmsDbContext.cs index b63b58f8..060c2cbe 100644 --- a/FMS.Infrastructure/Contexts/FmsDbContext.cs +++ b/FMS.Infrastructure/Contexts/FmsDbContext.cs @@ -119,7 +119,12 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity(entityType).Property(AuditProperties.InsertUser); builder.Entity(entityType).Property(AuditProperties.UpdateUser); } - + + // Auto Include Identy Properties + builder.Entity().Navigation(user => user.UserUnit).AutoInclude(); + builder.Entity().Navigation(user => user.UserProgram).AutoInclude(); + builder.Entity().Navigation(user => user.UserPosition).AutoInclude(); + // Fix primary key error for IdentityPasskeyData builder.Entity(e => e.HasNoKey()); } diff --git a/FMS.Infrastructure/Services/UserService.cs b/FMS.Infrastructure/Services/UserService.cs index 2cc97874..d61ccf41 100644 --- a/FMS.Infrastructure/Services/UserService.cs +++ b/FMS.Infrastructure/Services/UserService.cs @@ -1,4 +1,5 @@ -using FMS.Domain.Entities; +using DocumentFormat.OpenXml.Bibliography; +using FMS.Domain.Entities; using FMS.Domain.Entities.Users; using FMS.Domain.Services; using FMS.Infrastructure.Contexts; @@ -90,7 +91,7 @@ public async Task UpdateUserRolesAsync(Guid id, Dictionary UpdateUserDesignationsAsync(Guid id, UserProgram userProgram, OrganizationalUnit userUnit, UserPosition userPosition) + public async Task UpdateUserDesignationsAsync(Guid id, Guid? userProgramId, Guid? userUnitId, Guid? userPositionId) { var user = await _userManager.FindByIdAsync(id.ToString()); if (user == null) @@ -98,9 +99,17 @@ public async Task UpdateUserDesignationsAsync(Guid id, UserProgr return IdentityResult.Failed(_userManager.ErrorDescriber.DefaultError()); } - user.UserProgram = userProgram; - user.UserUnit = userUnit; - user.UserPosition = userPosition; + user.UserProgram = userProgramId is null + ? null + : await _context.UserPrograms.FindAsync(userProgramId.Value).ConfigureAwait(false); + + user.UserUnit = userUnitId is null + ? null + : await _context.OrganizationalUnits.FindAsync(userUnitId.Value).ConfigureAwait(false); + + user.UserPosition = userPositionId is null + ? null + : await _context.UserPositions.FindAsync(userPositionId.Value).ConfigureAwait(false); return await _userManager.UpdateAsync(user); } diff --git a/FMS/Pages/Account/Index.cshtml b/FMS/Pages/Account/Index.cshtml index df597fa8..017bd53c 100644 --- a/FMS/Pages/Account/Index.cshtml +++ b/FMS/Pages/Account/Index.cshtml @@ -1,4 +1,4 @@ -@page +@page "{id:Guid?}" @using FMS.Domain.Entities.Users @model FMS.Pages.Account.IndexModel @{ @@ -6,34 +6,34 @@ } @section Scripts { - + }

    @ViewData["Title"]


    -

    @Model.CurrentUser.DisplayName

    -

    @Model.CurrentUser.Email

    +

    @Model.CurrentUser?.DisplayName

    +

    @Model.CurrentUser?.Email

    -
    +
    - -
    - -
    - -
    @@ -41,9 +41,9 @@
    + - Clear
    diff --git a/FMS/Pages/Account/Index.cshtml.cs b/FMS/Pages/Account/Index.cshtml.cs index 71a94d10..2dc862ce 100644 --- a/FMS/Pages/Account/Index.cshtml.cs +++ b/FMS/Pages/Account/Index.cshtml.cs @@ -1,9 +1,9 @@ -using FMS.Domain.Entities; using FMS.Domain.Services; using FMS.Platform.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.Rendering; +using System.ComponentModel.DataAnnotations; namespace FMS.Pages.Account { @@ -17,33 +17,75 @@ public IndexModel(IUserService userService, ISelectListHelper listHelper) _listHelper = listHelper; } + [BindProperty] + public Guid Id { get; set; } public UserView CurrentUser { get; private set; } public IList Roles { get; private set; } - public SelectList UserUnits { get; private set; } + [BindProperty] + [Display(Name = "Program")] + public Guid? UserProgramId { get; set; } + + [BindProperty] + [Display(Name = "Unit")] + public Guid? UserUnitId { get; set; } + + [BindProperty] + [Display(Name = "Position")] + public Guid? UserPositionId { get; set; } + + public SelectList UserUnits { get; private set; } public SelectList UserPrograms { get; private set; } public SelectList UserPositions { get; private set; } - public DisplayMessage Message { get; private set; } + public DisplayMessage Message { get; set; } - public async Task OnGetAsync() + public async Task OnGetAsync(Guid? id) { - CurrentUser = await _userService.GetCurrentUserAsync() - ?? throw new Exception("Current user not found"); + if (id != null) + { + CurrentUser = await _userService.GetUserByIdAsync(id.Value) + ?? throw new Exception("Current user not found"); + Id = CurrentUser.Id; + } + else + { + CurrentUser = await _userService.GetCurrentUserAsync() + ?? throw new Exception("Current user not found"); + Id = CurrentUser.Id; + } Roles = await _userService.GetCurrentUserRolesAsync(); + UserProgramId = CurrentUser.UserProgram?.Id; + UserUnitId = CurrentUser.UserUnit?.Id; + UserPositionId = CurrentUser.UserPosition?.Id; await PopulateSelectsAsync(); + Message = TempData?.GetDisplayMessage(); return Page(); } - public async Task OnGetUserDesignationAsync() + public async Task OnPostAsync() { - CurrentUser = await _userService.GetCurrentUserAsync() - ?? throw new Exception("Current user not found"); + if (Id != null) + { + CurrentUser = await _userService.GetUserByIdAsync(Id) + ?? throw new Exception("Current user not found"); + } + else + { + TempData?.SetDisplayMessage(Context.Danger, $"Unable to find user with Id: {Id.ToString()}"); + await PopulateSelectsAsync(); + return Page(); + } + + Roles = await _userService.GetCurrentUserRolesAsync(); try { - await _userService.UpdateUserDesignationsAsync(CurrentUser.Id, CurrentUser.UserProgram, CurrentUser.UserUnit, CurrentUser.UserPosition); + await _userService.UpdateUserDesignationsAsync(CurrentUser.Id, + UserProgramId, + UserUnitId, + UserPositionId); } catch (Exception ex) { @@ -53,14 +95,15 @@ public async Task OnGetUserDesignationAsync() } TempData?.SetDisplayMessage(Context.Success, $"User designations successfully updated."); - return Page(); + return RedirectToPage(); } private async Task PopulateSelectsAsync() { UserPrograms = await _listHelper.UserProgramsSelectListAsync(); - UserUnits = await _listHelper.OrganizationalUnitsSelectListAsync(false, ["Remedial Sites 1", "Remedial Sites 2", "Remedial Sites 3", "DOD Facilities", "NPL Unit", "Treatment & Storage", "SW Env. Monitoring Compliance", "Voluntary Remediation"]); + UserUnits = await _listHelper.OrganizationalUnitsSelectListAsync(); UserPositions = await _listHelper.UserPositionsSelectListAsync(); + // false, ["Remedial Sites 1", "Remedial Sites 2", "Remedial Sites 3", "DOD Facilities", "NPL Unit", "Treatment & Storage", "SW Env. Monitoring Compliance", "Voluntary Remediation"] } } } \ No newline at end of file diff --git a/FMS/Pages/Users/Details.cshtml b/FMS/Pages/Users/Details.cshtml index 4682c27f..ef485519 100644 --- a/FMS/Pages/Users/Details.cshtml +++ b/FMS/Pages/Users/Details.cshtml @@ -22,7 +22,7 @@ } @if (User.Equals(Model.Id) || User.IsInRole(UserRoles.UserMaintenance)) { - Edit Program/Unit + Edit User Designations }

    @Model.Email

    From a20a69f2200a71f4b5bf73aad38a4edadd544de8 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 2 Jun 2026 18:22:56 -0400 Subject: [PATCH 16/37] Refine user ID validation and exception messages Updates user ID checks in the Account page to use `Guid.Empty` for more robust validation of provided IDs. Also refines exception messages to be more generic and consistent. Part of issue #954. --- FMS/Pages/Account/Index.cshtml.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/FMS/Pages/Account/Index.cshtml.cs b/FMS/Pages/Account/Index.cshtml.cs index 2dc862ce..43eab4bc 100644 --- a/FMS/Pages/Account/Index.cshtml.cs +++ b/FMS/Pages/Account/Index.cshtml.cs @@ -47,7 +47,7 @@ public async Task OnGetAsync(Guid? id) if (id != null) { CurrentUser = await _userService.GetUserByIdAsync(id.Value) - ?? throw new Exception("Current user not found"); + ?? throw new Exception("User not found"); Id = CurrentUser.Id; } else @@ -67,10 +67,10 @@ public async Task OnGetAsync(Guid? id) public async Task OnPostAsync() { - if (Id != null) + if (Id != Guid.Empty) { CurrentUser = await _userService.GetUserByIdAsync(Id) - ?? throw new Exception("Current user not found"); + ?? throw new Exception("User not found"); } else { From b6c54265473c2ed67f68389e58f24fc8ef36e3a8 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Fri, 5 Jun 2026 15:26:56 -0400 Subject: [PATCH 17/37] Streamline Dashboard accordions and fetch user roles Consolidates individual accordion components into a single main accordion for a cleaner UI. Refines padding and button styling for improved presentation. Also retrieves and exposes current user roles on the page model, preparing for role-based content display. Part of issue #954. --- FMS/Pages/Dashboard/Index.cshtml | 34 +++++++++++------------------ FMS/Pages/Dashboard/Index.cshtml.cs | 13 +++++++---- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 2f915512..85bf2e68 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -7,15 +7,15 @@ }

    @ViewData["Title"]

    -
    -
    +
    +
    -

    +

    -
    +
    @@ -27,18 +27,14 @@
    -
    -
    -
    -
    -

    -

    -
    +
    @@ -48,18 +44,14 @@
    -
    -
    - -
    -
    +
    -

    +

    -
    +
    diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index a1844f1c..a1a78925 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -1,6 +1,7 @@ using FMS.Domain.Entities; using FMS.Domain.Repositories; using FMS.Domain.Services; +using FMS.Infrastructure.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; @@ -13,9 +14,8 @@ public class IndexModel( IFacilityRepository _repository) : PageModel { public UserView CurrentUser { get; private set; } - public string UserName { get; set; } - + public IList Roles { get; private set; } public IList UserFacilities { get; set; } public async Task OnGetAsync() @@ -23,9 +23,14 @@ public async Task OnGetAsync() if (User.Identity is not { IsAuthenticated: true }) return Challenge(); - UserName = await userService.GetCurrentUserAsync() is UserView user ? user.DisplayName : "User"; + CurrentUser = await userService.GetCurrentUserAsync() + ?? throw new Exception("Current user not found"); + + Roles = await userService.GetCurrentUserRolesAsync(); + + UserName = CurrentUser.DisplayName; - //UserFacilities = await _repository.GetFacilitiesForUserAsync(user.Id); + //UserFacilities = await _repository.GetFacilitiesForUserAsync(CurrentUser.Id); return Page(); } From 55965e1ea8c3aa621bdbb7bd6eb661036260b66c Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Mon, 8 Jun 2026 14:09:07 -0400 Subject: [PATCH 18/37] Add initial Dashboard DTOs and repository for facility views Introduces data transfer objects and a repository layer to retrieve facility information specific to dashboard views. This allows fetching facilities associated with users, organizational units, and programs/facility types for display. Placeholders for event-related DTOs are also included. Part of issue #954. --- .../Dashboard/DashboardProgramEventsDto.cs | 10 ++ .../DashboardProgramFacilitiesDto.cs | 20 +++ .../Dto/Dashboard/DashboardUnitEventsDto.cs | 10 ++ .../Dashboard/DashboardUnitFacilitiesDto.cs | 20 +++ .../Dto/Dashboard/DashboardUserEventsDto.cs | 10 ++ .../Dashboard/DashboardUserFacilitiesDto.cs | 21 ++++ .../Repositories/IDashboardRepository.cs | 15 +++ .../Repositories/DashboardRepository.cs | 118 ++++++++++++++++++ FMS/FMS.csproj | 7 +- 9 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs create mode 100644 FMS.Domain/Dto/Dashboard/DashboardProgramFacilitiesDto.cs create mode 100644 FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs create mode 100644 FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs create mode 100644 FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs create mode 100644 FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs create mode 100644 FMS.Domain/Repositories/IDashboardRepository.cs create mode 100644 FMS.Infrastructure/Repositories/DashboardRepository.cs diff --git a/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs new file mode 100644 index 00000000..7c7a827a --- /dev/null +++ b/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Domain.Dto +{ + public class DashboardProgramEventsDto + { + } +} diff --git a/FMS.Domain/Dto/Dashboard/DashboardProgramFacilitiesDto.cs b/FMS.Domain/Dto/Dashboard/DashboardProgramFacilitiesDto.cs new file mode 100644 index 00000000..5b628d10 --- /dev/null +++ b/FMS.Domain/Dto/Dashboard/DashboardProgramFacilitiesDto.cs @@ -0,0 +1,20 @@ +using FMS.Domain.Entities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Domain.Dto +{ + public class DashboardProgramFacilitiesDto + { + public DashboardProgramFacilitiesDto(Facility facility) + { + Id = facility.Id; + Name = facility.Name; + Active = facility.Active; + } + public Guid Id { get; set; } + public string Name { get; set; } + public bool Active { get; set; } + } +} diff --git a/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs new file mode 100644 index 00000000..021cb314 --- /dev/null +++ b/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Domain.Dto +{ + public class DashboardUnitEventsDto + { + } +} diff --git a/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs new file mode 100644 index 00000000..872cd81f --- /dev/null +++ b/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs @@ -0,0 +1,20 @@ +using FMS.Domain.Entities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Domain.Dto +{ + public class DashboardUnitFacilitiesDto + { + public DashboardUnitFacilitiesDto(Facility facility) + { + Id = facility.Id; + Name = facility.Name; + Active = facility.Active; + } + public Guid Id { get; set; } + public string Name { get; set; } + public bool Active { get; set; } + } +} diff --git a/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs new file mode 100644 index 00000000..fe668c8f --- /dev/null +++ b/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Domain.Dto +{ + public class DashboardUserEventsDto + { + } +} diff --git a/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs new file mode 100644 index 00000000..ca178664 --- /dev/null +++ b/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs @@ -0,0 +1,21 @@ +using FMS.Domain.Entities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Domain.Dto +{ + public class DashboardUserFacilitiesDto + { + public DashboardUserFacilitiesDto(Facility facility) + { + Id = facility.Id; + Name = facility.Name; + Active = facility.Active; + } + + public Guid Id { get; set; } + public string Name { get; set; } + public bool Active { get; set; } + } +} diff --git a/FMS.Domain/Repositories/IDashboardRepository.cs b/FMS.Domain/Repositories/IDashboardRepository.cs new file mode 100644 index 00000000..6c776c26 --- /dev/null +++ b/FMS.Domain/Repositories/IDashboardRepository.cs @@ -0,0 +1,15 @@ +using FMS.Domain.Dto; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Domain.Repositories +{ + public interface IDashboardRepository : IDisposable + { + Task UserExistsAsync(Guid id); + Task> GetUserFacilitiesById(Guid id, bool includeInactive = false); + Task> GetUnitFacilitiesById(Guid id, bool includeInactive = false); + Task> GetProgramFacilitiesById(Guid id, bool includeInactive = false); + } +} diff --git a/FMS.Infrastructure/Repositories/DashboardRepository.cs b/FMS.Infrastructure/Repositories/DashboardRepository.cs new file mode 100644 index 00000000..21eff973 --- /dev/null +++ b/FMS.Infrastructure/Repositories/DashboardRepository.cs @@ -0,0 +1,118 @@ +using FMS.Domain.Dto; +using FMS.Domain.Repositories; +using FMS.Domain.Services; +using FMS.Infrastructure.Contexts; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FMS.Infrastructure.Repositories +{ + public class DashboardRepository : IDashboardRepository + { + private readonly FmsDbContext _context; + public DashboardRepository(FmsDbContext context) => _context = context; + + public async Task UserExistsAsync(Guid id) => + await _context.Users.AnyAsync(e => e.Id == id); + + public async Task> GetUserFacilitiesById(Guid id, bool includeInactive = false) + { + return await _context.Facilities.AsNoTracking() + .Include(e => e.ComplianceOfficer) + .Include(e => e.County) + .Include(e => e.Events) + .Include(e => e.FacilityStatus) + .Include(e => e.FacilityType) + .Include(e => e.HsrpFacilityProperties) + .Include(e => e.LocationDetails) + .ThenInclude(e => e.LocationClass) + .Include(e => e.OrganizationalUnit) + .Include(e => e.StatusDetails) + .ThenInclude(e => e.OverallStatus) + .Where(e => includeInactive || e.Active) + .Where(e => e.ComplianceOfficerId == id) + .OrderByDescending(e => e.Active) + .Select(e => new DashboardUserFacilitiesDto(e)) + .ToListAsync(); + } + + public async Task> GetUnitFacilitiesById(Guid id, bool includeInactive = false) + { + return await _context.Facilities.AsNoTracking() + .Include(e => e.ComplianceOfficer) + .Include(e => e.County) + .Include(e => e.Events) + .Include(e => e.FacilityStatus) + .Include(e => e.FacilityType) + .Include(e => e.HsrpFacilityProperties) + .Include(e => e.LocationDetails) + .ThenInclude(e => e.LocationClass) + .Include(e => e.OrganizationalUnit) + .Include(e => e.StatusDetails) + .ThenInclude(e => e.OverallStatus) + .Where(e => includeInactive || e.Active) + .Where(e => e.OrganizationalUnitId == id) + .OrderByDescending(e => e.Active) + .Select(e => new DashboardUnitFacilitiesDto(e)) + .ToListAsync(); + } + + public async Task> GetProgramFacilitiesById(Guid id, bool includeInactive = false) + { + return await _context.Facilities.AsNoTracking() + .Include(e => e.ComplianceOfficer) + .Include(e => e.County) + .Include(e => e.Events) + .Include(e => e.FacilityStatus) + .Include(e => e.FacilityType) + .Include(e => e.HsrpFacilityProperties) + .Include(e => e.LocationDetails) + .ThenInclude(e => e.LocationClass) + .Include(e => e.OrganizationalUnit) + .Include(e => e.StatusDetails) + .ThenInclude(e => e.OverallStatus) + .Where(e => includeInactive || e.Active) + .Where(e => e.FacilityTypeId == id) + .OrderByDescending(e => e.Active) + .Select(e => new DashboardProgramFacilitiesDto(e)) + .ToListAsync(); + } + + #region IDisposable Support + + private bool _disposedValue; + + protected virtual void Dispose(bool disposing) + { + if (_disposedValue) return; + + if (disposing) + { + // dispose managed state (managed objects) + _context.Dispose(); + } + + // free unmanaged resources (unmanaged objects) and override finalizer + // set large fields to null + _disposedValue = true; + } + + // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + ~DashboardRepository() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + #endregion + } +} diff --git a/FMS/FMS.csproj b/FMS/FMS.csproj index cd0721b1..3a6673cf 100644 --- a/FMS/FMS.csproj +++ b/FMS/FMS.csproj @@ -1,4 +1,4 @@ - + FMS @@ -73,6 +73,11 @@ filePath web.config + + dirPath + wwwroot\\Reporting + + From 660df735f56bc84df2ca1ea3c290391776461985 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 9 Jun 2026 18:28:34 -0400 Subject: [PATCH 19/37] Add UserProgram association to Organizational Units and refine dashboard user data Introduces a direct relationship between Organizational Units and User Programs, enabling more precise categorization and management. This includes: - Adding the `UserProgram` property to the `OrganizationalUnit` entity and its DTOs. - Updating `IOrganizationalUnitRepository` to support `UserProgram` in create and edit operations. - Generating a database migration to establish the `UserProgramId` foreign key on the `OrganizationalUnits` table. - Updating Organizational Unit maintenance pages (Add, Edit, Index) to allow selecting and displaying the associated program. Additionally, the Dashboard repository is enhanced to fetch `ApplicationUser` details and facilities associated with either the compliance officer or related events. Part of issue #954. --- .../OrganizationalUnitCreateDto.cs | 7 +- .../OrganizationalUnitEditDto.cs | 4 + .../OrganizationalUnitSummaryDto.cs | 4 + FMS.Domain/Entities/OrganizationalUnit.cs | 3 + .../Repositories/IDashboardRepository.cs | 5 +- .../IOrganizationalUnitRepository.cs | 2 +- ...0609210306_AddProgramToOrgUnit.Designer.cs | 3336 +++++++++++++++++ .../20260609210306_AddProgramToOrgUnit.cs | 49 + .../Migrations/FmsDbContextModelSnapshot.cs | 14 + .../Repositories/DashboardRepository.cs | 18 +- .../OrganizationalUnitRepository.cs | 15 +- FMS/Pages/Dashboard/Index.cshtml.cs | 4 +- .../Maintenance/OrganizationalUnit/Add.cshtml | 9 +- .../OrganizationalUnit/Add.cshtml.cs | 25 +- .../OrganizationalUnit/Edit.cshtml | 8 +- .../OrganizationalUnit/Edit.cshtml.cs | 32 +- .../OrganizationalUnit/Index.cshtml | 8 +- FMS/Startup.cs | 1 + 18 files changed, 3516 insertions(+), 28 deletions(-) create mode 100644 FMS.Infrastructure/Migrations/20260609210306_AddProgramToOrgUnit.Designer.cs create mode 100644 FMS.Infrastructure/Migrations/20260609210306_AddProgramToOrgUnit.cs diff --git a/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitCreateDto.cs b/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitCreateDto.cs index 3c4e366c..44880396 100644 --- a/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitCreateDto.cs +++ b/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitCreateDto.cs @@ -1,4 +1,6 @@ -namespace FMS.Domain.Dto +using FMS.Domain.Entities; + +namespace FMS.Domain.Dto { public class OrganizationalUnitCreateDto { @@ -6,6 +8,9 @@ public class OrganizationalUnitCreateDto [Display(Name = "Organizational Unit")] public string Name { get; set; } + [Display(Name = "Program")] + public UserProgram UserProgram { get; set; } + public void TrimAll() { Name = Name?.Trim(); diff --git a/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitEditDto.cs b/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitEditDto.cs index fbdc403e..a2e69e84 100644 --- a/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitEditDto.cs +++ b/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitEditDto.cs @@ -13,6 +13,7 @@ public OrganizationalUnitEditDto(OrganizationalUnit organizationalUnit) { Active = organizationalUnit.Active; Name = organizationalUnit.Name; + UserProgram = organizationalUnit.UserProgram; } public bool Active { get; set; } @@ -21,6 +22,9 @@ public OrganizationalUnitEditDto(OrganizationalUnit organizationalUnit) [Display(Name = "Organizational Unit")] public string Name { get; set; } + [Display(Name = "Program")] + public UserProgram UserProgram { get; set; } + public void TrimAll() { Name = Name?.Trim(); diff --git a/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitSummaryDto.cs b/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitSummaryDto.cs index 582d97b1..9555af68 100644 --- a/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitSummaryDto.cs +++ b/FMS.Domain/Dto/OrganizationalUnit/OrganizationalUnitSummaryDto.cs @@ -9,6 +9,7 @@ public OrganizationalUnitSummaryDto(OrganizationalUnit organizationalUnit) Id = organizationalUnit.Id; Active = organizationalUnit.Active; Name = organizationalUnit.Name; + UserProgram = organizationalUnit.UserProgram; } public Guid Id { get; } @@ -16,5 +17,8 @@ public OrganizationalUnitSummaryDto(OrganizationalUnit organizationalUnit) [Display(Name = "Organizational Unit")] public string Name { get; } + + [Display(Name = "Program")] + public UserProgram UserProgram { get; } } } diff --git a/FMS.Domain/Entities/OrganizationalUnit.cs b/FMS.Domain/Entities/OrganizationalUnit.cs index 38fed439..9251d3d1 100644 --- a/FMS.Domain/Entities/OrganizationalUnit.cs +++ b/FMS.Domain/Entities/OrganizationalUnit.cs @@ -10,6 +10,9 @@ public OrganizationalUnit() { } public OrganizationalUnit(OrganizationalUnitCreateDto newOrganizationalUnit) { Name = newOrganizationalUnit.Name; + UserProgram = newOrganizationalUnit.UserProgram; } + + public UserProgram UserProgram { get; set; } } } diff --git a/FMS.Domain/Repositories/IDashboardRepository.cs b/FMS.Domain/Repositories/IDashboardRepository.cs index 6c776c26..b93d4f7a 100644 --- a/FMS.Domain/Repositories/IDashboardRepository.cs +++ b/FMS.Domain/Repositories/IDashboardRepository.cs @@ -1,13 +1,12 @@ using FMS.Domain.Dto; -using System; -using System.Collections.Generic; -using System.Text; +using FMS.Domain.Entities.Users; namespace FMS.Domain.Repositories { public interface IDashboardRepository : IDisposable { Task UserExistsAsync(Guid id); + Task GetApplicationUser(Guid id); Task> GetUserFacilitiesById(Guid id, bool includeInactive = false); Task> GetUnitFacilitiesById(Guid id, bool includeInactive = false); Task> GetProgramFacilitiesById(Guid id, bool includeInactive = false); diff --git a/FMS.Domain/Repositories/IOrganizationalUnitRepository.cs b/FMS.Domain/Repositories/IOrganizationalUnitRepository.cs index 8b4cb93a..c82bacb3 100644 --- a/FMS.Domain/Repositories/IOrganizationalUnitRepository.cs +++ b/FMS.Domain/Repositories/IOrganizationalUnitRepository.cs @@ -9,7 +9,7 @@ public interface IOrganizationalUnitRepository : IDisposable Task GetOrganizationalUnitAsync(Guid id); Task> GetOrganizationalUnitListAsync(); Task CreateOrganizationalUnitAsync(OrganizationalUnitCreateDto organizationalUnit); - Task UpdateOrganizationalUnitAsync(Guid id, OrganizationalUnitEditDto organizationalUnitUpdates); + Task UpdateOrganizationalUnitAsync(Guid id, OrganizationalUnitEditDto organizationalUnitUpdates, Guid? programId); Task UpdateOrganizationalUnitStatusAsync(Guid id, bool active); } } \ No newline at end of file diff --git a/FMS.Infrastructure/Migrations/20260609210306_AddProgramToOrgUnit.Designer.cs b/FMS.Infrastructure/Migrations/20260609210306_AddProgramToOrgUnit.Designer.cs new file mode 100644 index 00000000..79e5d875 --- /dev/null +++ b/FMS.Infrastructure/Migrations/20260609210306_AddProgramToOrgUnit.Designer.cs @@ -0,0 +1,3336 @@ +// +using System; +using FMS.Infrastructure.Contexts; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace FMS.Infrastructure.Migrations +{ + [DbContext(typeof(FmsDbContext))] + [Migration("20260609210306_AddProgramToOrgUnit")] + partial class AddProgramToOrgUnit + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("FMS.Domain.Entities.AbandonedInactive", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AbandonedInactives"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ActionTaken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ActionsTaken"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.AllowedActionTaken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActionTakenId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("CompletionDateRequired") + .HasColumnType("bit"); + + b.Property("DueDateRequired") + .HasColumnType("bit"); + + b.Property("EventTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("StartDateRequired") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ActionTakenId"); + + b.HasIndex("EventTypeId"); + + b.ToTable("AllowedActionsTaken"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.BudgetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Code") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("ProjectNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("BudgetCodes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Cabinet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FirstFileLabel") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("Cabinets"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Chemical", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("CasNo") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemicalName") + .HasColumnType("nvarchar(max)"); + + b.Property("CommonName") + .HasColumnType("nvarchar(max)"); + + b.Property("FinalRc") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("MCLs") + .HasColumnType("nvarchar(max)"); + + b.Property("RQ") + .HasColumnType("nvarchar(max)"); + + b.Property("ToxValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Chemicals"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ComplianceOfficer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FamilyName") + .HasColumnType("nvarchar(max)"); + + b.Property("GivenName") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ComplianceOfficers"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("Company") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactTitle") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("FamilyName") + .HasColumnType("nvarchar(max)"); + + b.Property("GivenName") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("PostalCode") + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ContactTypeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Contacts"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ContactType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ContactTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.County", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Counties"); + + b.HasData( + new + { + Id = 131, + Name = "Appling" + }, + new + { + Id = 122, + Name = "Atkinson" + }, + new + { + Id = 110, + Name = "Bacon" + }, + new + { + Id = 105, + Name = "Baker" + }, + new + { + Id = 213, + Name = "Baldwin" + }, + new + { + Id = 179, + Name = "Banks" + }, + new + { + Id = 217, + Name = "Barrow" + }, + new + { + Id = 249, + Name = "Bartow" + }, + new + { + Id = 211, + Name = "Ben Hill" + }, + new + { + Id = 194, + Name = "Berrien" + }, + new + { + Id = 259, + Name = "Bibb" + }, + new + { + Id = 145, + Name = "Bleckley" + }, + new + { + Id = 108, + Name = "Brantley" + }, + new + { + Id = 158, + Name = "Brooks" + }, + new + { + Id = 111, + Name = "Bryan" + }, + new + { + Id = 159, + Name = "Bulloch" + }, + new + { + Id = 203, + Name = "Burke" + }, + new + { + Id = 188, + Name = "Butts" + }, + new + { + Id = 112, + Name = "Calhoun" + }, + new + { + Id = 169, + Name = "Camden" + }, + new + { + Id = 113, + Name = "Candler" + }, + new + { + Id = 241, + Name = "Carroll" + }, + new + { + Id = 225, + Name = "Catoosa" + }, + new + { + Id = 114, + Name = "Charlton" + }, + new + { + Id = 242, + Name = "Chatham" + }, + new + { + Id = 137, + Name = "Chattahoochee" + }, + new + { + Id = 174, + Name = "Chattooga" + }, + new + { + Id = 243, + Name = "Cherokee" + }, + new + { + Id = 252, + Name = "Clarke" + }, + new + { + Id = 104, + Name = "Clay" + }, + new + { + Id = 204, + Name = "Clayton" + }, + new + { + Id = 178, + Name = "Clinch" + }, + new + { + Id = 245, + Name = "Cobb" + }, + new + { + Id = 146, + Name = "Coffee" + }, + new + { + Id = 227, + Name = "Colquitt" + }, + new + { + Id = 232, + Name = "Columbia" + }, + new + { + Id = 212, + Name = "Cook" + }, + new + { + Id = 250, + Name = "Coweta" + }, + new + { + Id = 115, + Name = "Crawford" + }, + new + { + Id = 220, + Name = "Crisp" + }, + new + { + Id = 170, + Name = "Dade" + }, + new + { + Id = 180, + Name = "Dawson" + }, + new + { + Id = 244, + Name = "Decatur" + }, + new + { + Id = 218, + Name = "DeKalb" + }, + new + { + Id = 175, + Name = "Dodge" + }, + new + { + Id = 189, + Name = "Dooly" + }, + new + { + Id = 257, + Name = "Dougherty" + }, + new + { + Id = 253, + Name = "Douglas" + }, + new + { + Id = 195, + Name = "Early" + }, + new + { + Id = 103, + Name = "Echols" + }, + new + { + Id = 123, + Name = "Effingham" + }, + new + { + Id = 200, + Name = "Elbert" + }, + new + { + Id = 198, + Name = "Emanuel" + }, + new + { + Id = 109, + Name = "Evans" + }, + new + { + Id = 176, + Name = "Fannin" + }, + new + { + Id = 235, + Name = "Fayette" + }, + new + { + Id = 246, + Name = "Floyd" + }, + new + { + Id = 238, + Name = "Forsyth" + }, + new + { + Id = 201, + Name = "Franklin" + }, + new + { + Id = 261, + Name = "Fulton" + }, + new + { + Id = 165, + Name = "Gilmer" + }, + new + { + Id = 124, + Name = "Glascock" + }, + new + { + Id = 216, + Name = "Glynn" + }, + new + { + Id = 230, + Name = "Gordon" + }, + new + { + Id = 208, + Name = "Grady" + }, + new + { + Id = 171, + Name = "Greene" + }, + new + { + Id = 226, + Name = "Gwinnett" + }, + new + { + Id = 214, + Name = "Habersham" + }, + new + { + Id = 256, + Name = "Hall" + }, + new + { + Id = 107, + Name = "Hancock" + }, + new + { + Id = 209, + Name = "Haralson" + }, + new + { + Id = 138, + Name = "Harris" + }, + new + { + Id = 190, + Name = "Hart" + }, + new + { + Id = 152, + Name = "Heard" + }, + new + { + Id = 248, + Name = "Henry" + }, + new + { + Id = 239, + Name = "Houston" + }, + new + { + Id = 139, + Name = "Irwin" + }, + new + { + Id = 222, + Name = "Jackson" + }, + new + { + Id = 147, + Name = "Jasper" + }, + new + { + Id = 160, + Name = "Jeff Davis" + }, + new + { + Id = 199, + Name = "Jefferson" + }, + new + { + Id = 153, + Name = "Jenkins" + }, + new + { + Id = 140, + Name = "Johnson" + }, + new + { + Id = 161, + Name = "Jones" + }, + new + { + Id = 181, + Name = "Lamar" + }, + new + { + Id = 141, + Name = "Lanier" + }, + new + { + Id = 223, + Name = "Laurens" + }, + new + { + Id = 172, + Name = "Lee" + }, + new + { + Id = 148, + Name = "Liberty" + }, + new + { + Id = 125, + Name = "Lincoln" + }, + new + { + Id = 102, + Name = "Long" + }, + new + { + Id = 254, + Name = "Lowndes" + }, + new + { + Id = 192, + Name = "Lumpkin" + }, + new + { + Id = 182, + Name = "Macon" + }, + new + { + Id = 166, + Name = "Madison" + }, + new + { + Id = 133, + Name = "Marion" + }, + new + { + Id = 210, + Name = "McDuffie" + }, + new + { + Id = 126, + Name = "McIntosh" + }, + new + { + Id = 196, + Name = "Meriwether" + }, + new + { + Id = 154, + Name = "Miller" + }, + new + { + Id = 215, + Name = "Mitchell" + }, + new + { + Id = 184, + Name = "Monroe" + }, + new + { + Id = 134, + Name = "Montgomery" + }, + new + { + Id = 197, + Name = "Morgan" + }, + new + { + Id = 205, + Name = "Murray" + }, + new + { + Id = 258, + Name = "Muscogee" + }, + new + { + Id = 231, + Name = "Newton" + }, + new + { + Id = 191, + Name = "Oconee" + }, + new + { + Id = 127, + Name = "Oglethorpe" + }, + new + { + Id = 219, + Name = "Paulding" + }, + new + { + Id = 221, + Name = "Peach" + }, + new + { + Id = 185, + Name = "Pickens" + }, + new + { + Id = 116, + Name = "Pierce" + }, + new + { + Id = 149, + Name = "Pike" + }, + new + { + Id = 233, + Name = "Polk" + }, + new + { + Id = 155, + Name = "Pulaski" + }, + new + { + Id = 183, + Name = "Putnam" + }, + new + { + Id = 100, + Name = "Quitman" + }, + new + { + Id = 167, + Name = "Rabun" + }, + new + { + Id = 156, + Name = "Randolph" + }, + new + { + Id = 260, + Name = "Richmond" + }, + new + { + Id = 251, + Name = "Rockdale" + }, + new + { + Id = 135, + Name = "Schley" + }, + new + { + Id = 157, + Name = "Screven" + }, + new + { + Id = 150, + Name = "Seminole" + }, + new + { + Id = 240, + Name = "Spalding" + }, + new + { + Id = 229, + Name = "Stephens" + }, + new + { + Id = 117, + Name = "Stewart" + }, + new + { + Id = 236, + Name = "Sumter" + }, + new + { + Id = 128, + Name = "Talbot" + }, + new + { + Id = 118, + Name = "Taliaferro" + }, + new + { + Id = 136, + Name = "Tattnall" + }, + new + { + Id = 142, + Name = "Taylor" + }, + new + { + Id = 164, + Name = "Telfair" + }, + new + { + Id = 173, + Name = "Terrell" + }, + new + { + Id = 234, + Name = "Thomas" + }, + new + { + Id = 237, + Name = "Tift" + }, + new + { + Id = 151, + Name = "Toombs" + }, + new + { + Id = 129, + Name = "Towns" + }, + new + { + Id = 101, + Name = "Treutlen" + }, + new + { + Id = 247, + Name = "Troup" + }, + new + { + Id = 186, + Name = "Turner" + }, + new + { + Id = 130, + Name = "Twiggs" + }, + new + { + Id = 168, + Name = "Union" + }, + new + { + Id = 202, + Name = "Upson" + }, + new + { + Id = 224, + Name = "Walker" + }, + new + { + Id = 228, + Name = "Walton" + }, + new + { + Id = 177, + Name = "Ware" + }, + new + { + Id = 162, + Name = "Warren" + }, + new + { + Id = 206, + Name = "Washington" + }, + new + { + Id = 143, + Name = "Wayne" + }, + new + { + Id = 120, + Name = "Webster" + }, + new + { + Id = 106, + Name = "Wheeler" + }, + new + { + Id = 193, + Name = "White" + }, + new + { + Id = 255, + Name = "Whitfield" + }, + new + { + Id = 144, + Name = "Wilcox" + }, + new + { + Id = 163, + Name = "Wilkes" + }, + new + { + Id = 207, + Name = "Wilkinson" + }, + new + { + Id = 187, + Name = "Worth" + }, + new + { + Id = 99, + Name = "OUT OF STATE" + }); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActionTakenId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comment") + .HasColumnType("nvarchar(max)"); + + b.Property("CompletionDate") + .HasColumnType("date"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("EventAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("EventContractorId") + .HasColumnType("uniqueidentifier"); + + b.Property("EventTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ActionTakenId"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("EventContractorId"); + + b.HasIndex("EventTypeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.EventContractor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("EventContractors"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.EventType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("ReportEligible") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("EventTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("AdditionalDataRequested") + .HasColumnType("bit"); + + b.Property("Address") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("BudgetCodeId") + .HasColumnType("uniqueidentifier"); + + b.Property("City") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("CountyId") + .HasColumnType("int"); + + b.Property("DeferredOnSiteScoring") + .HasColumnType("bit"); + + b.Property("DeterminationLetterDate") + .HasColumnType("date"); + + b.Property("FacilityNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileId") + .HasColumnType("uniqueidentifier"); + + b.Property("HSInumber") + .HasMaxLength(5) + .HasColumnType("nvarchar(5)"); + + b.Property("HasERecord") + .HasColumnType("bit"); + + b.Property("HistoricalComplianceOfficer") + .HasColumnType("nvarchar(max)"); + + b.Property("HistoricalUnit") + .HasColumnType("nvarchar(max)"); + + b.Property("ImageChecked") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("IsRetained") + .HasColumnType("bit"); + + b.Property("Latitude") + .HasColumnType("decimal(8, 6)"); + + b.Property("Location") + .HasColumnType("nvarchar(max)"); + + b.Property("Longitude") + .HasColumnType("decimal(9, 6)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationalUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("PreRQSMcleanup") + .HasColumnType("bit"); + + b.Property("RNDateReceived") + .HasColumnType("date"); + + b.Property("State") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("VRPReferral") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("Active"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Active"), new[] { "FileId" }); + + b.HasIndex("BudgetCodeId"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("CountyId"); + + b.HasIndex("FacilityStatusId"); + + b.HasIndex("FacilityTypeId"); + + b.HasIndex("FileId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("FileId"), new[] { "Active", "FacilityNumber", "FacilityTypeId", "OrganizationalUnitId", "BudgetCodeId", "Name", "ComplianceOfficerId", "FacilityStatusId", "Location", "Address", "City", "State", "PostalCode", "Latitude", "Longitude", "CountyId", "IsRetained", "AdditionalDataRequested", "Comments", "DeferredOnSiteScoring", "DeterminationLetterDate", "HSInumber", "HasERecord", "HistoricalComplianceOfficer", "HistoricalUnit", "ImageChecked", "PreRQSMcleanup", "RNDateReceived", "VRPReferral" }); + + b.HasIndex("OrganizationalUnitId"); + + b.HasIndex("Active", "FileId"); + + b.HasIndex("Active", "FacilityTypeId", "Address"); + + b.HasIndex("Active", "FacilityTypeId", "Address", "City"); + + b.ToTable("Facilities"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FacilityStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FacilityStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FacilityType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("FacilityTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.File", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FileLabel") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FileLabel") + .IsUnique() + .HasFilter("[FileLabel] IS NOT NULL"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.FundingSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FundingSources"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GapsAssessment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GapsAssessments"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("A") + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("B1") + .HasColumnType("int"); + + b.Property("B2") + .HasColumnType("int"); + + b.Property("C") + .HasColumnType("int"); + + b.Property("CASNO") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemName") + .HasColumnType("nvarchar(max)"); + + b.Property("D2") + .HasColumnType("int"); + + b.Property("D3") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("E1") + .HasColumnType("int"); + + b.Property("E2") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("GWScore") + .HasColumnType("decimal(18,2)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Other") + .HasColumnType("nvarchar(max)"); + + b.Property("SubstanceId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("SubstanceId"); + + b.ToTable("GroundwaterScores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GroundwaterStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.HsrpFacilityProperties", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("BrownfieldDate") + .HasColumnType("date"); + + b.Property("BrownfieldTerminated") + .HasColumnType("bit"); + + b.Property("ComplianceOfficerId") + .HasColumnType("uniqueidentifier"); + + b.Property("DateDeListed") + .HasColumnType("date"); + + b.Property("DateListed") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationalUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("VRPDate") + .HasColumnType("date"); + + b.Property("VRPTerminated") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ComplianceOfficerId"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("OrganizationalUnitId"); + + b.ToTable("HsrpFacilityProperties"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LocationClassId") + .HasColumnType("uniqueidentifier"); + + b.Property("MapType") + .HasColumnType("nvarchar(max)"); + + b.Property("MapZoom") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("LocationClassId"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.LocationClass", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("LocationClasses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OnsiteScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("A") + .HasColumnType("int"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("B") + .HasColumnType("int"); + + b.Property("C") + .HasColumnType("int"); + + b.Property("CASNO") + .HasColumnType("nvarchar(max)"); + + b.Property("ChemName1D") + .HasColumnType("nvarchar(max)"); + + b.Property("D2") + .HasColumnType("int"); + + b.Property("D3") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("E1") + .HasColumnType("int"); + + b.Property("E2") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("OnsiteScoreValue") + .HasColumnType("decimal(18,2)"); + + b.Property("Other1D") + .HasColumnType("nvarchar(max)"); + + b.Property("SubstanceId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("SubstanceId"); + + b.ToTable("OnsiteScores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OrganizationalUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserProgramId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserProgramId"); + + b.ToTable("OrganizationalUnits"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OverallStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OverallStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Parcel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Acres") + .HasColumnType("float"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("DeListDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ListDate") + .HasColumnType("date"); + + b.Property("ParcelNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("ParcelTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("SubListParcelName") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("ParcelTypeId"); + + b.ToTable("Parcels"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.ParcelType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ParcelTypes"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Phone", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("ContactId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Number") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneType") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ContactId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Phones"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.RetentionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("BoxNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("ConsignmentNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("EndYear") + .HasColumnType("int"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("RetentionSchedule") + .HasColumnType("nvarchar(max)"); + + b.Property("ShelfNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("FacilityId"), new[] { "Active", "StartYear", "EndYear", "ConsignmentNumber", "BoxNumber", "ShelfNumber", "RetentionSchedule" }); + + b.ToTable("RetentionRecords"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Score", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ScoredDate") + .HasColumnType("date"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UseComments") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.SoilStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SoilStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.SourceStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SourceStatuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Status", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AbandonedInactiveId") + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Comments") + .HasColumnType("nvarchar(max)"); + + b.Property("CostEstimate") + .HasColumnType("decimal(18,2)"); + + b.Property("CostEstimateDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("FinancialAssurance") + .HasColumnType("bit"); + + b.Property("FundingSourceId") + .HasColumnType("uniqueidentifier"); + + b.Property("GAPSAssessmentId") + .HasColumnType("uniqueidentifier"); + + b.Property("GAPSModelDate") + .HasColumnType("date"); + + b.Property("GAPSNoOfUnknowns") + .HasColumnType("int"); + + b.Property("GAPSScore") + .HasColumnType("int"); + + b.Property("GroundwaterDate") + .HasColumnType("date"); + + b.Property("GroundwaterStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("ISWQS") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LandFill") + .HasColumnType("bit"); + + b.Property("Lien") + .HasColumnType("bit"); + + b.Property("OverallDate") + .HasColumnType("date"); + + b.Property("OverallStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("ReportComments") + .HasColumnType("nvarchar(max)"); + + b.Property("SoilDate") + .HasColumnType("date"); + + b.Property("SoilStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("SolidWastePermitNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("SourceDate") + .HasColumnType("date"); + + b.Property("SourceStatusId") + .HasColumnType("uniqueidentifier"); + + b.Property("UEC") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("AbandonedInactiveId"); + + b.HasIndex("FacilityId") + .IsUnique(); + + b.HasIndex("FundingSourceId"); + + b.HasIndex("GAPSAssessmentId"); + + b.HasIndex("GroundwaterStatusId"); + + b.HasIndex("OverallStatusId"); + + b.HasIndex("SoilStatusId"); + + b.HasIndex("SourceStatusId"); + + b.ToTable("Statuses"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Substance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("ChemicalId") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilityId") + .HasColumnType("uniqueidentifier"); + + b.Property("Groundwater") + .HasColumnType("bit"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Soil") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UseForGroundwaterScoring") + .HasColumnType("bit"); + + b.Property("UseForSoilScoring") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("ChemicalId"); + + b.HasIndex("FacilityId"); + + b.ToTable("Substances"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.UserPosition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("UserPositions"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.UserProgram", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .HasColumnType("bit"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("UserPrograms"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Users.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FamilyName") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("GivenName") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("MostRecentLogin") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ObjectId") + .HasMaxLength(36) + .HasColumnType("nvarchar(36)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("ProfileUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("UserPositionId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserProgramId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserUnitId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("ObjectId") + .IsUnique() + .HasFilter("[ObjectId] IS NOT NULL"); + + b.HasIndex("UserPositionId"); + + b.HasIndex("UserProgramId"); + + b.HasIndex("UserUnitId"); + + b.ToTable("AppUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityPasskeyData", b => + { + b.Property("AttestationObject") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("ClientDataJson") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("IsBackedUp") + .HasColumnType("bit"); + + b.Property("IsBackupEligible") + .HasColumnType("bit"); + + b.Property("IsUserVerified") + .HasColumnType("bit"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("SignCount") + .HasColumnType("bigint"); + + b.PrimitiveCollection("Transports") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.ToTable("IdentityPasskeyData"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AppRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AppRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AppUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AppUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AppUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("InsertDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("InsertUser") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDateTime") + .HasColumnType("datetimeoffset"); + + b.Property("UpdateUser") + .HasColumnType("nvarchar(max)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AppUserTokens", (string)null); + }); + + modelBuilder.Entity("FMS.Domain.Entities.AllowedActionTaken", b => + { + b.HasOne("FMS.Domain.Entities.ActionTaken", "ActionTaken") + .WithMany() + .HasForeignKey("ActionTakenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.EventType", "EventType") + .WithMany() + .HasForeignKey("EventTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ActionTaken"); + + b.Navigation("EventType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.HasOne("FMS.Domain.Entities.ContactType", "ContactType") + .WithMany() + .HasForeignKey("ContactTypeId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Contacts") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ContactType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Event", b => + { + b.HasOne("FMS.Domain.Entities.ActionTaken", "ActionTaken") + .WithMany() + .HasForeignKey("ActionTakenId"); + + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.EventContractor", "EventContractor") + .WithMany() + .HasForeignKey("EventContractorId"); + + b.HasOne("FMS.Domain.Entities.EventType", "EventType") + .WithMany() + .HasForeignKey("EventTypeId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Events") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ActionTaken"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("EventContractor"); + + b.Navigation("EventType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.HasOne("FMS.Domain.Entities.BudgetCode", "BudgetCode") + .WithMany() + .HasForeignKey("BudgetCodeId"); + + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.County", "County") + .WithMany() + .HasForeignKey("CountyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.FacilityStatus", "FacilityStatus") + .WithMany() + .HasForeignKey("FacilityStatusId"); + + b.HasOne("FMS.Domain.Entities.FacilityType", "FacilityType") + .WithMany() + .HasForeignKey("FacilityTypeId"); + + b.HasOne("FMS.Domain.Entities.File", "File") + .WithMany("Facilities") + .HasForeignKey("FileId"); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "OrganizationalUnit") + .WithMany() + .HasForeignKey("OrganizationalUnitId"); + + b.Navigation("BudgetCode"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("County"); + + b.Navigation("FacilityStatus"); + + b.Navigation("FacilityType"); + + b.Navigation("File"); + + b.Navigation("OrganizationalUnit"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.GroundwaterScore", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("GroundwaterScoreDetails") + .HasForeignKey("FMS.Domain.Entities.GroundwaterScore", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Substance", "Substance") + .WithMany() + .HasForeignKey("SubstanceId"); + + b.Navigation("Substance"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.HsrpFacilityProperties", b => + { + b.HasOne("FMS.Domain.Entities.ComplianceOfficer", "ComplianceOfficer") + .WithMany() + .HasForeignKey("ComplianceOfficerId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("HsrpFacilityProperties") + .HasForeignKey("FMS.Domain.Entities.HsrpFacilityProperties", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "OrganizationalUnit") + .WithMany() + .HasForeignKey("OrganizationalUnitId"); + + b.Navigation("ComplianceOfficer"); + + b.Navigation("OrganizationalUnit"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Location", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("LocationDetails") + .HasForeignKey("FMS.Domain.Entities.Location", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.LocationClass", "LocationClass") + .WithMany() + .HasForeignKey("LocationClassId"); + + b.Navigation("LocationClass"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OnsiteScore", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("OnsiteScoreDetails") + .HasForeignKey("FMS.Domain.Entities.OnsiteScore", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Substance", "Substance") + .WithMany() + .HasForeignKey("SubstanceId"); + + b.Navigation("Substance"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.OrganizationalUnit", b => + { + b.HasOne("FMS.Domain.Entities.UserProgram", "UserProgram") + .WithMany() + .HasForeignKey("UserProgramId"); + + b.Navigation("UserProgram"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Parcel", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Parcels") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.ParcelType", "ParcelType") + .WithMany() + .HasForeignKey("ParcelTypeId"); + + b.Navigation("ParcelType"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Phone", b => + { + b.HasOne("FMS.Domain.Entities.Contact", null) + .WithMany("Phones") + .HasForeignKey("ContactId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Phones") + .HasForeignKey("FacilityId"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.RetentionRecord", b => + { + b.HasOne("FMS.Domain.Entities.Facility", "Facility") + .WithMany("RetentionRecords") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Score", b => + { + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("ScoreDetails") + .HasForeignKey("FMS.Domain.Entities.Score", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Status", b => + { + b.HasOne("FMS.Domain.Entities.AbandonedInactive", "AbandonedInactive") + .WithMany() + .HasForeignKey("AbandonedInactiveId"); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithOne("StatusDetails") + .HasForeignKey("FMS.Domain.Entities.Status", "FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.FundingSource", "FundingSource") + .WithMany() + .HasForeignKey("FundingSourceId"); + + b.HasOne("FMS.Domain.Entities.GapsAssessment", "GAPSAssessment") + .WithMany() + .HasForeignKey("GAPSAssessmentId"); + + b.HasOne("FMS.Domain.Entities.GroundwaterStatus", "GroundwaterStatus") + .WithMany() + .HasForeignKey("GroundwaterStatusId"); + + b.HasOne("FMS.Domain.Entities.OverallStatus", "OverallStatus") + .WithMany() + .HasForeignKey("OverallStatusId"); + + b.HasOne("FMS.Domain.Entities.SoilStatus", "SoilStatus") + .WithMany() + .HasForeignKey("SoilStatusId"); + + b.HasOne("FMS.Domain.Entities.SourceStatus", "SourceStatus") + .WithMany() + .HasForeignKey("SourceStatusId"); + + b.Navigation("AbandonedInactive"); + + b.Navigation("FundingSource"); + + b.Navigation("GAPSAssessment"); + + b.Navigation("GroundwaterStatus"); + + b.Navigation("OverallStatus"); + + b.Navigation("SoilStatus"); + + b.Navigation("SourceStatus"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Substance", b => + { + b.HasOne("FMS.Domain.Entities.Chemical", "Chemical") + .WithMany() + .HasForeignKey("ChemicalId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Facility", null) + .WithMany("Substances") + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Chemical"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Users.ApplicationUser", b => + { + b.HasOne("FMS.Domain.Entities.UserPosition", "UserPosition") + .WithMany() + .HasForeignKey("UserPositionId"); + + b.HasOne("FMS.Domain.Entities.UserProgram", "UserProgram") + .WithMany() + .HasForeignKey("UserProgramId"); + + b.HasOne("FMS.Domain.Entities.OrganizationalUnit", "UserUnit") + .WithMany() + .HasForeignKey("UserUnitId"); + + b.Navigation("UserPosition"); + + b.Navigation("UserProgram"); + + b.Navigation("UserUnit"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("FMS.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Contact", b => + { + b.Navigation("Phones"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.Facility", b => + { + b.Navigation("Contacts"); + + b.Navigation("Events"); + + b.Navigation("GroundwaterScoreDetails"); + + b.Navigation("HsrpFacilityProperties"); + + b.Navigation("LocationDetails"); + + b.Navigation("OnsiteScoreDetails"); + + b.Navigation("Parcels"); + + b.Navigation("Phones"); + + b.Navigation("RetentionRecords"); + + b.Navigation("ScoreDetails"); + + b.Navigation("StatusDetails"); + + b.Navigation("Substances"); + }); + + modelBuilder.Entity("FMS.Domain.Entities.File", b => + { + b.Navigation("Facilities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FMS.Infrastructure/Migrations/20260609210306_AddProgramToOrgUnit.cs b/FMS.Infrastructure/Migrations/20260609210306_AddProgramToOrgUnit.cs new file mode 100644 index 00000000..9dc9ea29 --- /dev/null +++ b/FMS.Infrastructure/Migrations/20260609210306_AddProgramToOrgUnit.cs @@ -0,0 +1,49 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace FMS.Infrastructure.Migrations +{ + /// + public partial class AddProgramToOrgUnit : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UserProgramId", + table: "OrganizationalUnits", + type: "uniqueidentifier", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_OrganizationalUnits_UserProgramId", + table: "OrganizationalUnits", + column: "UserProgramId"); + + migrationBuilder.AddForeignKey( + name: "FK_OrganizationalUnits_UserPrograms_UserProgramId", + table: "OrganizationalUnits", + column: "UserProgramId", + principalTable: "UserPrograms", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_OrganizationalUnits_UserPrograms_UserProgramId", + table: "OrganizationalUnits"); + + migrationBuilder.DropIndex( + name: "IX_OrganizationalUnits_UserProgramId", + table: "OrganizationalUnits"); + + migrationBuilder.DropColumn( + name: "UserProgramId", + table: "OrganizationalUnits"); + } + } +} diff --git a/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs b/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs index 73090930..f26a96fc 100644 --- a/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs +++ b/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs @@ -1996,8 +1996,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("UpdateUser") .HasColumnType("nvarchar(max)"); + b.Property("UserProgramId") + .HasColumnType("uniqueidentifier"); + b.HasKey("Id"); + b.HasIndex("UserProgramId"); + b.ToTable("OrganizationalUnits"); }); @@ -3094,6 +3099,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Substance"); }); + modelBuilder.Entity("FMS.Domain.Entities.OrganizationalUnit", b => + { + b.HasOne("FMS.Domain.Entities.UserProgram", "UserProgram") + .WithMany() + .HasForeignKey("UserProgramId"); + + b.Navigation("UserProgram"); + }); + modelBuilder.Entity("FMS.Domain.Entities.Parcel", b => { b.HasOne("FMS.Domain.Entities.Facility", null) diff --git a/FMS.Infrastructure/Repositories/DashboardRepository.cs b/FMS.Infrastructure/Repositories/DashboardRepository.cs index 21eff973..f0e858e8 100644 --- a/FMS.Infrastructure/Repositories/DashboardRepository.cs +++ b/FMS.Infrastructure/Repositories/DashboardRepository.cs @@ -1,11 +1,9 @@ using FMS.Domain.Dto; +using FMS.Domain.Entities; +using FMS.Domain.Entities.Users; using FMS.Domain.Repositories; -using FMS.Domain.Services; using FMS.Infrastructure.Contexts; using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Text; namespace FMS.Infrastructure.Repositories { @@ -17,8 +15,18 @@ public class DashboardRepository : IDashboardRepository public async Task UserExistsAsync(Guid id) => await _context.Users.AnyAsync(e => e.Id == id); + public async Task GetApplicationUser (Guid id) => + await _context.Users.SingleOrDefaultAsync(e => e.Id == id); + + public async Task> GetUserFacilitiesById(Guid id, bool includeInactive = false) { + var currentUser = await GetApplicationUser(id); + if (currentUser.UserPosition.Name == "" ) + { + + } + return await _context.Facilities.AsNoTracking() .Include(e => e.ComplianceOfficer) .Include(e => e.County) @@ -32,7 +40,7 @@ public async Task> GetUserFacilitiesById(Guid i .Include(e => e.StatusDetails) .ThenInclude(e => e.OverallStatus) .Where(e => includeInactive || e.Active) - .Where(e => e.ComplianceOfficerId == id) + .Where(e => e.ComplianceOfficerId == id || e.Events.Any(ev => ev.ComplianceOfficerId == id)) .OrderByDescending(e => e.Active) .Select(e => new DashboardUserFacilitiesDto(e)) .ToListAsync(); diff --git a/FMS.Infrastructure/Repositories/OrganizationalUnitRepository.cs b/FMS.Infrastructure/Repositories/OrganizationalUnitRepository.cs index b33f251f..c27478f1 100644 --- a/FMS.Infrastructure/Repositories/OrganizationalUnitRepository.cs +++ b/FMS.Infrastructure/Repositories/OrganizationalUnitRepository.cs @@ -1,4 +1,5 @@ -using FMS.Domain.Dto; +using DocumentFormat.OpenXml.Spreadsheet; +using FMS.Domain.Dto; using FMS.Domain.Entities; using FMS.Domain.Repositories; using FMS.Domain.Utils; @@ -22,6 +23,7 @@ public Task OrganizationalUnitNameExistsAsync(string name, Guid? ignoreId public async Task GetOrganizationalUnitAsync(Guid id) { var organizationalUnit = await _context.OrganizationalUnits.AsNoTracking() + .Include(e => e.UserProgram) .SingleOrDefaultAsync(e => e.Id == id); if (organizationalUnit == null) @@ -34,6 +36,7 @@ public async Task GetOrganizationalUnitAsync(Guid id) public async Task> GetOrganizationalUnitListAsync() => await _context.OrganizationalUnits.AsNoTracking() + .Include(e => e.UserProgram) .OrderByDescending(e => e.Active) .ThenBy(e => e.Name) .Select(e => new OrganizationalUnitSummaryDto(e)) @@ -63,15 +66,15 @@ private async Task CreateOrganizationalUnitInternalAsync(OrganizationalUni } - public Task UpdateOrganizationalUnitAsync(Guid id, OrganizationalUnitEditDto organizationalUnitUpdates) + public Task UpdateOrganizationalUnitAsync(Guid id, OrganizationalUnitEditDto organizationalUnitUpdates, Guid? programId) { Prevent.NullOrEmpty(organizationalUnitUpdates.Name, nameof(organizationalUnitUpdates.Name)); - return UpdateOrganizationalUnitInternalAsync(id, organizationalUnitUpdates); + return UpdateOrganizationalUnitInternalAsync(id, organizationalUnitUpdates, programId); } private async Task UpdateOrganizationalUnitInternalAsync(Guid id, - OrganizationalUnitEditDto organizationalUnitUpdates) + OrganizationalUnitEditDto organizationalUnitUpdates, Guid? programId) { var organizationalUnit = await _context.OrganizationalUnits.FindAsync(id); @@ -86,6 +89,10 @@ private async Task UpdateOrganizationalUnitInternalAsync(Guid id, $"Organizational Unit Name '{organizationalUnitUpdates.Name}' already exists."); } + organizationalUnit.UserProgram = programId is null + ? null + : await _context.UserPrograms.FindAsync(programId.Value).ConfigureAwait(false); + organizationalUnit.Name = organizationalUnitUpdates.Name; await _context.SaveChangesAsync(); diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index a1a78925..20481625 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -11,7 +11,7 @@ namespace FMS.Pages.Dashboard public class IndexModel( IAuthorizationService authorization, IUserService userService, - IFacilityRepository _repository) : PageModel + IDashboardRepository _repository) : PageModel { public UserView CurrentUser { get; private set; } public string UserName { get; set; } @@ -30,7 +30,7 @@ public async Task OnGetAsync() UserName = CurrentUser.DisplayName; - //UserFacilities = await _repository.GetFacilitiesForUserAsync(CurrentUser.Id); + //UserFacilities = await _repository.(CurrentUser.Id); return Page(); } diff --git a/FMS/Pages/Maintenance/OrganizationalUnit/Add.cshtml b/FMS/Pages/Maintenance/OrganizationalUnit/Add.cshtml index e18d272a..ffe15a70 100644 --- a/FMS/Pages/Maintenance/OrganizationalUnit/Add.cshtml +++ b/FMS/Pages/Maintenance/OrganizationalUnit/Add.cshtml @@ -14,11 +14,18 @@
    -
    +
    +
    + + + +

    * denotes a required field

    diff --git a/FMS/Pages/Maintenance/OrganizationalUnit/Add.cshtml.cs b/FMS/Pages/Maintenance/OrganizationalUnit/Add.cshtml.cs index e2b82837..06ee2eac 100644 --- a/FMS/Pages/Maintenance/OrganizationalUnit/Add.cshtml.cs +++ b/FMS/Pages/Maintenance/OrganizationalUnit/Add.cshtml.cs @@ -1,10 +1,13 @@ using FMS.Domain.Dto; +using FMS.Domain.Entities; using FMS.Domain.Entities.Users; using FMS.Domain.Repositories; using FMS.Platform.Extensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc.Rendering; +using System.ComponentModel.DataAnnotations; namespace FMS.Pages.Maintenance.OrganizationalUnit { @@ -12,20 +15,29 @@ namespace FMS.Pages.Maintenance.OrganizationalUnit public class AddModel : PageModel { private readonly IOrganizationalUnitRepository _repository; - public AddModel(IOrganizationalUnitRepository repository) => _repository = repository; + private readonly ISelectListHelper _listHelper; + public AddModel(IOrganizationalUnitRepository repository, ISelectListHelper listHelper) + { + _repository = repository; + _listHelper = listHelper; + } [BindProperty] public OrganizationalUnitCreateDto OrganizationalUnit { get; set; } - public void OnGet() + public SelectList UserPrograms { get; private set; } + + public async Task OnGetAsync() { - // Method intentionally left empty. + await PopulateSelectsAsync(); + return Page(); } public async Task OnPostAsync() { if (!ModelState.IsValid) { + await PopulateSelectsAsync(); return Page(); } @@ -39,6 +51,7 @@ public async Task OnPostAsync() if (!ModelState.IsValid) { + await PopulateSelectsAsync(); return Page(); } @@ -46,9 +59,13 @@ public async Task OnPostAsync() TempData?.SetDisplayMessage(Context.Success, $"{MaintenanceOptions.OrganizationalUnit} \"{OrganizationalUnit.Name}\" successfully created."); - + await PopulateSelectsAsync(); return RedirectToPage("./Index", "select", new {MaintenanceSelection = MaintenanceOptions.OrganizationalUnit}); } + private async Task PopulateSelectsAsync() + { + UserPrograms = await _listHelper.UserProgramsSelectListAsync(); + } } } \ No newline at end of file diff --git a/FMS/Pages/Maintenance/OrganizationalUnit/Edit.cshtml b/FMS/Pages/Maintenance/OrganizationalUnit/Edit.cshtml index 8cd8c54a..0bf72d5f 100644 --- a/FMS/Pages/Maintenance/OrganizationalUnit/Edit.cshtml +++ b/FMS/Pages/Maintenance/OrganizationalUnit/Edit.cshtml @@ -23,11 +23,17 @@
    -
    +
    +
    + + +

    * denotes a required field

    diff --git a/FMS/Pages/Maintenance/OrganizationalUnit/Edit.cshtml.cs b/FMS/Pages/Maintenance/OrganizationalUnit/Edit.cshtml.cs index 284d1e50..6a8f4fa5 100644 --- a/FMS/Pages/Maintenance/OrganizationalUnit/Edit.cshtml.cs +++ b/FMS/Pages/Maintenance/OrganizationalUnit/Edit.cshtml.cs @@ -5,7 +5,9 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; namespace FMS.Pages.Maintenance.OrganizationalUnit { @@ -13,7 +15,12 @@ namespace FMS.Pages.Maintenance.OrganizationalUnit public class EditModel : PageModel { private readonly IOrganizationalUnitRepository _repository; - public EditModel(IOrganizationalUnitRepository repository) => _repository = repository; + private readonly ISelectListHelper _listHelper; + public EditModel(IOrganizationalUnitRepository repository, ISelectListHelper listHelper) + { + _repository = repository; + _listHelper = listHelper; + } [BindProperty] public OrganizationalUnitEditDto OrganizationalUnit { get; set; } @@ -21,21 +28,30 @@ public class EditModel : PageModel [BindProperty] public Guid Id { get; set; } + [BindProperty] + [Display(Name = "Program")] + public Guid? UserProgramId { get; set; } + + public SelectList UserPrograms { get; private set; } + public async Task OnGetAsync(Guid? id) { if (id == null) { + await PopulateSelectsAsync(); return NotFound(); } Id = id.Value; OrganizationalUnit = await _repository.GetOrganizationalUnitAsync(id.Value); + UserProgramId = OrganizationalUnit?.UserProgram?.Id; if (OrganizationalUnit == null) { + await PopulateSelectsAsync(); return NotFound(); } - + await PopulateSelectsAsync(); return Page(); } @@ -43,6 +59,7 @@ public async Task OnPostAsync() { if (!ModelState.IsValid) { + await PopulateSelectsAsync(); return Page(); } @@ -56,17 +73,19 @@ public async Task OnPostAsync() if (!ModelState.IsValid) { + await PopulateSelectsAsync(); return Page(); } try { - await _repository.UpdateOrganizationalUnitAsync(Id, OrganizationalUnit); + await _repository.UpdateOrganizationalUnitAsync(Id, OrganizationalUnit, UserProgramId); } catch (DbUpdateConcurrencyException) { if (!await _repository.OrganizationalUnitExistsAsync(Id)) { + await PopulateSelectsAsync(); return NotFound(); } @@ -75,8 +94,13 @@ public async Task OnPostAsync() TempData?.SetDisplayMessage(Context.Success, $"{MaintenanceOptions.OrganizationalUnit} \"{OrganizationalUnit.Name}\" successfully updated."); - + await PopulateSelectsAsync(); return RedirectToPage("./Index"); } + + private async Task PopulateSelectsAsync() + { + UserPrograms = await _listHelper.UserProgramsSelectListAsync(); + } } } \ No newline at end of file diff --git a/FMS/Pages/Maintenance/OrganizationalUnit/Index.cshtml b/FMS/Pages/Maintenance/OrganizationalUnit/Index.cshtml index 5ec09707..269a5c7d 100644 --- a/FMS/Pages/Maintenance/OrganizationalUnit/Index.cshtml +++ b/FMS/Pages/Maintenance/OrganizationalUnit/Index.cshtml @@ -20,8 +20,9 @@ - - + + + @@ -32,6 +33,9 @@ @Html.DisplayFor(m => item.Name, "StringOrNone") @Html.DisplayFor(m => item.Active, "BoolRemoved") + - - - - - - - - - +
    Unit Events: @unit.Name
    + +
    NameActionsNameProgramActions
    + @Html.DisplayFor(m => item.UserProgram.Name, "StringOrNone") + Edit @if (item.Active) diff --git a/FMS/Startup.cs b/FMS/Startup.cs index a2b64fdc..58cbed34 100644 --- a/FMS/Startup.cs +++ b/FMS/Startup.cs @@ -89,6 +89,7 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); //services.AddScoped(); services.AddScoped(); + services.AddScoped(); // Set up database services.AddHostedService(); From 06c4b5268281fbd36d8e98d86153b78e0ed26462 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Wed, 10 Jun 2026 09:49:40 -0400 Subject: [PATCH 20/37] Populate dashboard facility views with detailed user and unit data - Expands `DashboardUserFacilitiesDto` to include additional facility details like facility number, organizational unit, compliance officer, county, and location class. - Replaces the placeholder dashboard page (`Index.cshtml`) with tables displaying the current user's facilities and facilities belonging to their assigned organizational unit. - Corrects the `GetUnitFacilitiesById` method to filter by the current user's `UserUnit.Id`, ensuring facilities are correctly displayed for the user's associated unit. Part of issue #954. --- .../Dashboard/DashboardUserFacilitiesDto.cs | 27 ++- .../Repositories/DashboardRepository.cs | 10 +- FMS/Pages/Dashboard/Index.cshtml | 164 +++++++++++++++++- FMS/Pages/Dashboard/Index.cshtml.cs | 5 +- 4 files changed, 188 insertions(+), 18 deletions(-) diff --git a/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs index ca178664..e6ab4513 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs @@ -11,11 +11,36 @@ public DashboardUserFacilitiesDto(Facility facility) { Id = facility.Id; Name = facility.Name; + FacilityNumber = facility.FacilityNumber; + OrganizationalUnit = facility.OrganizationalUnit; + ComplianceOfficer = facility.ComplianceOfficer; + County = facility.County; + LocationClass = facility.LocationDetails?.LocationClass; + AddlOrgUnit = facility.HsrpFacilityProperties?.OrganizationalUnit; Active = facility.Active; } public Guid Id { get; set; } - public string Name { get; set; } + [Display(Name = "Facility Number")] + public string FacilityNumber { get; } + + [Display(Name = "Facility Name")] + public string Name { get; } + + [Display(Name = "Organizational Unit")] + public OrganizationalUnit OrganizationalUnit { get; set; } + + [Display(Name = "Compliance Officer")] + public ComplianceOfficer ComplianceOfficer { get; set; } + + [Display(Name = "County")] + public County County { get; set; } + + [Display(Name = "Location Class")] + public LocationClass LocationClass { get; set; } + + [Display(Name = "Add'l Org Unit")] + public OrganizationalUnit AddlOrgUnit { get; set; } public bool Active { get; set; } } } diff --git a/FMS.Infrastructure/Repositories/DashboardRepository.cs b/FMS.Infrastructure/Repositories/DashboardRepository.cs index f0e858e8..0cdccf34 100644 --- a/FMS.Infrastructure/Repositories/DashboardRepository.cs +++ b/FMS.Infrastructure/Repositories/DashboardRepository.cs @@ -21,12 +21,6 @@ public async Task GetApplicationUser (Guid id) => public async Task> GetUserFacilitiesById(Guid id, bool includeInactive = false) { - var currentUser = await GetApplicationUser(id); - if (currentUser.UserPosition.Name == "" ) - { - - } - return await _context.Facilities.AsNoTracking() .Include(e => e.ComplianceOfficer) .Include(e => e.County) @@ -48,6 +42,8 @@ public async Task> GetUserFacilitiesById(Guid i public async Task> GetUnitFacilitiesById(Guid id, bool includeInactive = false) { + var currentUser = await GetApplicationUser(id); + return await _context.Facilities.AsNoTracking() .Include(e => e.ComplianceOfficer) .Include(e => e.County) @@ -61,7 +57,7 @@ public async Task> GetUnitFacilitiesById(Guid i .Include(e => e.StatusDetails) .ThenInclude(e => e.OverallStatus) .Where(e => includeInactive || e.Active) - .Where(e => e.OrganizationalUnitId == id) + .Where(e => e.OrganizationalUnitId == currentUser.UserUnit.Id) .OrderByDescending(e => e.Active) .Select(e => new DashboardUnitFacilitiesDto(e)) .ToListAsync(); diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 85bf2e68..f2c5b970 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -11,18 +11,166 @@

    -

    -
    +
    -
    -
    -

    Facility Info

    -
    -
    + + +

    My Facilities

    + + + + + + + + + + + + + + @foreach (var item in Model.UserFacilities) + { + + + + + + + + + + + } + +
    NameNumberUnitC.O.CountyClassAdd'l Org UnitActions
    + @Html.DisplayFor(m => item.Name, "StringOrNone") + @Html.DisplayFor(m => item.Active, "BoolRemoved") + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + @Html.DisplayFor(m => item.OrganizationalUnit, "StringOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer, "StringOrNone") + + @Html.DisplayFor(m => item.County, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass, "StringOrNone") + + @Html.DisplayFor(m => item.AddlOrgUnit, "StringOrNone") + + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + +
    + +
    + +

    Active Unit Facilities

    + + + + + + + + + + + + + + @foreach (var item in Model.UserFacilities) + { + + + + + + + + + + + } + +
    NameNumberUnitC.O.CountyClassAdd'l Org UnitActions
    + @Html.DisplayFor(m => item.Name, "StringOrNone") + @Html.DisplayFor(m => item.Active, "BoolRemoved") + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + @Html.DisplayFor(m => item.OrganizationalUnit, "StringOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer, "StringOrNone") + + @Html.DisplayFor(m => item.County, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass, "StringOrNone") + + @Html.DisplayFor(m => item.AddlOrgUnit, "StringOrNone") + + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + +
    +
    +
    + +

    Active Program Facilities

    + + + + + + + + + + + + + + @foreach (var item in Model.UserFacilities) + { + + + + + + + + + + + } + +
    NameNumberUnitC.O.CountyClassAdd'l Org UnitActions
    + @Html.DisplayFor(m => item.Name, "StringOrNone") + @Html.DisplayFor(m => item.Active, "BoolRemoved") + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + @Html.DisplayFor(m => item.OrganizationalUnit, "StringOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer, "StringOrNone") + + @Html.DisplayFor(m => item.County, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass, "StringOrNone") + + @Html.DisplayFor(m => item.AddlOrgUnit, "StringOrNone") + + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + +
    +
    diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index 20481625..5386df3e 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -1,3 +1,4 @@ +using FMS.Domain.Dto; using FMS.Domain.Entities; using FMS.Domain.Repositories; using FMS.Domain.Services; @@ -16,7 +17,7 @@ public class IndexModel( public UserView CurrentUser { get; private set; } public string UserName { get; set; } public IList Roles { get; private set; } - public IList UserFacilities { get; set; } + public IList UserFacilities { get; set; } public async Task OnGetAsync() { @@ -30,7 +31,7 @@ public async Task OnGetAsync() UserName = CurrentUser.DisplayName; - //UserFacilities = await _repository.(CurrentUser.Id); + UserFacilities = await _repository.GetUserFacilitiesById(CurrentUser.Id); return Page(); } From 374190b7b1cc62816e1862a919bc7e2757a2d6f0 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Thu, 18 Jun 2026 10:40:30 -0400 Subject: [PATCH 21/37] Refine DomainConstants class and development publish profile - Makes `DomainConstants` a static class and removes unused namespaces for improved design and clarity. - Updates the development publish profile to `SkipExtraFilesOnServer = true` to prevent accidental deletion of non-deployed files on the server during publish. Part of issue #954. --- FMS.Domain/DomainConstants.cs | 8 ++------ FMS/Properties/PublishProfiles/Publish-Development.pubxml | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/FMS.Domain/DomainConstants.cs b/FMS.Domain/DomainConstants.cs index 3fe4673f..c10dbf6e 100644 --- a/FMS.Domain/DomainConstants.cs +++ b/FMS.Domain/DomainConstants.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace FMS.Domain +namespace FMS.Domain { - public class DomainConstants + public static class DomainConstants { public const string SiteSummaryReportPath = "/Reporting/SiteSummary/Report/"; diff --git a/FMS/Properties/PublishProfiles/Publish-Development.pubxml b/FMS/Properties/PublishProfiles/Publish-Development.pubxml index f2f5a2f1..e2809dc6 100644 --- a/FMS/Properties/PublishProfiles/Publish-Development.pubxml +++ b/FMS/Properties/PublishProfiles/Publish-Development.pubxml @@ -15,7 +15,7 @@ by editing this MSBuild file. In order to learn more about this please visit htt dev-fms.gaepd.org fms - false + true WMSVC true SOG\akarasch From 753bcc672e71ead1c6af564c83d81100b7083e36 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Mon, 6 Jul 2026 14:53:49 -0400 Subject: [PATCH 22/37] Enhance dashboard with role-based facility and event views - Expands dashboard DTOs to include comprehensive facility and event details. - Implements repository methods to fetch HSI facilities and recent events (last year), filtered by user, organizational unit, or program manager roles. - Updates the dashboard UI to dynamically display facilities and events relevant to the current user's responsibilities and position. - Adds conditional rendering for the Data Maintenance section based on user role. - Adds a `Facility` navigation property to the `Event` entity for eager loading facility details. Part of issue #954. --- .../Dashboard/DashboardProgramEventsDto.cs | 53 ++- .../DashboardProgramFacilitiesDto.cs | 29 +- .../Dto/Dashboard/DashboardUnitEventsDto.cs | 53 ++- .../Dashboard/DashboardUnitFacilitiesDto.cs | 31 +- .../Dto/Dashboard/DashboardUserEventsDto.cs | 53 ++- .../Dashboard/DashboardUserFacilitiesDto.cs | 1 + FMS.Domain/Entities/Event.cs | 1 + .../Repositories/IDashboardRepository.cs | 12 +- FMS.Domain/Services/UserView.cs | 6 +- .../Repositories/DashboardRepository.cs | 124 +++++- FMS/Pages/Dashboard/Index.cshtml | 387 ++++++++++++------ FMS/Pages/Dashboard/Index.cshtml.cs | 52 ++- 12 files changed, 652 insertions(+), 150 deletions(-) diff --git a/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs index 7c7a827a..b1375d5d 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs @@ -1,4 +1,5 @@ -using System; +using FMS.Domain.Entities; +using System; using System.Collections.Generic; using System.Text; @@ -6,5 +7,55 @@ namespace FMS.Domain.Dto { public class DashboardProgramEventsDto { + public DashboardProgramEventsDto() { } + + public DashboardProgramEventsDto(Event facilityEvent) + { + Id = facilityEvent.Id; + FacilityId = facilityEvent.FacilityId; + FacilityNumber = facilityEvent.Facility.FacilityNumber; + EventType = facilityEvent.EventType; + ActionTaken = facilityEvent.ActionTaken; + StartDate = facilityEvent.StartDate; + DueDate = facilityEvent.DueDate; + CompletionDate = facilityEvent.CompletionDate; + ComplianceOfficer = facilityEvent.ComplianceOfficer; + Contractor = facilityEvent.EventContractor; + EventAmount = facilityEvent.EventAmount; + Active = facilityEvent.Active; + } + + public Guid Id { get; set; } + + public Guid FacilityId { get; set; } + + [Display(Name = "Facility Number")] + public string FacilityNumber { get; } + + [Display(Name = "Event Type")] + public EventType EventType { get; } + + [Display(Name = "Action Taken")] + public ActionTaken ActionTaken { get; set; } + + [Display(Name = "Start Date")] + public DateOnly? StartDate { get; set; } + + [Display(Name = "Due Date")] + public DateOnly? DueDate { get; set; } + + [Display(Name = "Completion Date")] + public DateOnly? CompletionDate { get; set; } + + [Display(Name = "Compliance Officer")] + public ComplianceOfficer ComplianceOfficer { get; set; } + + [Display(Name = "Contractor")] + public EventContractor Contractor { get; set; } + + [Display(Name = "Event Amount")] + public Decimal? EventAmount { get; set; } + + public bool Active { get; set; } } } diff --git a/FMS.Domain/Dto/Dashboard/DashboardProgramFacilitiesDto.cs b/FMS.Domain/Dto/Dashboard/DashboardProgramFacilitiesDto.cs index 5b628d10..ca24f23a 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardProgramFacilitiesDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardProgramFacilitiesDto.cs @@ -11,10 +11,37 @@ public DashboardProgramFacilitiesDto(Facility facility) { Id = facility.Id; Name = facility.Name; + FacilityNumber = facility.FacilityNumber; + OrganizationalUnit = facility.OrganizationalUnit; + ComplianceOfficer = facility.ComplianceOfficer; + County = facility.County; + LocationClass = facility.LocationDetails?.LocationClass; + AddlOrgUnit = facility.HsrpFacilityProperties?.OrganizationalUnit; Active = facility.Active; } public Guid Id { get; set; } - public string Name { get; set; } + + [Display(Name = "Facility Number")] + public string FacilityNumber { get; } + + [Display(Name = "Facility Name")] + public string Name { get; } + + [Display(Name = "Organizational Unit")] + public OrganizationalUnit OrganizationalUnit { get; set; } + + [Display(Name = "Compliance Officer")] + public ComplianceOfficer ComplianceOfficer { get; set; } + + [Display(Name = "County")] + public County County { get; set; } + + [Display(Name = "Location Class")] + public LocationClass LocationClass { get; set; } + + [Display(Name = "Add'l Org Unit")] + public OrganizationalUnit AddlOrgUnit { get; set; } + public bool Active { get; set; } } } diff --git a/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs index 021cb314..077c04cd 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs @@ -1,4 +1,5 @@ -using System; +using FMS.Domain.Entities; +using System; using System.Collections.Generic; using System.Text; @@ -6,5 +7,55 @@ namespace FMS.Domain.Dto { public class DashboardUnitEventsDto { + public DashboardUnitEventsDto() { } + + public DashboardUnitEventsDto(Event facilityEvent) + { + Id = facilityEvent.Id; + FacilityId = facilityEvent.FacilityId; + FacilityNumber = facilityEvent.Facility?.FacilityNumber; + EventType = facilityEvent.EventType; + ActionTaken = facilityEvent.ActionTaken; + StartDate = facilityEvent.StartDate; + DueDate = facilityEvent.DueDate; + CompletionDate = facilityEvent.CompletionDate; + ComplianceOfficer = facilityEvent.ComplianceOfficer; + Contractor = facilityEvent.EventContractor; + EventAmount = facilityEvent.EventAmount; + Active = facilityEvent.Active; + } + + public Guid Id { get; set; } + + public Guid FacilityId { get; set; } + + [Display(Name = "Facility Number")] + public string FacilityNumber { get; } + + [Display(Name = "Event Type")] + public EventType EventType { get; } + + [Display(Name = "Action Taken")] + public ActionTaken ActionTaken { get; set; } + + [Display(Name = "Start Date")] + public DateOnly? StartDate { get; set; } + + [Display(Name = "Due Date")] + public DateOnly? DueDate { get; set; } + + [Display(Name = "Completion Date")] + public DateOnly? CompletionDate { get; set; } + + [Display(Name = "Compliance Officer")] + public ComplianceOfficer ComplianceOfficer { get; set; } + + [Display(Name = "Contractor")] + public EventContractor Contractor { get; set; } + + [Display(Name = "Event Amount")] + public Decimal? EventAmount { get; set; } + + public bool Active { get; set; } } } diff --git a/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs index 872cd81f..1ae0d192 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs @@ -7,14 +7,41 @@ namespace FMS.Domain.Dto { public class DashboardUnitFacilitiesDto { - public DashboardUnitFacilitiesDto(Facility facility) + public DashboardUnitFacilitiesDto(Facility facility) { Id = facility.Id; Name = facility.Name; + Name = facility.Name; + FacilityNumber = facility.FacilityNumber; + OrganizationalUnit = facility.OrganizationalUnit; + ComplianceOfficer = facility.ComplianceOfficer; + County = facility.County; + LocationClass = facility.LocationDetails?.LocationClass; + AddlOrgUnit = facility.HsrpFacilityProperties?.OrganizationalUnit; Active = facility.Active; } public Guid Id { get; set; } - public string Name { get; set; } + + [Display(Name = "Facility Number")] + public string FacilityNumber { get; } + + [Display(Name = "Facility Name")] + public string Name { get; } + + [Display(Name = "Organizational Unit")] + public OrganizationalUnit OrganizationalUnit { get; set; } + + [Display(Name = "Compliance Officer")] + public ComplianceOfficer ComplianceOfficer { get; set; } + + [Display(Name = "County")] + public County County { get; set; } + + [Display(Name = "Location Class")] + public LocationClass LocationClass { get; set; } + + [Display(Name = "Add'l Org Unit")] + public OrganizationalUnit AddlOrgUnit { get; set; } public bool Active { get; set; } } } diff --git a/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs index fe668c8f..8ad8e952 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs @@ -1,4 +1,5 @@ -using System; +using FMS.Domain.Entities; +using System; using System.Collections.Generic; using System.Text; @@ -6,5 +7,55 @@ namespace FMS.Domain.Dto { public class DashboardUserEventsDto { + public DashboardUserEventsDto() { } + + public DashboardUserEventsDto(Event facilityEvent) + { + Id = facilityEvent.Id; + FacilityId = facilityEvent.FacilityId; + FacilityNumber = facilityEvent.Facility.FacilityNumber; + EventType = facilityEvent.EventType; + ActionTaken = facilityEvent.ActionTaken; + StartDate = facilityEvent.StartDate; + DueDate = facilityEvent.DueDate; + CompletionDate = facilityEvent.CompletionDate; + ComplianceOfficer = facilityEvent.ComplianceOfficer; + Contractor = facilityEvent.EventContractor; + EventAmount = facilityEvent.EventAmount; + Active = facilityEvent.Active; + } + + public Guid Id { get; set; } + + public Guid FacilityId { get; set; } + + [Display(Name = "Facility Number")] + public string FacilityNumber { get; } + + [Display(Name = "Event Type")] + public EventType EventType { get; } + + [Display(Name = "Action Taken")] + public ActionTaken ActionTaken { get; set; } + + [Display(Name = "Start Date")] + public DateOnly? StartDate { get; set; } + + [Display(Name = "Due Date")] + public DateOnly? DueDate { get; set; } + + [Display(Name = "Completion Date")] + public DateOnly? CompletionDate { get; set; } + + [Display(Name = "Compliance Officer")] + public ComplianceOfficer ComplianceOfficer { get; set; } + + [Display(Name = "Contractor")] + public EventContractor Contractor { get; set; } + + [Display(Name = "Event Amount")] + public Decimal? EventAmount { get; set; } + + public bool Active { get; set; } } } diff --git a/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs index e6ab4513..58b6984f 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUserFacilitiesDto.cs @@ -21,6 +21,7 @@ public DashboardUserFacilitiesDto(Facility facility) } public Guid Id { get; set; } + [Display(Name = "Facility Number")] public string FacilityNumber { get; } diff --git a/FMS.Domain/Entities/Event.cs b/FMS.Domain/Entities/Event.cs index e911aef9..be354659 100644 --- a/FMS.Domain/Entities/Event.cs +++ b/FMS.Domain/Entities/Event.cs @@ -23,6 +23,7 @@ public Event(EventCreateDto eventDto) } public Guid FacilityId { get; set; } + public Facility Facility { get; set; } public Guid? ParentId { get; set; } diff --git a/FMS.Domain/Repositories/IDashboardRepository.cs b/FMS.Domain/Repositories/IDashboardRepository.cs index b93d4f7a..e0fd68a6 100644 --- a/FMS.Domain/Repositories/IDashboardRepository.cs +++ b/FMS.Domain/Repositories/IDashboardRepository.cs @@ -1,5 +1,6 @@ using FMS.Domain.Dto; using FMS.Domain.Entities.Users; +using FMS.Domain.Services; namespace FMS.Domain.Repositories { @@ -7,8 +8,13 @@ public interface IDashboardRepository : IDisposable { Task UserExistsAsync(Guid id); Task GetApplicationUser(Guid id); - Task> GetUserFacilitiesById(Guid id, bool includeInactive = false); - Task> GetUnitFacilitiesById(Guid id, bool includeInactive = false); - Task> GetProgramFacilitiesById(Guid id, bool includeInactive = false); + Task GetCOIdFromUser(UserView user); + Task> GetUserUnitListFromProgramById(Guid id, bool includeInactive = false); + Task> GetUserHSIFacilitiesById(Guid id, bool includeInactive = false); + Task> GetUnitHSIFacilitiesById(Guid id, bool includeInactive = false); + Task> GetProgramHSIFacilitiesById(Guid id, bool includeInactive = false); + Task> GetEventsByUserId(Guid id, bool includeInactive = false); + Task> GetUnitEventsByUserId(Guid id, bool includeInactive = false); + Task> GetProgramEventsByUserId(Guid id, bool includeInactive = false); } } diff --git a/FMS.Domain/Services/UserView.cs b/FMS.Domain/Services/UserView.cs index 96ba584a..d827f471 100644 --- a/FMS.Domain/Services/UserView.cs +++ b/FMS.Domain/Services/UserView.cs @@ -17,8 +17,8 @@ public UserView(ApplicationUser user) } public Guid Id { get; } - private string GivenName { get; } - private string FamilyName { get; } + public string GivenName { get; } + public string FamilyName { get; } public string Email { get; } public UserProgram UserProgram { get; } public OrganizationalUnit UserUnit { get; } @@ -26,5 +26,7 @@ public UserView(ApplicationUser user) public string DisplayName => string.Join(" ", new[] { GivenName, FamilyName }.Where(s => !string.IsNullOrEmpty(s))); + + public string Name => string.Join(", ", new[] { FamilyName, GivenName }.Where(s => !string.IsNullOrEmpty(s))); } } diff --git a/FMS.Infrastructure/Repositories/DashboardRepository.cs b/FMS.Infrastructure/Repositories/DashboardRepository.cs index 0cdccf34..54b8eb08 100644 --- a/FMS.Infrastructure/Repositories/DashboardRepository.cs +++ b/FMS.Infrastructure/Repositories/DashboardRepository.cs @@ -2,6 +2,7 @@ using FMS.Domain.Entities; using FMS.Domain.Entities.Users; using FMS.Domain.Repositories; +using FMS.Domain.Services; using FMS.Infrastructure.Contexts; using Microsoft.EntityFrameworkCore; @@ -17,11 +18,36 @@ public async Task UserExistsAsync(Guid id) => public async Task GetApplicationUser (Guid id) => await _context.Users.SingleOrDefaultAsync(e => e.Id == id); - - public async Task> GetUserFacilitiesById(Guid id, bool includeInactive = false) + public async Task GetCOIdFromUser(UserView user) { - return await _context.Facilities.AsNoTracking() + var coId = await _context.ComplianceOfficers + .Where(e => e.GivenName == user.GivenName && e.FamilyName == user.FamilyName) + .Where(e => e.Active) + .Select(e => e.Id) + .FirstOrDefaultAsync(); + + return coId; + } + + public async Task> GetUserUnitListFromProgramById(Guid id, bool includeInactive = false) + { + var currentUser = await GetApplicationUser(id); + + var units = await _context.OrganizationalUnits.AsNoTracking() + .Where(e => includeInactive || e.Active) + .Where(e => e.UserProgram.Name == currentUser.UserProgram.Name) + .OrderByDescending(e => e.Active) + .Select(e => new OrganizationalUnitSummaryDto(e)) + .ToListAsync(); + return units; + } + + #region Facilities + + public async Task> GetUserHSIFacilitiesById(Guid id, bool includeInactive = false) + { + var facilities = await _context.Facilities.AsNoTracking() .Include(e => e.ComplianceOfficer) .Include(e => e.County) .Include(e => e.Events) @@ -34,15 +60,19 @@ public async Task> GetUserFacilitiesById(Guid i .Include(e => e.StatusDetails) .ThenInclude(e => e.OverallStatus) .Where(e => includeInactive || e.Active) - .Where(e => e.ComplianceOfficerId == id || e.Events.Any(ev => ev.ComplianceOfficerId == id)) + .Where(e => e.ComplianceOfficerId == id) + .Where(e => e.FacilityType.Name == "HSI") + .Where(e => e.FacilityStatus.Status == "Active") .OrderByDescending(e => e.Active) .Select(e => new DashboardUserFacilitiesDto(e)) .ToListAsync(); + return facilities; } - public async Task> GetUnitFacilitiesById(Guid id, bool includeInactive = false) + public async Task> GetUnitHSIFacilitiesById(Guid id, bool includeInactive = false) { var currentUser = await GetApplicationUser(id); + var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); return await _context.Facilities.AsNoTracking() .Include(e => e.ComplianceOfficer) @@ -58,13 +88,19 @@ public async Task> GetUnitFacilitiesById(Guid i .ThenInclude(e => e.OverallStatus) .Where(e => includeInactive || e.Active) .Where(e => e.OrganizationalUnitId == currentUser.UserUnit.Id) - .OrderByDescending(e => e.Active) + .Where(e => e.FacilityType.Name == "HSI") + .Where(e => e.FacilityStatus.Status == "Active") + .Where(e => e.Events.Any(ev => ev.StartDate >= oneYearAgo)) + .OrderBy(e => e.ComplianceOfficer.FamilyName) + .ThenBy(e => e.FacilityNumber) .Select(e => new DashboardUnitFacilitiesDto(e)) .ToListAsync(); } - public async Task> GetProgramFacilitiesById(Guid id, bool includeInactive = false) + public async Task> GetProgramHSIFacilitiesById(Guid id, bool includeInactive = false) { + var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); + return await _context.Facilities.AsNoTracking() .Include(e => e.ComplianceOfficer) .Include(e => e.County) @@ -78,12 +114,82 @@ public async Task> GetProgramFacilitiesById( .Include(e => e.StatusDetails) .ThenInclude(e => e.OverallStatus) .Where(e => includeInactive || e.Active) - .Where(e => e.FacilityTypeId == id) - .OrderByDescending(e => e.Active) + .Where(e => e.OrganizationalUnit.UserProgram.Id == id) + .Where(e => e.FacilityType.Name == "HSI") + .Where(e => e.FacilityStatus.Status == "Active") + .Where(e => e.Events.Any(ev => ev.StartDate >= oneYearAgo)) + .OrderBy(e => e.OrganizationalUnit.Name) + .ThenBy(e => e.ComplianceOfficer.FamilyName) + .ThenBy(e => e.FacilityNumber) .Select(e => new DashboardProgramFacilitiesDto(e)) .ToListAsync(); } + #endregion + + #region Events + + public async Task> GetEventsByUserId(Guid id, bool includeInactive = false) + { + var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); + + return await _context.Events.AsNoTracking() + .Include(e => e.Facility) + .Include(e => e.ComplianceOfficer) + .Include(e => e.EventType) + .Include(e => e.ActionTaken) + .Include(e => e.EventContractor) + .Where(e => includeInactive || e.Active) + .Where(e => e.ComplianceOfficerId.Equals(id)) + .Where(e => e.StartDate >= oneYearAgo) + .OrderByDescending(e => e.StartDate) + .Select(e => new DashboardUserEventsDto(e)) + .ToListAsync(); + } + + public async Task> GetUnitEventsByUserId(Guid id, bool includeInactive = false) + { + var currentUser = await GetApplicationUser(id); + var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); + + return await _context.Events.AsNoTracking() + .Include(e => e.Facility) + .Include(e => e.ComplianceOfficer) + .Include(e => e.EventType) + .Include(e => e.ActionTaken) + .Include(e => e.EventContractor) + .Where(e => includeInactive || e.Active) + .Where(e => e.Facility.OrganizationalUnit.Id.Equals(currentUser.UserUnit.Id)) + .Where(e => e.StartDate >= oneYearAgo) + .OrderByDescending(e => e.StartDate) + .Select(e => new DashboardUnitEventsDto(e)) + .ToListAsync(); + } + + public async Task> GetProgramEventsByUserId(Guid id, bool includeInactive = false) + { + var currentUser = await GetApplicationUser(id); + var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); + + return await _context.Events.AsNoTracking() + .Include(e => e.Facility) + .Include(e => e.ComplianceOfficer) + .Include(e => e.EventType) + .Include(e => e.ActionTaken) + .Include(e => e.EventContractor) + .Where(e => includeInactive || e.Active) + .Where(e => e.Facility.OrganizationalUnit.UserProgram.Id.Equals(currentUser.UserProgram.Id)) + .Where(e => e.StartDate >= oneYearAgo) + .OrderBy(e => e.Facility.OrganizationalUnit.Name) + .ThenBy(e => e.ComplianceOfficer.FamilyName) + .ThenBy(e => e.Facility.FacilityNumber) + .Select(e => new DashboardProgramEventsDto(e)) + .ToListAsync(); + } + + + #endregion + #region IDisposable Support private bool _disposedValue; diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index f2c5b970..7ade908d 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -1,5 +1,6 @@ @page @using FMS.Pages.Dashboard +@using FMS.Domain.Entities.Users @model FMS.Pages.Dashboard.IndexModel @{ @@ -15,199 +16,337 @@ @Model.UserName's Facilities -
    +
    -

    My Facilities

    +

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    - + - - @foreach (var item in Model.UserFacilities) { - - - - - - - - - } - -
    Name NumberName UnitC.O. County Class Add'l Org UnitActions
    - @Html.DisplayFor(m => item.Name, "StringOrNone") - @Html.DisplayFor(m => item.Active, "BoolRemoved") - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - @Html.DisplayFor(m => item.OrganizationalUnit, "StringOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer, "StringOrNone") - - @Html.DisplayFor(m => item.County, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass, "StringOrNone") - - @Html.DisplayFor(m => item.AddlOrgUnit, "StringOrNone") - - + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone")
    -
    -
    - -

    Active Unit Facilities

    - - - - - - - - - - - - - - @foreach (var item in Model.UserFacilities) - { - - - - - }
    NameNumberUnitC.O.CountyClassAdd'l Org UnitActions
    + @Html.DisplayFor(m => item.Name, "StringOrNone") - @Html.DisplayFor(m => item.Active, "BoolRemoved") - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - @Html.DisplayFor(m => item.OrganizationalUnit, "StringOrNone") - @Html.DisplayFor(m => item.ComplianceOfficer, "StringOrNone") + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") - @Html.DisplayFor(m => item.County, "StringOrNone") + @Html.DisplayFor(m => item.County.Name, "StringOrNone") - @Html.DisplayFor(m => item.LocationClass, "StringOrNone") + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") - @Html.DisplayFor(m => item.AddlOrgUnit, "StringOrNone") - - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - + @Html.DisplayFor(m => item.AddlOrgUnit.Name, "StringOrNone")
    + @if (Model.CurrentUser.UserPosition.Name == "PM1") + { +
    + +

    Active Unit Facilities for Unit: @Model.CurrentUser.UserUnit.Name

    + + + + + + + + + + + + @foreach (var item in Model.UnitFacilities) + { + + + + + + + + + } + +
    NumberNameC.O.CountyClassAdd'l Org Unit
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") + + @Html.DisplayFor(m => item.AddlOrgUnit.Name, "StringOrNone") +
    +
    + } + @if (Model.CurrentUser.UserPosition.Name == "PM2") + { +
    + +

    Active Program Facilities for Program: @Model.CurrentUser.UserProgram.Name

    + + + + + + + + + + + + + @foreach (var item in Model.ProgramFacilities) + { + + + + + + + + + + } + +
    NumberNameUnitC.O.CountyClassAdd'l Org Unit
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.Name, "StringOrNone") + + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") + + @Html.DisplayFor(m => item.AddlOrgUnit.Name, "StringOrNone") +
    +
    + } +
    +
    +
    +
    + +
    +

    + +

    +
    +
    +
    - -

    Active Program Facilities

    +
    +

    Active Events for Compliance Officer: @Model.CurrentUser.Name

    - - - - - - - - + + + + + + + + - @foreach (var item in Model.UserFacilities) + @foreach (var item in Model.UserEvents) { - - - }
    NameNumberUnitC.O.CountyClassAdd'l Org UnitActionsFacility NumberEvent TypeAction TakenStart DateDue DateCompletion DateEvent AmountEvent Contractor
    - @Html.DisplayFor(m => item.Name, "StringOrNone") - @Html.DisplayFor(m => item.Active, "BoolRemoved") + + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - @Html.DisplayFor(m => item.OrganizationalUnit, "StringOrNone") + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - @Html.DisplayFor(m => item.ComplianceOfficer, "StringOrNone") + @Html.DisplayFor(m => item.StartDate, "DateOrNone") - @Html.DisplayFor(m => item.County, "StringOrNone") + @Html.DisplayFor(m => item.DueDate, "DateOrNone") - @Html.DisplayFor(m => item.LocationClass, "StringOrNone") + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") - @Html.DisplayFor(m => item.AddlOrgUnit, "StringOrNone") + @Html.DisplayFor(m => item.EventAmount, "DecimalOrNone") - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - + + @Html.DisplayFor(m => item.Contractor.Name, "StringOrNone")
    + @if (Model.CurrentUser.UserPosition.Name == "PM1") + { +
    + +

    Active Events for Unit: @Model.CurrentUser.UserUnit.Name

    + + + + + + + + + + + + + + @foreach (var item in Model.UserEvents) + { + + + + + + + + + + + } + +
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateEvent AmountEvent Contractor
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.EventAmount, "DecimalOrNone") + + @Html.DisplayFor(m => item.Contractor.Name, "StringOrNone") +
    +
    + } + @if (Model.CurrentUser.UserPosition.Name == "PM2") + { +
    + +

    Active Events for Program: @Model.CurrentUser.UserProgram.Name

    + + + + + + + + + + + + + + @foreach (var item in Model.UserEvents) + { + + + + + + + + + + + } + +
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateEvent AmountEvent Contractor
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.EventAmount, "DecimalOrNone") + + @Html.DisplayFor(m => item.Contractor.Name, "StringOrNone") +
    +
    + }
    - -
    -

    - -

    -
    -
    -
    -
    -

    Event Info

    -
    -
    -
    -
    -
    - -
    -

    - -

    -
    -
    -
    -
    -

    Data Maintenance Info

    + @if (Model.CurrentUser.UserPosition.Name == "PM2" && Model.Roles.Contains(UserRoles.SiteMaintenance)) + { +
    +

    + +

    +
    +
    +
    +
    +

    Data Maintenance Info

    +
    -
    + }
    \ No newline at end of file diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index 5386df3e..a71b5fb2 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -1,5 +1,6 @@ using FMS.Domain.Dto; using FMS.Domain.Entities; +using FMS.Domain.Entities.Users; using FMS.Domain.Repositories; using FMS.Domain.Services; using FMS.Infrastructure.Services; @@ -10,14 +11,20 @@ namespace FMS.Pages.Dashboard { public class IndexModel( - IAuthorizationService authorization, - IUserService userService, + IAuthorizationService authorization, + IUserService userService, IDashboardRepository _repository) : PageModel { public UserView CurrentUser { get; private set; } - public string UserName { get; set; } + public string UserName { get; set; } + public Guid UserCOId { get; set; } = Guid.Empty; public IList Roles { get; private set; } - public IList UserFacilities { get; set; } + public IList UserFacilities { get; set; } = new List(); + public IList UnitFacilities { get; set; } = new List(); + public IList ProgramFacilities { get; set; } = new List(); + public IList UserEvents { get; set; } = new List(); + public IList UnitEvents { get; set; } = new List(); + public IList ProgramEvents { get; set; } = new List(); public async Task OnGetAsync() { @@ -25,13 +32,46 @@ public async Task OnGetAsync() return Challenge(); CurrentUser = await userService.GetCurrentUserAsync() - ?? throw new Exception("Current user not found"); + ?? throw new InvalidOperationException("Current user not found", new Exception("User retrieval failed")); Roles = await userService.GetCurrentUserRolesAsync(); + UserCOId = await _repository.GetCOIdFromUser(CurrentUser); + + if (UserCOId == Guid.Empty || CurrentUser.UserPosition == null) + { + return RedirectToPage("/Facilities/Index"); + } + UserName = CurrentUser.DisplayName; - UserFacilities = await _repository.GetUserFacilitiesById(CurrentUser.Id); + // Get Facilities for the current user based on their role and position + + UserFacilities = await _repository.GetUserHSIFacilitiesById(UserCOId); + + if (CurrentUser.UserPosition.Name == "PM1") + { + UnitFacilities = await _repository.GetUnitHSIFacilitiesById(CurrentUser.Id); + } + + if (CurrentUser.UserPosition.Name == "PM2") + { + ProgramFacilities = await _repository.GetProgramHSIFacilitiesById(CurrentUser.UserProgram.Id); + } + + //Get Events for the current user based on their role and position + + UserEvents = await _repository.GetEventsByUserId(UserCOId); + + if (CurrentUser.UserPosition.Name == "PM1") + { + UnitEvents = await _repository.GetUnitEventsByUserId(CurrentUser.Id); + } + + if (CurrentUser.UserPosition.Name == "PM2") + { + ProgramEvents = await _repository.GetProgramEventsByUserId(CurrentUser.Id); + } return Page(); } From 2b98c691052324948faa5dcb5689753eac480644 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 7 Jul 2026 14:11:01 -0400 Subject: [PATCH 23/37] Refine dashboard event display, organization, and management - Replace event contractor and amount with event comment in event DTOs. - Add organizational unit to program event DTO and ensure eager loading. - Restructure dashboard event sections into nested accordions for improved clarity. - Implement role-based edit and delete actions directly on event tables. - Group program events by organizational unit for program managers (PM2). - Adjust event sorting in dashboard repository queries for better ordering. Part of issue #954. --- .../Dashboard/DashboardProgramEventsDto.cs | 12 +- .../Dto/Dashboard/DashboardUnitEventsDto.cs | 10 +- .../Dto/Dashboard/DashboardUserEventsDto.cs | 10 +- .../Repositories/DashboardRepository.cs | 6 +- FMS/Pages/Dashboard/Index.cshtml | 594 ++++++++++-------- FMS/Pages/Dashboard/Index.cshtml.cs | 10 + 6 files changed, 372 insertions(+), 270 deletions(-) diff --git a/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs index b1375d5d..a0c28d7b 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs @@ -20,8 +20,8 @@ public DashboardProgramEventsDto(Event facilityEvent) DueDate = facilityEvent.DueDate; CompletionDate = facilityEvent.CompletionDate; ComplianceOfficer = facilityEvent.ComplianceOfficer; - Contractor = facilityEvent.EventContractor; - EventAmount = facilityEvent.EventAmount; + Unit = facilityEvent.Facility.OrganizationalUnit; + Comment = facilityEvent.Comment?.Substring(0, Math.Min(facilityEvent.Comment.Length, 100)); Active = facilityEvent.Active; } @@ -50,11 +50,11 @@ public DashboardProgramEventsDto(Event facilityEvent) [Display(Name = "Compliance Officer")] public ComplianceOfficer ComplianceOfficer { get; set; } - [Display(Name = "Contractor")] - public EventContractor Contractor { get; set; } + [Display(Name = "Unit")] + public OrganizationalUnit Unit { get; set; } - [Display(Name = "Event Amount")] - public Decimal? EventAmount { get; set; } + [Display(Name = "Comment")] + public string Comment { get; set; } public bool Active { get; set; } } diff --git a/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs index 077c04cd..61a010dd 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs @@ -20,8 +20,7 @@ public DashboardUnitEventsDto(Event facilityEvent) DueDate = facilityEvent.DueDate; CompletionDate = facilityEvent.CompletionDate; ComplianceOfficer = facilityEvent.ComplianceOfficer; - Contractor = facilityEvent.EventContractor; - EventAmount = facilityEvent.EventAmount; + Comment = facilityEvent.Comment?.Substring(0, Math.Min(facilityEvent.Comment.Length, 100)); Active = facilityEvent.Active; } @@ -50,11 +49,8 @@ public DashboardUnitEventsDto(Event facilityEvent) [Display(Name = "Compliance Officer")] public ComplianceOfficer ComplianceOfficer { get; set; } - [Display(Name = "Contractor")] - public EventContractor Contractor { get; set; } - - [Display(Name = "Event Amount")] - public Decimal? EventAmount { get; set; } + [Display(Name = "Comment")] + public string Comment { get; set; } public bool Active { get; set; } } diff --git a/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs index 8ad8e952..acb7114e 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs @@ -20,8 +20,7 @@ public DashboardUserEventsDto(Event facilityEvent) DueDate = facilityEvent.DueDate; CompletionDate = facilityEvent.CompletionDate; ComplianceOfficer = facilityEvent.ComplianceOfficer; - Contractor = facilityEvent.EventContractor; - EventAmount = facilityEvent.EventAmount; + Comment = facilityEvent.Comment?.Substring(0, Math.Min(facilityEvent.Comment.Length, 100)); Active = facilityEvent.Active; } @@ -50,11 +49,8 @@ public DashboardUserEventsDto(Event facilityEvent) [Display(Name = "Compliance Officer")] public ComplianceOfficer ComplianceOfficer { get; set; } - [Display(Name = "Contractor")] - public EventContractor Contractor { get; set; } - - [Display(Name = "Event Amount")] - public Decimal? EventAmount { get; set; } + [Display(Name = "Comment")] + public string Comment { get; set; } public bool Active { get; set; } } diff --git a/FMS.Infrastructure/Repositories/DashboardRepository.cs b/FMS.Infrastructure/Repositories/DashboardRepository.cs index 54b8eb08..d118810c 100644 --- a/FMS.Infrastructure/Repositories/DashboardRepository.cs +++ b/FMS.Infrastructure/Repositories/DashboardRepository.cs @@ -161,7 +161,8 @@ public async Task> GetUnitEventsByUserId(Guid id, b .Where(e => includeInactive || e.Active) .Where(e => e.Facility.OrganizationalUnit.Id.Equals(currentUser.UserUnit.Id)) .Where(e => e.StartDate >= oneYearAgo) - .OrderByDescending(e => e.StartDate) + .OrderBy(e => e.ComplianceOfficer.FamilyName) + .ThenByDescending(e => e.StartDate) .Select(e => new DashboardUnitEventsDto(e)) .ToListAsync(); } @@ -177,12 +178,13 @@ public async Task> GetProgramEventsByUserId(Guid .Include(e => e.EventType) .Include(e => e.ActionTaken) .Include(e => e.EventContractor) + .Include(e => e.Facility.OrganizationalUnit) .Where(e => includeInactive || e.Active) .Where(e => e.Facility.OrganizationalUnit.UserProgram.Id.Equals(currentUser.UserProgram.Id)) .Where(e => e.StartDate >= oneYearAgo) .OrderBy(e => e.Facility.OrganizationalUnit.Name) .ThenBy(e => e.ComplianceOfficer.FamilyName) - .ThenBy(e => e.Facility.FacilityNumber) + .ThenByDescending(e => e.StartDate) .Select(e => new DashboardProgramEventsDto(e)) .ToListAsync(); } diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 7ade908d..d9f8f752 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -4,7 +4,7 @@ @model FMS.Pages.Dashboard.IndexModel @{ - ViewData["Title"] = $"{Model.UserName}'s Dashboard"; + ViewData["Title"] = $"Welcome, {Model.UserName}"; }

    @ViewData["Title"]

    @@ -12,28 +12,71 @@

    -

    -
    +
    -
    +
    + +

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    + + + + + + + + + + + + @foreach (var item in Model.UserFacilities) + { + + + + + + + + + } + +
    NumberNameUnitCountyClassAdd'l Org Unit
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.Name, "StringOrNone") + + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") + + @Html.DisplayFor(m => item.AddlOrgUnit.Name, "StringOrNone") +
    +
    + @if (Model.CurrentUser.UserPosition.Name == "PM1") + {
    - -

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    +
    +

    Active Unit Facilities for Unit: @Model.CurrentUser.UserUnit.Name

    - + - @foreach (var item in Model.UserFacilities) + @foreach (var item in Model.UnitFacilities) {
    Number NameUnitC.O. County Class Add'l Org Unit
    @@ -45,7 +88,7 @@ @Html.DisplayFor(m => item.Name, "StringOrNone") - @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") @Html.DisplayFor(m => item.County.Name, "StringOrNone") @@ -61,276 +104,331 @@
    - @if (Model.CurrentUser.UserPosition.Name == "PM1") - { -
    - -

    Active Unit Facilities for Unit: @Model.CurrentUser.UserUnit.Name

    - - - - - - - - - - - - @foreach (var item in Model.UnitFacilities) - { - - - - - - - - - } - -
    NumberNameC.O.CountyClassAdd'l Org Unit
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - - @Html.DisplayFor(m => item.County.Name, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") - - @Html.DisplayFor(m => item.AddlOrgUnit.Name, "StringOrNone") -
    -
    - } - @if (Model.CurrentUser.UserPosition.Name == "PM2") - { -
    - -

    Active Program Facilities for Program: @Model.CurrentUser.UserProgram.Name

    - - - - - - - - - - - - - @foreach (var item in Model.ProgramFacilities) - { - - - - - - - - - - } - -
    NumberNameUnitC.O.CountyClassAdd'l Org Unit
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.Name, "StringOrNone") - - @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - - @Html.DisplayFor(m => item.County.Name, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") - - @Html.DisplayFor(m => item.AddlOrgUnit.Name, "StringOrNone") -
    -
    - } -
    -
    -
    -
    - -
    -

    - -

    -
    -
    -
    + } + @if (Model.CurrentUser.UserPosition.Name == "PM2") + {
    - -

    Active Events for Compliance Officer: @Model.CurrentUser.Name

    +
    +

    Active Program Facilities for Program: @Model.CurrentUser.UserProgram.Name

    - - - - - - - - + + + + + + + - @foreach (var item in Model.UserEvents) + @foreach (var item in Model.ProgramFacilities) { - }
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateEvent AmountEvent ContractorNumberNameUnitC.O.CountyClassAdd'l Org Unit
    - + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + @Html.DisplayFor(m => item.Name, "StringOrNone") - @Html.DisplayFor(m => item.StartDate, "DateOrNone") + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") - @Html.DisplayFor(m => item.DueDate, "DateOrNone") + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + @Html.DisplayFor(m => item.County.Name, "StringOrNone") - @Html.DisplayFor(m => item.EventAmount, "DecimalOrNone") + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") - @Html.DisplayFor(m => item.Contractor.Name, "StringOrNone") + @Html.DisplayFor(m => item.AddlOrgUnit.Name, "StringOrNone")
    - @if (Model.CurrentUser.UserPosition.Name == "PM1") - { -
    - -

    Active Events for Unit: @Model.CurrentUser.UserUnit.Name

    - - - - - - - - - - - - - - @foreach (var item in Model.UserEvents) - { - - - - - - - - - - - } - -
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateEvent AmountEvent Contractor
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - - @Html.DisplayFor(m => item.StartDate, "DateOrNone") - - @Html.DisplayFor(m => item.DueDate, "DateOrNone") - - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") - - @Html.DisplayFor(m => item.EventAmount, "DecimalOrNone") - - @Html.DisplayFor(m => item.Contractor.Name, "StringOrNone") -
    -
    - } - @if (Model.CurrentUser.UserPosition.Name == "PM2") - { -
    - -

    Active Events for Program: @Model.CurrentUser.UserProgram.Name

    - - - - - - - - - - - - - - @foreach (var item in Model.UserEvents) + } + + + + + +
    +
    +
    +

    + +

    +
    +
    +
    +
    +

    + +

    +
    +
    + +
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateEvent AmountEvent Contractor
    +

    Active Events for Compliance Officer: @Model.CurrentUser.Name

    + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + + + @foreach (var item in Model.UserEvents) + { + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + } + +
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateCommentActions
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + +
    +
    +
    +
    +
    +
    + @if (Model.CurrentUser.UserPosition.Name == "PM1") + { +
    +
    +
    +

    + +

    +
    +
    +
    + +

    Active Events for Unit: @Model.CurrentUser.UserUnit.Name

    + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + + + @foreach (var item in Model.UnitEvents) + { + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + } + +
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateCompliance OfficerCommentActions
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + +
    +
    +
    +
    +
    +
    + } + @if (Model.CurrentUser.UserPosition.Name == "PM2") + { +
    +
    +
    +

    + +

    +
    +
    +

    Active Events for Program: @Model.CurrentUser.UserProgram.Name

    + @foreach (var unit in Model.UserUnits) { -
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - - @Html.DisplayFor(m => item.StartDate, "DateOrNone") - - @Html.DisplayFor(m => item.DueDate, "DateOrNone") - - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") - - @Html.DisplayFor(m => item.EventAmount, "DecimalOrNone") - - @Html.DisplayFor(m => item.Contractor.Name, "StringOrNone") -
    + + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + + + @foreach (var item in Model.ProgramEvents.Where(e => e.Unit == unit)) + { + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.SiteMaintenance)) + { + + } + + } + +
    Facility NumberEvent TypeAction TakenStart DateDue DateCompl. DateC. O.CommentActions
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + + @if (User.IsInRole(UserRoles.SiteMaintenance)) + { + + Delete + + } +
    + } -
    - - } - + + + + + } - @if (Model.CurrentUser.UserPosition.Name == "PM2" && Model.Roles.Contains(UserRoles.SiteMaintenance)) - { + + +@if (Model.CurrentUser.UserPosition.Name == "PM2" && Model.Roles.Contains(UserRoles.SiteMaintenance)) +{ +
    +

    - } + - \ No newline at end of file +} diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index a71b5fb2..f80ffda7 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -7,6 +7,8 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using System.Collections.ObjectModel; +using System.Linq; namespace FMS.Pages.Dashboard { @@ -25,6 +27,7 @@ public class IndexModel( public IList UserEvents { get; set; } = new List(); public IList UnitEvents { get; set; } = new List(); public IList ProgramEvents { get; set; } = new List(); + public IList UserUnits { get; set; } public async Task OnGetAsync() { @@ -71,7 +74,14 @@ public async Task OnGetAsync() if (CurrentUser.UserPosition.Name == "PM2") { ProgramEvents = await _repository.GetProgramEventsByUserId(CurrentUser.Id); + + UserUnits = [.. ProgramEvents + .Where(programEvent => programEvent.Unit != null) + .Select(programEvent => programEvent.Unit) + .Distinct() + .ToList()]; } + return Page(); } From 80541d566a507a668dbe679c4f4f4cc3f36f8bfc Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 7 Jul 2026 14:22:39 -0400 Subject: [PATCH 24/37] Refactor dashboard organizational unit collection - Initialize `UserUnits` property to ensure it's never null. - Replace LINQ-based collection of organizational units with a manual `foreach` loop. - Remove the unused `System.Linq` namespace import. Part of issue #954. --- FMS/Pages/Dashboard/Index.cshtml.cs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index f80ffda7..b0c28203 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using System.Collections.ObjectModel; -using System.Linq; namespace FMS.Pages.Dashboard { @@ -27,7 +26,7 @@ public class IndexModel( public IList UserEvents { get; set; } = new List(); public IList UnitEvents { get; set; } = new List(); public IList ProgramEvents { get; set; } = new List(); - public IList UserUnits { get; set; } + public IList UserUnits { get; set; } = new Collection(); public async Task OnGetAsync() { @@ -75,13 +74,20 @@ public async Task OnGetAsync() { ProgramEvents = await _repository.GetProgramEventsByUserId(CurrentUser.Id); - UserUnits = [.. ProgramEvents - .Where(programEvent => programEvent.Unit != null) - .Select(programEvent => programEvent.Unit) - .Distinct() - .ToList()]; + foreach (var programEvent in ProgramEvents) + { + if (UserUnits.Contains(programEvent.Unit)) + { + UserUnits.Add(programEvent.Unit); + } + } + + //UserUnits = [.. ProgramEvents + // .Where(programEvent => programEvent.Unit != null) + // .Select(programEvent => programEvent.Unit) + // .Distinct() + // .ToList()]; } - return Page(); } From 602c79f584124f329b7ccf586f625bd34c08634d Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 7 Jul 2026 16:11:53 -0400 Subject: [PATCH 25/37] Refine dashboard unit handling and update login page branding - Change dashboard `UserUnits` collection to store unit names as `string` instead of `OrganizationalUnit` objects. - Update dashboard display and filtering logic to utilize the simplified `UserUnits` string collection. - Enhance `UserUnits` population logic to collect unique unit names, including null checks. - Correct the application name displayed on the login page to "File Management System Web Application (FMS)". Part of issue #954. --- FMS/Pages/Account/Login.cshtml | 2 +- FMS/Pages/Dashboard/Index.cshtml | 4 ++-- FMS/Pages/Dashboard/Index.cshtml.cs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/FMS/Pages/Account/Login.cshtml b/FMS/Pages/Account/Login.cshtml index dcc6e13b..8d2b00d4 100644 --- a/FMS/Pages/Account/Login.cshtml +++ b/FMS/Pages/Account/Login.cshtml @@ -7,7 +7,7 @@

    @ViewData["Title"]

    - The Air Protection Branch Web Application (Air Web App) is a State of Georgia application. + The File Management System Web Application (FMS) is a State of Georgia application. It is provided for conducting official State business and is available for use by State of Georgia employees only.

    diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index d9f8f752..f9b4d29e 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -340,7 +340,7 @@

    Active Events for Program: @Model.CurrentUser.UserProgram.Name

    @foreach (var unit in Model.UserUnits) { -
    Unit Events: @unit.Name
    +
    Unit Events: @unit
    @@ -361,7 +361,7 @@ - @foreach (var item in Model.ProgramEvents.Where(e => e.Unit == unit)) + @foreach (var item in Model.ProgramEvents.Where(e => e.Unit?.Name == unit)) { +
    diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index b0c28203..a75760c0 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -26,7 +26,7 @@ public class IndexModel( public IList UserEvents { get; set; } = new List(); public IList UnitEvents { get; set; } = new List(); public IList ProgramEvents { get; set; } = new List(); - public IList UserUnits { get; set; } = new Collection(); + public IList UserUnits { get; set; } = new List(); public async Task OnGetAsync() { @@ -76,9 +76,9 @@ public async Task OnGetAsync() foreach (var programEvent in ProgramEvents) { - if (UserUnits.Contains(programEvent.Unit)) + if (!UserUnits.Contains(programEvent.Unit?.Name) && programEvent.Unit?.Name != null) { - UserUnits.Add(programEvent.Unit); + UserUnits.Add(programEvent.Unit?.Name); } } From 44c09c7e5f7e6159992efabc18cba2021a9dd665 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 7 Jul 2026 19:49:13 -0400 Subject: [PATCH 26/37] Integrate Bootstrap accordions for dashboard unit events - Enclose each organizational unit's active event table within a Bootstrap accordion component. - This enhances the dashboard's visual organization and improves navigability by allowing users to collapse and expand event lists for specific units. - Clean up commented-out LINQ code from the PageModel. Part of issue #954. --- FMS/Pages/Dashboard/Index.cshtml | 155 +++++++++++++++------------- FMS/Pages/Dashboard/Index.cshtml.cs | 6 -- 2 files changed, 84 insertions(+), 77 deletions(-) diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index f9b4d29e..86cd4273 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -340,80 +340,93 @@

    Active Events for Program: @Model.CurrentUser.UserProgram.Name

    @foreach (var unit in Model.UserUnits) { -
    Unit Events: @unit
    - - +
    +
    +

    + +

    +
    +
    +
    Unit Events: @unit
    + +
    - - - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } - - - - @foreach (var item in Model.ProgramEvents.Where(e => e.Unit?.Name == unit)) - { - - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.SiteMaintenance)) - { - + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + + + @foreach (var item in Model.ProgramEvents.Where(e => e.Unit?.Name == unit)) { - - Delete - + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.SiteMaintenance)) + { + + } + } - - } - - } - -
    Facility NumberEvent TypeAction TakenStart DateDue DateCompl. DateC. O.CommentActions
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - - @Html.DisplayFor(m => item.StartDate, "DateOrNone") - - @Html.DisplayFor(m => item.DueDate, "DateOrNone") - - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - - @Html.DisplayFor(m => item.Comment, "StringOrNone") - - - Edit - - @if (User.IsInRole(UserRoles.SiteMaintenance)) +
    Facility NumberEvent TypeAction TakenStart DateDue DateCompl. DateC. O.CommentActions
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + + @if (User.IsInRole(UserRoles.SiteMaintenance)) + { + + Delete + + } +
    - +
    + + + + + } diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index a75760c0..ac650565 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -81,12 +81,6 @@ public async Task OnGetAsync() UserUnits.Add(programEvent.Unit?.Name); } } - - //UserUnits = [.. ProgramEvents - // .Where(programEvent => programEvent.Unit != null) - // .Select(programEvent => programEvent.Unit) - // .Distinct() - // .ToList()]; } return Page(); From 552df6f4e748018619298754d6e194f111dbbeea Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 7 Jul 2026 20:00:32 -0400 Subject: [PATCH 27/37] Improve dashboard accordion unit display - Wrap organizational unit names in accordion headers with an `` tag. - This enhances visual hierarchy and readability for better user experience. Part of issue #954. --- FMS/Pages/Dashboard/Index.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 86cd4273..35d8cd16 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -344,7 +344,7 @@

    From 61ba420daad357049673ae868de9429c9fcce02d Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Wed, 8 Jul 2026 08:40:45 -0400 Subject: [PATCH 28/37] Remove 'Add'l Org Unit' column from dashboard event tables - This column provided information that was deemed unnecessary for the current dashboard views. - Simplifying the table display improves readability and focuses user attention on more relevant event details. Part of issue #954. --- FMS/Pages/Dashboard/Index.cshtml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 35d8cd16..8d5163d2 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -28,7 +28,6 @@ Unit County Class - Add'l Org Unit @@ -52,9 +51,6 @@ @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") - - @Html.DisplayFor(m => item.AddlOrgUnit.Name, "StringOrNone") - } @@ -72,7 +68,6 @@ C.O. County Class - Add'l Org Unit @@ -96,9 +91,6 @@ @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") - - @Html.DisplayFor(m => item.AddlOrgUnit.Name, "StringOrNone") - } @@ -118,7 +110,6 @@ C.O. County Class - Add'l Org Unit @@ -145,9 +136,6 @@ @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") - - @Html.DisplayFor(m => item.AddlOrgUnit.Name, "StringOrNone") - } From 9a98b401249b0ea98b7406e307d3cbf517bd8433 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Wed, 8 Jul 2026 08:57:36 -0400 Subject: [PATCH 29/37] Integrate Bootstrap accordions for dashboard facility lists - Enclose the "My Facilities", "My Unit Facilities", and "My Program Facilities" tables within individual Bootstrap accordion components. - This improves the dashboard's organization, reduces visual clutter, and allows users to collapse and expand specific facility lists for better navigation. Part of issue #954. --- FMS/Pages/Dashboard/Index.cshtml | 275 ++++++++++++++++++------------- 1 file changed, 157 insertions(+), 118 deletions(-) diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 8d5163d2..8427f70c 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -18,129 +18,168 @@
    -
    - -

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    - - - - - - - - - - - @foreach (var item in Model.UserFacilities) - { - - - - - - - - } - -
    NumberNameUnitCountyClass
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.Name, "StringOrNone") - - @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") - - @Html.DisplayFor(m => item.County.Name, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") -
    -
    +
    +
    +

    + +

    +
    +
    +
    + +

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    + + + + + + + + + + + @foreach (var item in Model.UserFacilities) + { + + + + + + + + } + +
    NumberNameUnitCountyClass
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.Name, "StringOrNone") + + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") +
    +
    +
    +
    +
    +
    @if (Model.CurrentUser.UserPosition.Name == "PM1") { -
    - -

    Active Unit Facilities for Unit: @Model.CurrentUser.UserUnit.Name

    - - - - - - - - - - - @foreach (var item in Model.UnitFacilities) - { - - - - - - - - } - -
    NumberNameC.O.CountyClass
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - - @Html.DisplayFor(m => item.County.Name, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") -
    -
    +
    +
    +

    + +

    +
    +
    +
    + +

    Active Facilities for Unit: @Model.CurrentUser.UserUnit.Name

    + + + + + + + + + + + @foreach (var item in Model.UnitFacilities) + { + + + + + + + + } + +
    NumberNameC.O.CountyClass
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") +
    +
    +
    +
    +
    +
    } @if (Model.CurrentUser.UserPosition.Name == "PM2") { -
    - -

    Active Program Facilities for Program: @Model.CurrentUser.UserProgram.Name

    - - - - - - - - - - - - @foreach (var item in Model.ProgramFacilities) - { - - - - - - - - - } - -
    NumberNameUnitC.O.CountyClass
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.Name, "StringOrNone") - - @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - - @Html.DisplayFor(m => item.County.Name, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") -
    -
    +
    +
    +

    + +

    +
    +
    +
    + +

    Active Facilities for Program: @Model.CurrentUser.UserProgram.Name

    + + + + + + + + + + + + @foreach (var item in Model.ProgramFacilities) + { + + + + + + + + + } + +
    NumberNameUnitC.O.CountyClass
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.Name, "StringOrNone") + + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") +
    +
    +
    +
    +
    +
    }
    From 5bd9e6c7a4cbc7ba55c4e75faf64a674737e299b Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Wed, 8 Jul 2026 09:08:00 -0400 Subject: [PATCH 30/37] Refine dashboard heading structure and update content - Adjust the main dashboard title (`ViewData["Title"]`) from `h3` to `h4` for improved semantic hierarchy. - Enclose the text within all accordion buttons (e.g., "My Facilities", "Data Maintenance") with `` tags to standardize heading levels within the collapsible sections. - Clarify the descriptive text for the 'Data Maintenance' section. - Correct the `aria-labelledby` attribute for the 'Data Maintenance' accordion to match its ID. These changes enhance visual consistency and clarity on the dashboard. Part of issue #954. --- FMS/Pages/Dashboard/Index.cshtml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 8427f70c..60fb015d 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -6,7 +6,7 @@ @{ ViewData["Title"] = $"Welcome, {Model.UserName}"; } -

    @ViewData["Title"]

    +

    @ViewData["Title"]

    @@ -22,7 +22,7 @@

    @@ -75,7 +75,7 @@

    @@ -129,7 +129,7 @@

    @@ -200,7 +200,7 @@

    @@ -277,7 +277,7 @@

    @@ -359,7 +359,7 @@

    @@ -472,14 +472,14 @@

    -
    +
    -

    Data Maintenance Info

    +

    Data that is inactive and may be deleted

    From dd3d47eb678ed1c67eddc03ffb86bd9ca3084b6e Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Wed, 8 Jul 2026 11:11:45 -0400 Subject: [PATCH 31/37] Remove unused IAuthorizationService from Dashboard IndexModel The `IAuthorizationService` dependency was no longer being consumed within the `IndexModel` constructor. Removing unused dependencies contributes to cleaner code and reduced overhead. Part of issue #954. --- FMS/Pages/Dashboard/Index.cshtml.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index ac650565..21d2f481 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -12,7 +12,6 @@ namespace FMS.Pages.Dashboard { public class IndexModel( - IAuthorizationService authorization, IUserService userService, IDashboardRepository _repository) : PageModel { From f7429234d5d4386e4d6d22dec95daa91c0e73641 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 14 Jul 2026 11:49:23 -0400 Subject: [PATCH 32/37] Update PM2 Dashboard --- .../Migrations/FmsDbContextModelSnapshot.cs | 6 +- FMS/Pages/Dashboard/Index.cshtml | 232 +++++++++--------- 2 files changed, 122 insertions(+), 116 deletions(-) diff --git a/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs b/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs index f26a96fc..4cdc7358 100644 --- a/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs +++ b/FMS.Infrastructure/Migrations/FmsDbContextModelSnapshot.cs @@ -2653,9 +2653,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasDatabaseName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); - b.HasIndex("ObjectId") - .IsUnique() - .HasFilter("[ObjectId] IS NOT NULL"); + //b.HasIndex("ObjectId") + // .IsUnique() + // .HasFilter("[ObjectId] IS NOT NULL"); b.HasIndex("UserPositionId"); diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 60fb015d..61c063db 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -18,57 +18,60 @@
    -
    -
    -

    - -

    -
    -
    -
    - -

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    - - - - - - - - - - - @foreach (var item in Model.UserFacilities) - { - - - - - - + @if (Model.CurrentUser.UserPosition.Name != "PM2") + { +
    +
    +

    + +

    +
    +
    + +
    NumberNameUnitCountyClass
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.Name, "StringOrNone") - - @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") - - @Html.DisplayFor(m => item.County.Name, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") -
    +

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    + + + + + + + - } - -
    NumberNameUnitCountyClass
    -
    + + + @foreach (var item in Model.UserFacilities) + { + + + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + + @Html.DisplayFor(m => item.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") + + + } + + + +
    -
    + } @if (Model.CurrentUser.UserPosition.Name == "PM1") {
    @@ -196,80 +199,83 @@
    -
    -
    -

    - -

    -
    -
    -
    - -

    Active Events for Compliance Officer: @Model.CurrentUser.Name

    - - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } - - - - @foreach (var item in Model.UserEvents) - { - - - - - - - - + @if (Model.CurrentUser.UserPosition.Name != "PM2") + { +
    +
    +

    + +

    +
    +
    + +
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateCommentActions
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - - @Html.DisplayFor(m => item.StartDate, "DateOrNone") - - @Html.DisplayFor(m => item.DueDate, "DateOrNone") - - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") - - @Html.DisplayFor(m => item.Comment, "StringOrNone") -
    +

    Active Events for Compliance Officer: @Model.CurrentUser.Name

    + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) { + + } + + + + @foreach (var item in Model.UserEvents) + { + - } - - } - -
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateCommentActions
    - - Edit + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone")
    -
    + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + + Edit + + + } + + } + + + +
    -
    + } @if (Model.CurrentUser.UserPosition.Name == "PM1") {
    From b39907b4470a7ae4ac2c89064090ec9bf003f817 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 14 Jul 2026 19:10:47 -0400 Subject: [PATCH 33/37] - Add `FacilityName` property to dashboard event DTOs and display it in corresponding event tables, providing immediate context. - Update facility list queries to sort by `Facility.Name` for a more intuitive display across user, unit, and program dashboards. - Introduce direct navigation links to Facilities Search and Map pages from the dashboard. - Refine the Program Manager (PM2) dashboard by organizing program facilities and events by organizational unit, improving overview and navigability. - Include facility and event total counts for better data summarization. - Perform minor code cleanup: remove duplicate property assignment in `DashboardUnitFacilitiesDto`, remove unused usings, and adjust event delete role check from `ComplianceOfficer` to `SiteMaintenance`. Part of issue #954. --- .../Dashboard/DashboardProgramEventsDto.cs | 4 + .../Dto/Dashboard/DashboardUnitEventsDto.cs | 4 + .../Dashboard/DashboardUnitFacilitiesDto.cs | 2 +- .../Dto/Dashboard/DashboardUserEventsDto.cs | 4 + .../Repositories/DashboardRepository.cs | 7 +- FMS/Pages/Dashboard/Index.cshtml | 923 ++++++++++-------- FMS/Pages/Dashboard/Index.cshtml.cs | 9 +- 7 files changed, 512 insertions(+), 441 deletions(-) diff --git a/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs index a0c28d7b..1d5e4b02 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardProgramEventsDto.cs @@ -14,6 +14,7 @@ public DashboardProgramEventsDto(Event facilityEvent) Id = facilityEvent.Id; FacilityId = facilityEvent.FacilityId; FacilityNumber = facilityEvent.Facility.FacilityNumber; + FacilityName = facilityEvent.Facility.Name; EventType = facilityEvent.EventType; ActionTaken = facilityEvent.ActionTaken; StartDate = facilityEvent.StartDate; @@ -32,6 +33,9 @@ public DashboardProgramEventsDto(Event facilityEvent) [Display(Name = "Facility Number")] public string FacilityNumber { get; } + [Display(Name = "Facility Name")] + public string FacilityName { get; set; } + [Display(Name = "Event Type")] public EventType EventType { get; } diff --git a/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs index 61a010dd..255f3a06 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUnitEventsDto.cs @@ -14,6 +14,7 @@ public DashboardUnitEventsDto(Event facilityEvent) Id = facilityEvent.Id; FacilityId = facilityEvent.FacilityId; FacilityNumber = facilityEvent.Facility?.FacilityNumber; + FacilityName = facilityEvent.Facility?.Name; EventType = facilityEvent.EventType; ActionTaken = facilityEvent.ActionTaken; StartDate = facilityEvent.StartDate; @@ -31,6 +32,9 @@ public DashboardUnitEventsDto(Event facilityEvent) [Display(Name = "Facility Number")] public string FacilityNumber { get; } + [Display(Name = "Facility Name")] + public string FacilityName { get; set; } + [Display(Name = "Event Type")] public EventType EventType { get; } diff --git a/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs index 1ae0d192..b1960c05 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUnitFacilitiesDto.cs @@ -11,7 +11,6 @@ public DashboardUnitFacilitiesDto(Facility facility) { Id = facility.Id; Name = facility.Name; - Name = facility.Name; FacilityNumber = facility.FacilityNumber; OrganizationalUnit = facility.OrganizationalUnit; ComplianceOfficer = facility.ComplianceOfficer; @@ -42,6 +41,7 @@ public DashboardUnitFacilitiesDto(Facility facility) [Display(Name = "Add'l Org Unit")] public OrganizationalUnit AddlOrgUnit { get; set; } + public bool Active { get; set; } } } diff --git a/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs index acb7114e..16ea0c8c 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs @@ -14,6 +14,7 @@ public DashboardUserEventsDto(Event facilityEvent) Id = facilityEvent.Id; FacilityId = facilityEvent.FacilityId; FacilityNumber = facilityEvent.Facility.FacilityNumber; + FacilityName = facilityEvent.Facility.Name; EventType = facilityEvent.EventType; ActionTaken = facilityEvent.ActionTaken; StartDate = facilityEvent.StartDate; @@ -31,6 +32,9 @@ public DashboardUserEventsDto(Event facilityEvent) [Display(Name = "Facility Number")] public string FacilityNumber { get; } + [Display(Name = "Facility Name")] + public string FacilityName { get; set; } + [Display(Name = "Event Type")] public EventType EventType { get; } diff --git a/FMS.Infrastructure/Repositories/DashboardRepository.cs b/FMS.Infrastructure/Repositories/DashboardRepository.cs index d118810c..35e73e5a 100644 --- a/FMS.Infrastructure/Repositories/DashboardRepository.cs +++ b/FMS.Infrastructure/Repositories/DashboardRepository.cs @@ -1,5 +1,4 @@ using FMS.Domain.Dto; -using FMS.Domain.Entities; using FMS.Domain.Entities.Users; using FMS.Domain.Repositories; using FMS.Domain.Services; @@ -63,7 +62,7 @@ public async Task> GetUserHSIFacilitiesById(Gui .Where(e => e.ComplianceOfficerId == id) .Where(e => e.FacilityType.Name == "HSI") .Where(e => e.FacilityStatus.Status == "Active") - .OrderByDescending(e => e.Active) + .OrderBy(e => e.Name) .Select(e => new DashboardUserFacilitiesDto(e)) .ToListAsync(); return facilities; @@ -92,7 +91,7 @@ public async Task> GetUnitHSIFacilitiesById(Gui .Where(e => e.FacilityStatus.Status == "Active") .Where(e => e.Events.Any(ev => ev.StartDate >= oneYearAgo)) .OrderBy(e => e.ComplianceOfficer.FamilyName) - .ThenBy(e => e.FacilityNumber) + .ThenBy(e => e.Name) .Select(e => new DashboardUnitFacilitiesDto(e)) .ToListAsync(); } @@ -120,7 +119,7 @@ public async Task> GetProgramHSIFacilitiesBy .Where(e => e.Events.Any(ev => ev.StartDate >= oneYearAgo)) .OrderBy(e => e.OrganizationalUnit.Name) .ThenBy(e => e.ComplianceOfficer.FamilyName) - .ThenBy(e => e.FacilityNumber) + .ThenBy(e => e.Name) .Select(e => new DashboardProgramFacilitiesDto(e)) .ToListAsync(); } diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 61c063db..9d8b0148 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -1,475 +1,536 @@ @page -@using FMS.Pages.Dashboard @using FMS.Domain.Entities.Users @model FMS.Pages.Dashboard.IndexModel @{ ViewData["Title"] = $"Welcome, {Model.UserName}"; } -

    @ViewData["Title"]

    +
    +
    +

    @ViewData["Title"] + New Search Tab + New Map Search Tab +

    +
    -
    -
    -

    - -

    -
    -
    - @if (Model.CurrentUser.UserPosition.Name != "PM2") - { -
    -
    -

    - -

    -
    -
    -
    - -

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    - - - - - - - - - - - @foreach (var item in Model.UserFacilities) - { - - - - - - - - } - -
    NumberNameUnitCountyClass
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.Name, "StringOrNone") - - @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") - - @Html.DisplayFor(m => item.County.Name, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") -
    -
    + @if (Model.CurrentUser.UserPosition.Name != "PM2") + { +
    +
    +
    +
    +

    + +

    +
    +
    +
    +
    +
    + +
    +
    +

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    +
    +
    + @(Model.UserFacilities.Count() > 0 ? $"Total Facilities: {Model.UserFacilities.Count()}" : "No Facilities Assigned")
    - - - } - @if (Model.CurrentUser.UserPosition.Name == "PM1") - { -
    -
    -

    - -

    -
    -
    - -
    -

    Active Facilities for Unit: @Model.CurrentUser.UserUnit.Name

    - - - - - - - - - - - @foreach (var item in Model.UnitFacilities) - { - - - - - - - - } - -
    NumberNameC.O.CountyClass
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - - @Html.DisplayFor(m => item.County.Name, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") -
    -
    + + + Number + Name + Unit + County + Class + + + + @foreach (var item in Model.UserFacilities) + { + + + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + + @Html.DisplayFor(m => item.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") + + + } + + + +
    +
    +
    +
    + } + @if (Model.CurrentUser.UserPosition.Name == "PM1") + { +
    +
    +

    + +

    +
    +
    +
    + +
    +
    +

    Active Facilities for Unit: @Model.CurrentUser.UserUnit.Name

    +
    +
    + @(Model.UnitFacilities.Count() > 0 ? $"Total Facilities: {Model.UnitFacilities.Count()}" : "No Facilities Assigned")
    + + + + + + + + + + + @foreach (var item in Model.UnitFacilities) + { + + + + + + + + } + +
    NumberNameC.O.CountyClass
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") +
    +
    +
    +
    +
    +
    + } + @if (Model.CurrentUser.UserPosition.Name == "PM2") + { +
    +
    +

    + +

    +
    +
    +
    +
    +

    Active Facilities for Program: @Model.CurrentUser.UserProgram.Name

    +
    +
    + @(Model.ProgramFacilities.Count() > 0 ? $"Total Facilities: {Model.ProgramFacilities.Count()}" : "No Facilities Assigned")
    - } - @if (Model.CurrentUser.UserPosition.Name == "PM2") - { -
    -
    -

    - -

    -
    -
    -
    - -

    Active Facilities for Program: @Model.CurrentUser.UserProgram.Name

    - - - - - - - - - - - - @foreach (var item in Model.ProgramFacilities) - { - - - - - - - + @foreach (var unit in Model.UserUnits) + { +
    +
    +

    + +

    +
    +
    +
    +
    + @(Model.ProgramFacilities.Where(e => e.OrganizationalUnit?.Name == unit).Count() > 0 ? $"{Model.ProgramFacilities.Where(e => e.OrganizationalUnit?.Name == unit).Count()} out of {Model.ProgramFacilities.Count()} Total Facilities: " : "No Facilities Assigned") +
    +
    + +
    NumberNameUnitC.O.CountyClass
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.Name, "StringOrNone") - - @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - - @Html.DisplayFor(m => item.County.Name, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") -
    + + + + + + + + - } - -
    NumberNameUnitC.O.CountyClass
    -
    + + + @foreach (var item in Model.ProgramFacilities.Where(e => e.OrganizationalUnit?.Name == unit)) + { + + + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + + @Html.DisplayFor(m => item.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") + + + } + + + +
    -
    - } + } +
    -
    + }
    -
    -
    -

    - -

    -
    -
    - @if (Model.CurrentUser.UserPosition.Name != "PM2") - { -
    -
    -

    - -

    -
    -
    -
    - -

    Active Events for Compliance Officer: @Model.CurrentUser.Name

    - - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } - - - - @foreach (var item in Model.UserEvents) - { - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } - - } - -
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateCommentActions
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - - @Html.DisplayFor(m => item.StartDate, "DateOrNone") - - @Html.DisplayFor(m => item.DueDate, "DateOrNone") - - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") - - @Html.DisplayFor(m => item.Comment, "StringOrNone") - - - Edit - -
    -
    + @if (Model.CurrentUser.UserPosition.Name != "PM2") + { +
    +
    +

    + +

    +
    +
    +
    + +
    +
    +

    Active Events for Compliance Officer: @Model.CurrentUser.DisplayName

    +
    +
    + @(Model.UserEvents.Count() > 0 ? $"Total Events: {Model.UserEvents.Count()}" : "No Events Assigned") +
    +
    + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + + + @foreach (var item in Model.UserEvents) + { + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + } + +
    Facility NumberFacility NameEvent TypeAction TakenStart DateDue DateCompletion DateCommentActions
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + +
    +
    +
    +
    +
    +
    + } + @if (Model.CurrentUser.UserPosition.Name == "PM1") + { +
    +
    +

    + +

    +
    +
    +
    + +
    +
    +

    Active Events for Unit: @Model.CurrentUser.UserUnit.Name

    +
    +
    + @(Model.UnitEvents.Count() > 0 ? $"Total Events: {Model.UnitEvents.Count()}" : "No Events Assigned")
    + + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + + + @foreach (var item in Model.UnitEvents) + { + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + } + +
    Facility NumberFacility NameEvent TypeAction TakenStart DateDue DateCompletion DateCompliance OfficerCommentActions
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + +
    +
    +
    +
    +
    +
    + } + @if (Model.CurrentUser.UserPosition.Name == "PM2") + { +
    +
    +

    + +

    +
    +
    +
    +
    +

    Active Events for Program: @Model.CurrentUser.UserProgram.Name

    +
    +
    + @(Model.ProgramEvents.Count() > 0 ? $"Total Events: {Model.ProgramEvents.Count()}" : "No Events Assigned")
    - } - @if (Model.CurrentUser.UserPosition.Name == "PM1") - { -
    -
    -
    -

    - -

    -
    -
    -
    - -

    Active Events for Unit: @Model.CurrentUser.UserUnit.Name

    - - - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } - - - - @foreach (var item in Model.UnitEvents) - { - - - - - - - - - + @foreach (var unit in Model.UserUnits) + { +
    +
    +

    + +

    +
    +
    +
    +
    + @(Model.ProgramEvents.Where(e => e.Unit?.Name == unit).Count() > 0 ? $"{Model.ProgramEvents.Where(e => e.Unit?.Name == unit).Count()} out of {Model.ProgramEvents.Count()} Total Events: " : "No Events Assigned") +
    +
    + +
    Facility NumberEvent TypeAction TakenStart DateDue DateCompletion DateCompliance OfficerCommentActions
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - - @Html.DisplayFor(m => item.StartDate, "DateOrNone") - - @Html.DisplayFor(m => item.DueDate, "DateOrNone") - - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - - @Html.DisplayFor(m => item.Comment, "StringOrNone") -
    + + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) { + + } + + + + @foreach (var item in Model.ProgramEvents.Where(e => e.Unit?.Name == unit)) + { + - } - - } - -
    Facility NumberFacility NameEvent TypeAction TakenStart DateDue DateCompl. DateC. O.CommentActions
    - - Edit + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone")
    -
    -
    -
    -
    -
    - } - @if (Model.CurrentUser.UserPosition.Name == "PM2") - { -
    -
    -
    -

    - -

    -
    -
    -

    Active Events for Program: @Model.CurrentUser.UserProgram.Name

    - @foreach (var unit in Model.UserUnits) - { -
    -
    -

    - -

    -
    -
    -
    Unit Events: @unit
    -
    - - - - - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } - - - - @foreach (var item in Model.ProgramEvents.Where(e => e.Unit?.Name == unit)) + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.SiteMaintenance)) + { + - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.SiteMaintenance)) - { - - } - + + Delete + } - -
    Facility NumberEvent TypeAction TakenStart DateDue DateCompl. DateC. O.CommentActions
    + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + + @if (User.IsInRole(UserRoles.SiteMaintenance)) { -
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - - @Html.DisplayFor(m => item.StartDate, "DateOrNone") - - @Html.DisplayFor(m => item.DueDate, "DateOrNone") - - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - - @Html.DisplayFor(m => item.Comment, "StringOrNone") - - - Edit - - @if (User.IsInRole(UserRoles.SiteMaintenance)) - { - - Delete - - } -
    -
    -
    -
    -
    -
    - } + + } + + } + + + +
    -
    - } + } +
    -
    + }
    @if (Model.CurrentUser.UserPosition.Name == "PM2" && Model.Roles.Contains(UserRoles.SiteMaintenance)) { diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index 21d2f481..64c52f66 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -1,13 +1,8 @@ using FMS.Domain.Dto; -using FMS.Domain.Entities; -using FMS.Domain.Entities.Users; using FMS.Domain.Repositories; using FMS.Domain.Services; -using FMS.Infrastructure.Services; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using System.Collections.ObjectModel; namespace FMS.Pages.Dashboard { @@ -27,6 +22,8 @@ public class IndexModel( public IList ProgramEvents { get; set; } = new List(); public IList UserUnits { get; set; } = new List(); + public IList UserCOs { get; set; } = new List(); + public async Task OnGetAsync() { if (User.Identity is not { IsAuthenticated: true }) @@ -84,5 +81,7 @@ public async Task OnGetAsync() return Page(); } + + } } From d3390655aed607c3eea48d70eb04b138095980e0 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 14 Jul 2026 19:14:51 -0400 Subject: [PATCH 34/37] Use C# 9 empty array literal for list initializations in dashboard model Part of issue #954. --- FMS/Pages/Dashboard/Index.cshtml.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index 64c52f66..9e07b790 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -14,15 +14,13 @@ public class IndexModel( public string UserName { get; set; } public Guid UserCOId { get; set; } = Guid.Empty; public IList Roles { get; private set; } - public IList UserFacilities { get; set; } = new List(); - public IList UnitFacilities { get; set; } = new List(); - public IList ProgramFacilities { get; set; } = new List(); - public IList UserEvents { get; set; } = new List(); - public IList UnitEvents { get; set; } = new List(); - public IList ProgramEvents { get; set; } = new List(); - public IList UserUnits { get; set; } = new List(); - - public IList UserCOs { get; set; } = new List(); + public IList UserFacilities { get; set; } = []; + public IList UnitFacilities { get; set; } = []; + public IList ProgramFacilities { get; set; } = []; + public IList UserEvents { get; set; } = []; + public IList UnitEvents { get; set; } = []; + public IList ProgramEvents { get; set; } = []; + public IList UserUnits { get; set; } = []; public async Task OnGetAsync() { From 986d9cd92a932ff2e4fc87d5fded1a34afc60fd0 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Fri, 17 Jul 2026 20:17:28 -0400 Subject: [PATCH 35/37] Enhance dashboard event and facility views with fiscal year context and improved organization Part of issue #954. - Introduce fiscal year-based event filtering (`GetFiscalYearStartDate`) to provide a more relevant view of active events, including those without specific start or completion dates. - Expand facility type filtering to include "VRP" alongside "HSI" in dashboard facility lists. - Refactor the dashboard UI to prioritize event display, presenting detailed event tables categorized by completion status (pending, ongoing, completed). - Implement hierarchical grouping of events for Program Managers (PM1 by Compliance Officer, PM2 by Unit then Compliance Officer) for better oversight. - Reorder dashboard sections to place events before facilities for non-PM2 users. - Remove an unused commented-out line from `Startup.cs`. --- .../Repositories/DashboardRepository.cs | 31 +- FMS/Pages/Dashboard/Index.cshtml | 967 ++++++++++++------ FMS/Pages/Dashboard/Index.cshtml.cs | 18 + FMS/Startup.cs | 1 - 4 files changed, 688 insertions(+), 329 deletions(-) diff --git a/FMS.Infrastructure/Repositories/DashboardRepository.cs b/FMS.Infrastructure/Repositories/DashboardRepository.cs index 35e73e5a..6d83f26a 100644 --- a/FMS.Infrastructure/Repositories/DashboardRepository.cs +++ b/FMS.Infrastructure/Repositories/DashboardRepository.cs @@ -12,6 +12,13 @@ public class DashboardRepository : IDashboardRepository private readonly FmsDbContext _context; public DashboardRepository(FmsDbContext context) => _context = context; + public DateOnly GetFiscalYearStartDate() + { + var today = DateTime.Today; + var fiscalYearStart = new DateTime(today.Month < 7 ? today.Year - 1 : today.Year, 7, 1); + return DateOnly.FromDateTime(fiscalYearStart); + } + public async Task UserExistsAsync(Guid id) => await _context.Users.AnyAsync(e => e.Id == id); @@ -25,7 +32,6 @@ public async Task GetCOIdFromUser(UserView user) .Where(e => e.Active) .Select(e => e.Id) .FirstOrDefaultAsync(); - return coId; } @@ -60,7 +66,7 @@ public async Task> GetUserHSIFacilitiesById(Gui .ThenInclude(e => e.OverallStatus) .Where(e => includeInactive || e.Active) .Where(e => e.ComplianceOfficerId == id) - .Where(e => e.FacilityType.Name == "HSI") + .Where(e => e.FacilityType.Name == "HSI" || e.FacilityType.Name == "VRP") .Where(e => e.FacilityStatus.Status == "Active") .OrderBy(e => e.Name) .Select(e => new DashboardUserFacilitiesDto(e)) @@ -72,6 +78,7 @@ public async Task> GetUnitHSIFacilitiesById(Gui { var currentUser = await GetApplicationUser(id); var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); + var fiscalYearStart = GetFiscalYearStartDate(); return await _context.Facilities.AsNoTracking() .Include(e => e.ComplianceOfficer) @@ -87,9 +94,9 @@ public async Task> GetUnitHSIFacilitiesById(Gui .ThenInclude(e => e.OverallStatus) .Where(e => includeInactive || e.Active) .Where(e => e.OrganizationalUnitId == currentUser.UserUnit.Id) - .Where(e => e.FacilityType.Name == "HSI") + .Where(e => e.FacilityType.Name == "HSI" || e.FacilityType.Name == "VRP") .Where(e => e.FacilityStatus.Status == "Active") - .Where(e => e.Events.Any(ev => ev.StartDate >= oneYearAgo)) + .Where(e => e.Events.Any(ev => ev.StartDate >= fiscalYearStart || ev.CompletionDate >= fiscalYearStart || (ev.CompletionDate == null && ev.StartDate == null))) .OrderBy(e => e.ComplianceOfficer.FamilyName) .ThenBy(e => e.Name) .Select(e => new DashboardUnitFacilitiesDto(e)) @@ -99,6 +106,7 @@ public async Task> GetUnitHSIFacilitiesById(Gui public async Task> GetProgramHSIFacilitiesById(Guid id, bool includeInactive = false) { var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); + var fiscalYearStart = GetFiscalYearStartDate(); return await _context.Facilities.AsNoTracking() .Include(e => e.ComplianceOfficer) @@ -114,9 +122,9 @@ public async Task> GetProgramHSIFacilitiesBy .ThenInclude(e => e.OverallStatus) .Where(e => includeInactive || e.Active) .Where(e => e.OrganizationalUnit.UserProgram.Id == id) - .Where(e => e.FacilityType.Name == "HSI") + .Where(e => e.FacilityType.Name == "HSI" || e.FacilityType.Name == "VRP") .Where(e => e.FacilityStatus.Status == "Active") - .Where(e => e.Events.Any(ev => ev.StartDate >= oneYearAgo)) + .Where(e => e.Events.Any(ev => ev.StartDate >= fiscalYearStart || ev.CompletionDate >= fiscalYearStart || (ev.CompletionDate == null && ev.StartDate == null))) .OrderBy(e => e.OrganizationalUnit.Name) .ThenBy(e => e.ComplianceOfficer.FamilyName) .ThenBy(e => e.Name) @@ -131,6 +139,7 @@ public async Task> GetProgramHSIFacilitiesBy public async Task> GetEventsByUserId(Guid id, bool includeInactive = false) { var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); + var fiscalYearStart = GetFiscalYearStartDate(); return await _context.Events.AsNoTracking() .Include(e => e.Facility) @@ -140,7 +149,7 @@ public async Task> GetEventsByUserId(Guid id, bool .Include(e => e.EventContractor) .Where(e => includeInactive || e.Active) .Where(e => e.ComplianceOfficerId.Equals(id)) - .Where(e => e.StartDate >= oneYearAgo) + .Where(e => e.StartDate >= fiscalYearStart || e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null)) .OrderByDescending(e => e.StartDate) .Select(e => new DashboardUserEventsDto(e)) .ToListAsync(); @@ -149,7 +158,7 @@ public async Task> GetEventsByUserId(Guid id, bool public async Task> GetUnitEventsByUserId(Guid id, bool includeInactive = false) { var currentUser = await GetApplicationUser(id); - var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); + var fiscalYearStart = GetFiscalYearStartDate(); return await _context.Events.AsNoTracking() .Include(e => e.Facility) @@ -159,7 +168,7 @@ public async Task> GetUnitEventsByUserId(Guid id, b .Include(e => e.EventContractor) .Where(e => includeInactive || e.Active) .Where(e => e.Facility.OrganizationalUnit.Id.Equals(currentUser.UserUnit.Id)) - .Where(e => e.StartDate >= oneYearAgo) + .Where(e => e.StartDate >= fiscalYearStart || e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null)) .OrderBy(e => e.ComplianceOfficer.FamilyName) .ThenByDescending(e => e.StartDate) .Select(e => new DashboardUnitEventsDto(e)) @@ -169,7 +178,7 @@ public async Task> GetUnitEventsByUserId(Guid id, b public async Task> GetProgramEventsByUserId(Guid id, bool includeInactive = false) { var currentUser = await GetApplicationUser(id); - var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); + var fiscalYearStart = GetFiscalYearStartDate(); return await _context.Events.AsNoTracking() .Include(e => e.Facility) @@ -180,7 +189,7 @@ public async Task> GetProgramEventsByUserId(Guid .Include(e => e.Facility.OrganizationalUnit) .Where(e => includeInactive || e.Active) .Where(e => e.Facility.OrganizationalUnit.UserProgram.Id.Equals(currentUser.UserProgram.Id)) - .Where(e => e.StartDate >= oneYearAgo) + .Where(e => e.StartDate >= fiscalYearStart || e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null)) .OrderBy(e => e.Facility.OrganizationalUnit.Name) .ThenBy(e => e.ComplianceOfficer.FamilyName) .ThenByDescending(e => e.StartDate) diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index 9d8b0148..c39b653e 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -7,7 +7,8 @@ }
    -

    @ViewData["Title"] +

    + @ViewData["Title"] New Search Tab New Map Search Tab

    @@ -16,120 +17,167 @@
    @if (Model.CurrentUser.UserPosition.Name != "PM2") { -
    +
    -
    -
    -

    - -

    -
    -
    -
    +

    + +

    +
    - +
    -
    -

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    +
    +

    Active Events for Compliance Officer: @Model.CurrentUser.DisplayName

    - @(Model.UserFacilities.Count() > 0 ? $"Total Facilities: {Model.UserFacilities.Count()}" : "No Facilities Assigned") + @(Model.UserEvents.Count() > 0 ? $"Total Events: {Model.UserEvents.Count()}" : "No Events Assigned")
    - - - - - + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } - @foreach (var item in Model.UserFacilities) + @foreach (var item in Model.UserEvents.Where(e => e.CompletionDate == null && e.StartDate == null)) { - + - - - + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } } - -
    NumberNameUnitCountyClassFacility NumberFacility NameEvent TypeAction TakenStart DateDue DateCompletion DateCommentActions
    - + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - @Html.DisplayFor(m => item.Name, "StringOrNone") + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") - @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + +
    -
    -
    -
    -
    -
    - } - @if (Model.CurrentUser.UserPosition.Name == "PM1") - { -
    -
    -

    - -

    -
    -
    -
    - -
    -
    -

    Active Facilities for Unit: @Model.CurrentUser.UserUnit.Name

    -
    -
    - @(Model.UnitFacilities.Count() > 0 ? $"Total Facilities: {Model.UnitFacilities.Count()}" : "No Facilities Assigned") -
    -
    - - - - - - - - - - - @foreach (var item in Model.UnitFacilities) + @foreach (var item in Model.UserEvents.Where(e => e.CompletionDate == null && e.StartDate != null)) { - + - - - + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + } + + @foreach (var item in Model.UserEvents.Where(e => e.CompletionDate != null)) + { + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } } @@ -140,82 +188,457 @@ } - @if (Model.CurrentUser.UserPosition.Name == "PM2") + @if (Model.CurrentUser.UserPosition.Name == "PM1") { -
    +
    -

    -

    -
    +
    -
    -

    Active Facilities for Program: @Model.CurrentUser.UserProgram.Name

    +
    +

    Active Events for Unit: @Model.CurrentUser.UserUnit.Name

    - @(Model.ProgramFacilities.Count() > 0 ? $"Total Facilities: {Model.ProgramFacilities.Count()}" : "No Facilities Assigned") + @(Model.UnitEvents.Count() > 0 ? $"Total Events: {Model.UnitEvents.Count()}" : "No Events Assigned")
    - @foreach (var unit in Model.UserUnits) + @foreach (var co in Model.UserCOs) { -
    -
    -

    - -

    -
    -
    -
    -
    - @(Model.ProgramFacilities.Where(e => e.OrganizationalUnit?.Name == unit).Count() > 0 ? $"{Model.ProgramFacilities.Where(e => e.OrganizationalUnit?.Name == unit).Count()} out of {Model.ProgramFacilities.Count()} Total Facilities: " : "No Facilities Assigned") + if (Model.UnitEvents.Where(e => e.ComplianceOfficer?.Name == co).Count() > 0) + { +
    +
    +

    + +

    +
    +
    +
    +
    + @(Model.UnitEvents.Where(e => e.ComplianceOfficer?.Name == co).Count() > 0 ? $"{Model.UnitEvents.Where(e => e.ComplianceOfficer?.Name == co).Count()} out of {Model.UnitEvents.Count()} Total Events: " : "No Events Assigned") +
    -
    - -
    NumberNameC.O.CountyClass
    - + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - @Html.DisplayFor(m => item.Name, "StringOrNone") + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + Edit + +
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + +
    - - - - - - - - - - - - @foreach (var item in Model.ProgramFacilities.Where(e => e.OrganizationalUnit?.Name == unit)) - { - - - - - - - + +
    NumberNameUnitC.O.CountyClass
    - - @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - - - @Html.DisplayFor(m => item.Name, "StringOrNone") - - @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - - @Html.DisplayFor(m => item.County.Name, "StringOrNone") - - @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") -
    + + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } - } - -
    Facility NumberFacility NameEvent TypeAction TakenStart DateDue DateCompletion DateCompliance OfficerCommentActions
    -
    + + + @foreach (var item in Model.UnitEvents.Where(e => e.CompletionDate == null && e.StartDate == null && e.ComplianceOfficer?.Name == co)) + { + + + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") + + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + + Edit + + + } + + } + @foreach (var item in Model.UnitEvents.Where(e => e.CompletionDate == null && e.StartDate != null && e.ComplianceOfficer?.Name == co)) + { + + + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") + + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + + Edit + + + } + + } + @foreach (var item in Model.UnitEvents.Where(e => e.CompletionDate != null && e.ComplianceOfficer?.Name == co)) + { + + + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") + + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + + Edit + + + } + + } + + + +
    +
    +
    +
    + } + } +
    +
    +
    +
    + } + @if (Model.CurrentUser.UserPosition.Name == "PM2") + { +
    +
    +

    + +

    +
    +
    +
    +
    +

    Active Events for Program: @Model.CurrentUser.UserProgram.Name

    +
    +
    + @(Model.ProgramEvents.Count() > 0 ? $"Total Events: {Model.ProgramEvents.Count()}" : "No Events Assigned") +
    +
    + @foreach (var unit in Model.UserUnits) + { +
    +
    +

    + +

    +
    +
    +
    +
    + @(Model.ProgramEvents.Where(e => e.Unit?.Name == unit).Count() > 0 ? $"{Model.ProgramEvents.Where(e => e.Unit?.Name == unit).Count()} out of {Model.ProgramEvents.Count()} Total Events: " : "No Events Assigned") +
    +
    + @foreach (var co in Model.UserCOs) + { + if (Model.ProgramEvents.Where(e => e.ComplianceOfficer?.Name == co && e.Unit?.Name == unit).Count() > 0) + { +
    +
    +

    + +

    +
    +
    +
    +
    + @(Model.ProgramEvents.Where(e => e.ComplianceOfficer?.Name == co && e.Unit?.Name == unit).Count() > 0 ? $"{Model.ProgramEvents.Where(e => e.ComplianceOfficer?.Name == co && e.Unit?.Name == unit).Count()} out of {Model.ProgramEvents.Count()} Total Events: " : "No Events Assigned") +
    +
    +
    + + + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) + { + + } + + + + @foreach (var item in Model.ProgramEvents.Where(e => e.ComplianceOfficer?.Name == co && e.Unit?.Name == unit && e.CompletionDate == null && e.StartDate == null)) + { + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.SiteMaintenance)) + { + + } + + } + @foreach (var item in Model.ProgramEvents.Where(e => e.ComplianceOfficer?.Name == co && e.Unit?.Name == unit && e.CompletionDate == null && e.StartDate != null)) + { + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.SiteMaintenance)) + { + + } + + } + + @foreach (var item in Model.ProgramEvents.Where(e => e.ComplianceOfficer?.Name == co && e.Unit?.Name == unit && e.CompletionDate != null)) + { + + + + + + + + + + + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.SiteMaintenance)) + { + + } + + } + +
    Facility NumberFacility NameEvent TypeAction TakenStart DateDue DateCompl. DateC. O.CommentActions
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + + @if (User.IsInRole(UserRoles.SiteMaintenance)) + { + + Delete + + } +
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + + @if (User.IsInRole(UserRoles.SiteMaintenance)) + { + + Delete + + } +
    + + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") + + + @Html.DisplayFor(m => item.FacilityName, "StringOrNone") + + @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") + + @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") + + @Html.DisplayFor(m => item.StartDate, "DateOrNone") + + @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") + + @Html.DisplayFor(m => item.Comment, "StringOrNone") + + + Edit + + @if (User.IsInRole(UserRoles.SiteMaintenance)) + { + + Delete + + } +
    +
    +
    +
    +
    +
    + } + }
    @@ -230,82 +653,59 @@
    @if (Model.CurrentUser.UserPosition.Name != "PM2") { -
    +
    -

    - -

    -
    +
    +
    +

    + +

    +
    +
    +
    - +
    -
    -

    Active Events for Compliance Officer: @Model.CurrentUser.DisplayName

    +
    +

    Facilities for Compliance Officer: @Model.CurrentUser.DisplayName

    - @(Model.UserEvents.Count() > 0 ? $"Total Events: {Model.UserEvents.Count()}" : "No Events Assigned") + @(Model.UserFacilities.Count() > 0 ? $"Total Facilities: {Model.UserFacilities.Count()}" : "No Facilities Assigned")
    - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } + + + + + - @foreach (var item in Model.UserEvents) + @foreach (var item in Model.UserFacilities) { - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } } @@ -318,86 +718,55 @@ } @if (Model.CurrentUser.UserPosition.Name == "PM1") { -
    +
    -

    -

    -
    +
    -
    Facility NumberFacility NameEvent TypeAction TakenStart DateDue DateCompletion DateCommentActionsNumberNameUnitCountyClass
    - + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - @Html.DisplayFor(m => item.FacilityName, "StringOrNone") - - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - - @Html.DisplayFor(m => item.StartDate, "DateOrNone") + @Html.DisplayFor(m => item.Name, "StringOrNone") - @Html.DisplayFor(m => item.DueDate, "DateOrNone") + + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") - @Html.DisplayFor(m => item.Comment, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") - - Edit - -
    +
    -
    -

    Active Events for Unit: @Model.CurrentUser.UserUnit.Name

    +
    +

    Active Facilities for Unit: @Model.CurrentUser.UserUnit.Name

    - @(Model.UnitEvents.Count() > 0 ? $"Total Events: {Model.UnitEvents.Count()}" : "No Events Assigned") + @(Model.UnitFacilities.Count() > 0 ? $"Total Facilities: {Model.UnitFacilities.Count()}" : "No Facilities Assigned")
    - - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } + + + + + - @foreach (var item in Model.UnitEvents) + @foreach (var item in Model.UnitFacilities) { - - - - - - - + - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } } @@ -410,112 +779,75 @@ } @if (Model.CurrentUser.UserPosition.Name == "PM2") { -
    +
    -

    -

    -
    +
    -
    -

    Active Events for Program: @Model.CurrentUser.UserProgram.Name

    +
    +

    Active Facilities for Program: @Model.CurrentUser.UserProgram.Name

    - @(Model.ProgramEvents.Count() > 0 ? $"Total Events: {Model.ProgramEvents.Count()}" : "No Events Assigned") + @(Model.ProgramFacilities.Count() > 0 ? $"Total Facilities: {Model.ProgramFacilities.Count()}" : "No Facilities Assigned")
    @foreach (var unit in Model.UserUnits) { -
    +
    -

    -

    -
    +
    - @(Model.ProgramEvents.Where(e => e.Unit?.Name == unit).Count() > 0 ? $"{Model.ProgramEvents.Where(e => e.Unit?.Name == unit).Count()} out of {Model.ProgramEvents.Count()} Total Events: " : "No Events Assigned") + @(Model.ProgramFacilities.Where(e => e.OrganizationalUnit?.Name == unit).Count() > 0 ? $"{Model.ProgramFacilities.Where(e => e.OrganizationalUnit?.Name == unit).Count()} out of {Model.ProgramFacilities.Count()} Total Facilities: " : "No Facilities Assigned")
    -
    Facility NumberFacility NameEvent TypeAction TakenStart DateDue DateCompletion DateCompliance OfficerCommentActionsNumberNameC.O.CountyClass
    - + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - @Html.DisplayFor(m => item.FacilityName, "StringOrNone") - - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - - @Html.DisplayFor(m => item.StartDate, "DateOrNone") - - @Html.DisplayFor(m => item.DueDate, "DateOrNone") - - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + @Html.DisplayFor(m => item.Name, "StringOrNone") + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - @Html.DisplayFor(m => item.Comment, "StringOrNone") + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") - - Edit - -
    - +
    - - - - - - - - - - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) - { - - } + + + + + + - @foreach (var item in Model.ProgramEvents.Where(e => e.Unit?.Name == unit)) + @foreach (var item in Model.ProgramFacilities.Where(e => e.OrganizationalUnit?.Name == unit)) { - - - - - - - + - @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.SiteMaintenance)) - { - - } } @@ -532,6 +864,7 @@ } + @if (Model.CurrentUser.UserPosition.Name == "PM2" && Model.Roles.Contains(UserRoles.SiteMaintenance)) {
    diff --git a/FMS/Pages/Dashboard/Index.cshtml.cs b/FMS/Pages/Dashboard/Index.cshtml.cs index 9e07b790..a929b283 100644 --- a/FMS/Pages/Dashboard/Index.cshtml.cs +++ b/FMS/Pages/Dashboard/Index.cshtml.cs @@ -21,6 +21,7 @@ public class IndexModel( public IList UnitEvents { get; set; } = []; public IList ProgramEvents { get; set; } = []; public IList UserUnits { get; set; } = []; + public IList UserCOs { get; set; } = []; public async Task OnGetAsync() { @@ -62,6 +63,14 @@ public async Task OnGetAsync() if (CurrentUser.UserPosition.Name == "PM1") { UnitEvents = await _repository.GetUnitEventsByUserId(CurrentUser.Id); + + foreach (var unitEvent in UnitEvents) + { + if (!UserCOs.Contains(unitEvent.ComplianceOfficer?.Name) && unitEvent.ComplianceOfficer?.Name != null) + { + UserCOs.Add(unitEvent.ComplianceOfficer?.Name); + } + } } if (CurrentUser.UserPosition.Name == "PM2") @@ -75,8 +84,17 @@ public async Task OnGetAsync() UserUnits.Add(programEvent.Unit?.Name); } } + + foreach (var programEvent in ProgramEvents) + { + if (!UserCOs.Contains(programEvent.ComplianceOfficer?.Name) && programEvent.ComplianceOfficer?.Name != null) + { + UserCOs.Add(programEvent.ComplianceOfficer?.Name); + } + } } + return Page(); } diff --git a/FMS/Startup.cs b/FMS/Startup.cs index 58cbed34..c155731a 100644 --- a/FMS/Startup.cs +++ b/FMS/Startup.cs @@ -87,7 +87,6 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); - //services.AddScoped(); services.AddScoped(); services.AddScoped(); From dc671d276c94faadc96152c9854132f65abb128e Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 21 Jul 2026 11:16:21 -0400 Subject: [PATCH 36/37] Add Organizational Unit to dashboard event views Part of issue #954. - Enhance dashboard event tables by displaying the associated Organizational Unit for each event, providing improved context. - Implement more consistent event sorting across dashboard views by prioritizing completion and due dates. - Remove unused variables and unnecessary namespace imports for cleaner code. - Mark `GetFiscalYearStartDate` as a static method. --- .../Dto/Dashboard/DashboardUserEventsDto.cs | 7 ++++--- .../Repositories/DashboardRepository.cs | 15 ++++++++++----- FMS/Pages/Dashboard/Index.cshtml | 10 ++++++++++ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs b/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs index 16ea0c8c..8fe80b6d 100644 --- a/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs +++ b/FMS.Domain/Dto/Dashboard/DashboardUserEventsDto.cs @@ -1,7 +1,4 @@ using FMS.Domain.Entities; -using System; -using System.Collections.Generic; -using System.Text; namespace FMS.Domain.Dto { @@ -21,6 +18,7 @@ public DashboardUserEventsDto(Event facilityEvent) DueDate = facilityEvent.DueDate; CompletionDate = facilityEvent.CompletionDate; ComplianceOfficer = facilityEvent.ComplianceOfficer; + OrganizationalUnit = facilityEvent.Facility.OrganizationalUnit; Comment = facilityEvent.Comment?.Substring(0, Math.Min(facilityEvent.Comment.Length, 100)); Active = facilityEvent.Active; } @@ -53,6 +51,9 @@ public DashboardUserEventsDto(Event facilityEvent) [Display(Name = "Compliance Officer")] public ComplianceOfficer ComplianceOfficer { get; set; } + [Display(Name = "Organizational Unit")] + public OrganizationalUnit OrganizationalUnit { get; set; } + [Display(Name = "Comment")] public string Comment { get; set; } diff --git a/FMS.Infrastructure/Repositories/DashboardRepository.cs b/FMS.Infrastructure/Repositories/DashboardRepository.cs index 6d83f26a..63849731 100644 --- a/FMS.Infrastructure/Repositories/DashboardRepository.cs +++ b/FMS.Infrastructure/Repositories/DashboardRepository.cs @@ -12,10 +12,11 @@ public class DashboardRepository : IDashboardRepository private readonly FmsDbContext _context; public DashboardRepository(FmsDbContext context) => _context = context; - public DateOnly GetFiscalYearStartDate() + public static DateOnly GetFiscalYearStartDate() { var today = DateTime.Today; - var fiscalYearStart = new DateTime(today.Month < 7 ? today.Year - 1 : today.Year, 7, 1); + DateTime fiscalYearStart = new(today.Month < 7 ? today.Year - 1 : today.Year, 7, 1, 0, 0, 0, + DateTimeKind.Local); return DateOnly.FromDateTime(fiscalYearStart); } @@ -77,7 +78,6 @@ public async Task> GetUserHSIFacilitiesById(Gui public async Task> GetUnitHSIFacilitiesById(Guid id, bool includeInactive = false) { var currentUser = await GetApplicationUser(id); - var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); var fiscalYearStart = GetFiscalYearStartDate(); return await _context.Facilities.AsNoTracking() @@ -105,7 +105,6 @@ public async Task> GetUnitHSIFacilitiesById(Gui public async Task> GetProgramHSIFacilitiesById(Guid id, bool includeInactive = false) { - var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); var fiscalYearStart = GetFiscalYearStartDate(); return await _context.Facilities.AsNoTracking() @@ -138,12 +137,12 @@ public async Task> GetProgramHSIFacilitiesBy public async Task> GetEventsByUserId(Guid id, bool includeInactive = false) { - var oneYearAgo = DateOnly.FromDateTime(DateTime.UtcNow.AddYears(-1)); var fiscalYearStart = GetFiscalYearStartDate(); return await _context.Events.AsNoTracking() .Include(e => e.Facility) .Include(e => e.ComplianceOfficer) + .Include(e => e.Facility.OrganizationalUnit) .Include(e => e.EventType) .Include(e => e.ActionTaken) .Include(e => e.EventContractor) @@ -151,6 +150,8 @@ public async Task> GetEventsByUserId(Guid id, bool .Where(e => e.ComplianceOfficerId.Equals(id)) .Where(e => e.StartDate >= fiscalYearStart || e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null)) .OrderByDescending(e => e.StartDate) + .ThenByDescending(e => e.CompletionDate) + .ThenByDescending(e => e.DueDate) .Select(e => new DashboardUserEventsDto(e)) .ToListAsync(); } @@ -171,6 +172,8 @@ public async Task> GetUnitEventsByUserId(Guid id, b .Where(e => e.StartDate >= fiscalYearStart || e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null)) .OrderBy(e => e.ComplianceOfficer.FamilyName) .ThenByDescending(e => e.StartDate) + .ThenByDescending(e => e.CompletionDate) + .ThenByDescending(e => e.DueDate) .Select(e => new DashboardUnitEventsDto(e)) .ToListAsync(); } @@ -193,6 +196,8 @@ public async Task> GetProgramEventsByUserId(Guid .OrderBy(e => e.Facility.OrganizationalUnit.Name) .ThenBy(e => e.ComplianceOfficer.FamilyName) .ThenByDescending(e => e.StartDate) + .ThenByDescending(e => e.CompletionDate) + .ThenByDescending(e => e.DueDate) .Select(e => new DashboardProgramEventsDto(e)) .ToListAsync(); } diff --git a/FMS/Pages/Dashboard/Index.cshtml b/FMS/Pages/Dashboard/Index.cshtml index c39b653e..d2498e61 100644 --- a/FMS/Pages/Dashboard/Index.cshtml +++ b/FMS/Pages/Dashboard/Index.cshtml @@ -45,6 +45,7 @@
    + @if (User.IsInRole(UserRoles.FileEditor) || User.IsInRole(UserRoles.ComplianceOfficer)) { @@ -79,6 +80,9 @@ + @@ -121,6 +125,9 @@ + @@ -164,6 +171,9 @@ + From 1f7f93801a188fbfe621070459badf3f226aabf7 Mon Sep 17 00:00:00 2001 From: Tom Karasch Date: Tue, 21 Jul 2026 17:05:25 -0400 Subject: [PATCH 37/37] Refine dashboard event fiscal year filtering Ensure ongoing events without a completion date are always included in fiscal year views. Part of issue #954. --- FMS.Infrastructure/Repositories/DashboardRepository.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/FMS.Infrastructure/Repositories/DashboardRepository.cs b/FMS.Infrastructure/Repositories/DashboardRepository.cs index 63849731..e35dfbc4 100644 --- a/FMS.Infrastructure/Repositories/DashboardRepository.cs +++ b/FMS.Infrastructure/Repositories/DashboardRepository.cs @@ -148,7 +148,7 @@ public async Task> GetEventsByUserId(Guid id, bool .Include(e => e.EventContractor) .Where(e => includeInactive || e.Active) .Where(e => e.ComplianceOfficerId.Equals(id)) - .Where(e => e.StartDate >= fiscalYearStart || e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null)) + .Where(e => e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null) || (e.CompletionDate == null)) .OrderByDescending(e => e.StartDate) .ThenByDescending(e => e.CompletionDate) .ThenByDescending(e => e.DueDate) @@ -169,7 +169,7 @@ public async Task> GetUnitEventsByUserId(Guid id, b .Include(e => e.EventContractor) .Where(e => includeInactive || e.Active) .Where(e => e.Facility.OrganizationalUnit.Id.Equals(currentUser.UserUnit.Id)) - .Where(e => e.StartDate >= fiscalYearStart || e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null)) + .Where(e => e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null) || (e.CompletionDate == null)) .OrderBy(e => e.ComplianceOfficer.FamilyName) .ThenByDescending(e => e.StartDate) .ThenByDescending(e => e.CompletionDate) @@ -192,7 +192,7 @@ public async Task> GetProgramEventsByUserId(Guid .Include(e => e.Facility.OrganizationalUnit) .Where(e => includeInactive || e.Active) .Where(e => e.Facility.OrganizationalUnit.UserProgram.Id.Equals(currentUser.UserProgram.Id)) - .Where(e => e.StartDate >= fiscalYearStart || e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null)) + .Where(e => e.CompletionDate >= fiscalYearStart || (e.CompletionDate == null && e.StartDate == null) || (e.CompletionDate == null)) .OrderBy(e => e.Facility.OrganizationalUnit.Name) .ThenBy(e => e.ComplianceOfficer.FamilyName) .ThenByDescending(e => e.StartDate)
    Facility NumberFacility NameEvent TypeAction TakenStart DateDue DateCompl. DateC. O.CommentActionsNumberNameUnitC.O.CountyClass
    - + @Html.DisplayFor(m => item.FacilityNumber, "StringOrNone") - @Html.DisplayFor(m => item.FacilityName, "StringOrNone") - - @Html.DisplayFor(m => item.EventType.Name, "StringOrNone") - - @Html.DisplayFor(m => item.ActionTaken.Name, "StringOrNone") - - @Html.DisplayFor(m => item.StartDate, "DateOrNone") - - @Html.DisplayFor(m => item.DueDate, "DateOrNone") + @Html.DisplayFor(m => item.Name, "StringOrNone") - @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + @Html.DisplayFor(m => item.ComplianceOfficer.Name, "StringOrNone") - @Html.DisplayFor(m => item.Comment, "StringOrNone") + + @Html.DisplayFor(m => item.County.Name, "StringOrNone") + + @Html.DisplayFor(m => item.LocationClass.Name, "StringOrNone") - - Edit - - @if (User.IsInRole(UserRoles.SiteMaintenance)) - { - - Delete - - } -
    Start Date Due Date Completion DateOrganizational Unit Comment @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + @Html.DisplayFor(m => item.Comment, "StringOrNone") @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + @Html.DisplayFor(m => item.Comment, "StringOrNone") @Html.DisplayFor(m => item.CompletionDate, "DateOrNone") + @Html.DisplayFor(m => item.OrganizationalUnit.Name, "StringOrNone") + @Html.DisplayFor(m => item.Comment, "StringOrNone")