Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions src/Humans.Web/Controllers/AdminDuplicateAccountsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -63,16 +64,21 @@ public async Task<IActionResult> 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);
Expand Down Expand Up @@ -100,9 +106,9 @@ public async Task<IActionResult> Resolve(Guid sourceUserId, Guid targetUserId, s
}

private async Task<ProfileSummaryViewModel> 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)
Expand All @@ -125,4 +131,18 @@ private async Task<ProfileSummaryViewModel> BuildProfileCardAsync(
Teams = activeTeamNames
};
}

private static List<DuplicateAccountEmailRowViewModel> 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();
}
24 changes: 22 additions & 2 deletions src/Humans.Web/Models/AdminViewModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> Account1EmailSources { get; set; } = [];
public List<string> Account2EmailSources { get; set; } = [];

/// <summary>Raw <c>User.Email</c> Identity column for account A (null when unset).</summary>
public string? Account1IdentityEmail { get; set; }

/// <summary>Raw <c>User.Email</c> Identity column for account B (null when unset).</summary>
public string? Account2IdentityEmail { get; set; }

/// <summary>All <c>UserEmails</c> rows for account A (full list, not just the conflicting overlap).</summary>
public List<DuplicateAccountEmailRowViewModel> Account1Emails { get; set; } = [];

/// <summary>All <c>UserEmails</c> rows for account B (full list, not just the conflicting overlap).</summary>
public List<DuplicateAccountEmailRowViewModel> Account2Emails { get; set; } = [];
}

/// <summary>One <c>UserEmail</c> row rendered on the duplicate-account detail page.</summary>
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; }
}

/// <summary>
Expand Down
108 changes: 96 additions & 12 deletions src/Humans.Web/Views/AdminDuplicateAccounts/Detail.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,56 @@
</div>
<div class="card-body">
<partial name="_ProfileCard" model="Model.Account1" />
<div class="small text-muted mt-3">
<strong>Email sources:</strong>
<ul class="mb-0 ps-3">
@foreach (var source in Model.Account1EmailSources)
<div class="small mt-3">
<div>
<strong>User.Email</strong> <span class="text-muted">(Identity column):</span>
@if (!string.IsNullOrEmpty(Model.Account1IdentityEmail))
{
<li>@source</li>
<code>@Model.Account1IdentityEmail</code>
}
</ul>
else
{
<span class="text-muted fst-italic">none</span>
}
</div>
<div class="mt-2">
<strong>UserEmails</strong> <span class="text-muted">(@Model.Account1Emails.Count):</span>
@if (Model.Account1Emails.Count == 0)
{
<span class="text-muted fst-italic">none</span>
}
else
{
<ul class="list-unstyled mb-0 ps-3">
@foreach (var email in Model.Account1Emails)
{
<li>
<code>@email.Email</code>
@if (email.IsPrimary)
{
<span class="badge bg-primary ms-1">Primary</span>
}
@if (email.IsVerified)
{
<span class="badge bg-success ms-1">Verified</span>
}
else
{
<span class="badge bg-secondary ms-1">Unverified</span>
}
@if (email.IsGoogle)
{
<span class="badge bg-light text-dark border ms-1">Google</span>
}
@if (!string.IsNullOrEmpty(email.Provider))
{
<span class="badge bg-light text-dark border ms-1">@email.Provider</span>
}
</li>
}
</ul>
}
</div>
</div>
</div>
</div>
Expand All @@ -54,14 +96,56 @@
</div>
<div class="card-body">
<partial name="_ProfileCard" model="Model.Account2" />
<div class="small text-muted mt-3">
<strong>Email sources:</strong>
<ul class="mb-0 ps-3">
@foreach (var source in Model.Account2EmailSources)
<div class="small mt-3">
<div>
<strong>User.Email</strong> <span class="text-muted">(Identity column):</span>
@if (!string.IsNullOrEmpty(Model.Account2IdentityEmail))
{
<li>@source</li>
<code>@Model.Account2IdentityEmail</code>
}
</ul>
else
{
<span class="text-muted fst-italic">none</span>
}
</div>
<div class="mt-2">
<strong>UserEmails</strong> <span class="text-muted">(@Model.Account2Emails.Count):</span>
@if (Model.Account2Emails.Count == 0)
{
<span class="text-muted fst-italic">none</span>
}
else
{
<ul class="list-unstyled mb-0 ps-3">
@foreach (var email in Model.Account2Emails)
{
<li>
<code>@email.Email</code>
@if (email.IsPrimary)
{
<span class="badge bg-primary ms-1">Primary</span>
}
@if (email.IsVerified)
{
<span class="badge bg-success ms-1">Verified</span>
}
else
{
<span class="badge bg-secondary ms-1">Unverified</span>
}
@if (email.IsGoogle)
{
<span class="badge bg-light text-dark border ms-1">Google</span>
}
@if (!string.IsNullOrEmpty(email.Provider))
{
<span class="badge bg-light text-dark border ms-1">@email.Provider</span>
}
</li>
}
</ul>
}
</div>
</div>
</div>
</div>
Expand Down
Loading