feat(election): candidate photos/symbols, voting interface, withdrawal, live vote counts

- Candidate board: show client profile photos and election symbols via CBS ContentStore
  (lazy-fetch base64 via GET /election/{id}/candidate/{id}/symbol), zoom/drag viewer
- Candidate board: live vote count badge per candidate (VOTING_OPEN/CLOSED only)
- Election dashboard: total voters/votes chips on VOTING_OPEN election cards
- Voting interface: fix candidates not loading (paged response .content extraction),
  lazy-load candidate photos and symbols, gold-bordered symbol on ballot card
- Nomination withdrawal: implement withdraw button in nomination-detail and nomination-list
- ImagePreviewDialogComponent: DomSanitizer support for data: URLs
- election.service: uploadCandidateSymbol (FormData), getCandidateSymbol, getClientImage,
  getVoteSummary methods
- main.scss: ipd-dialog / ipd-backdrop styles for image preview dialog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-06-26 18:38:11 +05:30
co-authored by Claude Sonnet 4.6
parent 9e33392b01
commit f1b829a100
19 changed files with 1217 additions and 182 deletions
@@ -28,7 +28,7 @@
<div class="stats-row">
<div class="stat-card">
<span class="stat-value">{{ totalElements }}</span>
<span class="stat-label">Total Elections</span>
<span class="stat-label">Total</span>
</div>
<div class="stat-card">
<span class="stat-value">{{ countByStatus('ACTIVE') }}</span>
@@ -81,6 +81,16 @@
@if (election.description) {
<p class="description">{{ election.description }}</p>
}
@if (voteSummaryMap[election.id]) {
<div class="voter-count-row">
<span class="voter-count-chip">
<mat-icon>how_to_vote</mat-icon>
{{ voteSummaryMap[election.id].totalVoters }} members voted
</span>
<span class="vote-total-chip"> {{ voteSummaryMap[election.id].totalVotes }} total votes </span>
</div>
}
</div>
<div class="card-actions">