1407 lines
34 KiB
Vue
1407 lines
34 KiB
Vue
<template>
|
|
<div class="voter-home-page container-fluid">
|
|
<div class="row home-hero-row">
|
|
<div class="col-xs-12">
|
|
<div class="home-section-container home-hero-section">
|
|
<section class="home-hero-wrap">
|
|
<div class="hero-banner home-hero-banner">
|
|
<img :src="bannerUrl" alt="Banner" class="hero-banner-image">
|
|
<div class="home-hero-banner-shine" aria-hidden="true"></div>
|
|
</div>
|
|
<div class="hero-banner-caption home-hero-caption">
|
|
<h1 class="hero-banner-title">Portal Pengundian</h1>
|
|
<p class="hero-banner-subtitle">
|
|
Gunakan <strong class="home-hero-strong">pautan pantas</strong> di bawah untuk navigasi pantas ke
|
|
halaman utama portal.
|
|
</p>
|
|
<div class="home-hero-badges">
|
|
<span class="home-badge home-badge--location">
|
|
<i class="fa fa-map-marker"></i> {{ kehadiranLabel }}
|
|
</span>
|
|
<span class="home-badge" :class="hasVoted() ? 'home-badge--ok' : 'home-badge--pending'">
|
|
<i class="fa" :class="hasVoted() ? 'fa-check-circle' : 'fa-clock-o'"></i>
|
|
{{ hasVoted() ? 'Sudah mengundi' : 'Belum mengundi' }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Butiran Keahlian and Coupon Claim Code -->
|
|
<div class="row home-content-row">
|
|
<div class="col-xs-12 col-md-6 home-column home-column--primary">
|
|
<!-- Member Information Card -->
|
|
<div class="home-section-container">
|
|
<h2 class="home-section-heading">
|
|
<span class="home-section-heading-icon" aria-hidden="true"><i class="fa fa-list-alt"></i></span>
|
|
Maklumat Anggota
|
|
</h2>
|
|
|
|
<div v-if="showRegisterCta" class="home-member-cta">
|
|
<div class="home-member-cta-hint home-member-cta-hint--warning" role="alert">
|
|
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
|
|
<span>Sila semak butiran keahlian anda sebelum mendaftar kehadiran.</span>
|
|
</div>
|
|
<div class="home-member-cta-btn-container">
|
|
<router-link :to="{ name: 'Attendance' }" class="btn btn-primary btn-block home-member-cta-btn">
|
|
<i class="fa fa-pencil-square-o" aria-hidden="true"></i>
|
|
Daftar MAT Sekarang
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="member-info-card home-member-card home-member-card--in-section">
|
|
<div v-for="row in memberRows" :key="row.label" class="member-field">
|
|
<span class="member-field-icon" aria-hidden="true"><i class="fa" :class="row.icon"></i></span>
|
|
<div class="member-field-body">
|
|
<span class="member-field-label">{{ row.label }}</span>
|
|
<span class="member-field-value">{{ row.value }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Coupon Claim Code Card -->
|
|
<div class="col-xs-12 col-md-6 home-column home-column--secondary">
|
|
<div class="home-claim-row">
|
|
<div class="home-section-container home-section-container--claim">
|
|
<h2 class="home-section-heading">
|
|
<span class="home-section-heading-icon home-section-heading-icon--teal" aria-hidden="true"><i
|
|
class="fa fa-ticket"></i></span>
|
|
Kod Tuntutan Elaun
|
|
</h2>
|
|
<div class="home-claim-card">
|
|
<div v-if="!canGenerateClaimCode" class="home-claim-locked">
|
|
<div class="home-claim-locked-icon"><i class="fa fa-lock"></i></div>
|
|
<p class="home-claim-locked-text">
|
|
Kod tuntutan hanya tersedia untuk kehadiran <strong>Fizikal</strong> selepas anda
|
|
<strong>selesai mengundi</strong>.
|
|
</p>
|
|
</div>
|
|
|
|
<div v-else class="home-claim-active">
|
|
<p class="home-claim-hint">
|
|
Tunjukkan kod ini kepada petugas di kaunter untuk pengesahan bayaran tunai.
|
|
</p>
|
|
|
|
<div v-if="claimError" class="alert alert-danger home-claim-alert">
|
|
{{ claimError }}
|
|
</div>
|
|
|
|
<div v-if="claimCode" class="home-claim-code-box">
|
|
<div class="home-claim-code">{{ claimCode }}</div>
|
|
<div class="home-claim-expiry">
|
|
Tamat tempoh: <strong>{{ claimExpiresAt || '—' }}</strong>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-primary btn-block home-claim-btn" :disabled="claimLoading"
|
|
@click="generateClaimCode()">
|
|
<i class="fa fa-refresh" :class="{ 'fa-spin': claimLoading }"></i>
|
|
{{ claimLoading ? ' Menjana...' : (claimCode ? ' Jana Semula Kod' : ' Jana Kod Tuntutan') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pautan Pantas -->
|
|
<div class="row home-quick-links-row">
|
|
<div class="col-xs-12">
|
|
<div class="home-section-container">
|
|
<h2 class="home-section-heading">
|
|
<span class="home-section-heading-icon" aria-hidden="true"><i class="fa fa-th-large"></i></span>
|
|
Pautan Pantas
|
|
</h2>
|
|
<div class="home-quick-links-grid">
|
|
<div v-for="link in quickLinks" :key="link.key" class="home-quick-link-wrap">
|
|
<a v-if="link.external" :href="link.href" class="home-cta-link" :class="link.variant" target="_blank"
|
|
rel="noopener noreferrer">
|
|
<span class="home-cta-icon"><i class="fa" :class="link.icon"></i></span>
|
|
<span class="home-cta-body">
|
|
<span class="home-cta-title">{{ link.title }}</span>
|
|
<span class="home-cta-desc">{{ link.desc }}</span>
|
|
</span>
|
|
<span class="home-cta-chevron" aria-hidden="true"><i class="fa fa-external-link"></i></span>
|
|
</a>
|
|
<router-link v-else :to="link.route" class="home-cta-link" :class="link.variant">
|
|
<span class="home-cta-icon"><i class="fa" :class="link.icon"></i></span>
|
|
<span class="home-cta-body">
|
|
<span class="home-cta-title">{{ link.title }}</span>
|
|
<span class="home-cta-desc">{{ link.desc }}</span>
|
|
</span>
|
|
<span class="home-cta-chevron" aria-hidden="true"><i class="fa fa-angle-right"></i></span>
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Kad Ahli Digital -->
|
|
<!-- <div class="col-xs-12">
|
|
<div class="home-section-container home-section-container--kad">
|
|
<h2 class="home-section-heading">
|
|
<span class="home-section-heading-icon" aria-hidden="true"><i class="fa fa-credit-card"></i></span>
|
|
Kad Ahli Digital
|
|
</h2>
|
|
<div class="home-kad-wrap">
|
|
<div class="kad-keahlian">
|
|
<div class="member-card-vertical">MEMBER CARD</div>
|
|
|
|
<div class="kad-logo">
|
|
<img src="/images/logo_kad.png" alt="Logo" class="kad-logo-img">
|
|
</div>
|
|
|
|
<div class="kad-content">
|
|
<img src="images/KOPKB_Final.png" class="watermark" alt="">
|
|
|
|
<div class="member-details">
|
|
<div class="detail-item">
|
|
<span class="detail-label">NAMA:</span>
|
|
<span class="detail-value">{{ data.user.name }}</span>
|
|
</div>
|
|
|
|
<div class="detail-item">
|
|
<span class="detail-label">NO KAD PENGENALAN:</span>
|
|
<span class="detail-value">{{ data.user.no_kp }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">NO ANGGOTA:</span>
|
|
<span class="detail-value">{{ data.user.no_anggota }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">UNIT:</span>
|
|
<span class="detail-value">{{ data.user.unit }}</span>
|
|
</div>
|
|
|
|
<div class="detail-item detail-item--barcode">
|
|
<svg id="barcode" class="detail-value">{{ data.user.no_kp }}</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div> -->
|
|
|
|
<!-- Saham Vote Blocked Message -->
|
|
<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>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import JsBarcode from "jsbarcode";
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
noKp: "",
|
|
claimLoading: false,
|
|
claimCode: "",
|
|
claimExpiresAt: "",
|
|
claimError: "",
|
|
matLinkUrl: "https://linktr.ee/kopkb",
|
|
portalSettings: null,
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
etikaUrl: function () {
|
|
var url = this.portalSettings && this.portalSettings.attendance_etika_url
|
|
? String(this.portalSettings.attendance_etika_url).trim()
|
|
: '';
|
|
return url;
|
|
},
|
|
|
|
bannerUrl() {
|
|
const p = this.portalSettings && this.portalSettings.home_banner_image_path
|
|
? this.portalSettings.home_banner_image_path
|
|
: "/images/banner.jpeg";
|
|
|
|
const v = this.portalSettings && this.portalSettings.updated_at
|
|
? encodeURIComponent(this.portalSettings.updated_at)
|
|
: "";
|
|
|
|
if (!v) return p;
|
|
return p.includes("?") ? `${p}&v=${v}` : `${p}?v=${v}`;
|
|
},
|
|
/**
|
|
* Maya: boleh mengundi selepas daftar kehadiran. Fizikal: perlu disahkan pentadbir dahulu.
|
|
*/
|
|
canProceedToVote() {
|
|
const u = this.data?.user;
|
|
if (!u || Number(u.kehadiran) === 0) return false;
|
|
if (Number(u.kehadiran) !== 1) return true;
|
|
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() {
|
|
const user = this.data?.user || {};
|
|
const election = this.data?.election || {};
|
|
const noAnggota = user.no_anggota;
|
|
const matLabel = (this.portalSettings && this.portalSettings.home_mat_label)
|
|
? this.portalSettings.home_mat_label
|
|
: "MAT KE-29";
|
|
const links = [
|
|
{
|
|
key: "attendance",
|
|
title: "Daftar MAT",
|
|
desc: "Daftar kehadiran fizikal atau maya",
|
|
icon: "fa-pencil-square-o",
|
|
route: { name: "Attendance" },
|
|
variant: "home-cta-link--teal",
|
|
external: false
|
|
},
|
|
{
|
|
key: "calon",
|
|
title: "Maklumat Calon",
|
|
desc: "Senarai calon dan profil penuh mengikut jawatan",
|
|
icon: "fa-users",
|
|
route: { name: "Voter Calon" },
|
|
variant: "home-cta-link--indigo",
|
|
external: false
|
|
},
|
|
{
|
|
key: "mat28",
|
|
title: matLabel,
|
|
desc: "Pautan luar ke maklumat mesyuarat dan sumber berkaitan",
|
|
icon: "fa-globe",
|
|
href: this.matLinkUrl,
|
|
variant: "home-cta-link--violet",
|
|
external: true
|
|
},
|
|
// Muat Turun Etika Mesyuarat Agung
|
|
{
|
|
key: "etika-mesyuarat",
|
|
title: "Etika Mesyuarat Agung",
|
|
desc: "Muat Turun etika mesyuarat agung",
|
|
icon: "fa-file-pdf-o",
|
|
href: this.etikaUrl,
|
|
variant: "home-cta-link--grey",
|
|
external: true
|
|
},
|
|
];
|
|
|
|
if (
|
|
Number(election.status) === 2 &&
|
|
!this.hasVoted() &&
|
|
this.canProceedToVote &&
|
|
this.canVoteBySaham
|
|
) {
|
|
links.push({
|
|
key: "vote",
|
|
title: "Undian",
|
|
desc: "Undi calon pilihan anda",
|
|
icon: "fa-check-square-o",
|
|
route: { name: "Vote" },
|
|
variant: "home-cta-link--rose",
|
|
external: false
|
|
});
|
|
}
|
|
|
|
if (Number(user.kehadiran) !== 0) {
|
|
links.push({
|
|
key: "zoom",
|
|
title: "Pautan Mesyuarat",
|
|
desc: "Sambung ke mesyuarat dalam talian",
|
|
icon: "fa-video-camera",
|
|
route: { name: "Zoom" },
|
|
variant: "home-cta-link--cyan",
|
|
external: false
|
|
});
|
|
}
|
|
|
|
links.push({
|
|
key: "penyata",
|
|
title: "Penyata Ahli",
|
|
desc: "Lihat dan kemaskini penyata ahli",
|
|
icon: "fa-file-text-o",
|
|
route: { name: "penyata", params: { no_anggota: noAnggota } },
|
|
variant: "home-cta-link--amber",
|
|
external: false
|
|
});
|
|
|
|
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() {
|
|
if (Number(this.data?.user?.kehadiran) !== 1) return false;
|
|
if (this.hasVoted()) return true;
|
|
return this.canProceedToVote && !this.canVoteBySaham;
|
|
},
|
|
|
|
kehadiranLabel() {
|
|
const k = Number(this.data?.user?.kehadiran);
|
|
if (k === 1) return "Kehadiran Fizikal";
|
|
if (k === 2) return "Kehadiran Maya";
|
|
return "Belum daftar kehadiran";
|
|
},
|
|
|
|
showRegisterCta() {
|
|
// Only show the registration CTA when user hasn't registered attendance yet.
|
|
return Number(this.data?.user?.kehadiran) === 0;
|
|
},
|
|
|
|
memberRows() {
|
|
const u = this.data?.user || {};
|
|
const fmt = (n) => {
|
|
if (n === null || n === undefined || n === "") return "—";
|
|
return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
};
|
|
return [
|
|
{ icon: "fa-user", label: "Nama", value: u.name || "—" },
|
|
{ icon: "fa-credit-card", label: "No. Kad Pengenalan", value: u.no_kp || "—" },
|
|
{ icon: "fa-barcode", label: "No. Anggota", value: u.no_anggota || "—" },
|
|
{ icon: "fa-building", label: "Unit", value: u.unit || "—" },
|
|
{ icon: "fa-line-chart", label: "Saham", value: "RM " + fmt(u.saham) },
|
|
{ icon: "fa-money", label: "Yuran", value: "RM " + fmt(u.yuran) },
|
|
{ icon: "fa-percent", label: "Dividen Simpanan Khas (Tabung 1) Tahun 2025", value: "RM " + fmt(u.pelaburan) }
|
|
];
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
this.syncBarcodeFromUser();
|
|
this.fetchPortalSettings();
|
|
},
|
|
|
|
watch: {
|
|
"data.user.no_kp": function () {
|
|
this.syncBarcodeFromUser();
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
async fetchPortalSettings() {
|
|
try {
|
|
this.util.setAuthorization();
|
|
const res = await axios.get(config.API + "portal-settings");
|
|
if (res && res.data && res.data.status === "success") {
|
|
this.portalSettings = res.data.data || null;
|
|
}
|
|
} catch (e) {
|
|
// Non-critical — fall back to defaults.
|
|
}
|
|
},
|
|
|
|
syncBarcodeFromUser() {
|
|
if (this.data?.user?.no_kp) {
|
|
this.noKp = this.data.user.no_kp;
|
|
this.$nextTick(() => this.generateBarcode());
|
|
}
|
|
},
|
|
|
|
generateBarcode() {
|
|
if (!this.noKp) return;
|
|
|
|
JsBarcode("#barcode", this.noKp, {
|
|
format: "CODE128",
|
|
width: 1.3,
|
|
height: 30,
|
|
displayValue: false
|
|
});
|
|
},
|
|
|
|
hasVoted() {
|
|
return Array.isArray(this.data.result) && this.data.result.length > 0;
|
|
},
|
|
|
|
async generateClaimCode() {
|
|
if (this.claimLoading) return;
|
|
this.claimError = "";
|
|
this.claimLoading = true;
|
|
try {
|
|
this.util.setAuthorization();
|
|
const response = await axios.post(config.API + "allowance/code", { method: "cash" });
|
|
if (response && response.data && response.data.status === "success") {
|
|
this.claimCode = response.data.code || "";
|
|
this.claimExpiresAt = response.data.expires_at || "";
|
|
this.util.notify(response.data.message || "Kod dijana.", "success");
|
|
} else {
|
|
this.claimError = (response && response.data && response.data.message) ? response.data.message : "Gagal menjana kod.";
|
|
this.util.notify(this.claimError, "error");
|
|
}
|
|
} catch (error) {
|
|
this.claimError =
|
|
(error && error.response && error.response.data && error.response.data.message)
|
|
? error.response.data.message
|
|
: (error && error.message ? error.message : "Gagal menjana kod.");
|
|
if (error && error.response) this.util.showResult(error, "error");
|
|
else this.util.notify(this.claimError, "error");
|
|
} finally {
|
|
this.claimLoading = false;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
|
|
<style>
|
|
.voter-home-page {
|
|
margin-top: 16px;
|
|
margin-bottom: 48px;
|
|
padding-left: 0;
|
|
padding-right: 0;
|
|
}
|
|
|
|
.home-hero-row {
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
}
|
|
|
|
.home-content-row {
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
}
|
|
|
|
.home-quick-links-row {
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
margin-top: 18px;
|
|
}
|
|
|
|
.home-claim-row {
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
margin-top: 18px;
|
|
}
|
|
|
|
.home-content-row .home-column--secondary .home-claim-row {
|
|
margin-top: 0;
|
|
}
|
|
|
|
.home-column--primary {
|
|
padding-top: 4px;
|
|
}
|
|
|
|
.home-column--secondary {
|
|
padding-top: 4px;
|
|
}
|
|
|
|
@media (max-width: 767px) {
|
|
.home-column--secondary {
|
|
margin-top: 8px;
|
|
padding-top: 0;
|
|
}
|
|
|
|
.home-content-row .home-column--secondary .home-claim-row {
|
|
margin-top: 18px;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 992px) {
|
|
.home-column--primary {
|
|
padding-right: 20px;
|
|
}
|
|
|
|
.home-column--secondary {
|
|
padding-left: 20px;
|
|
}
|
|
}
|
|
|
|
/* —— Hero —— */
|
|
.home-hero-wrap {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.home-hero-section {
|
|
padding: 16px 18px 20px;
|
|
}
|
|
|
|
.home-hero-section .hero-banner {
|
|
margin-bottom: 16px;
|
|
border-radius: 18px;
|
|
}
|
|
|
|
.home-hero-section .hero-banner-caption {
|
|
box-shadow: 0 8px 28px rgba(35, 64, 97, 0.08);
|
|
}
|
|
|
|
.hero-banner {
|
|
position: relative;
|
|
margin-bottom: 20px;
|
|
border-radius: 24px;
|
|
overflow: hidden;
|
|
box-shadow:
|
|
0 20px 50px rgba(11, 39, 79, 0.2),
|
|
0 0 0 1px rgba(255, 255, 255, 0.06) inset;
|
|
background: linear-gradient(145deg, #e8f0ff 0%, #f4f8fd 50%, #eef5ff 100%);
|
|
}
|
|
|
|
.home-hero-banner-shine {
|
|
pointer-events: none;
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
height: 42%;
|
|
background: linear-gradient(to top,
|
|
rgba(13, 110, 253, 0.07) 0%,
|
|
transparent 100%);
|
|
}
|
|
|
|
.hero-banner-image {
|
|
width: 100%;
|
|
max-height: 360px;
|
|
object-fit: contain;
|
|
object-position: center;
|
|
display: block;
|
|
}
|
|
|
|
.hero-banner-caption {
|
|
margin-top: 0;
|
|
margin-bottom: 6px;
|
|
padding: 22px 24px;
|
|
border-radius: 20px;
|
|
background: #fff;
|
|
border: 1px solid rgba(13, 110, 253, 0.12);
|
|
box-shadow:
|
|
0 16px 40px rgba(35, 64, 97, 0.1),
|
|
0 1px 0 rgba(255, 255, 255, 0.8) inset;
|
|
}
|
|
|
|
.hero-banner-title {
|
|
margin: 0 0 10px;
|
|
color: #0f172a;
|
|
font-size: 2.85rem;
|
|
font-weight: 800;
|
|
line-height: 1.15;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
|
|
.hero-banner-subtitle {
|
|
max-width: 720px;
|
|
margin: 0 0 16px;
|
|
color: #475569;
|
|
font-size: 1.45rem;
|
|
line-height: 1.65;
|
|
}
|
|
|
|
.home-hero-strong {
|
|
color: #1d4ed8;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.home-hero-badges {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.home-hero-actions {
|
|
margin: 14px 0 8px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
max-width: 520px;
|
|
}
|
|
|
|
.home-hero-register-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 10px;
|
|
padding: 12px 16px;
|
|
border-radius: 12px;
|
|
font-weight: 800;
|
|
font-size: 1.35rem;
|
|
box-shadow: 0 10px 24px rgba(13, 110, 253, 0.35);
|
|
}
|
|
|
|
.home-hero-register-hint {
|
|
color: #64748b;
|
|
font-size: 1.2rem;
|
|
line-height: 1.35;
|
|
}
|
|
|
|
.home-member-cta {
|
|
margin: 0 0 14px;
|
|
}
|
|
|
|
.home-member-cta-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 10px;
|
|
padding: 12px 16px;
|
|
border-radius: 12px;
|
|
font-weight: 800;
|
|
font-size: 1.35rem;
|
|
box-shadow: 0 10px 24px rgba(13, 110, 253, 0.35);
|
|
}
|
|
|
|
.home-member-cta-hint {
|
|
margin-top: 8px;
|
|
color: #64748b;
|
|
font-size: 1.2rem;
|
|
line-height: 1.35;
|
|
}
|
|
|
|
.home-member-cta-hint--warning {
|
|
margin-top: 0;
|
|
margin-bottom: 10px;
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: flex-start;
|
|
padding: 10px 12px;
|
|
border-radius: 12px;
|
|
background: #fffbeb;
|
|
border: 1px solid rgba(245, 158, 11, 0.45);
|
|
color: #92400e;
|
|
}
|
|
|
|
.home-member-cta-hint--warning .fa {
|
|
flex-shrink: 0;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.home-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 8px 14px;
|
|
border-radius: 999px;
|
|
font-size: 1.25rem;
|
|
font-weight: 600;
|
|
border: 1px solid transparent;
|
|
}
|
|
|
|
.home-badge .fa {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.home-badge--location {
|
|
background: #f1f5f9;
|
|
color: #334155;
|
|
border-color: #e2e8f0;
|
|
}
|
|
|
|
.home-badge--ok {
|
|
background: linear-gradient(135deg, #ecfdf5, #d1fae5);
|
|
color: #047857;
|
|
border-color: rgba(16, 185, 129, 0.35);
|
|
}
|
|
|
|
.home-badge--pending {
|
|
background: linear-gradient(135deg, #fffbeb, #fef3c7);
|
|
color: #b45309;
|
|
border-color: rgba(245, 158, 11, 0.4);
|
|
}
|
|
|
|
/* —— Section containers (hero, quick links, panels) —— */
|
|
.home-section-container {
|
|
background: #fff;
|
|
border: 1px solid #e5ebf3;
|
|
border-radius: 18px;
|
|
box-shadow: 0 12px 32px rgba(35, 64, 97, 0.08);
|
|
padding: 20px 22px 24px;
|
|
margin-bottom: 28px;
|
|
}
|
|
|
|
.home-content-row .home-section-container {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.home-column--secondary .home-section-container:not(:last-child) {
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.home-section-heading {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
margin: 0 0 18px;
|
|
padding-bottom: 16px;
|
|
border-bottom: 1px solid #eef2f7;
|
|
font-size: 1.75rem;
|
|
font-weight: 800;
|
|
color: #0f172a;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
|
|
.home-section-heading-icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 12px;
|
|
background: linear-gradient(135deg, #0d6efd, #3b82f6);
|
|
color: #fff;
|
|
font-size: 1.65rem;
|
|
box-shadow: 0 8px 20px rgba(13, 110, 253, 0.28);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.home-section-heading-icon--teal {
|
|
background: linear-gradient(135deg, #0f766e, #14b8a6);
|
|
box-shadow: 0 8px 20px rgba(20, 184, 166, 0.35);
|
|
}
|
|
|
|
.home-member-card--in-section {
|
|
background: #f8fafc;
|
|
border: 1px solid #eef2f7;
|
|
box-shadow: none;
|
|
border-radius: 14px;
|
|
}
|
|
|
|
.home-section-container--kad .home-kad-wrap {
|
|
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 {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
gap: 16px;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.home-quick-link-wrap {
|
|
min-width: 0;
|
|
display: flex;
|
|
}
|
|
|
|
.home-quick-link-wrap .home-cta-link {
|
|
flex: 1;
|
|
width: 100%;
|
|
min-height: 100%;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* —— CTA tiles —— */
|
|
.home-cta-link {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
padding: 18px 20px;
|
|
border-radius: 16px;
|
|
text-decoration: none !important;
|
|
color: #fff !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
}
|
|
|
|
.home-cta-link:hover {
|
|
transform: translateY(-2px);
|
|
color: #fff !important;
|
|
}
|
|
|
|
.home-cta-link--indigo {
|
|
background: linear-gradient(135deg, #1e3a8a 0%, #2563eb 48%, #1d4ed8 100%);
|
|
box-shadow: 0 14px 36px rgba(37, 99, 235, 0.35);
|
|
}
|
|
|
|
.home-cta-link--indigo:hover {
|
|
box-shadow: 0 18px 44px rgba(37, 99, 235, 0.45);
|
|
}
|
|
|
|
.home-cta-link--violet {
|
|
background: linear-gradient(135deg, #5b21b6 0%, #7c3aed 50%, #6d28d9 100%);
|
|
box-shadow: 0 14px 36px rgba(124, 58, 237, 0.35);
|
|
}
|
|
|
|
.home-cta-link--violet:hover {
|
|
box-shadow: 0 18px 44px rgba(124, 58, 237, 0.48);
|
|
}
|
|
|
|
.home-cta-link--teal {
|
|
background: linear-gradient(135deg, #0f766e 0%, #14b8a6 50%, #0d9488 100%);
|
|
box-shadow: 0 14px 36px rgba(20, 184, 166, 0.32);
|
|
}
|
|
|
|
.home-cta-link--teal:hover {
|
|
box-shadow: 0 18px 44px rgba(20, 184, 166, 0.45);
|
|
}
|
|
|
|
.home-cta-link--rose {
|
|
background: linear-gradient(135deg, #9f1239 0%, #e11d48 50%, #be123c 100%);
|
|
box-shadow: 0 14px 36px rgba(225, 29, 72, 0.32);
|
|
}
|
|
|
|
.home-cta-link--rose:hover {
|
|
box-shadow: 0 18px 44px rgba(225, 29, 72, 0.45);
|
|
}
|
|
|
|
.home-cta-link--cyan {
|
|
background: linear-gradient(135deg, #0e7490 0%, #06b6d4 50%, #0891b2 100%);
|
|
box-shadow: 0 14px 36px rgba(6, 182, 212, 0.3);
|
|
}
|
|
|
|
.home-cta-link--cyan:hover {
|
|
box-shadow: 0 18px 44px rgba(6, 182, 212, 0.42);
|
|
}
|
|
|
|
.home-cta-link--amber {
|
|
background: linear-gradient(135deg, #b45309 0%, #f59e0b 50%, #d97706 100%);
|
|
box-shadow: 0 14px 36px rgba(245, 158, 11, 0.32);
|
|
}
|
|
|
|
.home-cta-link--amber:hover {
|
|
box-shadow: 0 18px 44px rgba(245, 158, 11, 0.45);
|
|
}
|
|
|
|
.home-cta-link--grey {
|
|
background: linear-gradient(135deg, #64748b 0%, #94a3b8 50%, #64748b 100%);
|
|
box-shadow: 0 14px 36px rgba(100, 116, 139, 0.3);
|
|
}
|
|
|
|
.home-cta-link--grey:hover {
|
|
box-shadow: 0 18px 44px rgba(100, 116, 139, 0.42);
|
|
}
|
|
|
|
.home-cta-icon {
|
|
flex-shrink: 0;
|
|
width: 52px;
|
|
height: 52px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 14px;
|
|
background: rgba(255, 255, 255, 0.15);
|
|
font-size: 2.2rem;
|
|
}
|
|
|
|
.home-cta-body {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
.home-cta-title {
|
|
font-size: 1.65rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.02em;
|
|
}
|
|
|
|
.home-cta-desc {
|
|
font-size: 1.25rem;
|
|
opacity: 0.92;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.home-cta-chevron {
|
|
flex-shrink: 0;
|
|
font-size: 2.4rem;
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.member-info-card {
|
|
margin-bottom: 0;
|
|
padding: 0;
|
|
border-radius: 18px;
|
|
background: #fff;
|
|
border: 1px solid #e5ebf3;
|
|
box-shadow: 0 12px 32px rgba(35, 64, 97, 0.08);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.home-member-card {
|
|
padding: 6px 0;
|
|
}
|
|
|
|
.member-field {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
padding: 14px 16px;
|
|
border-bottom: 1px solid #eef2f7;
|
|
transition: background 0.15s ease;
|
|
}
|
|
|
|
.member-field:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
|
|
.member-field:hover {
|
|
background: #f8fafc;
|
|
}
|
|
|
|
.member-field-icon {
|
|
flex-shrink: 0;
|
|
width: 36px;
|
|
height: 36px;
|
|
margin-top: 2px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 10px;
|
|
background: #eff6ff;
|
|
color: #2563eb;
|
|
font-size: 1.45rem;
|
|
}
|
|
|
|
.member-field-body {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 12px 18px;
|
|
}
|
|
|
|
.member-field-label {
|
|
flex: 0 0 38%;
|
|
max-width: 200px;
|
|
font-size: 1.2rem;
|
|
font-weight: 700;
|
|
color: #64748b;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
padding-top: 4px;
|
|
line-height: 1.35;
|
|
}
|
|
|
|
.member-field-value {
|
|
flex: 1;
|
|
text-align: right;
|
|
font-size: 1.45rem;
|
|
font-weight: 700;
|
|
color: #0f172a;
|
|
line-height: 1.4;
|
|
word-break: break-word;
|
|
}
|
|
|
|
/* —— Claim —— */
|
|
.home-claim-card {
|
|
padding: 20px 22px;
|
|
}
|
|
|
|
.home-claim-locked {
|
|
display: flex;
|
|
gap: 16px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.home-claim-locked-icon {
|
|
flex-shrink: 0;
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 12px;
|
|
background: #f1f5f9;
|
|
color: #64748b;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 2rem;
|
|
}
|
|
|
|
.home-claim-locked-text {
|
|
margin: 0;
|
|
font-size: 1.35rem;
|
|
line-height: 1.55;
|
|
color: #64748b;
|
|
}
|
|
|
|
.home-claim-hint {
|
|
margin: 0 0 14px;
|
|
font-size: 1.3rem;
|
|
line-height: 1.5;
|
|
color: #475569;
|
|
}
|
|
|
|
.home-claim-alert {
|
|
margin-bottom: 12px;
|
|
border-radius: 12px;
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
.home-claim-code-box {
|
|
margin-bottom: 16px;
|
|
padding: 18px 16px;
|
|
border-radius: 14px;
|
|
background: linear-gradient(180deg, #f0fdf4 0%, #ecfdf5 100%);
|
|
border: 2px dashed rgba(16, 185, 129, 0.45);
|
|
text-align: center;
|
|
}
|
|
|
|
.home-claim-code {
|
|
font-weight: 800;
|
|
font-size: 2.8rem;
|
|
letter-spacing: 0.22em;
|
|
color: #047857;
|
|
font-family: ui-monospace, "Cascadia Code", Menlo, monospace;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.home-claim-expiry {
|
|
margin-top: 10px;
|
|
font-size: 1.2rem;
|
|
color: #64748b;
|
|
}
|
|
|
|
.home-claim-btn {
|
|
padding-top: 12px;
|
|
padding-bottom: 12px;
|
|
font-size: 1.35rem;
|
|
font-weight: 700;
|
|
border-radius: 12px;
|
|
border: 0;
|
|
box-shadow: 0 8px 24px rgba(13, 110, 253, 0.35);
|
|
}
|
|
|
|
.home-claim-btn .fa {
|
|
margin-right: 4px;
|
|
}
|
|
|
|
/* —— Kad —— */
|
|
.home-kad-wrap {
|
|
display: flex;
|
|
justify-content: center;
|
|
padding: 8px 0 12px;
|
|
}
|
|
|
|
.watermark {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 60mm;
|
|
opacity: 0.09;
|
|
transform: translate(-50%, -50%);
|
|
z-index: 5;
|
|
}
|
|
|
|
.kad-keahlian {
|
|
width: 100%;
|
|
max-width: 420px;
|
|
aspect-ratio: 1.3 / 1;
|
|
max-height: 220px;
|
|
overflow: hidden;
|
|
padding: 20px;
|
|
font-size: 0.8rem;
|
|
border-radius: 16px;
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
box-shadow:
|
|
0 20px 50px rgba(0, 0, 0, 0.25),
|
|
0 0 0 1px rgba(0, 0, 0, 0.06);
|
|
background: #2d3748;
|
|
position: relative;
|
|
font-family: Arial, sans-serif;
|
|
box-sizing: border-box;
|
|
font-weight: bold;
|
|
color: #f8fafc;
|
|
cursor: default;
|
|
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
|
background-image: url("/images/kad_bg.png");
|
|
background-size: cover;
|
|
background-position: center;
|
|
}
|
|
|
|
.kad-keahlian:hover {
|
|
transform: translateY(-3px) scale(1.01);
|
|
box-shadow:
|
|
0 24px 56px rgba(0, 0, 0, 0.3),
|
|
0 0 0 1px rgba(255, 255, 255, 0.08);
|
|
}
|
|
|
|
@media (max-height: 600px) {
|
|
.kad-keahlian {
|
|
max-height: 200px;
|
|
padding: 12px;
|
|
font-size: 0.75rem;
|
|
}
|
|
}
|
|
|
|
.kad-logo {
|
|
position: absolute;
|
|
top: 10px;
|
|
right: 10px;
|
|
text-align: right;
|
|
}
|
|
|
|
.kad-logo-img {
|
|
width: 124px;
|
|
height: auto;
|
|
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.5));
|
|
}
|
|
|
|
.member-card-vertical {
|
|
position: absolute;
|
|
top: 18px;
|
|
left: 0;
|
|
writing-mode: vertical-rl;
|
|
text-orientation: mixed;
|
|
font-weight: bold;
|
|
font-size: 1.4rem;
|
|
letter-spacing: 2px;
|
|
padding: 5px;
|
|
color: #fff;
|
|
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7);
|
|
}
|
|
|
|
.kad-keahlian::before {
|
|
content: "";
|
|
position: absolute;
|
|
background-size: 100%;
|
|
opacity: 0.06;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: #000;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.member-details {
|
|
font-family: Arial, sans-serif;
|
|
font-weight: normal;
|
|
font-size: 1.05rem;
|
|
line-height: 1.3;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 5px;
|
|
margin-left: 38px;
|
|
}
|
|
|
|
.kad-content {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
margin-top: 8px;
|
|
position: relative;
|
|
z-index: 5;
|
|
}
|
|
|
|
.detail-item {
|
|
position: relative;
|
|
text-align: left;
|
|
}
|
|
|
|
.detail-item--barcode {
|
|
margin-top: 4px;
|
|
}
|
|
|
|
#barcode {
|
|
transform: scaleX(1.45);
|
|
transform-origin: left;
|
|
max-width: 100%;
|
|
}
|
|
|
|
.detail-label {
|
|
text-align: left;
|
|
}
|
|
|
|
.detail-value {
|
|
text-align: right;
|
|
}
|
|
|
|
.no-anggota {
|
|
position: absolute;
|
|
top: 5px;
|
|
right: 8px;
|
|
text-align: right;
|
|
z-index: 10;
|
|
}
|
|
|
|
.contact-info {
|
|
text-align: right;
|
|
font-size: 9px;
|
|
line-height: 1.2;
|
|
color: rgb(12, 12, 12);
|
|
}
|
|
|
|
.contact-info i {
|
|
margin-right: 3px;
|
|
font-size: 8px;
|
|
font-weight: normal;
|
|
}
|
|
|
|
@media (max-width: 991px) {
|
|
.voter-home-page {
|
|
margin-top: 16px;
|
|
}
|
|
|
|
.hero-banner-image {
|
|
max-height: 300px;
|
|
}
|
|
|
|
.hero-banner-title {
|
|
font-size: 2.35rem;
|
|
}
|
|
|
|
.member-field-label {
|
|
flex: 0 0 42%;
|
|
max-width: 220px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 767px) {
|
|
.hero-banner {
|
|
border-radius: 18px;
|
|
}
|
|
|
|
.hero-banner-image {
|
|
max-height: 240px;
|
|
}
|
|
|
|
.hero-banner-caption {
|
|
padding: 18px;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.hero-banner-title {
|
|
font-size: 1.95rem;
|
|
}
|
|
|
|
.hero-banner-subtitle {
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
.home-section-container {
|
|
padding: 16px 14px 18px;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.home-hero-section {
|
|
padding: 12px 12px 16px;
|
|
}
|
|
|
|
.home-section-heading {
|
|
font-size: 1.45rem;
|
|
gap: 10px;
|
|
padding-bottom: 14px;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.home-section-heading-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
font-size: 1.45rem;
|
|
}
|
|
|
|
.home-cta-link {
|
|
flex-wrap: wrap;
|
|
padding: 16px;
|
|
}
|
|
|
|
.home-cta-title {
|
|
font-size: 1.45rem;
|
|
}
|
|
|
|
.home-cta-desc {
|
|
font-size: 1.15rem;
|
|
}
|
|
|
|
/* Butiran Keahlian: icon + stacked label/value — shorter rows, full width text */
|
|
.home-member-card--in-section {
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.home-member-card--in-section .member-field {
|
|
padding: 12px 12px;
|
|
gap: 10px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.home-member-card--in-section .member-field-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
margin-top: 0;
|
|
font-size: 1.35rem;
|
|
border-radius: 11px;
|
|
}
|
|
|
|
.home-member-card--in-section .member-field-body {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 5px;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.home-member-card--in-section .member-field-label {
|
|
flex: none;
|
|
max-width: none;
|
|
width: 100%;
|
|
padding-top: 0;
|
|
margin: 0;
|
|
font-size: 1.15rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.06em;
|
|
color: #475569;
|
|
text-transform: uppercase;
|
|
line-height: 1.25;
|
|
}
|
|
|
|
.home-member-card--in-section .member-field-value {
|
|
flex: none;
|
|
width: 100%;
|
|
text-align: left;
|
|
font-size: 1.42rem;
|
|
font-weight: 700;
|
|
color: #0f172a;
|
|
line-height: 1.35;
|
|
word-break: break-word;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.home-claim-code {
|
|
font-size: 2.2rem;
|
|
letter-spacing: 0.14em;
|
|
}
|
|
}
|
|
</style>
|