vue for penyata anggota

This commit is contained in:
nurafrinaalimi16
2026-02-24 15:44:15 +08:00
parent 53881198eb
commit b8b44b361d
4 changed files with 534 additions and 520 deletions
@@ -0,0 +1,207 @@
<template>
<div>
<!-- SEARCH SECTION -->
<!-- <div class="mb-3 d-flex gap-2">
<input
type="text"
class="form-control"
placeholder="Cari No Anggota..."
v-model="searchNoAnggota"
@keyup.enter="searchPenyata"
style="max-width: 250px;"
/>
<button
class="btn btn-primary btn-sm"
@click="searchPenyata"
>
Search
</button>
<button
class="btn btn-outline-secondary btn-sm"
@click="resetSearch"
>
Reset
</button>
</div> -->
<!-- TABLE -->
<table class="table table-bordered table-striped">
<thead class="table-light">
<tr>
<th>No Anggota</th>
<th>Unit</th>
<th>Nama</th>
<th>Status</th>
<th>Tarikh Sah</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
<tr v-for="voter in penyata" :key="voter.id">
<td>{{ voter.no_anggota }}</td>
<td>{{ voter.unit }}</td>
<td>{{ voter.name }}</td>
<!-- STATUS -->
<td>
<span
class="badge"
:class="{
'bg-success': voter.status_penyata === 'DISAHKAN',
'bg-warning text-dark': voter.status_penyata === 'PERLU_SEMAK',
'bg-secondary': !voter.status_penyata
}"
>
{{ voter.status_penyata ?? 'BELUM SAH' }}
</span>
</td>
<!-- TARIKH SAH -->
<td>
{{ voter.tarikh_sah ?? '-' }}
</td>
<!-- PDF BUTTON -->
<td>
<button
class="btn btn-sm btn-success"
@click="cetakPDF(voter.no_anggota)"
:disabled="!bolehCetak(voter.status_penyata)"
>
PDF
</button>
</td>
</tr>
<!-- NO DATA -->
<tr v-if="!loading && penyata.length === 0">
<td colspan="6" class="text-center text-muted">
Tiada data penyata
</td>
</tr>
<!-- LOADING -->
<tr v-if="loading">
<td colspan="6" class="text-center">
Memuatkan data...
</td>
</tr>
</tbody>
</table>
<!-- PAGINATION -->
<div
class="d-flex justify-content-end gap-2"
v-if="meta.last_page > 1"
>
<button
class="btn btn-sm btn-outline-secondary"
:disabled="meta.current_page === 1"
@click="changePage(meta.current_page - 1)"
>
Sebelumnya
</button>
<button
class="btn btn-sm btn-outline-secondary"
:disabled="meta.current_page === meta.last_page"
@click="changePage(meta.current_page + 1)"
>
Seterusnya
</button>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'ManagePenyata',
data() {
return {
penyata: [],
loading: false,
searchNoAnggota: '',
meta: {
current_page: 1,
last_page: 1
}
};
},
mounted() {
this.getPenyata();
},
methods: {
async getPenyata(page = 1) {
this.loading = true;
try {
const res = await axios.get('/api/v1/voter', {
params: {
page: page,
no_anggota: this.searchNoAnggota
}
});
this.penyata = res.data.data;
this.meta.current_page = res.data.current_page;
this.meta.last_page = res.data.last_page;
} catch (err) {
console.error(err);
alert('Gagal ambil senarai penyata');
} finally {
this.loading = false;
}
},
changePage(page) {
this.getPenyata(page);
},
searchPenyata() {
this.getPenyata(1);
},
resetSearch() {
this.searchNoAnggota = '';
this.getPenyata(1);
},
bolehCetak(status) {
return ['DISAHKAN', 'PERLU_SEMAK'].includes(status);
},
cetakPDF(noAnggota) {
axios({
url: `/api/v1/penyata/${noAnggota}/pdf`,
method: 'GET',
responseType: 'blob'
})
.then((response) => {
const file = new Blob([response.data], {
type: 'application/pdf'
});
const fileURL = URL.createObjectURL(file);
window.open(fileURL);
})
.catch((error) => {
console.error(error);
alert('Gagal jana PDF.');
});
}
}
};
</script>
@@ -40,6 +40,10 @@
<a href="#">Pembiayaan Anggota </a> <a href="#">Pembiayaan Anggota </a>
</router-link> </router-link>
<router-link :to="{name: 'Penyata Anggota'}" v-if="data.user.role==1" tag="li">
<a href="#">Penyata Anggota</a>
</router-link>
</ul> </ul>
<ul class="nav navbar-right navbar-nav"> <ul class="nav navbar-right navbar-nav">
@@ -1,40 +1,33 @@
<template> <template>
<div class="penyata-wrapper container"> <div class="penyata-wrapper container">
<!-- HEADER SECTION (2 Columns) --> <!-- HEADER -->
<div class="header-section"> <div class="header-section">
<!-- Left -->
<div class="header-left"> <div class="header-left">
<div class="logo-wrapper"> <div class="logo-wrapper">
<img src="/images/logo-kopkb.jpeg" alt="KoPKB Logo" class="kopkb-logo" /> <img src="/images/logo-kopkb.jpeg" alt="KoPKB Logo" class="kopkb-logo" />
<div> <div>
<div style="text-align: left;"> <h4 class="fw-bold mb-1">Koperasi Permodalan Kelantan Berhad (KoPKB)</h4>
<h4 class="fw-bold mb-1"> <p style="font-size:13px; line-height:1.3; margin:0;">
<strong>Koperasi Permodalan Kelantan Berhad (KoPKB)</strong> Lot PT 448, Tingkat 1, Jalan Kuala Krai, Batu 3, Wakaf Che Yeh, 15150 Kota Bharu, Kelantan<br />
</h4> Tel: 09-7414339 | Website: www.koppkb.com
<p style="font-size: 13px; line-height: 1.3; margin: 0;"> </p>
Lot PT 448, Tingkat 1, Jalan Kuala Krai, Batu 3,
Wakaf Che Yeh, 15150 Kota Bharu, Kelantan<br />
Tel: 09-7414339 | Website: www.koppkb.com
</p>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="header-right"> <div class="header-right">
<!-- Boxed Title -->
<div class="title-box"> <div class="title-box">
<h5>PENYATA AHLI</h5> <h5>PENYATA AHLI</h5>
</div> </div>
<h4 class="text-muted mt-2" text-center> {{ new Date().getFullYear() }}</h4> <h4 class="text-muted mt-2">{{ new Date().getFullYear() }}</h4>
</div> </div>
</div> </div>
<hr class="mt-3" /> <hr class="mt-3" />
<!-- MEMBER INFO SECTION (2 Columns) --> <!-- MEMBER INFO -->
<div class="form-section two-column "> <div v-if="data.user.no_anggota" class="form-section two-column">
<div class="form-group"> <div class="form-group">
<label class="form-label fw-semibold">Tarikh :</label> <label class="form-label fw-semibold">Tarikh :</label>
<input type="date" class="form-control form-control-sm" v-model="tarikh" /> <input type="date" class="form-control form-control-sm" v-model="tarikh" />
@@ -42,202 +35,162 @@
<div class="form-group"> <div class="form-group">
<label class="form-label fw-semibold">No. Anggota:</label> <label class="form-label fw-semibold">No. Anggota:</label>
{{ data.user.no_anggota }} <span>{{ data.user.no_anggota }}</span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label fw-semibold">Nama:</label> <label class="form-label fw-semibold">Nama:</label>
{{ data.user.name }} <span>{{ data.user.name }}</span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label fw-semibold">Pejabat / Syarikat:</label> <label class="form-label fw-semibold">Pejabat / Syarikat:</label>
{{ data.user.unit }} <span>{{ data.user.unit }}</span>
</div> </div>
</div> </div>
<!-- MAIN CONTENT --> <!-- MAIN CONTENT -->
<h5 class="mt-4"><strong>PENGESAHAN PENYATA BAKI AKAUN AHLI</strong></h5> <h5 class="mt-4"><strong>PENGESAHAN PENYATA BAKI AKAUN AHLI</strong></h5>
<p> <p>Adalah dimaklumkan kedudukan baki akaun tuan/puan pada <b>31/12/2025</b> adalah seperti berikut:</p>
Adalah dimaklumkan kedudukan baki akaun tuan/puan pada <strong>{{ tarikh }}</strong> dengan Koperasi Permodalan
Kelantan Berhad adalah seperti berikut:
</p>
<p style="text-align: justify;"> <!-- 2-COLUMN TABLE -->
Sukacita dapat tuan/puan mengesahkan baki tersebut dan menandatangani salinan penyata akaun berkembar <table class="boxed-table mt-4">
untuk tujuan audit dan kembalikan salinan tersebut kepada koperasi sebelum <input type="date"
class="form-control form-control-sm" v-model="tarikh" />
</p>
<!-- 2-COLUMN TABLE -->
<div id="app" data-penyata='@json($penyata)'>
<table class="boxed-table mt-4">
<tbody>
<tr>
<td>Saham</td>
<td>{{ data.user.saham }}</td>
<td>Yuran</td>
<td>{{ data.user.yuran }}</td>
</tr>
<tr>
<td>Pelaburan T1</td>
<td>{{ data.user.pelaburanT1 }}</td>
<td>Pinjaman BAI</td>
<td>{{ data.user.pinjamanBAI }}</td>
</tr>
<tr>
<td>Pelaburan T2</td>
<td>{{ data.user.pelaburanT2 }}</td>
<td>Pinjaman BYS</td>
<td>{{ data.user.pinjamanBYS }}</td>
</tr>
<tr>
<td>Pinjaman AQH</td>
<td>{{ data.user.pinjamanAQH }}</td>
<td>Insurans / Cukai Jalan</td>
<td>{{ data.user.insuransCukai }}</td>
</tr>
<tr>
<td>Pinjaman BBA</td>
<td>{{ data.user.pinjamanBBA }}</td>
<td>Pinjaman Pelbagai</td>
<td>{{ data.user.pinjamanPelbagai }}</td>
</tr>
</tbody>
</table>
</div>
<!-- FOOTER SECTION -->
<p class="mt-4"><em>
Dimaklumkan juga bahawa sekiranya kami tidak menerima pengesahan tuan/puan ke atas penyata ini dalam tempoh
<strong>14 hari</strong> daripada tarikh surat ini, maka ia dianggap tuan/puan bersetuju dan memperakui ia
adalah
benar.</em>
</p>
<p>Sekian, dimaklumkan.</p><br>
<p class="italic-note">Surat ini dikeluarkan oleh komputer, tidak perlu ditandatangani.</p>
<hr />
<p style="text-align: center;">
<h5><strong>PERSETUJUAN AHLI </strong></h5>
</p> <br>
<p style="text-align: justify;">
<h5>
Saya, <strong>{{ data.user.name }}</strong>,
No Anggota: <strong>{{ data.user.no_anggota }}</strong> ,
<strong>
<label class="ms-2">
<input type="radio" value="Bersetuju" v-model="persetujuan" />
Bersetuju
</label>
<label class="ms-3">
<input type="radio" value="Tidak Bersetuju" v-model="persetujuan" />
Tidak Bersetuju
</label></strong>
. Anda <strong>{{ persetujuan || 'belum membuat pilihan' }}</strong>
dengan yang tercatit dalam penyata baki seperti di atas. <br>
</h5>
<p style="text-align: justify;"></p>Jika Tidak Bersetuju, Sila kemukakan yang sepatutnya seperti :
<br>
<!-- 2-COLUMN TABLE -->
<div id="app" data-penyata='@json($penyata)'></div>
<table class="table table-sm no-lines text-center align-middle">
<tbody> <tbody>
<tr> <tr>
<td>Saham</td> <td>Saham</td>
<td><input type="number" class="form-control-sm keyin-input" v-model="data.user.saham" /></td> <td>{{ data.user.saham }}</td>
<td>Pinjaman AQH</td>
<td><input type="number" class="form-control-sm keyin-input" v-model="data.user.pinjamanAQH" /></td>
</tr>
<tr>
<td>Yuran</td> <td>Yuran</td>
<td><input type="number" class="form-control-sm keyin-input" v-model="data.user.yuran" /></td> <td>{{ data.user.yuran }}</td>
<td>Pinjaman BBA</td>
<td><input type="number" class="form-control-sm keyin-input" v-model="data.user.pinjamanBBA" /></td>
</tr> </tr>
<tr> <tr>
<td>Pelaburan T1</td> <td>Pelaburan T1</td>
<td><input type="number" class="form-control-sm keyin-input" v-model="data.user.pelaburanT1" /></td> <td>{{ data.user.pelaburan }}</td>
<td>Pembiayaan Peribadi</td>
<td>Pinjaman BAI</td> <td>{{ data.user.peribadi }}</td>
<td><input type="number" class="form-control-sm keyin-input" v-model="data.user.pinjamanBAI" /></td>
</tr> </tr>
<tr> <tr>
<td>Pelaburan T2</td> <td>Pelaburan T2</td>
<td><input type="number" class="form-control-sm keyin-input" v-model="data.user.pelaburanT2" /></td> <td>{{ data.user.pelaburanT2 }}</td>
<td>Pembiayaan Berjamin Yuran</td>
<td>Pinjaman BYS</td> <td>{{ data.user.berjamin_yuran }}</td>
<td><input type="number" class="form-control-sm keyin-input" v-model="data.user.pinjamanBYS" /></td>
</tr> </tr>
<tr> <tr>
<td>&nbsp;</td> <td>Pembiayaan Kecemasan</td>
<td>&nbsp;</td> <td>{{ data.user.tergempar }}</td>
<td>Pembiayaan Takaful / Cukai Jalan</td>
<td>Insurans / Cukai Jalan</td> <td>{{ data.user.roadtax }}</td>
<td><input type="number" class="form-control-sm keyin-input" v-model="data.user.insuransCukai" /></td>
</tr> </tr>
<tr> <tr>
<td>&nbsp;</td> <td>Pembiayaan Pengguna</td>
<td>&nbsp;</td> <td>{{ data.user.barangan }}</td>
<td>Pembiayaan Pelbagai</td>
<td>Pinjaman Pelbagai</td> <td>{{ data.user.pelbagai }}</td>
<td><input type="text" class="form-control-sm keyin-input" v-model="data.user.pinjamanPelbagai" /></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<p class="mt-4"><em>Sekiranya kami tidak menerima pengesahan dalam tempoh <strong>14 hari</strong>, ia dianggap
bersetuju dan memperakui benar.</em></p>
<p>Sekian, dimaklumkan.</p>
<p class="italic-note">Surat ini dikeluarkan oleh komputer, tidak perlu ditandatangani.</p>
<!-- PERSETUJUAN AHLI -->
<div class="text-center mt-4 mb-3">
<!-- Sah Button --> <div style="font-weight:700; font-size:18px; letter-spacing:1px;">
<div class="text-right mb-3 no-print"> PERSETUJUAN AHLI
<button type="button" class="btn btn-primary btn-effect" style="width: 100px;" @click="SahkanPenyata"> </div>
Sahkan <hr style="width:200px; margin:8px auto;">
</button>
</div> </div>
<!-- Tandatangan & Tarikh Section --> <div class="text-center">
<label class="ms-2">
<input type="radio" name="persetujuan" value="Bersetuju" v-model="persetujuan"
:disabled="data.user.status_penyata === 'DISAHKAN'" />
Bersetuju
</label>
<label class="ms-3">
<input type="radio" name="persetujuan" value="Tidak Bersetuju" v-model="persetujuan"
:disabled="data.user.status_penyata === 'DISAHKAN'" />
Tidak Bersetuju
</label>
</div>
<p class="text-center">
<strong>
<span v-if="data.user.status_penyata === 'DISAHKAN'" class="badge-disahkan">
Disahkan
</span>
<span v-else-if="persetujuan === 'TIDAK SETUJU'" class="badge-tidak-setuju">
Tidak Setuju
</span>
<span v-else>
{{ persetujuan || 'Belum membuat pilihan' }}
</span>
</strong>
</p>
<!-- EDITABLE TABLE IF TIDAK BERSETUJU -->
<table class="table table-sm no-lines text-center align-middle mt-2">
<tbody>
<tr>
<td>Saham</td>
<td><input type="number" class="keyin-input" v-model="data.user.saham" /></td>
<td>Pembiayaan Kecemasan</td>
<td><input type="number" class="keyin-input" v-model="data.user.tergempar" /></td>
</tr>
<tr>
<td>Yuran</td>
<td><input type="number" class="keyin-input" v-model="data.user.yuran" /></td>
<td>Pembiayaan Pengguna</td>
<td><input type="number" class="keyin-input" v-model="data.user.barangan" /></td>
</tr>
<tr>
<td>Pelaburan T1</td>
<td><input type="number" class="keyin-input" v-model="data.user.pelaburan" /></td>
<td>Pembiayaan Berjamin Yuran</td>
<td><input type="number" class="keyin-input" v-model="data.user.berjamin_yuran" /></td>
</tr>
<tr>
<td>Pelaburan T2</td>
<td><input type="number" class="keyin-input" v-model="data.user.pelaburanT2" /></td>
<td>Pembiayaan Takaful / Cukai Jalan</td>
<td><input type="number" class="keyin-input" v-model="data.user.roadtax" /></td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>Pembiayaan Pelaburan</td>
<td><input type="text" class="keyin-input" v-model="data.user.pelbagai" /></td>
</tr>
</tbody>
</table>
<!-- BUTTONS -->
<div class="text-right mb-3 no-print">
<button class="btn btn-primary btn-effect" style="width:100px;" @click="SahkanPenyata"
:disabled="data.user.status_penyata === 'DISAHKAN'">
{{ data.user.status_penyata === 'DISAHKAN' ? 'Disahkan' : 'Sahkan' }}
</button>
<button class="btn btn-danger ms-2" @click="cetakPDF">Cetak PDF</button>
</div>
<!-- TANDATANGAN & TARIKH -->
<div class="signature-section"> <div class="signature-section">
<div class="sign-column"> <div class="sign-column">
<label class="form-label">No Kad Pengenalan:</label> <label class="form-label">No Kad Pengenalan:</label>
<input type="number" class="keyin-line" v-model="nokp" /> <input type="number" class="keyin-line" v-model="nokp" />
</div> </div>
<div class="sig-column"> <div class="sig-column">
<label class="form-label">Tarikh:</label> <label class="form-label">Tarikh:</label>
<input type="date" class="keyin-line" v-model="tarikh" /> <input type="date" class="keyin-line" v-model="tarikh" />
</div> </div>
</div> </div>
<br><br>
</div> </div>
</template> </template>
@@ -247,87 +200,67 @@ import axios from 'axios';
export default { export default {
name: "PenyataView", name: "PenyataView",
data() { data() {
return { return {
tarikh: new Date().toISOString().substring(0, 10), tarikh: new Date().toISOString().substring(0, 10),
noAhli: "",
nama: "",
unit: "",
noAhliPersetujuan: "",
persetujuan: "", persetujuan: "",
penyata: { nokp: "",
saham: "", data: { user: {} },
yuran: "", loading: false
pelaburanT1: "",
pelaburanT2: "",
pinjamanAQH: "",
pinjamanBBA: "",
pinjamanBAI: "",
pinjamanBYS: "",
insuransCukai: "",
pinjamanPelbagai: ""
}
}; };
}, },
mounted() { mounted() {
const id = this.$route.params.id; const noAnggota = this.$route.params.no_anggota;
this.noAhli = id; if (noAnggota) this.getPenyata(noAnggota);
this.noAhliPersetujuan = id;
this.getPenyata(id);
}, },
methods: { methods: {
async getPenyata(id) { async getPenyata(id) {
this.loading = true;
try { try {
const res = await axios.get(`/api/penyata/${id}`); const res = await axios.get(`/api/v1/penyata/${id}`);
this.penyata = res.data; this.data = res.data;
} catch (error) { } catch (err) {
console.error("Failed to fetch penyata:", error.response?.data || error); console.error(err);
alert("Gagal ambil data penyata");
} finally {
this.loading = false;
} }
}, },
cetakPDF() {
data() { const noAnggota = this.$route.params.no_anggota;
return { if (noAnggota) window.open(`/api/v1/penyata/${noAnggota}/pdf`, '_blank');
penyata: window.penyataData
};
}, },
async SahkanPenyata() {
const noAnggota = this.$route.params.no_anggota;
SahkanPenyata() { if (!this.persetujuan) { alert("Sila pilih Bersetuju atau Tidak Bersetuju"); return; }
if (confirm("Adakah anda pasti untuk mengesahkan penyata ini?")) { if (!this.nokp) { alert("Sila isi No Kad Pengenalan"); return; }
this.cetakPenyata(); if (!confirm("Adakah anda pasti untuk mengesahkan penyata ini?")) return;
alert("Penyata berjaya disahkan."); try {
await axios.post(`/api/v1/penyata/${noAnggota}/sahkan`, {
// Delay sedikit supaya Vue Router push jalan dengan betul persetujuan: this.persetujuan,
setTimeout(() => { nokp: this.nokp,
this.$router.push({ name: 'lamanUtama' }); tarikh: this.tarikh
}, 60); });
alert("Penyata berjaya DISAHKAN");
this.cetakPDF();
this.$router.push({ name: 'lamanUtama' });
} catch (err) {
console.error(err);
alert("Gagal sahkan penyata");
} }
},
// pastikan ada method lain di sini jika perlu
cetakPenyata() {
console.log("Penyata dicetak");
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
/* ------------------ GENERAL ------------------ */
.penyata-wrapper { .penyata-wrapper {
max-width: 800px; max-width: 800px;
margin: 0 auto; margin: 0 auto;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
} }
/* ✅ Header */
.header-section { .header-section {
display: grid; display: grid;
grid-template-columns: 1.5fr 1fr; grid-template-columns: 1.5fr 1fr;
@@ -350,37 +283,25 @@ export default {
text-align: right; text-align: right;
} }
/* ✅ Member Info (2 columns) */ .title-box {
/* Member Info - 2 columns */ background: #000;
color: #fff;
padding: 8px 15px;
display: inline-block;
border-radius: 4px;
text-align: center;
}
.form-section.two-column { .form-section.two-column {
display: grid; display: grid;
/* width: 100%; */
max-width: 780px;
/* ⭐ SAME WIDTH AS TABLES */
margin: 0 auto;
/* ⭐ center align same as tables */
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
column-gap: 30px; column-gap: 30px;
row-gap: 15px; row-gap: 15px;
margin-bottom: 20px; margin-bottom: 20px;
max-width: 780px;
margin: 0 auto;
} }
.form-section.two-column .form-group {
min-width: 0;
/* ⭐ prevents content from stretching columns */
}
.form-section.two-column input,
.form-section.two-column span {
width: 100%;
/* keep content inside column */
display: block;
word-break: break-word;
/* wrap long text */
}
.form-control-sm { .form-control-sm {
border: none; border: none;
border-bottom: 1px solid #666; border-bottom: 1px solid #666;
@@ -390,24 +311,77 @@ export default {
padding: 4px; padding: 4px;
} }
/* ✅ Table */ .boxed-table {
.table,
table.boxed-table {
width: 100%; width: 100%;
border-collapse: separate; border-collapse: separate;
border-spacing: 4px; border-spacing: 4px;
} }
table.boxed-table td { .boxed-table td {
border: 1px inset #000; border: 1px inset #000;
padding: 6px 8px; padding: 6px 8px;
font-size: 13px; font-size: 13px;
background-color: #fafafa; background: #fafafa;
vertical-align: middle; vertical-align: middle;
} }
/* ✅ Responsive */ .no-lines td {
@media (max-width: 768px) { border: none !important;
padding: 4px 6px;
font-size: 13px;
text-align: left;
vertical-align: middle;
}
.keyin-input,
.keyin-line {
border: none;
border-bottom: 1px solid #000;
background: transparent;
padding: 2px 4px;
font-size: 12px;
text-align: right;
width: 90%;
}
.keyin-input:focus,
.keyin-line:focus {
border-bottom: 1px solid #007bff;
}
.signature-section {
display: grid;
grid-template-columns: 1fr 1fr;
column-gap: 40px;
row-gap: 15px;
width: 100%;
max-width: 780px;
margin: 20px auto 0;
}
.btn-effect {
transition: 0.3s ease;
}
.btn-effect:hover {
transform: scale(1.05);
box-shadow: 0 0 10px rgba(8, 8, 8, 0.818);
}
.italic-note {
font-style: italic;
}
.badge-disahkan {
background: #28a745;
color: #fff;
padding: 6px 14px;
border-radius: 20px;
font-weight: 600;
letter-spacing: 0.3px;
}
@media(max-width:768px) {
.header-section { .header-section {
grid-template-columns: 1fr; grid-template-columns: 1fr;
text-align: center; text-align: center;
@@ -422,197 +396,24 @@ table.boxed-table td {
justify-content: center; justify-content: center;
} }
}
.title-box {
background-color: #000;
/* black background */
color: #fff;
/* white text */
padding: 8px 15px;
display: inline-block;
border-radius: 4px;
text-align: center;
border: 1px solid #000;
}
.title-box h4 {
margin: 0;
font-weight: bold;
letter-spacing: 1px;
}
/* Remove all inner lines */
.no-lines {
border-collapse: separate;
border-spacing: 0 4px;
/* small space between rows */
width: 100%;
}
.no-lines td {
border: none !important;
/* no borders inside */
padding: 4px 6px;
font-size: 13px;
vertical-align: middle;
text-align: left;
}
/* Key-in style input fields */
.keyin-input {
width: 90%;
border: none;
border-bottom: 1px solid #000;
outline: none;
background: transparent;
font-size: 12px;
padding: 2px 4px;
text-align: right;
}
.keyin-input:focus {
border-bottom: 1px solid #007bff;
}
.signature-section {
display: grid;
grid-template-columns: 1fr 1fr;
/* equal 50/50 width */
column-gap: 40px;
row-gap: 15px;
width: 100%;
max-width: 780px;
/* ⭐ same width as tables */
margin: 20px auto 0 auto;
/* ⭐ center align */
}
.signature-section .sign-column,
.signature-section .sig-column {
min-width: 0;
/* allows equal split */
}
.signature-section input {
width: 90%;
/* align input edges */
}
.keyin-line {
border: none;
border-bottom: 1px solid #000;
/* underline style */
background: transparent;
outline: none;
padding: 2px 0;
font-size: 12px;
}
.keyin-line:focus {
border-bottom: 1px solid #007bff;
}
@media (max-width: 768px) {
.signature-section { .signature-section {
grid-template-columns: 1fr; grid-template-columns: 1fr;
/* stack on small screens */
} }
} }
/* ✅ PRINT SETTINGS */
@media print { @media print {
body {
/* Chrome/Safari */
/* Firefox */
background: #f0f0f0 !important;
}
/* A4 page size and margin */
@page {
size: A4 portrait;
margin: 10mm;
/* Reduce margin to fit more content */
}
body {
background: #fff;
color: #000;
font-size: 12px;
line-height: 1.3;
}
/* Fit all content to one page */
html,
body {
height: 100%;
overflow: hidden;
zoom: 0.8;
/* Shrinks entire page content to fit */
}
.penyata-wrapper {
background-color: #f5f5f5 !important;
/* Your desired background */
/* -webkit-print-color-adjust: exact;
print-color-adjust: exact; */
padding: 20px;
}
/* Hide buttons when printing */
.no-print { .no-print {
display: none !important; display: none !important;
} }
body {
font-size: 12px;
line-height: 1.3;
}
/* Prevent page breaks */ table,
.penyata-wrapper, .penyata-wrapper {
.container,
table {
page-break-inside: avoid; page-break-inside: avoid;
} }
@media print {
#penyata {
position: absolute;
top: 10px;
right: 10px;
text-align: right !important;
}
}
/* Keep spacing compact */
p,
label,
input,
td {
margin: 0;
padding: 2px 4px;
}
}
.italic-note {
font-style: italic;
}
.keyin-line {
border: none;
border-bottom: 1px dashed black;
width: 140px;
padding: 2px 4px;
}
.btn-effect {
transition: 0.3s ease;
}
.btn-effect:hover {
transform: scale(1.05);
box-shadow: 0 0 10px rgba(8, 8, 8, 0.818);
} }
</style> </style>
@@ -1,88 +1,88 @@
<template> <template>
<div> <div>
<div v-if="!loading"> <div v-if="!loading">
<nav class="navbar navbar-default navbar-fixed-top" id="nav"> <nav class="navbar navbar-default navbar-fixed-top" id="nav">
<div class="container"> <div class="container">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar">
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" :href="data.baseURL" ><img src='/images/mykopkb.png' border='0' width="150" height="auto" alt="MyKoPKB Logo"></a> <a class="navbar-brand" :href="data.baseURL"><img src='/images/mykopkb.png' border='0'
width="150" height="auto" alt="MyKoPKB Logo"></a>
</div>
<div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav">
<router-link :to="{ name: 'Voter Home' }" tag="li" exact>
<a href="#"><b>LAMAN UTAMA</b></a>
</router-link>
<router-link :to="{ name: 'Mat' }" tag="li" exact>
<a href="#"><b>MAT KE-28</b></a>
</router-link>
<router-link :to="{ name: 'Attendance' }" tag="li" exact>
<a href="#"><b>DAFTAR MAT</b></a>
</router-link>
<router-link :to="{ name: 'Vote' }"
v-if="data.election.status == 2 && data.user.saham >= 500 && !hasVoted() && data.user.kehadiran != 0"
tag="li" exact>
<a href="#"><b>UNDIAN</b></a>
</router-link>
<router-link :to="{ name: 'Zoom' }" v-if="data.user.kehadiran != 0" tag="li" exact>
<a href="#"><b>PAUTAN MESYUARAT</b></a>
</router-link>
<router-link :to="{ name: 'penyata', params: { no_anggota: data.user.no_anggota } }" tag="li" exact>
<a href="#"><b>PENYATA AHLI</b></a>
</router-link>
</ul>
<ul class="nav navbar-right navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-haspopup="true" aria-expanded="false">
<span class="fa fa-user"></span> {{ data.user.name }} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li @click="logout()"><a>Log Keluar</a></li>
</ul>
</li>
</ul>
</div>
</div> </div>
<div class="collapse navbar-collapse" id="myNavbar"> </nav>
<ul class="nav navbar-nav"> <router-view></router-view>
</div>
<router-link :to="{name: 'Voter Home'}" tag="li" exact> <div v-else class="container">
<a href="#"><b>LAMAN UTAMA</b></a> <div class="jumbotron">
</router-link> <h1>Loading <i class="fa fa-refresh fa-spin"></i></h1>
<router-link :to="{name: 'Mat'}" tag="li" exact>
<a href="#"><b>MAT KE-28</b></a>
</router-link>
<router-link :to="{name: 'Attendance'}" tag="li" exact>
<a href="#"><b>DAFTAR MAT</b></a>
</router-link>
<router-link :to="{name: 'Vote'}" v-if="data.election.status==2 && data.user.saham >= 500 && !hasVoted() && data.user.kehadiran != 0" tag="li" exact>
<a href="#"><b>UNDIAN</b></a>
</router-link>
<router-link :to="{name: 'Zoom'}" v-if="data.user.kehadiran != 0" tag="li" exact>
<a href="#"><b>PAUTAN MESYUARAT</b></a>
</router-link>
<!-- <router-link :to="{name: 'Penyata'}" tag="li" exact>
<a href="#"><b>PENYATA AHLI</b></a>
</router-link> -->
</ul>
<ul class="nav navbar-right navbar-nav">
<li class="dropdown">
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button" aria-haspopup="true"
aria-expanded="false">
<span class="fa fa-user"></span> {{ data.user.name }} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li @click="logout()"><a>Log Keluar</a></li>
</ul>
</li>
</ul>
</div>
</div> </div>
</nav>
<router-view></router-view>
</div>
<div v-else class="container">
<div class="jumbotron">
<h1>Loading <i class="fa fa-refresh fa-spin"></i></h1>
</div> </div>
</div> </div>
</div>
</template> </template>
<script> <script>
export default{ export default {
data: () => ({ data: () => ({
loading: true loading: true
}), }),
created: function(){ created: function () {
this.refreshInfo(); this.refreshInfo();
}, },
methods: { methods: {
logout: function () { logout: function () {
localStorage.clear(); localStorage.clear();
this.$router.push({name: 'Voter Login'}); this.$router.push({ name: 'Voter Login' });
}, },
hasVoted: function () { hasVoted: function () {
@@ -92,8 +92,8 @@ export default{
refreshInfo: function () { refreshInfo: function () {
var vm = this; var vm = this;
this.util.setAuthorization(); this.util.setAuthorization();
axios.get(config.API+'election/information') axios.get(config.API + 'election/information')
.then(response=>{ .then(response => {
console.log(response); console.log(response);
vm.loading = false; vm.loading = false;
vm.data.election = response.data.election; vm.data.election = response.data.election;
@@ -103,10 +103,10 @@ export default{
vm.data.user = response.data.voter; vm.data.user = response.data.voter;
vm.data.result = response.data.result; vm.data.result = response.data.result;
}) })
.catch(error=>{ .catch(error => {
vm.loading = false; vm.loading = false;
if (vm.util.showResult(error, 'error') == 401) { if (vm.util.showResult(error, 'error') == 401) {
vm.$router.push({name: 'Voter Login'}); vm.$router.push({ name: 'Voter Login' });
} }
}); });
} }
@@ -117,44 +117,46 @@ export default{
<style scoped> <style scoped>
body {
body{
padding: 70px 5px; padding: 70px 5px;
} }
/* Change navbar background color */ /* Change navbar background color */
#nav.navbar { #nav.navbar {
background-color: #021324; /* Dark blue/gray background */ background-color: #021324;
border: none; /* Dark blue/gray background */
border: none;
} }
/* Change navbar link text color */ /* Change navbar link text color */
#nav .nav > li > a, #nav .nav>li>a,
#nav .navbar-brand, #nav .navbar-brand,
#nav .navbar-toggle { #nav .navbar-toggle {
color: #f39c12; /* Light gray text */ color: #f39c12;
/* Light gray text */
} }
/* Hover effect */ /* Hover effect */
#nav .nav > li > a:hover, #nav .nav>li>a:hover,
#nav .navbar-brand:hover { #nav .navbar-brand:hover {
color: #f39c12; /* Gold/yellow on hover */ color: #f39c12;
/* Gold/yellow on hover */
} }
/* Dropdown text color */ /* Dropdown text color */
#nav .dropdown-menu > li > a { #nav .dropdown-menu>li>a {
color: #2c3e50; /* Dark text in dropdown */ color: #2c3e50;
/* Dark text in dropdown */
} }
/* Dropdown hover */ /* Dropdown hover */
#nav .dropdown-menu > li > a:hover { #nav .dropdown-menu>li>a:hover {
background-color: #f39c12; background-color: #f39c12;
color: white; color: white;
} }
.navbar-brand img { .navbar-brand img {
background: transparent !important; background: transparent !important;
box-shadow: none; box-shadow: none;
} }
</style> </style>