revisi dawg
This commit is contained in:
@@ -61,12 +61,19 @@
|
||||
|
||||
async function getBeasiswa() {
|
||||
try {
|
||||
const data = await fetchRemote();
|
||||
return { data, source: 'remote' };
|
||||
} catch (err) {
|
||||
console.warn('[BeasiswaAPI] API remote gagal, fallback ke data lokal:', err.message);
|
||||
// Prioritas: data lokal (lengkap: url_sumber, syarat_umum, kontak tersedia)
|
||||
// Remote API digunakan hanya sebagai fallback jika data lokal tidak ditemukan.
|
||||
const data = await fetchLocal();
|
||||
return { data, source: 'local' };
|
||||
} catch (err) {
|
||||
console.warn('[BeasiswaAPI] Data lokal gagal, fallback ke API remote:', err.message);
|
||||
try {
|
||||
const data = await fetchRemote();
|
||||
return { data, source: 'remote' };
|
||||
} catch (remoteErr) {
|
||||
console.error('[BeasiswaAPI] Semua sumber gagal:', remoteErr.message);
|
||||
throw remoteErr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
const ITEMS_PER_PAGE = 6; // 2 baris × 3 kolom desktop
|
||||
|
||||
let allData = []; // Semua data beasiswa dari JSON
|
||||
let activeFilter = 'semua';
|
||||
let allData = []; // Semua data beasiswa dari JSON
|
||||
let activeFilter = 'semua';
|
||||
let searchKeyword = '';
|
||||
let currentPage = 1;
|
||||
let currentPage = 1;
|
||||
|
||||
// Format tanggal
|
||||
function formatDate(str) {
|
||||
@@ -32,7 +32,38 @@ function sanitize(str) {
|
||||
* Gunakan textContent/setAttribute — hindari langsung innerHTML dari user input
|
||||
*/
|
||||
function createCard(bsw) {
|
||||
const isBuka = bsw.status === 'Buka' || bsw.status === 'Dibuka';
|
||||
// Tentukan badge & warna tombol status
|
||||
let statusClass = 'status-btn-ditutup';
|
||||
let statusLabel = 'Ditutup';
|
||||
const statusLower = (bsw.status || '').toLowerCase();
|
||||
if (statusLower === 'buka' || statusLower === 'dibuka') {
|
||||
statusClass = 'status-btn-buka';
|
||||
statusLabel = 'Pendaftaran Dibuka';
|
||||
} else if (statusLower.includes('segera')) {
|
||||
statusClass = 'status-btn-segera';
|
||||
statusLabel = 'Segera Dibuka';
|
||||
}
|
||||
|
||||
// Ambil pills dari tags lokal: ["jenjang", "kategori/swasta/pemerintah", "negara", "tipe_pendanaan", ...]
|
||||
const tags = Array.isArray(bsw.tags) ? bsw.tags : [];
|
||||
const jenjang = tags[0] || '';
|
||||
|
||||
// Negara: cari tag yang bukan jenjang (S1/S2/S3), bukan kategori sumber (Swasta/Pemerintah),
|
||||
// dan bukan tipe pendanaan (Fully/Partially Funded)
|
||||
const skipPatterns = /^(S1|S2|S3|D3|D4|Swasta|Pemerintah|Fully Funded|Partially Funded|Beasiswa Penuh|Beasiswa Sebagian|Prestasi)$/i;
|
||||
const negara = tags.slice(1).find(t => !skipPatterns.test(t)) || '';
|
||||
|
||||
// Tipe pendanaan untuk subtitle — ambil dari kategori field atau dari deskripsi_singkat
|
||||
const tipe = bsw.kategori || '';
|
||||
|
||||
const pillsHtml = [jenjang, negara]
|
||||
.filter(Boolean)
|
||||
.map((t, i) => `<span class="card-pill ${i === 0 ? 'card-pill-level' : ''}">${t}</span>`)
|
||||
.join('');
|
||||
|
||||
// Subtitle: hindari duplikasi "Beasiswa Beasiswa ..."
|
||||
const rawTipe = tipe.replace(/^beasiswa\s+/i, '');
|
||||
const subtitle = rawTipe ? `Beasiswa ${rawTipe}` : '';
|
||||
|
||||
const article = document.createElement('article');
|
||||
article.className = 'scholarship-card fade-in-up';
|
||||
@@ -40,34 +71,27 @@ function createCard(bsw) {
|
||||
article.setAttribute('data-id', bsw.id);
|
||||
|
||||
article.innerHTML = `
|
||||
<div class="card-img-wrap">
|
||||
<img
|
||||
src="${bsw.thumbnail}"
|
||||
alt="${bsw.nama}"
|
||||
class="card-img"
|
||||
loading="lazy"
|
||||
onerror="this.style.display='none';this.parentElement.style.background='#eef2ff';this.parentElement.innerHTML+='<i class=\\'fas fa-graduation-cap\\' style=\\'font-size:40px;color:#c7d2fe;\\'></i>'"
|
||||
>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="badge-status ${isBuka ? 'badge-buka' : 'badge-ditutup'}">
|
||||
${bsw.status}
|
||||
</span>
|
||||
<h3 class="card-title">${bsw.nama}</h3>
|
||||
<p class="card-desc">${bsw.deskripsi_singkat}</p>
|
||||
<div class="card-date">
|
||||
<i class="fas fa-calendar-alt"></i>
|
||||
<span>${formatDate(bsw.tanggal_tutup)}</span>
|
||||
${subtitle ? `<p class="card-subtitle">${subtitle}</p>` : ''}
|
||||
${pillsHtml ? `<div class="card-pills">${pillsHtml}</div>` : ''}
|
||||
<hr class="card-divider">
|
||||
<div class="card-dates">
|
||||
<div class="card-date-col">
|
||||
<span class="card-date-label">Mulai</span>
|
||||
<span class="card-date-value">${formatDate(bsw.tanggal_buka)}</span>
|
||||
</div>
|
||||
<div class="card-date-col">
|
||||
<span class="card-date-label">Deadline</span>
|
||||
<span class="card-date-value">${formatDate(bsw.tanggal_tutup)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button class="card-status-btn ${statusClass}" disabled>${statusLabel}</button>
|
||||
<button
|
||||
class="btn-selengkapnya"
|
||||
onclick="openPopup('${bsw.id}')"
|
||||
aria-label="Lihat detail beasiswa ${bsw.nama}"
|
||||
>
|
||||
Selengkapnya <i class="fas fa-arrow-right" style="font-size:10px;"></i>
|
||||
</button>
|
||||
>Selengkapnya</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -84,8 +108,8 @@ function getFiltered() {
|
||||
// Cocok dengan filter tab
|
||||
const matchFilter =
|
||||
activeFilter === 'semua' ||
|
||||
bsw.status === activeFilter ||
|
||||
bsw.kategori === activeFilter;
|
||||
bsw.status === activeFilter ||
|
||||
bsw.kategori === activeFilter;
|
||||
|
||||
// Cocok dengan pencarian (nama atau deskripsi atau tags)
|
||||
const matchSearch =
|
||||
@@ -100,9 +124,9 @@ function getFiltered() {
|
||||
|
||||
/** Render kartu + paginasi ke DOM */
|
||||
function renderCards() {
|
||||
const grid = document.getElementById('beasiswa-grid');
|
||||
const pagCont = document.getElementById('pagination');
|
||||
const filtered = getFiltered();
|
||||
const grid = document.getElementById('beasiswa-grid');
|
||||
const pagCont = document.getElementById('pagination');
|
||||
const filtered = getFiltered();
|
||||
|
||||
// Bersihkan grid
|
||||
grid.innerHTML = '';
|
||||
@@ -148,7 +172,7 @@ function renderPagination(total, container) {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = `page-btn${isActive ? ' active' : ''}`;
|
||||
btn.innerHTML = content;
|
||||
btn.disabled = disabled;
|
||||
btn.disabled = disabled;
|
||||
btn.setAttribute('aria-label', typeof page === 'number' ? `Halaman ${page}` : content);
|
||||
if (!disabled) {
|
||||
btn.addEventListener('click', () => {
|
||||
@@ -188,7 +212,7 @@ function initFilterTabs() {
|
||||
btn.classList.add('active');
|
||||
btn.setAttribute('aria-selected', 'true');
|
||||
activeFilter = btn.dataset.filter;
|
||||
currentPage = 1;
|
||||
currentPage = 1;
|
||||
renderCards();
|
||||
});
|
||||
});
|
||||
@@ -202,7 +226,7 @@ function initSearch() {
|
||||
input.addEventListener('input', () => {
|
||||
// Sanitasi input sebelum digunakan (cegah XSS)
|
||||
searchKeyword = sanitize(input.value).trim();
|
||||
currentPage = 1;
|
||||
currentPage = 1;
|
||||
renderCards();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -51,14 +51,6 @@ function openPopup(id) {
|
||||
|
||||
// Render konten popup
|
||||
popupBody.innerHTML = `
|
||||
<!-- Thumbnail / Hero gambar -->
|
||||
<img
|
||||
src="${bsw.thumbnail || ''}"
|
||||
alt="${bsw.nama}"
|
||||
class="popup-hero"
|
||||
onerror="this.style.display='none'"
|
||||
>
|
||||
|
||||
<div class="popup-body">
|
||||
<!-- Badge status + kategori -->
|
||||
<div class="popup-kategori-row">
|
||||
|
||||
Reference in New Issue
Block a user