Allowance check
This commit is contained in:
@@ -1,95 +1,468 @@
|
||||
<template>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<h4>Tambah Pengundi</h4>
|
||||
<form @submit.prevent="add()" id="add-form">
|
||||
<div class="form-group">
|
||||
<label for="name">Nama Anggota</label>
|
||||
<input type="text" name="name" class="form-control" required>
|
||||
<div class="panel panel-default voter-add-page">
|
||||
<div class="panel-body">
|
||||
<div class="voter-add-page__header clearfix">
|
||||
<h4 class="pull-left">Tambah Pengundi</h4>
|
||||
<div class="pull-right voter-add-page__header-actions">
|
||||
<button type="button" class="btn btn-success" @click="openManualModal">
|
||||
<i class="fa fa-user-plus"></i> Daftar pengundi baru
|
||||
</button>
|
||||
<router-link :to="{ name: 'Manage Voter' }" class="btn btn-default">
|
||||
<i class="fa fa-arrow-left"></i> Kembali
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="no_kp">No. Kad Pengenalan</label>
|
||||
<input type="text" name="no_kp" class="form-control" required>
|
||||
<p class="text-muted small voter-add-page__intro">
|
||||
Kiri: keseluruhan pengundi (ikut No. KP, data terkini). Kanan: yang ditanda layak untuk pilihan raya
|
||||
semasa.
|
||||
Yang tidak berada di senarai kanan akan dipadam daripada pilihan raya ini apabila anda simpan.
|
||||
</p>
|
||||
|
||||
<div class="voter-add-split row">
|
||||
<div class="col-md-6 voter-add-split__col">
|
||||
<div class="panel panel-default voter-add-split__panel">
|
||||
<div class="panel-heading clearfix">
|
||||
<strong class="pull-left">Semua pengundi</strong>
|
||||
<span v-if="catalogLoading" class="text-muted small pull-left voter-add-heading__loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</span>
|
||||
<span class="badge pull-right" title="Jumlah pengundi unik (No. KP)">{{ catalog.length
|
||||
}}</span>
|
||||
<span v-if="catalogFilter"
|
||||
class="text-muted small pull-right voter-add-heading__filter-hint">
|
||||
{{ filteredCatalog.length }} dipaparkan ·
|
||||
</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-inline voter-batch-toolbar">
|
||||
<input type="text" class="form-control" v-model.trim="catalogFilter"
|
||||
placeholder="Tapis nama / No. KP / No. anggota…" />
|
||||
<button type="button" class="btn btn-default btn-sm" @click="selectAllFiltered">Tanda
|
||||
semua (ditapis)</button>
|
||||
<button type="button" class="btn btn-default btn-sm" @click="clearAllFiltered">Nyahtanda
|
||||
(ditapis)</button>
|
||||
</div>
|
||||
<div v-if="catalogError" class="alert alert-danger">{{ catalogError }}</div>
|
||||
<div class="table-responsive voter-add-table-wrap">
|
||||
<table class="table table-bordered table-condensed table-striped"
|
||||
v-if="!catalogLoading || catalog.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:44px;">
|
||||
<input type="checkbox" :checked="headerChecked"
|
||||
@change="toggleHeader($event)" />
|
||||
</th>
|
||||
<th>Nama</th>
|
||||
<th>No. KP</th>
|
||||
<th>No. Anggota</th>
|
||||
<th>Unit</th>
|
||||
<th style="width:88px;">PR semasa</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in filteredCatalog" :key="row.no_kp">
|
||||
<td>
|
||||
<input type="checkbox" :checked="isSelected(row.no_kp)"
|
||||
@change="toggleRow(row.no_kp, $event)" />
|
||||
</td>
|
||||
<td>{{ row.name }}</td>
|
||||
<td>{{ row.no_kp }}</td>
|
||||
<td>{{ row.no_anggota }}</td>
|
||||
<td>{{ row.unit }}</td>
|
||||
<td>
|
||||
<span v-if="row.in_current_election"
|
||||
class="label label-success">Ya</span>
|
||||
<span v-else class="label label-default">—</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p v-if="!catalogLoading && !catalog.length" class="text-muted">Tiada rekod pengundi dalam
|
||||
pangkalan data.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 voter-add-split__col">
|
||||
<div class="panel panel-info voter-add-split__panel">
|
||||
<div class="panel-heading clearfix">
|
||||
<strong class="pull-left">Layak mengundi (pilihan raya semasa)</strong>
|
||||
<span class="badge pull-right" title="Jumlah ditanda layak">{{ selectedNoKp.length }}</span>
|
||||
<span v-if="selectedFilter"
|
||||
class="text-muted small pull-right voter-add-heading__filter-hint">
|
||||
{{ filteredSelectedRows.length }} dipaparkan ·
|
||||
</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-inline voter-batch-toolbar voter-batch-toolbar--right">
|
||||
<input type="text" class="form-control" v-model.trim="selectedFilter"
|
||||
placeholder="Tapis senarai dipilih…" />
|
||||
<button type="button" class="btn btn-default btn-sm" @click="clearAllSelected"
|
||||
:disabled="!selectedNoKp.length">
|
||||
Kosongkan semua
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary"
|
||||
:disabled="catalogSaving || catalogLoading" @click="saveApplicable()">
|
||||
Simpan pilihan
|
||||
</button>
|
||||
</div>
|
||||
<div class="table-responsive voter-add-table-wrap voter-add-table-wrap--selected">
|
||||
<table class="table table-bordered table-condensed table-striped"
|
||||
v-if="filteredSelectedRows.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nama</th>
|
||||
<th>No. KP</th>
|
||||
<th>No. Anggota</th>
|
||||
<th style="width:52px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in filteredSelectedRows" :key="row.no_kp">
|
||||
<td>{{ row.name }}</td>
|
||||
<td>{{ row.no_kp }}</td>
|
||||
<td>{{ row.no_anggota }}</td>
|
||||
<td class="text-center">
|
||||
<button type="button" class="btn btn-xs btn-danger" title="Buang"
|
||||
@click="removeSelected(row.no_kp)">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p v-else class="text-muted voter-add-empty-selected">Tiada pengundi ditanda. Tandakan
|
||||
pada jadual kiri.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="no_anggota">No. Anggota</label>
|
||||
<input type="text" name="no_anggota" class="form-control" required>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="unit">Unit</label>
|
||||
<input type="text" name="unit" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alamat">Alamat</label>
|
||||
<input type="text" name="alamat" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="status_anggota">Status Anggota</label>
|
||||
<select name="status_anggota" class="form-control" required>
|
||||
<option value="inactive">-- Pilih Status --</option>
|
||||
<option value="active">Aktif</option>
|
||||
<option value="inactive">Berhenti</option>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="telefon">No. Telefon</label>
|
||||
<input type="text" name="telefon" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="saham">Saham</label>
|
||||
<input type="text" name="saham" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="yuran">Yuran</label>
|
||||
<input type="text" name="yuran" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-success">Submit</button>
|
||||
<router-link :to="{name: 'Manage Voter'}" class="btn btn-default">Back</router-link>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Modal for adding new voter -->
|
||||
<modal id="add-voter-manual-modal">
|
||||
<modal-header>Daftar pengundi baru</modal-header>
|
||||
<modal-body>
|
||||
<form @submit.prevent="add()" id="add-form">
|
||||
<div class="form-group">
|
||||
<label for="add-form-name">Nama Anggota</label>
|
||||
<input id="add-form-name" type="text" name="name" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="add-form-no_kp">No. Kad Pengenalan</label>
|
||||
<input id="add-form-no_kp" type="text" name="no_kp" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="add-form-no_anggota">No. Anggota</label>
|
||||
<input id="add-form-no_anggota" type="text" name="no_anggota" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="add-form-unit">Unit</label>
|
||||
<input id="add-form-unit" type="text" name="unit" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="add-form-alamat">Alamat</label>
|
||||
<input id="add-form-alamat" type="text" name="alamat" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="add-form-status_anggota">Status Anggota</label>
|
||||
<select id="add-form-status_anggota" name="status_anggota" class="form-control" required>
|
||||
<option value="inactive">-- Pilih Status --</option>
|
||||
<option value="active">Aktif</option>
|
||||
<option value="inactive">Berhenti</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="add-form-telefon">No. Telefon</label>
|
||||
<input id="add-form-telefon" type="text" name="telefon" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="add-form-saham">Saham</label>
|
||||
<input id="add-form-saham" type="text" name="saham" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="add-form-yuran">Yuran</label>
|
||||
<input id="add-form-yuran" type="text" name="yuran" class="form-control" required>
|
||||
</div>
|
||||
</form>
|
||||
</modal-body>
|
||||
<modal-footer>
|
||||
<button type="button" class="btn btn-success" :disabled="loading" @click="add()">Hantar</button>
|
||||
<button type="button" class="btn btn-default" @click="closeManualModal">Batal</button>
|
||||
</modal-footer>
|
||||
</modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.voter-add-page__header {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.voter-add-page__header h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.voter-add-page__header-actions .btn {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.voter-add-page__intro {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.voter-add-split__col {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.voter-add-split__panel {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.voter-batch-toolbar .form-control {
|
||||
min-width: 180px;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.voter-batch-toolbar .btn {
|
||||
margin-right: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.voter-add-table-wrap {
|
||||
max-height: min(520px, calc(100vh - 280px));
|
||||
overflow: auto;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.voter-add-table-wrap--selected {
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.voter-add-empty-selected {
|
||||
margin: 16px 0 0;
|
||||
}
|
||||
|
||||
.voter-add-heading__loading {
|
||||
margin-left: 8px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.voter-add-heading__filter-hint {
|
||||
margin-right: 8px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default{
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
loading: false
|
||||
loading: false,
|
||||
catalog: [],
|
||||
catalogLoading: false,
|
||||
catalogSaving: false,
|
||||
catalogError: '',
|
||||
catalogFilter: '',
|
||||
selectedFilter: '',
|
||||
selectedNoKp: []
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
catalogByKp: function () {
|
||||
var map = {};
|
||||
var i;
|
||||
for (i = 0; i < this.catalog.length; i++) {
|
||||
map[this.catalog[i].no_kp] = this.catalog[i];
|
||||
}
|
||||
return map;
|
||||
},
|
||||
|
||||
selectedRows: function () {
|
||||
var map = this.catalogByKp;
|
||||
var out = [];
|
||||
var i;
|
||||
for (i = 0; i < this.selectedNoKp.length; i++) {
|
||||
var kp = this.selectedNoKp[i];
|
||||
if (map[kp]) out.push(map[kp]);
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
filteredCatalog: function () {
|
||||
var q = (this.catalogFilter || '').toLowerCase();
|
||||
if (!q) return this.catalog;
|
||||
return this.catalog.filter(function (row) {
|
||||
return (row.name && String(row.name).toLowerCase().indexOf(q) !== -1)
|
||||
|| (row.no_kp && String(row.no_kp).toLowerCase().indexOf(q) !== -1)
|
||||
|| (row.no_anggota && String(row.no_anggota).toLowerCase().indexOf(q) !== -1)
|
||||
|| (row.unit && String(row.unit).toLowerCase().indexOf(q) !== -1);
|
||||
});
|
||||
},
|
||||
|
||||
filteredSelectedRows: function () {
|
||||
var rows = this.selectedRows;
|
||||
var q = (this.selectedFilter || '').toLowerCase();
|
||||
if (!q) return rows;
|
||||
return rows.filter(function (row) {
|
||||
return (row.name && String(row.name).toLowerCase().indexOf(q) !== -1)
|
||||
|| (row.no_kp && String(row.no_kp).toLowerCase().indexOf(q) !== -1)
|
||||
|| (row.no_anggota && String(row.no_anggota).toLowerCase().indexOf(q) !== -1)
|
||||
|| (row.unit && String(row.unit).toLowerCase().indexOf(q) !== -1);
|
||||
});
|
||||
},
|
||||
|
||||
headerChecked: function () {
|
||||
var rows = this.filteredCatalog;
|
||||
if (!rows.length) return false;
|
||||
var vm = this;
|
||||
return rows.every(function (r) { return vm.isSelected(r.no_kp); });
|
||||
}
|
||||
},
|
||||
|
||||
created: function () {
|
||||
this.loadMemberCatalog();
|
||||
},
|
||||
|
||||
methods: {
|
||||
openManualModal: function () {
|
||||
this.util.showModal('#add-voter-manual-modal');
|
||||
},
|
||||
|
||||
closeManualModal: function () {
|
||||
this.util.hideModal('#add-voter-manual-modal');
|
||||
},
|
||||
|
||||
resetManualForm: function () {
|
||||
var el = document.getElementById('add-form');
|
||||
if (el) el.reset();
|
||||
},
|
||||
|
||||
loadMemberCatalog: function () {
|
||||
var vm = this;
|
||||
this.catalogLoading = true;
|
||||
this.catalogError = '';
|
||||
axios.get(config.API + 'voter/member-catalog')
|
||||
.then(function (res) {
|
||||
vm.catalog = (res.data && res.data.catalog) ? res.data.catalog : [];
|
||||
vm.selectedNoKp = vm.catalog.filter(function (r) { return r.in_current_election; }).map(function (r) { return r.no_kp; });
|
||||
})
|
||||
.catch(function (err) {
|
||||
vm.catalogError = (err.response && err.response.data && err.response.data.message)
|
||||
? err.response.data.message
|
||||
: 'Gagal memuatkan senarai.';
|
||||
vm.util.showResult(err);
|
||||
})
|
||||
.finally(function () {
|
||||
vm.catalogLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
isSelected: function (noKp) {
|
||||
return this.selectedNoKp.indexOf(noKp) !== -1;
|
||||
},
|
||||
|
||||
toggleRow: function (noKp, evt) {
|
||||
var arr = this.selectedNoKp.slice();
|
||||
var i = arr.indexOf(noKp);
|
||||
if (evt.target.checked) {
|
||||
if (i === -1) arr.push(noKp);
|
||||
} else {
|
||||
if (i !== -1) arr.splice(i, 1);
|
||||
}
|
||||
this.selectedNoKp = arr;
|
||||
},
|
||||
|
||||
toggleHeader: function (evt) {
|
||||
var want = evt.target.checked;
|
||||
var keys = this.filteredCatalog.map(function (r) { return r.no_kp; });
|
||||
var set = {};
|
||||
var i;
|
||||
for (i = 0; i < this.selectedNoKp.length; i++) {
|
||||
set[this.selectedNoKp[i]] = true;
|
||||
}
|
||||
if (want) {
|
||||
for (i = 0; i < keys.length; i++) set[keys[i]] = true;
|
||||
} else {
|
||||
for (i = 0; i < keys.length; i++) delete set[keys[i]];
|
||||
}
|
||||
this.selectedNoKp = Object.keys(set);
|
||||
},
|
||||
|
||||
selectAllFiltered: function () {
|
||||
var set = {};
|
||||
var i;
|
||||
for (i = 0; i < this.selectedNoKp.length; i++) {
|
||||
set[this.selectedNoKp[i]] = true;
|
||||
}
|
||||
var rows = this.filteredCatalog;
|
||||
for (i = 0; i < rows.length; i++) set[rows[i].no_kp] = true;
|
||||
this.selectedNoKp = Object.keys(set);
|
||||
},
|
||||
|
||||
clearAllFiltered: function () {
|
||||
var remove = {};
|
||||
var i;
|
||||
var rows = this.filteredCatalog;
|
||||
for (i = 0; i < rows.length; i++) remove[rows[i].no_kp] = true;
|
||||
this.selectedNoKp = this.selectedNoKp.filter(function (kp) { return !remove[kp]; });
|
||||
},
|
||||
|
||||
removeSelected: function (noKp) {
|
||||
this.selectedNoKp = this.selectedNoKp.filter(function (kp) { return kp !== noKp; });
|
||||
},
|
||||
|
||||
clearAllSelected: function () {
|
||||
this.selectedNoKp = [];
|
||||
},
|
||||
|
||||
saveApplicable: function () {
|
||||
if (this.catalogSaving) return;
|
||||
var vm = this;
|
||||
this.catalogSaving = true;
|
||||
this.util.notify('Menyimpan…', 'loading');
|
||||
axios.post(config.API + 'voter/sync-applicable', { selected_no_kp: this.selectedNoKp })
|
||||
.then(function (res) {
|
||||
$.notifyClose();
|
||||
if (vm.util.showResult(res, 'success')) {
|
||||
vm.$router.push({ name: 'Manage Voter' });
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
$.notifyClose();
|
||||
vm.util.showResult(err);
|
||||
})
|
||||
.finally(function () {
|
||||
vm.catalogSaving = false;
|
||||
});
|
||||
},
|
||||
|
||||
add: function () {
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
var vm = this;
|
||||
this.util.notify('Adding Voter', 'loading')
|
||||
axios.post(config.API+'voter', $('#add-form').serialize())
|
||||
.then(response=>{
|
||||
this.util.notify('Adding Voter', 'loading');
|
||||
axios.post(config.API + 'voter', $('#add-form').serialize())
|
||||
.then(function (response) {
|
||||
$.notifyClose();
|
||||
vm.loading = false;
|
||||
if (vm.util.showResult(response, 'success')) {
|
||||
vm.$router.push({name:'Manage Voter'});
|
||||
vm.closeManualModal();
|
||||
vm.resetManualForm();
|
||||
vm.$router.push({ name: 'Manage Voter' });
|
||||
}
|
||||
})
|
||||
.catch(error=>{
|
||||
.catch(function (error) {
|
||||
$.notifyClose();
|
||||
vm.loading = false;
|
||||
vm.util.showResult(error);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user