Fix CV verification scoring and UI alignment issues

ProcessCVCheckJob:
- Recognize "contract", "contract work", "contract role" as freelance
- Add "various", "various clients" for multiple short-term contracts
- Prevents false matching of contract work to dissolved companies

Report.razor:
- Fix stat blocks centering (icon, number, label now centered)
- Fix employment table column alignment with fixed widths
- Add inline styles to header for consistent centering
- Improve GetPointsForCompany to show -10 for unverified companies

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 11:20:38 +00:00
parent 7510ef3670
commit cfe06788e2
2 changed files with 70 additions and 13 deletions

View File

@@ -523,13 +523,22 @@ public sealed class ProcessCVCheckJob
name == "self employed" || name == "self employed" ||
name == "selfemployed" || name == "selfemployed" ||
name == "contractor" || name == "contractor" ||
name == "contract" || // Working on contract basis
name == "contract work" ||
name == "contract role" ||
name == "various" || // Multiple short-term contracts
name == "various clients" ||
name == "various companies" ||
name.StartsWith("freelance ") || name.StartsWith("freelance ") ||
name.StartsWith("self-employed ") || name.StartsWith("self-employed ") ||
name.StartsWith("self employed ") || name.StartsWith("self employed ") ||
name.StartsWith("contract ") ||
name.StartsWith("contracting ") ||
name.Contains("(freelance)") || name.Contains("(freelance)") ||
name.Contains("(self-employed)") || name.Contains("(self-employed)") ||
name.Contains("(self employed)") || name.Contains("(self employed)") ||
name.Contains("(contractor)"); name.Contains("(contractor)") ||
name.Contains("(contract)");
} }
private static bool IsPublicSectorEmployer(string companyName) private static bool IsPublicSectorEmployer(string companyName)

View File

@@ -214,11 +214,11 @@
<div class="card-body p-0"> <div class="card-body p-0">
<div class="employment-list"> <div class="employment-list">
<div class="employment-header"> <div class="employment-header">
<div></div> <div style="text-align: center;"></div>
<div>Employer</div> <div>Employer</div>
<div>Period</div> <div style="text-align: center;">Period</div>
<div>Match</div> <div style="text-align: center;">Match</div>
<div>Pts</div> <div style="text-align: center;">Pts</div>
</div> </div>
@for (int i = 0; i < _report.EmploymentVerifications.Count; i++) @for (int i = 0; i < _report.EmploymentVerifications.Count; i++)
{ {
@@ -573,6 +573,10 @@
/* Stat Items */ /* Stat Items */
.stat-item { .stat-item {
padding: 0.5rem; padding: 0.5rem;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
} }
.stat-icon { .stat-icon {
@@ -641,7 +645,7 @@
.employment-header { .employment-header {
display: grid; display: grid;
grid-template-columns: 24px 1fr auto 60px 50px; grid-template-columns: 24px 1fr 120px 60px 50px;
gap: 0.5rem; gap: 0.5rem;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
background-color: #f8fafc; background-color: #f8fafc;
@@ -651,17 +655,24 @@
color: #64748b; color: #64748b;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.025em; letter-spacing: 0.025em;
align-items: center;
}
.employment-header div {
display: flex;
align-items: center;
} }
.employment-header div:nth-child(3), .employment-header div:nth-child(3),
.employment-header div:nth-child(4), .employment-header div:nth-child(4),
.employment-header div:nth-child(5) { .employment-header div:nth-child(5) {
justify-content: center;
text-align: center; text-align: center;
} }
.employment-row { .employment-row {
display: grid; display: grid;
grid-template-columns: 24px 1fr auto 60px 50px; grid-template-columns: 24px 1fr 120px 60px 50px;
gap: 0.5rem; gap: 0.5rem;
align-items: center; align-items: center;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
@@ -690,6 +701,7 @@
.employment-company { .employment-company {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center;
gap: 0.125rem; gap: 0.125rem;
min-width: 0; min-width: 0;
} }
@@ -707,18 +719,25 @@
} }
.employment-dates { .employment-dates {
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem; font-size: 0.75rem;
color: #6b7280; color: #6b7280;
white-space: nowrap; white-space: nowrap;
text-align: right; text-align: center;
} }
.employment-score { .employment-score {
text-align: center; display: flex;
align-items: center;
justify-content: center;
} }
.employment-points { .employment-points {
text-align: center; display: flex;
align-items: center;
justify-content: center;
font-size: 0.8125rem; font-size: 0.8125rem;
} }
@@ -970,7 +989,7 @@
private int GetPointsForCompany(string claimedCompany, string? matchedCompany, int index) private int GetPointsForCompany(string claimedCompany, string? matchedCompany, int index)
{ {
if (_report?.Flags is null) return 0; if (_report?.Flags is null || _report?.EmploymentVerifications is null) return 0;
// Only show points for the first occurrence of each company // Only show points for the first occurrence of each company
if (!_firstOccurrenceIndices.Contains(index)) if (!_firstOccurrenceIndices.Contains(index))
@@ -978,7 +997,13 @@
return 0; return 0;
} }
// Sum up all flags that mention this company in their description var totalPoints = 0;
// Get the verification result for this company
var verification = _report.EmploymentVerifications.ElementAtOrDefault(index);
// Check if there's an "Unverified Company" flag for this company
// Search by both title pattern and description containing company name
var companyFlags = _report.Flags var companyFlags = _report.Flags
.Where(f => f.ScoreImpact < 0 && .Where(f => f.ScoreImpact < 0 &&
((!string.IsNullOrEmpty(f.Description) && f.Description.Contains(claimedCompany, StringComparison.OrdinalIgnoreCase)) || ((!string.IsNullOrEmpty(f.Description) && f.Description.Contains(claimedCompany, StringComparison.OrdinalIgnoreCase)) ||
@@ -987,6 +1012,29 @@
.Select(g => g.First()) .Select(g => g.First())
.ToList(); .ToList();
return companyFlags.Sum(f => f.ScoreImpact); totalPoints = companyFlags.Sum(f => f.ScoreImpact);
// If company is unverified but no flag was found in description search,
// check if there's a generic "Unverified Company" flag that might use different wording
if (verification != null && !verification.IsVerified && totalPoints == 0)
{
// Look for any Unverified Company flag that might apply
var unverifiedFlag = _report.Flags
.FirstOrDefault(f => f.Title == "Unverified Company" && f.ScoreImpact < 0 &&
!string.IsNullOrEmpty(f.Description) &&
f.Description.Contains(claimedCompany, StringComparison.OrdinalIgnoreCase));
if (unverifiedFlag != null)
{
totalPoints = unverifiedFlag.ScoreImpact;
}
else
{
// Company is unverified but no specific flag found - show the standard penalty
totalPoints = -10;
}
}
return totalPoints;
} }
} }