feat(election,family): family member KYC display, collapsible cards, linked/manual badges, create-as-client flow
- nomination-apply: show full KYC for linked family members via nested forkJoin, accordion expand/collapse cards, owner chips on KYC headers, 'Create as Client' button for manual entries - family-members-tab: show Linked Client / Manual Entry badges, entry type in detail grid, 'Create as Client' button navigating with prefill state - create-client + client-general-step: accept prefillData @Input from navigation state, patch form fields from family member data - Add MPL-2.0 headers to all election module files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
822fa4c68d
commit
10b301c30c
@@ -0,0 +1,130 @@
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<div class="election-dashboard">
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">Election Management</h2>
|
||||
<a routerLink="/election/config/new" class="btn-primary"> <mat-icon>add</mat-icon> Create Election </a>
|
||||
</div>
|
||||
|
||||
@if (loading) {
|
||||
<div class="loading-state">
|
||||
<div class="spinner"></div>
|
||||
<p>Loading elections...</p>
|
||||
</div>
|
||||
} @else if (elections.length === 0) {
|
||||
<div class="empty-state">
|
||||
<mat-icon class="empty-icon">how_to_vote</mat-icon>
|
||||
<h3>No elections found</h3>
|
||||
<p>Create a new election to get started.</p>
|
||||
<a routerLink="/election/config/new" class="btn-primary">Create Election</a>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="stats-row">
|
||||
<div class="stat-card">
|
||||
<span class="stat-value">{{ elections.length }}</span>
|
||||
<span class="stat-label">Total Elections</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<span class="stat-value">{{ countByStatus('ACTIVE') }}</span>
|
||||
<span class="stat-label">Active</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<span class="stat-value">{{ countByStatus('VOTING_OPEN') }}</span>
|
||||
<span class="stat-label">Voting Open</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<span class="stat-value">{{ countByStatus('RESULT_DECLARED') }}</span>
|
||||
<span class="stat-label">Completed</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="elections-grid">
|
||||
@for (election of elections; track election.id) {
|
||||
<div class="election-card">
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="election-title">{{ election.title }}</h3>
|
||||
<span class="election-type">{{ election.electionType }}</span>
|
||||
</div>
|
||||
<span class="status-chip" [ngClass]="statusClass(election.status)">
|
||||
{{ statusLabel(election.status) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="date-row">
|
||||
<div class="date-item">
|
||||
<mat-icon>event_note</mat-icon>
|
||||
<div>
|
||||
<span class="date-label">Nominations</span>
|
||||
<span class="date-value">
|
||||
{{ election.nominationStartDate | date: 'dd MMM' }} –
|
||||
{{ election.nominationEndDate | date: 'dd MMM yyyy' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="date-item">
|
||||
<mat-icon>how_to_vote</mat-icon>
|
||||
<div>
|
||||
<span class="date-label">Election Date</span>
|
||||
<span class="date-value">{{ election.electionDate | date: 'dd MMM yyyy' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (election.description) {
|
||||
<p class="description">{{ election.description }}</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="card-actions">
|
||||
<a [routerLink]="['/election', election.id, 'nominations']" class="btn-outline">
|
||||
<mat-icon>people</mat-icon> Nominations
|
||||
</a>
|
||||
<a [routerLink]="['/election', election.id, 'candidates']" class="btn-outline">
|
||||
<mat-icon>person_pin</mat-icon> Candidates
|
||||
</a>
|
||||
|
||||
@if (election.status === 'DRAFT') {
|
||||
<button class="btn-activate" [disabled]="actionLoading[election.id]" (click)="activate(election)">
|
||||
<mat-icon>play_circle</mat-icon>
|
||||
{{ actionLoading[election.id] ? 'Activating...' : 'Activate' }}
|
||||
</button>
|
||||
}
|
||||
@if (canNominate(election)) {
|
||||
<a [routerLink]="['/election', election.id, 'nomination', 'apply']" class="btn-primary">
|
||||
<mat-icon>person_add</mat-icon> Nominate Member
|
||||
</a>
|
||||
}
|
||||
@if (election.status === 'ACTIVE') {
|
||||
<button class="btn-warn" [disabled]="actionLoading[election.id]" (click)="closeNominations(election)">
|
||||
<mat-icon>lock</mat-icon> Close Nominations
|
||||
</button>
|
||||
}
|
||||
@if (election.status === 'NOMINATION_CLOSED') {
|
||||
<button class="btn-activate" [disabled]="actionLoading[election.id]" (click)="openVoting(election)">
|
||||
<mat-icon>how_to_vote</mat-icon> Open Voting
|
||||
</button>
|
||||
}
|
||||
@if (canVote(election)) {
|
||||
<a [routerLink]="['/election', election.id, 'vote']" class="btn-vote">
|
||||
<mat-icon>ballot</mat-icon> Vote Now
|
||||
</a>
|
||||
}
|
||||
@if (canViewResults(election)) {
|
||||
<a [routerLink]="['/election', election.id, 'results']" class="btn-results">
|
||||
<mat-icon>bar_chart</mat-icon> Results
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
Reference in New Issue
Block a user