feat(election): compact candidate analysis UI + savings card enhancements
- Reduce all spacing/padding/font sizes for denser layout (hero, KPI strip, section headers, cards, donuts, legends, influence metrics, loan stats) - Fix Savings Account Depth card: remove empty space (justify-content flex-start), fix donut notch on single segment, add 3-stat grid (active/no-savings/penetration%) and insight note - Remove Chart.js borderWidth/borderRadius on single-segment donuts to eliminate white gap artefact Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
dac169ae15
commit
78b87fb6ce
@@ -487,7 +487,7 @@
|
||||
<div class="section-header">
|
||||
<mat-icon>savings</mat-icon>
|
||||
<h2>Financial & Family Engagement</h2>
|
||||
<span class="section-badge">6 Analysis Dimensions</span>
|
||||
<span class="section-badge">5 Analysis Dimensions</span>
|
||||
</div>
|
||||
|
||||
<div class="a-grid">
|
||||
@@ -584,6 +584,7 @@
|
||||
<div class="ac-head">
|
||||
<div class="ac-icon"><mat-icon>savings</mat-icon></div>
|
||||
<span class="ac-title">Savings Account Depth</span>
|
||||
<span class="ac-tag">{{ activeSavingsPct(data) }}% Penetration</span>
|
||||
</div>
|
||||
<div class="chart-body flex-col">
|
||||
@if (data.savingsBreakdown?.length > 0) {
|
||||
@@ -605,6 +606,29 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="sav-stats">
|
||||
<div class="sav-stat">
|
||||
<div class="sav-num" style="color: #0ea5e9">{{ data.activeSavingsHolders }}</div>
|
||||
<div class="sav-lbl">Active Savings</div>
|
||||
</div>
|
||||
<div class="sav-stat">
|
||||
<div class="sav-num" style="color: #94a3b8">
|
||||
{{ data.totalActiveShareholders - data.activeSavingsHolders }}
|
||||
</div>
|
||||
<div class="sav-lbl">No Savings</div>
|
||||
</div>
|
||||
<div class="sav-stat">
|
||||
<div class="sav-num" style="color: #16a34a">{{ activeSavingsPct(data) }}%</div>
|
||||
<div class="sav-lbl">Penetration Rate</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loan-note" style="margin-top: 10px; width: 100%">
|
||||
<mat-icon>info_outline</mat-icon>
|
||||
<span
|
||||
>High savings penetration means members are financially active and have stronger ties to the
|
||||
institution — a positive signal for candidate support.</span
|
||||
>
|
||||
</div>
|
||||
} @else {
|
||||
<p class="no-data">No savings data available.</p>
|
||||
}
|
||||
@@ -655,61 +679,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Family Member Network (full width) -->
|
||||
<div class="a-card a-full" style="--ac: #0d9488; --ac-bg: rgba(13, 148, 136, 0.08)">
|
||||
<div class="ac-head">
|
||||
<div class="ac-icon"><mat-icon>family_restroom</mat-icon></div>
|
||||
<span class="ac-title">Family Member Network Influence</span>
|
||||
</div>
|
||||
<div class="fam-metrics-layout">
|
||||
<div class="fam-metrics">
|
||||
<div class="fam-metric">
|
||||
<div class="fm-icon-badge" style="color: #0d9488; background: rgba(13, 148, 136, 0.08)">
|
||||
<mat-icon>group</mat-icon>
|
||||
</div>
|
||||
<div class="fm-details">
|
||||
<div class="fm-val">{{ data.shareholdersWithFamily }}</div>
|
||||
<div class="fm-lbl">Shareholders with Family Records</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fam-sep"></div>
|
||||
<div class="fam-metric">
|
||||
<div class="fm-icon-badge" style="color: #0f766e; background: rgba(15, 118, 110, 0.08)">
|
||||
<mat-icon>diversity_1</mat-icon>
|
||||
</div>
|
||||
<div class="fm-details">
|
||||
<div class="fm-val">{{ data.totalFamilyMembers }}</div>
|
||||
<div class="fm-lbl">Total Family Members Registered</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fam-sep"></div>
|
||||
<div class="fam-metric">
|
||||
<div class="fm-icon-badge" style="color: #115e59; background: rgba(17, 94, 89, 0.08)">
|
||||
<mat-icon>calculate</mat-icon>
|
||||
</div>
|
||||
<div class="fm-details">
|
||||
<div class="fm-val">
|
||||
{{
|
||||
data.shareholdersWithFamily > 0
|
||||
? (data.totalFamilyMembers / data.shareholdersWithFamily | number: '1.1-1')
|
||||
: '—'
|
||||
}}
|
||||
</div>
|
||||
<div class="fm-lbl">Avg Registered Members / Household</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="insight-note">
|
||||
<mat-icon>insights</mat-icon>
|
||||
<span
|
||||
>Family records indicate broader community networks. A single voting shareholder with multiple family
|
||||
records acts as a proxy shapeholder, often scaling voting decisions across their entire
|
||||
household.</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ $accent-green: #10b981;
|
||||
min-height: 100%;
|
||||
font-family: $font;
|
||||
color: $text-main;
|
||||
padding-bottom: 40px;
|
||||
padding-bottom: 24px;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
@@ -89,7 +89,7 @@ $accent-green: #10b981;
|
||||
// ─── Hero Header (Redesigned - Light Mode Infographic Style) ──────────────────
|
||||
.hero {
|
||||
background: linear-gradient(135deg, #eef2ff 0%, #e0f2fe 50%, #f0fdf4 100%);
|
||||
padding: 24px 32px 36px;
|
||||
padding: 14px 24px 18px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #cbd5e1;
|
||||
@@ -132,9 +132,9 @@ $accent-green: #10b981;
|
||||
gap: 4px;
|
||||
color: #475569;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 24px;
|
||||
margin-bottom: 12px;
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
@@ -160,14 +160,14 @@ $accent-green: #10b981;
|
||||
.hero-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28px;
|
||||
gap: 18px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
@media (width <= 768px) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,13 +177,13 @@ $accent-green: #10b981;
|
||||
}
|
||||
|
||||
.hero-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #6366f1 0%, #3b82f6 100%);
|
||||
border: 2px solid #fff;
|
||||
color: #fff;
|
||||
font-size: 38px;
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -249,21 +249,21 @@ $accent-green: #10b981;
|
||||
}
|
||||
|
||||
.hero-name {
|
||||
font-size: 32px;
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
color: #0f172a;
|
||||
margin: 0 0 12px;
|
||||
letter-spacing: -0.8px;
|
||||
margin: 0 0 6px;
|
||||
letter-spacing: -0.5px;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.hero-criteria {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 14px;
|
||||
padding-top: 12px;
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid #cbd5e1;
|
||||
}
|
||||
|
||||
@@ -376,8 +376,8 @@ $accent-green: #10b981;
|
||||
.kpi-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr repeat(5, 1fr);
|
||||
gap: 16px;
|
||||
padding: 24px 32px;
|
||||
gap: 10px;
|
||||
padding: 12px 24px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
@@ -387,8 +387,8 @@ $accent-green: #10b981;
|
||||
|
||||
@media (width <= 768px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
padding: 16px 20px;
|
||||
gap: 12px;
|
||||
padding: 10px 16px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media (width <= 480px) {
|
||||
@@ -398,11 +398,11 @@ $accent-green: #10b981;
|
||||
|
||||
.kpi {
|
||||
background: white;
|
||||
border-radius: $radius;
|
||||
padding: 18px 20px;
|
||||
border-radius: $radius-sm;
|
||||
padding: 10px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
gap: 10px;
|
||||
box-shadow: $shadow-sm;
|
||||
border: 1px solid $border-color;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
@@ -425,18 +425,18 @@ $accent-green: #10b981;
|
||||
}
|
||||
|
||||
.kpi-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
mat-icon {
|
||||
font-size: 24px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
font-size: 18px;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,54 +445,54 @@ $accent-green: #10b981;
|
||||
}
|
||||
|
||||
.kpi-val {
|
||||
font-size: 28px;
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
color: $text-main;
|
||||
line-height: 1;
|
||||
margin-bottom: 4px;
|
||||
letter-spacing: -0.8px;
|
||||
margin-bottom: 2px;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.featured-val {
|
||||
color: #2563eb;
|
||||
font-size: 32px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.kpi-lbl {
|
||||
font-size: 11px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
color: $text-sub;
|
||||
line-height: 1.35;
|
||||
line-height: 1.3;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.kpi-sub {
|
||||
font-size: 11px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
color: #2563eb;
|
||||
margin-top: 3px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.kpi-sub-text {
|
||||
font-size: 10px;
|
||||
font-size: 9.5px;
|
||||
font-weight: 500;
|
||||
color: $text-sub;
|
||||
margin-top: 2px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
// ─── Dashboard Sections Layout ───────────────────────────────────────────────
|
||||
.dashboard-sections {
|
||||
padding: 0 32px;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 36px;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
@media (width <= 768px) {
|
||||
padding: 0 20px;
|
||||
gap: 28px;
|
||||
padding: 0 14px;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,20 +521,20 @@ $accent-green: #10b981;
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 8px;
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 2px solid #e2e8f0;
|
||||
|
||||
mat-icon {
|
||||
color: $primary;
|
||||
font-size: 26px;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
font-size: 20px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
font-size: 15px;
|
||||
font-weight: 800;
|
||||
color: $text-main;
|
||||
margin: 0;
|
||||
@@ -543,11 +543,11 @@ $accent-green: #10b981;
|
||||
|
||||
.section-badge {
|
||||
margin-left: auto;
|
||||
font-size: 11px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
color: $primary;
|
||||
background: rgb(79 70 229 / 8%);
|
||||
padding: 3px 10px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 99px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
@@ -558,7 +558,7 @@ $accent-green: #10b981;
|
||||
.a-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
gap: 12px;
|
||||
|
||||
@media (width <= 1024px) {
|
||||
grid-template-columns: 1fr;
|
||||
@@ -568,7 +568,7 @@ $accent-green: #10b981;
|
||||
.a-card {
|
||||
background: white;
|
||||
border-radius: $radius;
|
||||
padding: 24px;
|
||||
padding: 16px;
|
||||
box-shadow:
|
||||
0 4px 20px -2px rgb(0 0 0 / 3%),
|
||||
0 1px 3px rgb(0 0 0 / 2%);
|
||||
@@ -577,7 +577,7 @@ $accent-green: #10b981;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
|
||||
&:hover {
|
||||
box-shadow:
|
||||
@@ -596,14 +596,14 @@ $accent-green: #10b981;
|
||||
.ac-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.ac-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
background: var(--ac-bg, #f1f5f9);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -611,15 +611,15 @@ $accent-green: #10b981;
|
||||
flex-shrink: 0;
|
||||
|
||||
mat-icon {
|
||||
font-size: 20px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
font-size: 16px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
color: var(--ac, #64748b);
|
||||
}
|
||||
}
|
||||
|
||||
.ac-title {
|
||||
font-size: 15px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: $text-main;
|
||||
flex: 1;
|
||||
@@ -674,11 +674,11 @@ $accent-green: #10b981;
|
||||
// ─── Donut Center Overlay ────────────────────────────────────────────────────
|
||||
.donut-wrap {
|
||||
position: relative;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 20px;
|
||||
filter: drop-shadow(0 6px 12px rgb(0 0 0 / 4%));
|
||||
margin-bottom: 12px;
|
||||
filter: drop-shadow(0 4px 8px rgb(0 0 0 / 4%));
|
||||
}
|
||||
|
||||
.donut-center {
|
||||
@@ -698,7 +698,7 @@ $accent-green: #10b981;
|
||||
}
|
||||
|
||||
.dc-val {
|
||||
font-size: 24px;
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
color: $text-main;
|
||||
line-height: 1;
|
||||
@@ -719,14 +719,14 @@ $accent-green: #10b981;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.cl-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 12px;
|
||||
gap: 8px;
|
||||
padding: 5px 8px;
|
||||
background: #f8fafc;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #f1f5f9;
|
||||
@@ -946,18 +946,18 @@ $accent-green: #10b981;
|
||||
|
||||
.loan-stats {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.loan-stat {
|
||||
flex: 1;
|
||||
min-width: 180px;
|
||||
min-width: 160px;
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
padding: 10px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&.loan-stat-active {
|
||||
@@ -997,12 +997,12 @@ $accent-green: #10b981;
|
||||
}
|
||||
|
||||
.ls-num {
|
||||
font-size: 34px;
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
color: $text-main;
|
||||
line-height: 1;
|
||||
margin-bottom: 4px;
|
||||
letter-spacing: -1px;
|
||||
margin-bottom: 3px;
|
||||
letter-spacing: -0.8px;
|
||||
}
|
||||
|
||||
.ls-pct {
|
||||
@@ -1137,8 +1137,8 @@ $accent-green: #10b981;
|
||||
.influence-insight {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
margin-top: 8px;
|
||||
gap: 12px;
|
||||
margin-top: 6px;
|
||||
|
||||
@media (width <= 1100px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
@@ -1154,11 +1154,11 @@ $accent-green: #10b981;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 20px 16px;
|
||||
padding: 12px;
|
||||
background: #f8fafc;
|
||||
border-radius: 14px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&.im-highlight {
|
||||
@@ -1175,41 +1175,75 @@ $accent-green: #10b981;
|
||||
}
|
||||
|
||||
.im-icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 12px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 9px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 6px;
|
||||
margin-bottom: 4px;
|
||||
flex-shrink: 0;
|
||||
|
||||
mat-icon {
|
||||
font-size: 22px;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
font-size: 18px;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.im-val {
|
||||
font-size: 2.4rem;
|
||||
font-size: 1.8rem;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
color: $text-main;
|
||||
letter-spacing: -1.5px;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
.im-pct {
|
||||
font-size: 1rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.im-label {
|
||||
font-size: 11px;
|
||||
font-size: 10.5px;
|
||||
color: $text-sub;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
max-width: 160px;
|
||||
max-width: 140px;
|
||||
}
|
||||
|
||||
// ─── Savings Stats Grid ──────────────────────────────────────────────────────
|
||||
.sav-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.sav-stat {
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 10px 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sav-num {
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
margin-bottom: 3px;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.sav-lbl {
|
||||
font-size: 9.5px;
|
||||
color: #64748b;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -313,9 +313,9 @@ export class CandidateAnalysisComponent implements OnInit, OnDestroy {
|
||||
{
|
||||
data: data,
|
||||
backgroundColor: colors,
|
||||
borderWidth: 3,
|
||||
borderWidth: breakdown.length === 1 ? 0 : 3,
|
||||
borderColor: '#ffffff',
|
||||
borderRadius: 5, // Rounded corners on doughnut segments for clean graphic style
|
||||
borderRadius: breakdown.length === 1 ? 0 : 5,
|
||||
hoverOffset: 6
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user