Files
E-Vote/resources/assets/js/components/demo/admin/home/index.vue
T

460 lines
13 KiB
Vue

<template>
<div class="panel panel-default">
<div class="panel-body">
<ul class="nav nav-tabs election-tabs">
<li :class="{ active: activeTab === 'current' }">
<a href="#" @click.prevent="activeTab = 'current'">Terkini</a>
</li>
<li :class="{ active: activeTab === 'history' }">
<a href="#" @click.prevent="activeTab = 'history'">Sejarah Undian</a>
</li>
</ul>
<div class="tab-content election-tab-content">
<div class="tab-pane" :class="{ active: activeTab === 'current' }">
<div class="election-header">
<div class="election-header__title">
<div class="election-header__kicker">Undian Terkini</div>
<div class="election-header__name">{{ electionLatestHeading }}</div>
</div>
<div class="election-header__meta">
<span class="election-status" :class="electionStatusClass">{{ electionStatusLabel }}</span>
<label v-if="data.election.status == 2" class="election-label">
Undian bermula pada {{ data.election.start }}
</label>
<div class="election-header__meta-actions">
<button class="btn btn-success btn-xs"
v-if="Number(data.election.status) === 1 && data.user.role == 1"
@click="openStartElectionModal">
Mula Undian
</button>
</div>
</div>
</div>
<div class="election-actions form-group">
<button class="btn btn-primary" v-if="Number(data.election.status) === 3 && data.user.role == 1"
@click="util.showModal('#register-next-session-modal')">Daftar Sesi Mengundi Baru</button>
<router-link :to="{ name: 'Current Result' }" class="btn btn-info"
v-if="data.election.status == 2">
Keputusan Semasa
</router-link>
<button class="btn btn-danger" v-if="data.election.status == 2"
@click="util.showModal('#stop-election-modal')">Tamat Undian</button>
</div>
<div class="alert alert-info election-hint" v-if="Number(data.user.role) === 1">
<b>Makluman:</b> Sila semak &amp; kemaskini <b>Maklumat Calon</b> dan <b>Anggota Koperasi</b>
(butiran
pengundi) untuk sesi ini sebelum memulakan undian.
</div>
</div>
<div class="tab-pane" :class="{ active: activeTab === 'history' }">
<div class="election-history">
<div class="election-history__title">Sejarah Undian</div>
<admin-data-table :headers="electionTableHeaders" :items="data.elections"
:loading="electionLoading" :items-per-page="10" :show-pagination="true"
empty-text="Tiada undian" :exportable="true" export-file-name="undian">
<template v-slot:item-actions="{ item }">
<router-link :to="{ name: 'Election Result', params: { election_id: item.id } }"
class="btn btn-info">
Ringkasan Keputusan
</router-link>
<a class="btn btn-warning" :href="getPDFurl(item.id)">Muat Turun PDF</a>
</template>
</admin-data-table>
</div>
</div>
</div>
<form @submit.prevent="start">
<div class="modal fade" id="start-election-modal" tabindex="-1" role="dialog"
aria-labelledby="start-election-modal" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Mulakan Undian</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="start-election-name">Nama Undian</label>
<input id="start-election-name" v-model.trim="startForm.name" type="text"
name="name" autocomplete="off" class="form-control" placeholder="(Pilihan)"
readonly tabindex="-1" />
</div>
<div class="form-group">
<label for="start-election-password">Kata Laluan</label>
<input id="start-election-password" v-model="startForm.password" type="password"
name="password" class="form-control" required autocomplete="current-password"
:disabled="startSubmitting" />
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-info" :disabled="startSubmitting">
{{ startSubmitting ? 'Memproses…' : 'Mula' }}
</button>
<input type="button" data-dismiss="modal" class="btn btn-default" value="Batal" />
</div>
</div>
</div>
</div>
</form>
<form @submit.prevent="registerNextSession" id="register_next_form">
<div class="modal fade" id="register-next-session-modal" tabindex="-1" role="dialog"
aria-labelledby="register-next-session-modal" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="registerNextModalLabel">Daftar Sesi Mengundi Baru</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p class="text-muted">Sesi baharu akan dibuka untuk pusingan undian seterusnya. Undian
yang
telah tamat kekal dalam rekod.</p>
<div class="form-group">
<label for="register-next-name">Nama Undian</label>
<input id="register-next-name" type="text" name="name" autocomplete="on"
class="form-control" />
</div>
<div class="form-group">
<label for="register-next-password">Kata Laluan</label>
<input id="register-next-password" type="password" name="password"
class="form-control" required autocomplete="current-password" />
</div>
</div>
<div class="modal-footer">
<input type="submit" class="btn btn-primary" value="Daftar" />
<input type="button" data-dismiss="modal" class="btn btn-default" value="Batal" />
</div>
</div>
</div>
</div>
</form>
<form @submit.prevent="stop" id="stop_form">
<div class="modal fade" id="stop-election-modal" tabindex="-1" role="dialog"
aria-labelledby="stop-election-modal" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Tamat Undian</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="name">Nama Undian</label>
<input type="text" name="name" :value="data.election.name" autocomplete="on"
class="form-control" placeholder="(Optional)" readonly tabindex="-1" />
</div>
<div class="form-group">
<label for="password">Kata Laluan</label>
<input type="password" name="password" autocomplete="on" class="form-control"
required />
</div>
</div>
<div class="modal-footer">
<input type="submit" class="btn btn-info" value="Tamat Undian" />
<input type="button" data-dismiss="modal" class="btn btn-default" value="Batal" />
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</template>
<style>
.election-label {
display: inline-block;
margin-left: 10px;
font-size: 13px;
color: #ffffff;
background-color: #0a4c9c;
padding: 4px 8px;
border-radius: 4px;
}
.election-tabs {
margin: 0 0 12px 0;
}
.election-tab-content>.tab-pane {
display: none;
}
.election-tab-content>.tab-pane.active {
display: block;
}
.election-header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
padding: 14px 15px;
border-bottom: 1px solid #eee;
}
.election-header__kicker {
font-size: 12px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: #777;
margin-bottom: 4px;
}
.election-header__name {
font-size: 18px;
font-weight: 700;
line-height: 1.2;
color: #222;
}
.election-status {
display: inline-flex;
align-items: center;
padding: 4px 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
border: 1px solid transparent;
white-space: nowrap;
}
.election-status--draft {
background: #f5f5f5;
color: #555;
border-color: #e6e6e6;
}
.election-status--active {
background: #e8f5e9;
color: #2e7d32;
border-color: #c8e6c9;
}
.election-status--done {
background: #ffebee;
color: #c62828;
border-color: #ffcdd2;
}
.election-header__meta {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 8px;
}
.election-header__meta-actions .btn {
padding: 4px 10px;
font-weight: 700;
}
.election-actions {
margin-top: 12px;
}
.election-hint {
margin-top: 10px;
margin-bottom: 12px;
}
.election-history {
margin-top: 4px;
padding-top: 12px;
border-top: 1px solid #eee;
}
.election-history__title {
font-size: 13px;
font-weight: 800;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #666;
margin: 0 0 10px 0;
}
.center-header {
text-align: center;
}
/* Ensure Bootstrap modals appear above the admin floating header (z-index: 2000). */
.modal {
z-index: 2200;
}
.modal-backdrop {
z-index: 2190 !important;
}
</style>
<script>
export default {
data: () => ({
activeTab: 'current',
electionLoading: false,
startSubmitting: false,
startForm: {
name: '',
password: ''
},
electionTableHeaders: [
{ title: 'Nama Undian', key: 'name', sortable: true },
{ title: 'Undian Mula', key: 'start', sortable: true },
{ title: 'Undian Tamat', key: 'end', sortable: true },
{ title: 'Tindakan', key: 'actions', sortable: false }
]
}),
created: function () {
this.refreshElection();
},
computed: {
electionLatestHeading: function () {
var e = this.data.election;
var n = e && e.name != null ? String(e.name).trim() : '';
return n !== '' ? n : 'Undian Terkini';
},
electionStatusLabel: function () {
var s = Number(this.data && this.data.election ? this.data.election.status : NaN);
if (s === 1) return 'Draf';
if (s === 2) return 'Sesi Mengundi Sedang Berjalan';
if (s === 3) return 'Tamat';
return '—';
},
electionStatusClass: function () {
var s = Number(this.data && this.data.election ? this.data.election.status : NaN);
if (s === 1) return 'election-status--draft';
if (s === 2) return 'election-status--active';
if (s === 3) return 'election-status--done';
return '';
}
},
methods: {
openStartElectionModal: function () {
var e = this.data.election;
this.startForm.name = e && e.name != null ? String(e.name) : '';
this.startForm.password = '';
this.startSubmitting = false;
this.util.showModal('#start-election-modal');
},
stop: function () {
var vm = this;
// this.util.hideModal('#stop-election-modal');
this.util.notify('Stopping Election', 'loading');
axios.post(config.API + 'election/stop', $('#stop_form').serialize())
.then(response => {
$.notifyClose();
if (vm.util.showResult(response, 'success')) {
vm.refreshElection();
vm.data.election = response.data.election;
}
})
.catch(error => {
$.notifyClose();
vm.util.showResult(error, 'error');
})
},
start: function () {
var vm = this;
if (vm.startSubmitting) {
return;
}
vm.startSubmitting = true;
this.util.notify('Memproses…', 'loading');
axios.post(config.API + 'election/start', {
name: vm.startForm.name,
password: vm.startForm.password
})
.then(response => {
$.notifyClose();
if (vm.util.showResult(response, 'success')) {
vm.refreshElection();
vm.data.election = response.data.election;
vm.startForm.password = '';
vm.util.hideModal('#start-election-modal');
}
})
.catch(error => {
$.notifyClose();
vm.util.showResult(error, 'error');
})
.finally(function () {
vm.startSubmitting = false;
})
},
registerNextSession: function () {
var vm = this;
this.util.notify('Memproses…', 'loading');
axios.post(config.API + 'election/register-next', $('#register_next_form').serialize())
.then(response => {
$.notifyClose();
if (vm.util.showResult(response, 'success')) {
vm.refreshElection();
vm.data.election = response.data.election;
vm.util.hideModal('#register-next-session-modal');
axios.get(config.API + 'admin/information').then(function (info) {
vm.data.election = info.data.election;
vm.data.partylists = info.data.partylist;
vm.data.positions = info.data.position;
}).catch(function (err) {
vm.util.showResult(err, 'error');
});
}
})
.catch(error => {
$.notifyClose();
vm.util.showResult(error, 'error');
})
},
refreshElection: function () {
this.util.notify('Refreshing Election', 'loading');
var vm = this;
this.electionLoading = true;
axios.get(config.API + 'election')
.then(response => {
$.notifyClose();
vm.data.elections = response.data;
})
.catch(error => {
$.notifyClose();
vm.util.showResult(error, 'error');
})
.finally(function () {
vm.electionLoading = false;
})
},
getPDFurl(Id) {
// Construct and return the URL based on the item ID
return `/result/${Id}`;
}
}
}
</script>