DONE: remove verified by ajk, fix bug on pagination, improve UI

This commit is contained in:
ISMAIL MASSERAN
2026-04-27 12:06:54 +08:00
parent bd2306b073
commit 4425fa6176
20 changed files with 77081 additions and 535 deletions
@@ -6,36 +6,6 @@
<span>{{ sahamVoteBlockedMessage }}</span>
</div>
</div>
<div class="col-md-4 vote-sidebar-column">
<div class="vote-summary-card">
<div class="vote-section-kicker">Ringkasan Undian</div>
<h4 class="vote-page-title">UNDIAN CALON ANGGOTA LEMBAGA</h4>
<p class="vote-page-subtitle">Semak calon yang telah dipilih sebelum menghantar undi anda.</p>
<div class="vote-limit-pill">
Anda hanya boleh memilih <b>satu (1) orang calon</b> sahaja.
</div>
<ul class="list-group vote-summary-list">
<li class="list-group-item vote-summary-item">
<div class="vote-position-name">Calon Dipilih</div>
<div v-if="allSelectedNomineeIds.length" class="vote-selected-list">
<div class="vote-selected-item" v-for="nominee_id in allSelectedNomineeIds" :key="nominee_id">
<span class="vote-selected-badge">#{{ getNomineeNo(nominee_id) }}</span>
<span>{{ getSelectedNomineeName(nominee_id) }}</span>
</div>
</div>
<div v-else class="vote-empty-selection">
Belum ada calon dipilih.
</div>
</li>
<li class="list-group-item vote-summary-action">
<button class="btn btn-success btn-block vote-submit-button" :disabled="!canVoteBySaham"
@click="checkUndi()">UNDI</button>
</li>
</ul>
</div>
</div>
<div class="col-md-8 vote-content-column">
<div class="vote-content-header">
<div>
@@ -65,11 +35,12 @@
<tbody>
<tr v-for="(nominee, index) in data.nominees" v-if="nominee.position_id == position.id"
:key="nominee.id" class="vote-row">
<td class="vote-checkbox-cell"><input type="checkbox" :id="nominee.id" :disabled="disabledCheckboxes[index]"
@change="vote(position.id, nominee.id)" />
<td class="vote-checkbox-cell"><input type="checkbox" :id="nominee.id"
:disabled="disabledCheckboxes[index]" @change="vote(position.id, nominee.id)" />
</td>
<td>{{ nominee.no_calon }}</td>
<td><img @click="view(i)" :src="createBase64ImageUrl(nominee.photo)" class="thumbnail vote-nominee-photo" />
<td><img @click="view(i)" :src="createBase64ImageUrl(nominee.photo)"
class="thumbnail vote-nominee-photo" />
</td>
<td>{{ nominee.name }}</td>
<td>{{ nominee.unit }}</td>
@@ -80,8 +51,54 @@
</div>
</div>
<div class="modal fade app-modal vote-confirm-modal" id="vote-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="col-xs-12 vote-summary-bottom">
<div class="vote-summary-card">
<div class="vote-section-kicker">Ringkasan Undian</div>
<h4 class="vote-page-title">UNDIAN CALON ANGGOTA LEMBAGA</h4>
<p class="vote-page-subtitle">Semak calon yang telah dipilih sebelum menghantar undi anda.</p>
<div class="vote-limit-pill">
Anda hanya boleh memilih <b>satu (1) orang calon</b> sahaja.
</div>
<ul class="list-group vote-summary-list">
<li class="list-group-item vote-summary-item">
<div class="vote-position-name">Calon Dipilih</div>
<div v-if="allSelectedNomineeIds.length" class="vote-selected-list">
<div class="vote-selected-item" v-for="nominee_id in allSelectedNomineeIds"
:key="nominee_id">
<span class="vote-selected-badge">#{{ getNomineeNo(nominee_id) }}</span>
<span>{{ getSelectedNomineeName(nominee_id) }}</span>
</div>
</div>
<div v-else class="vote-empty-selection">
Belum ada calon dipilih.
</div>
</li>
<li class="list-group-item vote-summary-action">
<button class="btn btn-success btn-block vote-submit-button" :disabled="!canVoteBySaham"
@click="checkUndi()">UNDI</button>
</li>
</ul>
</div>
</div>
<div class="vote-mobile-actionbar" v-if="canVoteBySaham">
<div class="vote-mobile-actionbar__inner">
<div class="vote-mobile-actionbar__meta">
<div class="vote-mobile-actionbar__title">Undi</div>
<div class="vote-mobile-actionbar__hint">
Dipilih: <b>{{ allSelectedNomineeIds.length }}</b> / {{ max }}
</div>
</div>
<button class="btn btn-success vote-mobile-actionbar__btn" @click="checkUndi()">
UNDI
</button>
</div>
</div>
<div class="modal fade app-modal vote-confirm-modal" id="vote-modal" tabindex="-1" role="dialog"
aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content vote-modal-content">
<div class="modal-header vote-modal-header">
@@ -97,7 +114,8 @@
<h5 class="vote-modal-note">Nota: Sila pastikan anda telah membuat undian dengan betul.</h5>
<div class="vote-modal-selection" v-if="allSelectedNomineeIds.length">
<div class="vote-modal-selection-title">Calon dipilih</div>
<div class="vote-selected-item" v-for="nominee_id in allSelectedNomineeIds" :key="nominee_id">
<div class="vote-selected-item" v-for="nominee_id in allSelectedNomineeIds"
:key="nominee_id">
<span class="vote-selected-badge">#{{ getNomineeNo(nominee_id) }}</span>
<span>{{ getSelectedNomineeName(nominee_id) }}</span>
</div>
@@ -105,7 +123,8 @@
</div>
<div class="modal-footer vote-modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Tutup</button>
<button type="button" class="btn btn-primary" :disabled="!canVoteBySaham" @click="submit()">Hantar</button>
<button type="button" class="btn btn-primary" :disabled="!canVoteBySaham"
@click="submit()">Hantar</button>
</div>
</div>
</div>
@@ -371,17 +390,17 @@ export default {
.vote-sidebar-column,
.vote-content-column {
margin-bottom: 24px;
margin-bottom: 6px;
}
.vote-summary-card {
padding: 22px 20px;
border-radius: 20px;
background: linear-gradient(180deg, #ffffff 0%, #f7fbff 100%);
border: 1px solid #e2ebf5;
box-shadow: 0 16px 34px rgba(35, 64, 97, 0.08);
position: sticky;
top: 120px;
position: static;
}
.vote-section-kicker {
@@ -652,9 +671,56 @@ export default {
gap: 8px;
}
.vote-table thead th:nth-child(5),
.vote-table tbody td:nth-child(5) {
/* Keep Unit visible on mobile; hide photo column instead */
.vote-table thead th:nth-child(3),
.vote-table tbody td:nth-child(3) {
display: none;
}
.vote-content-column {
padding-bottom: 86px;
/* room for sticky action bar */
}
.vote-mobile-actionbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 1040;
/* below modal (1050), above page */
padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
background: rgba(255, 255, 255, 0.92);
backdrop-filter: blur(10px);
border-top: 1px solid #e5edf7;
box-shadow: 0 -10px 24px rgba(35, 64, 97, 0.12);
}
.vote-mobile-actionbar__inner {
max-width: 960px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.vote-mobile-actionbar__title {
font-weight: 800;
color: #17324d;
}
.vote-mobile-actionbar__hint {
color: #5f7388;
font-size: 12px;
}
.vote-mobile-actionbar__btn {
min-width: 140px;
border-radius: 999px;
font-weight: 800;
letter-spacing: 0.04em;
box-shadow: 0 12px 24px rgba(40, 167, 69, 0.24);
}
}
</style>