DONE: centralize theme color, interactive dashboard, increase winner result, refer TODO
This commit is contained in:
@@ -1,35 +1,76 @@
|
||||
<template>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<h4>Undian Terkini</h4>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-success" v-if="data.election.status == 1 && data.user.role == 1"
|
||||
@click="util.showModal('#start-election-modal')">Mula Undian</button>
|
||||
<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>
|
||||
|
||||
<router-link :to="{ name: 'Current Result' }" class="btn btn-info" v-if="data.election.status == 2">
|
||||
Papar Keputusan
|
||||
</router-link>
|
||||
<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>
|
||||
|
||||
<button class="btn btn-danger" v-if="data.election.status == 2"
|
||||
@click="util.showModal('#stop-election-modal')">Tamat Undian</button>
|
||||
<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>
|
||||
|
||||
<label v-if="data.election.status == 2">Undian bermula {{ start_date }}</label>
|
||||
<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 & 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>
|
||||
|
||||
<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">
|
||||
Keputusan Undian
|
||||
</router-link>
|
||||
<a class="btn btn-warning" :href="getPDFurl(item.id)">Muat Turun PDF</a>
|
||||
</template>
|
||||
</admin-data-table>
|
||||
|
||||
<form @submit.prevent="start" id="start_form">
|
||||
<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">
|
||||
@@ -42,19 +83,60 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="name">Nama Undian</label>
|
||||
<input type="text" name="name" autocomplete="on" class="form-control"
|
||||
placeholder="(Optional)" />
|
||||
<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="password">Kata Laluan</label>
|
||||
<input type="password" name="password" class="form-control" required />
|
||||
<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">
|
||||
<input type="submit" class="btn btn-info" value="Start" />
|
||||
<input type="button" data-dismiss="modal" class="btn btn-default" value="Cancel" />
|
||||
<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">×</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>
|
||||
@@ -76,7 +158,7 @@
|
||||
<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)" />
|
||||
class="form-control" placeholder="(Optional)" readonly tabindex="-1" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@@ -86,19 +168,129 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<input type="submit" class="btn btn-info" value="Stop Election" />
|
||||
<input type="button" data-dismiss="modal" class="btn btn-default" value="Cancel" />
|
||||
<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;
|
||||
}
|
||||
@@ -116,25 +308,58 @@
|
||||
<script>
|
||||
export default {
|
||||
data: () => ({
|
||||
start_date: null,
|
||||
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: 'Papar', key: 'actions', sortable: false }
|
||||
{ title: 'Tindakan', key: 'actions', sortable: false }
|
||||
]
|
||||
}),
|
||||
created: function () {
|
||||
this.refreshElection();
|
||||
var vm = this;
|
||||
setInterval(() => {
|
||||
vm.start_date = moment(vm.data.election.start).fromNow();
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
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');
|
||||
@@ -145,11 +370,6 @@ export default {
|
||||
if (vm.util.showResult(response, 'success')) {
|
||||
vm.refreshElection();
|
||||
vm.data.election = response.data.election;
|
||||
vm.data.positions = [];
|
||||
vm.data.partylists = [];
|
||||
vm.data.voters = [];
|
||||
vm.data.nominees = [];
|
||||
vm.data.results = [];
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -160,15 +380,50 @@ export default {
|
||||
|
||||
start: function () {
|
||||
var vm = this;
|
||||
// this.util.hideModal('#start-election-modal');
|
||||
console.log('this');
|
||||
this.util.notify('Loading please wait', 'loading');
|
||||
axios.post(config.API + 'election/start', $('#start_form').serialize())
|
||||
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 => {
|
||||
|
||||
Reference in New Issue
Block a user