From cfe06788e2c4132c8b7f9b9007495c29cfd82c14 Mon Sep 17 00:00:00 2001 From: Peter Foster Date: Wed, 21 Jan 2026 11:20:38 +0000 Subject: [PATCH] Fix CV verification scoring and UI alignment issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../Jobs/ProcessCVCheckJob.cs | 11 ++- src/TrueCV.Web/Components/Pages/Report.razor | 72 +++++++++++++++---- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/TrueCV.Infrastructure/Jobs/ProcessCVCheckJob.cs b/src/TrueCV.Infrastructure/Jobs/ProcessCVCheckJob.cs index e0dbb9b..b70bd8f 100644 --- a/src/TrueCV.Infrastructure/Jobs/ProcessCVCheckJob.cs +++ b/src/TrueCV.Infrastructure/Jobs/ProcessCVCheckJob.cs @@ -523,13 +523,22 @@ public sealed class ProcessCVCheckJob name == "self employed" || name == "selfemployed" || 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("self-employed ") || name.StartsWith("self employed ") || + name.StartsWith("contract ") || + name.StartsWith("contracting ") || name.Contains("(freelance)") || name.Contains("(self-employed)") || name.Contains("(self employed)") || - name.Contains("(contractor)"); + name.Contains("(contractor)") || + name.Contains("(contract)"); } private static bool IsPublicSectorEmployer(string companyName) diff --git a/src/TrueCV.Web/Components/Pages/Report.razor b/src/TrueCV.Web/Components/Pages/Report.razor index 26d2c17..b7e2a0b 100644 --- a/src/TrueCV.Web/Components/Pages/Report.razor +++ b/src/TrueCV.Web/Components/Pages/Report.razor @@ -214,11 +214,11 @@
-
+
Employer
-
Period
-
Match
-
Pts
+
Period
+
Match
+
Pts
@for (int i = 0; i < _report.EmploymentVerifications.Count; i++) { @@ -573,6 +573,10 @@ /* Stat Items */ .stat-item { padding: 0.5rem; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; } .stat-icon { @@ -641,7 +645,7 @@ .employment-header { display: grid; - grid-template-columns: 24px 1fr auto 60px 50px; + grid-template-columns: 24px 1fr 120px 60px 50px; gap: 0.5rem; padding: 0.5rem 0.75rem; background-color: #f8fafc; @@ -651,17 +655,24 @@ color: #64748b; text-transform: uppercase; 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(4), .employment-header div:nth-child(5) { + justify-content: center; text-align: center; } .employment-row { display: grid; - grid-template-columns: 24px 1fr auto 60px 50px; + grid-template-columns: 24px 1fr 120px 60px 50px; gap: 0.5rem; align-items: center; padding: 0.5rem 0.75rem; @@ -690,6 +701,7 @@ .employment-company { display: flex; flex-direction: column; + justify-content: center; gap: 0.125rem; min-width: 0; } @@ -707,18 +719,25 @@ } .employment-dates { + display: flex; + align-items: center; + justify-content: center; font-size: 0.75rem; color: #6b7280; white-space: nowrap; - text-align: right; + text-align: center; } .employment-score { - text-align: center; + display: flex; + align-items: center; + justify-content: center; } .employment-points { - text-align: center; + display: flex; + align-items: center; + justify-content: center; font-size: 0.8125rem; } @@ -970,7 +989,7 @@ 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 if (!_firstOccurrenceIndices.Contains(index)) @@ -978,7 +997,13 @@ 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 .Where(f => f.ScoreImpact < 0 && ((!string.IsNullOrEmpty(f.Description) && f.Description.Contains(claimedCompany, StringComparison.OrdinalIgnoreCase)) || @@ -987,6 +1012,29 @@ .Select(g => g.First()) .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; } }