diff --git a/src/Humans.Web/Controllers/AdminDuplicateAccountsController.cs b/src/Humans.Web/Controllers/AdminDuplicateAccountsController.cs index b60b9a5da..b83bfb187 100644 --- a/src/Humans.Web/Controllers/AdminDuplicateAccountsController.cs +++ b/src/Humans.Web/Controllers/AdminDuplicateAccountsController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using Humans.Web.Authorization; using Humans.Web.Models; +using Humans.Application; using Humans.Application.Interfaces.Teams; using Humans.Application.Interfaces.Profiles; using Humans.Application.Interfaces.Users; @@ -63,16 +64,21 @@ public async Task Detail(Guid userId1, Guid userId2) var account1 = group.Accounts.First(a => a.UserId == userId1); var account2 = group.Accounts.First(a => a.UserId == userId2); - var profile1 = await BuildProfileCardAsync(userId1, account1); - var profile2 = await BuildProfileCardAsync(userId2, account2); + var info1 = await _userService.GetUserInfoAsync(userId1); + var info2 = await _userService.GetUserInfoAsync(userId2); + + var profile1 = await BuildProfileCardAsync(userId1, account1, info1); + var profile2 = await BuildProfileCardAsync(userId2, account2, info2); var viewModel = new DuplicateAccountDetailViewModel { SharedEmail = group.SharedEmail, Account1 = profile1, Account2 = profile2, - Account1EmailSources = account1.EmailSources, - Account2EmailSources = account2.EmailSources + Account1IdentityEmail = info1?.IdentityEmailColumn, + Account2IdentityEmail = info2?.IdentityEmailColumn, + Account1Emails = MapEmails(info1), + Account2Emails = MapEmails(info2) }; return View(viewModel); @@ -100,9 +106,9 @@ public async Task Resolve(Guid sourceUserId, Guid targetUserId, s } private async Task BuildProfileCardAsync( - Guid userId, DuplicateAccountInfo accountInfo) + Guid userId, DuplicateAccountInfo accountInfo, UserInfo? info) { - var profile = (await _userService.GetUserInfoAsync(userId))?.Profile; + var profile = info?.Profile; var teams = await teamService.GetUserTeamsAsync(userId); var activeTeamNames = teams .Where(m => m.LeftAt is null) @@ -125,4 +131,18 @@ private async Task BuildProfileCardAsync( Teams = activeTeamNames }; } + + private static List MapEmails(UserInfo? info) => + info is null + ? [] + : info.UserEmails + .Select(e => new DuplicateAccountEmailRowViewModel + { + Email = e.Email, + IsPrimary = e.IsPrimary, + IsVerified = e.IsVerified, + IsGoogle = e.IsGoogle, + Provider = e.Provider + }) + .ToList(); } diff --git a/src/Humans.Web/Models/AdminViewModels.cs b/src/Humans.Web/Models/AdminViewModels.cs index f96be7d48..4ccf7c978 100644 --- a/src/Humans.Web/Models/AdminViewModels.cs +++ b/src/Humans.Web/Models/AdminViewModels.cs @@ -358,8 +358,28 @@ public class DuplicateAccountDetailViewModel public string SharedEmail { get; set; } = string.Empty; public ProfileSummaryViewModel Account1 { get; set; } = new(); public ProfileSummaryViewModel Account2 { get; set; } = new(); - public List Account1EmailSources { get; set; } = []; - public List Account2EmailSources { get; set; } = []; + + /// Raw User.Email Identity column for account A (null when unset). + public string? Account1IdentityEmail { get; set; } + + /// Raw User.Email Identity column for account B (null when unset). + public string? Account2IdentityEmail { get; set; } + + /// All UserEmails rows for account A (full list, not just the conflicting overlap). + public List Account1Emails { get; set; } = []; + + /// All UserEmails rows for account B (full list, not just the conflicting overlap). + public List Account2Emails { get; set; } = []; +} + +/// One UserEmail row rendered on the duplicate-account detail page. +public class DuplicateAccountEmailRowViewModel +{ + public string Email { get; set; } = string.Empty; + public bool IsPrimary { get; set; } + public bool IsVerified { get; set; } + public bool IsGoogle { get; set; } + public string? Provider { get; set; } } /// diff --git a/src/Humans.Web/Views/AdminDuplicateAccounts/Detail.cshtml b/src/Humans.Web/Views/AdminDuplicateAccounts/Detail.cshtml index 8c443fb77..2a65bc97e 100644 --- a/src/Humans.Web/Views/AdminDuplicateAccounts/Detail.cshtml +++ b/src/Humans.Web/Views/AdminDuplicateAccounts/Detail.cshtml @@ -35,14 +35,56 @@
-
- Email sources: -
    - @foreach (var source in Model.Account1EmailSources) +
    +
    + User.Email (Identity column): + @if (!string.IsNullOrEmpty(Model.Account1IdentityEmail)) { -
  • @source
  • + @Model.Account1IdentityEmail } -
+ else + { + none + } +
+
+ UserEmails (@Model.Account1Emails.Count): + @if (Model.Account1Emails.Count == 0) + { + none + } + else + { +
    + @foreach (var email in Model.Account1Emails) + { +
  • + @email.Email + @if (email.IsPrimary) + { + Primary + } + @if (email.IsVerified) + { + Verified + } + else + { + Unverified + } + @if (email.IsGoogle) + { + Google + } + @if (!string.IsNullOrEmpty(email.Provider)) + { + @email.Provider + } +
  • + } +
+ } +
@@ -54,14 +96,56 @@
-
- Email sources: -
    - @foreach (var source in Model.Account2EmailSources) +
    +
    + User.Email (Identity column): + @if (!string.IsNullOrEmpty(Model.Account2IdentityEmail)) { -
  • @source
  • + @Model.Account2IdentityEmail } -
+ else + { + none + } +
+
+ UserEmails (@Model.Account2Emails.Count): + @if (Model.Account2Emails.Count == 0) + { + none + } + else + { +
    + @foreach (var email in Model.Account2Emails) + { +
  • + @email.Email + @if (email.IsPrimary) + { + Primary + } + @if (email.IsVerified) + { + Verified + } + else + { + Unverified + } + @if (email.IsGoogle) + { + Google + } + @if (!string.IsNullOrEmpty(email.Provider)) + { + @email.Provider + } +
  • + } +
+ } +