DONE: settle disable vote for voter with saham under 500, fix issue on...
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
[ ] checking user can vote one or two based on number of nominees
|
[ ] checking user can vote one or two based on number of nominees
|
||||||
[x] disable auto trigger tac
|
[x] disable auto trigger tac
|
||||||
[ ] set the env back to production in prod
|
[x] set the env back to production in prod
|
||||||
|
[x] saham < rm500 cannot vote
|
||||||
|
[x] note/ rujukan cannot be fetch on modal
|
||||||
@@ -52,9 +52,21 @@ class PayoutController extends Controller
|
|||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce "must have voted" (officer check)
|
|
||||||
$hasVoted = Result::where('election_id', $electionId)->where('voter_id', $voterId)->exists();
|
$hasVoted = Result::where('election_id', $electionId)->where('voter_id', $voterId)->exists();
|
||||||
if (!$hasVoted) {
|
|
||||||
|
// Tunai: normally must have voted; exception — fizikal hadir & layak mesyuarat tetapi tidak dibenarkan
|
||||||
|
// mengundi kerana saham < RM500 (same rule as claim code).
|
||||||
|
$kehadiran = (int) $voter->kehadiran;
|
||||||
|
$saham = (float) $voter->saham;
|
||||||
|
$canVoteBySaham = $saham >= 500.0;
|
||||||
|
$canProceedToVote = $kehadiran !== 0
|
||||||
|
&& ($kehadiran !== 1 || $voter->fizikal_registration_verified_at !== null);
|
||||||
|
$eligibleCashPayoutWithoutVote = $method === 'cash'
|
||||||
|
&& $kehadiran === 1
|
||||||
|
&& $canProceedToVote
|
||||||
|
&& !$canVoteBySaham;
|
||||||
|
|
||||||
|
if (!$hasVoted && !$eligibleCashPayoutWithoutVote) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Voter has not voted yet.',
|
'message' => 'Voter has not voted yet.',
|
||||||
|
|||||||
@@ -48,9 +48,21 @@ class ClaimCodeController extends Controller
|
|||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must have voted
|
|
||||||
$hasVoted = Result::where('election_id', $electionId)->where('voter_id', $voterId)->exists();
|
$hasVoted = Result::where('election_id', $electionId)->where('voter_id', $voterId)->exists();
|
||||||
if (!$hasVoted) {
|
|
||||||
|
// Tunai: normally must have voted; exception — fizikal hadir & layak mesyuarat tetapi tidak dibenarkan
|
||||||
|
// mengundi kerana saham < RM500 (same rule as voter UI).
|
||||||
|
$kehadiran = (int) $voter->kehadiran;
|
||||||
|
$saham = (float) $voter->saham;
|
||||||
|
$canVoteBySaham = $saham >= 500.0;
|
||||||
|
$canProceedToVote = $kehadiran !== 0
|
||||||
|
&& ($kehadiran !== 1 || $voter->fizikal_registration_verified_at !== null);
|
||||||
|
$eligibleClaimCashWithoutVote = $method === 'cash'
|
||||||
|
&& $kehadiran === 1
|
||||||
|
&& $canProceedToVote
|
||||||
|
&& !$canVoteBySaham;
|
||||||
|
|
||||||
|
if (!$hasVoted && !$eligibleClaimCashWithoutVote) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Sila undi terlebih dahulu sebelum menuntut elaun.',
|
'message' => 'Sila undi terlebih dahulu sebelum menuntut elaun.',
|
||||||
|
|||||||
@@ -45,6 +45,13 @@ class GetController extends Controller
|
|||||||
->where('allowance_payouts.status', 'paid');
|
->where('allowance_payouts.status', 'paid');
|
||||||
}, 'cash_payout_id');
|
}, 'cash_payout_id');
|
||||||
|
|
||||||
|
// Latest paid cash payout — Rujukan / Nota for admin allowance modal ($electionId bound as int)
|
||||||
|
$eid = (int) $electionId;
|
||||||
|
$q->addSelect([
|
||||||
|
DB::raw("(SELECT ap.reference FROM allowance_payouts ap WHERE ap.voter_id = voter.id AND ap.election_id = {$eid} AND ap.method = 'cash' AND ap.status = 'paid' ORDER BY ap.id DESC LIMIT 1) as cash_payout_reference"),
|
||||||
|
DB::raw("(SELECT ap.note FROM allowance_payouts ap WHERE ap.voter_id = voter.id AND ap.election_id = {$eid} AND ap.method = 'cash' AND ap.status = 'paid' ORDER BY ap.id DESC LIMIT 1) as cash_payout_note"),
|
||||||
|
]);
|
||||||
|
|
||||||
$q->selectSub(function ($sq) use ($electionId) {
|
$q->selectSub(function ($sq) use ($electionId) {
|
||||||
$sq->from('allowance_payouts')
|
$sq->from('allowance_payouts')
|
||||||
->selectRaw('MAX(paid_at)')
|
->selectRaw('MAX(paid_at)')
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -163,6 +163,14 @@
|
|||||||
<span v-else class="label label-warning">Menunggu pengesahan</span>
|
<span v-else class="label label-warning">Menunggu pengesahan</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Saham (RM)</th>
|
||||||
|
<td>{{ formatRmDisplay(selectedVoter.saham) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Yuran (RM)</th>
|
||||||
|
<td>{{ formatRmDisplay(selectedVoter.yuran) }}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Status Undi</th>
|
<th>Status Undi</th>
|
||||||
<td>
|
<td>
|
||||||
@@ -170,6 +178,9 @@
|
|||||||
:class="Number(selectedVoter.votes_count) > 0 ? 'label-success' : 'label-default'">
|
:class="Number(selectedVoter.votes_count) > 0 ? 'label-success' : 'label-default'">
|
||||||
{{ Number(selectedVoter.votes_count) > 0 ? 'Ya' : 'Tidak' }}
|
{{ Number(selectedVoter.votes_count) > 0 ? 'Ya' : 'Tidak' }}
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="selectedVoterShowsNoVoteEligibleNote"
|
||||||
|
class="label label-info" style="margin-left: 6px;">Tidak layak
|
||||||
|
mengundi (saham)</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -206,14 +217,15 @@
|
|||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label>Rujukan (optional)</label>
|
<label>Rujukan (optional)</label>
|
||||||
<input type="text" class="form-control" v-model.trim="payoutReference"
|
<input type="text" class="form-control" v-model.trim="payoutReference"
|
||||||
maxlength="80" />
|
maxlength="80" placeholder="Wakil/Sendiri" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" style="margin-top: 10px;">
|
<div class="row" style="margin-top: 10px;">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<label>Nota (optional)</label>
|
<label>Nota (optional)</label>
|
||||||
<input type="text" class="form-control" v-model.trim="payoutNote" />
|
<input type="text" class="form-control" v-model.trim="payoutNote"
|
||||||
|
placeholder="Nama Wakil" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -251,7 +263,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body" v-if="verifyModalVoter">
|
<div class="modal-body" v-if="verifyModalVoter">
|
||||||
<p class="kehadiran-verify-lead">
|
<p class="kehadiran-verify-lead" v-if="verifyModalVoterNeedsLowSahamNote">
|
||||||
|
Pengundi fizikal ini mempunyai saham di bawah <strong>RM 500</strong> dan
|
||||||
|
<strong>tidak dibenarkan mengundi</strong>. Sahkan pendaftaran untuk mengesahkan
|
||||||
|
kehadiran
|
||||||
|
di kaunter dan membolehkan mereka menuntut elaun tunai (mengikut peraturan portal).
|
||||||
|
</p>
|
||||||
|
<p class="kehadiran-verify-lead" v-else>
|
||||||
Sahkan bahawa pengundi ini hadir di kaunter. Selepas ini, pengundi boleh mengundi.
|
Sahkan bahawa pengundi ini hadir di kaunter. Selepas ini, pengundi boleh mengundi.
|
||||||
</p>
|
</p>
|
||||||
<table class="table table-condensed table-bordered kehadiran-verify-summary">
|
<table class="table table-condensed table-bordered kehadiran-verify-summary">
|
||||||
@@ -272,6 +290,14 @@
|
|||||||
<th>Unit</th>
|
<th>Unit</th>
|
||||||
<td>{{ verifyModalVoter.unit }}</td>
|
<td>{{ verifyModalVoter.unit }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Saham (RM)</th>
|
||||||
|
<td>{{ formatRmDisplay(verifyModalVoter.saham) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Yuran (RM)</th>
|
||||||
|
<td>{{ formatRmDisplay(verifyModalVoter.yuran) }}</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="alert alert-danger" v-if="verifyModalError" style="margin-bottom: 0;">
|
<div class="alert alert-danger" v-if="verifyModalError" style="margin-bottom: 0;">
|
||||||
@@ -615,19 +641,53 @@ export default {
|
|||||||
canPayCash: function (voter) {
|
canPayCash: function (voter) {
|
||||||
if (!voter) return false;
|
if (!voter) return false;
|
||||||
if (this.isCashPaid(voter)) return false;
|
if (this.isCashPaid(voter)) return false;
|
||||||
// Must be fizikal, must have voted
|
if (Number(voter.kehadiran) !== 1) return false;
|
||||||
return Number(voter.kehadiran) === 1 && Number(voter.votes_count) > 0;
|
if (Number(voter.votes_count) > 0) return true;
|
||||||
|
return this.canClaimAllowanceWithoutVoteAdmin(voter);
|
||||||
},
|
},
|
||||||
|
|
||||||
openDetail: function (item) {
|
openDetail: function (item) {
|
||||||
this.detailError = '';
|
this.detailError = '';
|
||||||
this.selectedVoter = Object.assign({}, item);
|
this.selectedVoter = Object.assign({}, item);
|
||||||
this.claimCodeInput = '';
|
this.claimCodeInput = '';
|
||||||
this.payoutReference = '';
|
this.payoutReference =
|
||||||
this.payoutNote = '';
|
item.cash_payout_reference != null && item.cash_payout_reference !== undefined
|
||||||
|
? String(item.cash_payout_reference)
|
||||||
|
: '';
|
||||||
|
this.payoutNote =
|
||||||
|
item.cash_payout_note != null && item.cash_payout_note !== undefined
|
||||||
|
? String(item.cash_payout_note)
|
||||||
|
: '';
|
||||||
this.util.showModal('#allowance-modal');
|
this.util.showModal('#allowance-modal');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Saham / Yuran — same RM formatting as voter portal. */
|
||||||
|
formatRmDisplay: function (val) {
|
||||||
|
if (val === null || val === undefined || val === '') {
|
||||||
|
return '—';
|
||||||
|
}
|
||||||
|
var n = Number(val);
|
||||||
|
if (Number.isNaN(n)) {
|
||||||
|
return String(val);
|
||||||
|
}
|
||||||
|
return n.toLocaleString('en-MY', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||||
|
},
|
||||||
|
|
||||||
|
canClaimAllowanceWithoutVoteAdmin: function (voter) {
|
||||||
|
if (!voter || Number(voter.kehadiran) !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Number(voter.votes_count) > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var s = Number(voter.saham);
|
||||||
|
var canVoteBySaham = !Number.isNaN(s) && s >= 500;
|
||||||
|
var canProceed =
|
||||||
|
Number(voter.kehadiran) !== 0 &&
|
||||||
|
(Number(voter.kehadiran) !== 1 || voter.fizikal_registration_verified_at);
|
||||||
|
return canProceed && !canVoteBySaham;
|
||||||
|
},
|
||||||
|
|
||||||
payCash: function () {
|
payCash: function () {
|
||||||
if (!this.selectedVoter) return;
|
if (!this.selectedVoter) return;
|
||||||
this.detailError = '';
|
this.detailError = '';
|
||||||
@@ -660,8 +720,17 @@ export default {
|
|||||||
this.refreshVoter();
|
this.refreshVoter();
|
||||||
// keep modal open but reflect paid state (best effort)
|
// keep modal open but reflect paid state (best effort)
|
||||||
if (response && response.data && response.data.payout) {
|
if (response && response.data && response.data.payout) {
|
||||||
this.selectedVoter.cash_paid_at = response.data.payout.paid_at;
|
var p = response.data.payout;
|
||||||
this.selectedVoter.cash_payout_id = response.data.payout.id;
|
this.selectedVoter.cash_paid_at = p.paid_at;
|
||||||
|
this.selectedVoter.cash_payout_id = p.id;
|
||||||
|
if (p.reference != null) {
|
||||||
|
this.selectedVoter.cash_payout_reference = p.reference;
|
||||||
|
this.payoutReference = p.reference ? String(p.reference) : '';
|
||||||
|
}
|
||||||
|
if (p.note != null) {
|
||||||
|
this.selectedVoter.cash_payout_note = p.note;
|
||||||
|
this.payoutNote = p.note ? String(p.note) : '';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.selectedVoter.cash_paid_at = (new Date()).toISOString();
|
this.selectedVoter.cash_paid_at = (new Date()).toISOString();
|
||||||
}
|
}
|
||||||
@@ -703,6 +772,10 @@ export default {
|
|||||||
this.refreshVoter();
|
this.refreshVoter();
|
||||||
this.selectedVoter.cash_paid_at = null;
|
this.selectedVoter.cash_paid_at = null;
|
||||||
this.selectedVoter.cash_payout_id = null;
|
this.selectedVoter.cash_payout_id = null;
|
||||||
|
this.selectedVoter.cash_payout_reference = null;
|
||||||
|
this.selectedVoter.cash_payout_note = null;
|
||||||
|
this.payoutReference = '';
|
||||||
|
this.payoutNote = '';
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -787,6 +860,19 @@ export default {
|
|||||||
return this.$route.query.fizikal_reg ? this.$route.query.fizikal_reg : null;
|
return this.$route.query.fizikal_reg ? this.$route.query.fizikal_reg : null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
verifyModalVoterNeedsLowSahamNote: function () {
|
||||||
|
if (!this.verifyModalVoter) return false;
|
||||||
|
var v = this.verifyModalVoter;
|
||||||
|
if (Number(v.kehadiran) !== 1) return false;
|
||||||
|
var s = Number(v.saham);
|
||||||
|
return !Number.isNaN(s) && s < 500;
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedVoterShowsNoVoteEligibleNote: function () {
|
||||||
|
if (!this.selectedVoter) return false;
|
||||||
|
return this.canClaimAllowanceWithoutVoteAdmin(this.selectedVoter);
|
||||||
|
},
|
||||||
|
|
||||||
// Calculate total attendance percentage
|
// Calculate total attendance percentage
|
||||||
attendancePercentage() {
|
attendancePercentage() {
|
||||||
// Count total members who attended physically (Fizikal)
|
// Count total members who attended physically (Fizikal)
|
||||||
|
|||||||
@@ -96,9 +96,19 @@
|
|||||||
{{ data.user.kehadiran == 1 ? 'FIZIKAL' : 'MAYA' }}
|
{{ data.user.kehadiran == 1 ? 'FIZIKAL' : 'MAYA' }}
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
<router-link :to="{ name: 'Vote' }" class="btn btn-warning attendance-vote-button">
|
<template v-if="canVoteBySaham">
|
||||||
Undi Sekarang
|
<router-link :to="{ name: 'Vote' }" class="btn btn-warning attendance-vote-button">
|
||||||
</router-link>
|
Undi Sekarang
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="alert alert-warning attendance-saham-block-alert" role="alert">
|
||||||
|
{{ sahamVoteBlockedMessage }}
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-warning attendance-vote-button" disabled>
|
||||||
|
Undi Sekarang
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="data.user.kehadiran != 0 && !canProceedToVote" class="attendance-pending-card">
|
<div v-else-if="data.user.kehadiran != 0 && !canProceedToVote" class="attendance-pending-card">
|
||||||
@@ -291,6 +301,15 @@
|
|||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.attendance-saham-block-alert {
|
||||||
|
margin: 0 auto 16px;
|
||||||
|
max-width: 520px;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
.attendance-pending-card {
|
.attendance-pending-card {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
@@ -410,6 +429,16 @@ export default {
|
|||||||
if (!u || Number(u.kehadiran) === 0) return false;
|
if (!u || Number(u.kehadiran) === 0) return false;
|
||||||
if (Number(u.kehadiran) !== 1) return true;
|
if (Number(u.kehadiran) !== 1) return true;
|
||||||
return Boolean(u.fizikal_registration_verified_at);
|
return Boolean(u.fizikal_registration_verified_at);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Saham (RM) must be at least 500 to vote — same rule as home / vote pages. */
|
||||||
|
canVoteBySaham: function () {
|
||||||
|
var s = Number(this.data && this.data.user && this.data.user.saham);
|
||||||
|
return !Number.isNaN(s) && s >= 500;
|
||||||
|
},
|
||||||
|
|
||||||
|
sahamVoteBlockedMessage: function () {
|
||||||
|
return 'Undian tidak tersedia: jumlah saham anda mestilah sekurang-kurangnya RM 500.';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -460,9 +489,10 @@ export default {
|
|||||||
vm.loading = false;
|
vm.loading = false;
|
||||||
|
|
||||||
if (vm.util.showResult(response, 'success')) {
|
if (vm.util.showResult(response, 'success')) {
|
||||||
// ✅ Redirect to vote page
|
|
||||||
location.reload();
|
location.reload();
|
||||||
vm.$router.push({ name: 'Vote' }); // OR use path: vm.$router.push('/vote');
|
if (vm.canVoteBySaham) {
|
||||||
|
vm.$router.push({ name: 'Vote' });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
|||||||
@@ -63,6 +63,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showSahamVoteBlockedMessage" class="row home-saham-block-row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="alert alert-warning home-saham-block-alert" role="alert">
|
||||||
|
<span class="home-saham-block-icon" aria-hidden="true"><i class="fa fa-exclamation-triangle"></i></span>
|
||||||
|
<span>{{ sahamVoteBlockedMessage }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row home-content-row">
|
<div class="row home-content-row">
|
||||||
<div class="col-xs-12 col-md-6 home-column home-column--primary">
|
<div class="col-xs-12 col-md-6 home-column home-column--primary">
|
||||||
<div class="home-section-container">
|
<div class="home-section-container">
|
||||||
@@ -198,6 +207,26 @@ export default {
|
|||||||
return Boolean(u.fizikal_registration_verified_at);
|
return Boolean(u.fizikal_registration_verified_at);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Saham (RM) must be at least 500 to vote — see Voter model `saham`. */
|
||||||
|
canVoteBySaham() {
|
||||||
|
const s = Number(this.data?.user?.saham);
|
||||||
|
return !Number.isNaN(s) && s >= 500;
|
||||||
|
},
|
||||||
|
|
||||||
|
showSahamVoteBlockedMessage() {
|
||||||
|
const election = this.data?.election || {};
|
||||||
|
return (
|
||||||
|
Number(election.status) === 2 &&
|
||||||
|
!this.hasVoted() &&
|
||||||
|
this.canProceedToVote &&
|
||||||
|
!this.canVoteBySaham
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
sahamVoteBlockedMessage() {
|
||||||
|
return "Undian tidak tersedia: jumlah saham anda mestilah sekurang-kurangnya RM 500.";
|
||||||
|
},
|
||||||
|
|
||||||
quickLinks() {
|
quickLinks() {
|
||||||
const user = this.data?.user || {};
|
const user = this.data?.user || {};
|
||||||
const election = this.data?.election || {};
|
const election = this.data?.election || {};
|
||||||
@@ -235,7 +264,8 @@ export default {
|
|||||||
if (
|
if (
|
||||||
Number(election.status) === 2 &&
|
Number(election.status) === 2 &&
|
||||||
!this.hasVoted() &&
|
!this.hasVoted() &&
|
||||||
this.canProceedToVote
|
this.canProceedToVote &&
|
||||||
|
this.canVoteBySaham
|
||||||
) {
|
) {
|
||||||
links.push({
|
links.push({
|
||||||
key: "vote",
|
key: "vote",
|
||||||
@@ -273,8 +303,14 @@ export default {
|
|||||||
return links;
|
return links;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tunai: kehadiran Fizikal, dan sama ada sudah mengundi atau tidak layak mengundi
|
||||||
|
* (saham < RM500) tetapi kehadiran/tetapan mesyuarat sudah memenuhi syarat — masih boleh tuntut elaun.
|
||||||
|
*/
|
||||||
canGenerateClaimCode() {
|
canGenerateClaimCode() {
|
||||||
return Number(this.data?.user?.kehadiran) === 1 && this.hasVoted();
|
if (Number(this.data?.user?.kehadiran) !== 1) return false;
|
||||||
|
if (this.hasVoted()) return true;
|
||||||
|
return this.canProceedToVote && !this.canVoteBySaham;
|
||||||
},
|
},
|
||||||
|
|
||||||
kehadiranLabel() {
|
kehadiranLabel() {
|
||||||
@@ -602,6 +638,28 @@ export default {
|
|||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home-saham-block-row {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-saham-block-alert {
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-radius: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-saham-block-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.home-quick-links-grid {
|
.home-quick-links-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vote-page row col-md-10 col-md-offset-1">
|
<div class="vote-page row col-md-10 col-md-offset-1">
|
||||||
|
<div v-if="!canVoteBySaham" class="col-xs-12 vote-saham-block-banner">
|
||||||
|
<div class="alert alert-danger vote-saham-block-alert" role="alert">
|
||||||
|
<i class="fa fa-ban" aria-hidden="true"></i>
|
||||||
|
<span>{{ sahamVoteBlockedMessage }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="col-md-4 vote-sidebar-column">
|
<div class="col-md-4 vote-sidebar-column">
|
||||||
<div class="vote-summary-card">
|
<div class="vote-summary-card">
|
||||||
<div class="vote-section-kicker">Ringkasan Undian</div>
|
<div class="vote-section-kicker">Ringkasan Undian</div>
|
||||||
@@ -24,7 +30,8 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="list-group-item vote-summary-action">
|
<li class="list-group-item vote-summary-action">
|
||||||
<button class="btn btn-success btn-block vote-submit-button" @click="checkUndi()">UNDI</button>
|
<button class="btn btn-success btn-block vote-submit-button" :disabled="!canVoteBySaham"
|
||||||
|
@click="checkUndi()">UNDI</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,7 +105,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer vote-modal-footer">
|
<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-secondary" data-dismiss="modal">Tutup</button>
|
||||||
<button type="button" class="btn btn-primary" @click="submit()">Hantar</button>
|
<button type="button" class="btn btn-primary" :disabled="!canVoteBySaham" @click="submit()">Hantar</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -107,8 +114,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
const SAHAM_MINIMUM_UNDIAN = 500;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
created: function () {
|
created: function () {
|
||||||
|
const saham = Number(this.data?.user?.saham);
|
||||||
|
if (Number.isNaN(saham) || saham < SAHAM_MINIMUM_UNDIAN) {
|
||||||
|
this.util.notify(this.sahamVoteBlockedMessage, 'error');
|
||||||
|
this.$router.push({ name: 'Voter Home' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.data.result.length > 0 || this.data.election.status != 2)
|
if (this.data.result.length > 0 || this.data.election.status != 2)
|
||||||
this.$router.push({ name: 'Voter Home' })
|
this.$router.push({ name: 'Voter Home' })
|
||||||
for (var i in this.data.positions) {
|
for (var i in this.data.positions) {
|
||||||
@@ -130,6 +145,15 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
canVoteBySaham() {
|
||||||
|
const s = Number(this.data?.user?.saham);
|
||||||
|
return !Number.isNaN(s) && s >= SAHAM_MINIMUM_UNDIAN;
|
||||||
|
},
|
||||||
|
|
||||||
|
sahamVoteBlockedMessage() {
|
||||||
|
return 'Undian tidak tersedia: jumlah saham anda mestilah sekurang-kurangnya RM 500.';
|
||||||
|
},
|
||||||
|
|
||||||
/** All nominee ids chosen across every position (sidebar + modal must use this, not selected[0]). */
|
/** All nominee ids chosen across every position (sidebar + modal must use this, not selected[0]). */
|
||||||
allSelectedNomineeIds() {
|
allSelectedNomineeIds() {
|
||||||
var ids = [];
|
var ids = [];
|
||||||
@@ -143,6 +167,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
disabledCheckboxes() {
|
disabledCheckboxes() {
|
||||||
|
if (!this.canVoteBySaham) {
|
||||||
|
return this.data.nominees.map(() => true);
|
||||||
|
}
|
||||||
const selectedNomineeIds = this.allSelectedNomineeIds;
|
const selectedNomineeIds = this.allSelectedNomineeIds;
|
||||||
return this.data.nominees.map(nominee => {
|
return this.data.nominees.map(nominee => {
|
||||||
return selectedNomineeIds.includes(nominee.id) ? false : this.checkDisabled();
|
return selectedNomineeIds.includes(nominee.id) ? false : this.checkDisabled();
|
||||||
@@ -167,6 +194,10 @@ export default {
|
|||||||
this.util.showModal('#vote-modal')
|
this.util.showModal('#vote-modal')
|
||||||
},
|
},
|
||||||
submit: function () {
|
submit: function () {
|
||||||
|
if (!this.canVoteBySaham) {
|
||||||
|
this.util.notify(this.sahamVoteBlockedMessage, 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.totalSelectedCount() !== this.max) {
|
if (this.totalSelectedCount() !== this.max) {
|
||||||
// No selections made
|
// No selections made
|
||||||
this.util.notify(`Sila pastikan anda memilih (${this.max}) orang calon.`, 'error');
|
this.util.notify(`Sila pastikan anda memilih (${this.max}) orang calon.`, 'error');
|
||||||
@@ -288,6 +319,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
checkUndi: function () {
|
checkUndi: function () {
|
||||||
|
if (!this.canVoteBySaham) {
|
||||||
|
this.util.notify(this.sahamVoteBlockedMessage, 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.totalSelectedCount() <= 0) {
|
if (this.totalSelectedCount() <= 0) {
|
||||||
this.util.notify('Sila pastikan anda memilih (1) orang calon.', 'error');
|
this.util.notify('Sila pastikan anda memilih (1) orang calon.', 'error');
|
||||||
} else {
|
} else {
|
||||||
@@ -315,6 +350,25 @@ export default {
|
|||||||
margin-bottom: 36px;
|
margin-bottom: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vote-saham-block-banner {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-saham-block-alert {
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-radius: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-saham-block-alert .fa {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.vote-sidebar-column,
|
.vote-sidebar-column,
|
||||||
.vote-content-column {
|
.vote-content-column {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
|||||||
Reference in New Issue
Block a user