Enhance UI design and update to UK English

- Add custom CSS design system with brand colours and variables
- Enhance Report page with SVG score ring and improved flag styling
- Improve Dashboard with better table design and score badges
- Enhance Check page upload area with animated icon and file styling
- Update spellings to UK English (analysing, recognised)
- Add user-select: none to prevent text cursor on clickable elements
- All date formats already use UK-friendly dd MMM yyyy format

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-20 23:35:36 +01:00
parent 14ee569617
commit 5bfefdfd1d
6 changed files with 782 additions and 215 deletions

View File

@@ -17,10 +17,20 @@
@if (_isLoading)
{
<div class="text-center py-5">
<div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Loading...</span>
<div class="loading-placeholder">
<div class="placeholder-glow mb-4">
<div class="placeholder col-12 rounded-4" style="height: 180px;"></div>
</div>
<div class="placeholder-glow mb-3">
<div class="placeholder col-12 rounded-4" style="height: 280px;"></div>
</div>
</div>
<div class="mt-4">
<div class="spinner-border text-primary" role="status" style="width: 2.5rem; height: 2.5rem;">
<span class="visually-hidden">Loading report...</span>
</div>
<p class="mt-3 text-muted fw-medium">Loading verification report...</p>
</div>
<p class="mt-3 text-muted">Loading report...</p>
</div>
}
else if (_errorMessage is not null)
@@ -41,7 +51,7 @@
<span class="visually-hidden">Processing...</span>
</div>
<h4 class="mb-2">Processing Your CV</h4>
<p class="text-muted mb-4">Our AI is analyzing the document. This usually takes 1-2 minutes.</p>
<p class="text-muted mb-4">Our AI is analysing the document. This usually takes 1-2 minutes.</p>
<div class="progress" style="height: 8px;">
<div class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar"
@@ -86,26 +96,28 @@
else if (_report is not null && _check is not null)
{
<!-- Report Header -->
<div class="row mb-4">
<div class="col">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/dashboard">Dashboard</a></li>
<li class="breadcrumb-item active" aria-current="page">Report</li>
</ol>
</nav>
<h1 class="fw-bold">Verification Report</h1>
<p class="text-muted">
@_check.OriginalFileName | Generated @_report.GeneratedAt.ToString("dd MMM yyyy HH:mm")
</p>
</div>
<div class="col-auto">
<button class="btn btn-outline-primary" @onclick="DownloadReport" disabled="@(_report is null)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download me-1" viewBox="0 0 16 16">
<div class="page-header mb-4">
<nav aria-label="breadcrumb" class="mb-2">
<ol class="breadcrumb small">
<li class="breadcrumb-item"><a href="/dashboard" class="text-decoration-none">Dashboard</a></li>
<li class="breadcrumb-item active text-muted" aria-current="page">Report</li>
</ol>
</nav>
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3">
<div>
<h1 class="fw-bold mb-1">Verification Report</h1>
<p class="text-muted mb-0">
<span class="fw-medium text-dark">@_check.OriginalFileName</span>
<span class="mx-2">|</span>
Generated @_report.GeneratedAt.ToString("dd MMM yyyy") at @_report.GeneratedAt.ToString("HH:mm")
</p>
</div>
<button class="btn btn-primary" @onclick="DownloadReport" disabled="@(_report is null)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download me-2" viewBox="0 0 16 16">
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
</svg>
Download Report
Download PDF
</button>
</div>
</div>
@@ -113,29 +125,60 @@
<!-- Score Card -->
<div class="row mb-4">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-body p-4">
<div class="row align-items-center">
<div class="col-md-4 text-center border-end">
<div class="score-circle @GetScoreColorClass(_report!.OverallScore) mx-auto mb-2">
<span class="score-value">@_report.OverallScore</span>
<div class="card border-0 shadow-sm overflow-hidden">
<div class="score-header @GetScoreHeaderClass(_report!.OverallScore)">
<div class="row align-items-center py-4 px-3">
<div class="col-md-4 text-center">
<div class="score-ring-container">
<svg class="score-ring" viewBox="0 0 120 120">
<circle class="score-ring-bg" cx="60" cy="60" r="54" />
<circle class="score-ring-progress" cx="60" cy="60" r="54"
stroke-dasharray="@(339.3 * _report.OverallScore / 100) 339.3" />
</svg>
<div class="score-value-container">
<span class="score-value">@_report.OverallScore</span>
<span class="score-max">/100</span>
</div>
</div>
<h5 class="mb-0">@_report.ScoreLabel</h5>
<small class="text-muted">Veracity Score</small>
<h5 class="mb-0 mt-2 fw-semibold text-white">@_report.ScoreLabel</h5>
<small class="text-white-50">Veracity Score</small>
</div>
<div class="col-md-8">
<div class="row text-center">
<div class="row g-4 text-center text-md-start">
<div class="col-4">
<h3 class="mb-0 text-primary">@_report.EmploymentVerifications.Count</h3>
<small class="text-muted">Employers Checked</small>
<div class="stat-item">
<div class="stat-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
<path d="M4 2.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1Zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1Zm3.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1ZM4 5.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1ZM7.5 5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1Zm2.5.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1Z"/>
<path d="M2 1a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V1Zm11 0H3v14h3v-2.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 .5.5V15h3V1Z"/>
</svg>
</div>
<h3 class="mb-0 fw-bold text-white">@_report.EmploymentVerifications.Count</h3>
<small class="text-white-50">Employers Checked</small>
</div>
</div>
<div class="col-4">
<h3 class="mb-0 text-warning">@_report.TimelineAnalysis.TotalGapMonths</h3>
<small class="text-muted">Months of Gaps</small>
<div class="stat-item">
<div class="stat-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
<path d="M6.146 7.146a.5.5 0 0 1 .708 0L8 8.293l1.146-1.147a.5.5 0 1 1 .708.708L8.707 9l1.147 1.146a.5.5 0 0 1-.708.708L8 9.707l-1.146 1.147a.5.5 0 0 1-.708-.708L7.293 9 6.146 7.854a.5.5 0 0 1 0-.708z"/>
<path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/>
</svg>
</div>
<h3 class="mb-0 fw-bold text-white">@_report.TimelineAnalysis.TotalGapMonths</h3>
<small class="text-white-50">Gap Months</small>
</div>
</div>
<div class="col-4">
<h3 class="mb-0 text-danger">@_report.Flags.Count</h3>
<small class="text-muted">Flags Raised</small>
<div class="stat-item">
<div class="stat-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
<path d="M14.778.085A.5.5 0 0 1 15 .5V8a.5.5 0 0 1-.314.464L14.5 8l.186.464-.003.001-.006.003-.023.009a12.435 12.435 0 0 1-.397.15c-.264.095-.631.223-1.047.35-.816.252-1.879.523-2.71.523-.847 0-1.548-.28-2.158-.525l-.028-.01C7.68 8.71 7.14 8.5 6.5 8.5c-.7 0-1.638.23-2.437.477A19.626 19.626 0 0 0 3 9.342V15.5a.5.5 0 0 1-1 0V.5a.5.5 0 0 1 1 0v.282c.226-.079.496-.17.79-.26C4.606.272 5.67 0 6.5 0c.84 0 1.524.277 2.121.519l.043.018C9.286.788 9.828 1 10.5 1c.7 0 1.638-.23 2.437-.477a19.587 19.587 0 0 0 1.349-.476l.019-.007.004-.002h.001"/>
</svg>
</div>
<h3 class="mb-0 fw-bold text-white">@_report.Flags.Count</h3>
<small class="text-white-50">Flags Raised</small>
</div>
</div>
</div>
</div>
@@ -395,10 +438,12 @@
</h6>
@foreach (var flag in criticalFlags)
{
<div class="alert alert-danger mb-2">
<strong>@flag.Title</strong>
<span class="badge bg-danger ms-2">-@flag.ScoreImpact pts</span>
<p class="mb-0 mt-1 small">@flag.Description</p>
<div class="flag-item flag-critical">
<div class="d-flex justify-content-between align-items-start">
<strong class="flag-title">@flag.Title</strong>
<span class="flag-points bg-danger-subtle text-danger">-@flag.ScoreImpact pts</span>
</div>
<p class="flag-description">@flag.Description</p>
</div>
}
}
@@ -414,17 +459,19 @@
</h6>
@foreach (var flag in warningFlags)
{
<div class="alert alert-warning mb-2">
<strong>@flag.Title</strong>
<span class="badge bg-warning text-dark ms-2">-@flag.ScoreImpact pts</span>
<p class="mb-0 mt-1 small">@flag.Description</p>
<div class="flag-item flag-warning">
<div class="d-flex justify-content-between align-items-start">
<strong class="flag-title">@flag.Title</strong>
<span class="flag-points bg-warning-subtle text-warning">-@flag.ScoreImpact pts</span>
</div>
<p class="flag-description">@flag.Description</p>
</div>
}
}
@if (infoFlags.Count > 0)
{
<h6 class="text-info fw-bold mb-3 @(criticalFlags.Count > 0 || warningFlags.Count > 0 ? "mt-4" : "")">
<h6 class="text-primary fw-bold mb-3 @(criticalFlags.Count > 0 || warningFlags.Count > 0 ? "mt-4" : "")">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-info-circle me-1" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
@@ -433,9 +480,9 @@
</h6>
@foreach (var flag in infoFlags)
{
<div class="alert alert-info mb-2">
<strong>@flag.Title</strong>
<p class="mb-0 mt-1 small">@flag.Description</p>
<div class="flag-item flag-info">
<strong class="flag-title">@flag.Title</strong>
<p class="flag-description">@flag.Description</p>
</div>
}
}
@@ -446,46 +493,169 @@
</div>
<style>
.score-circle {
width: 120px;
height: 120px;
border-radius: 50%;
display: flex;
/* Score Header with Gradient */
.score-header {
color: white;
transition: background-color 0.3s ease;
}
.score-header.score-high {
background: linear-gradient(135deg, #059669 0%, #047857 100%);
}
.score-header.score-medium {
background: linear-gradient(135deg, #d97706 0%, #b45309 100%);
}
.score-header.score-low {
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
}
/* SVG Score Ring */
.score-ring-container {
position: relative;
width: 140px;
height: 140px;
margin: 0 auto;
}
.score-ring {
transform: rotate(-90deg);
width: 100%;
height: 100%;
}
.score-ring-bg {
fill: none;
stroke: rgba(255, 255, 255, 0.2);
stroke-width: 8;
}
.score-ring-progress {
fill: none;
stroke: white;
stroke-width: 8;
stroke-linecap: round;
transition: stroke-dasharray 0.6s ease;
}
.score-value-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.score-value-container .score-value {
font-size: 2.5rem;
font-weight: 700;
line-height: 1;
color: white;
}
.score-value-container .score-max {
font-size: 1rem;
opacity: 0.7;
color: white;
}
/* Stat Items */
.stat-item {
padding: 0.5rem;
}
.stat-icon {
width: 44px;
height: 44px;
background: rgba(255, 255, 255, 0.2);
border-radius: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 8px solid;
margin-bottom: 0.5rem;
color: white;
}
.score-circle.score-high {
border-color: #198754;
background-color: rgba(25, 135, 84, 0.1);
/* Flag Items */
.flag-item {
border-radius: 12px;
padding: 1rem 1.25rem;
margin-bottom: 0.75rem;
border-left: 4px solid;
}
.score-circle.score-medium {
border-color: #ffc107;
background-color: rgba(255, 193, 7, 0.1);
.flag-item.flag-critical {
background-color: #fef2f2;
border-left-color: #dc2626;
}
.score-circle.score-low {
border-color: #dc3545;
background-color: rgba(220, 53, 69, 0.1);
.flag-item.flag-warning {
background-color: #fffbeb;
border-left-color: #d97706;
}
.score-value {
font-size: 2.5rem;
font-weight: bold;
.flag-item.flag-info {
background-color: #eff6ff;
border-left-color: #2563eb;
}
.score-high .score-value {
color: #198754;
.flag-title {
font-weight: 600;
margin-bottom: 0.25rem;
color: #374151;
}
.score-medium .score-value {
color: #ffc107;
.flag-description {
color: #6b7280;
font-size: 0.875rem;
margin: 0.5rem 0 0 0;
}
.score-low .score-value {
color: #dc3545;
.flag-points {
font-size: 0.75rem;
font-weight: 600;
padding: 0.25rem 0.5rem;
border-radius: 4px;
}
/* Mobile Responsiveness */
@@media (max-width: 768px) {
.score-header .row {
flex-direction: column;
}
.score-header .col-md-4 {
border-right: none !important;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
padding-bottom: 1.5rem;
margin-bottom: 1.5rem;
}
.stat-item h3 {
font-size: 1.5rem;
}
.score-ring-container {
width: 120px;
height: 120px;
}
.score-value-container .score-value {
font-size: 2rem;
}
.table-responsive {
font-size: 0.875rem;
}
.page-header .d-flex {
flex-direction: column;
}
.page-header .btn {
width: 100%;
}
}
</style>
@@ -631,6 +801,16 @@
};
}
private static string GetScoreHeaderClass(int score)
{
return score switch
{
> 70 => "score-high",
>= 50 => "score-medium",
_ => "score-low"
};
}
private static string GetMatchScoreBadgeClass(int score)
{
return score switch