revisi dawg

This commit is contained in:
Araya's Project
2026-06-04 17:55:58 +07:00
parent df4878a8af
commit a40a67875c
6 changed files with 363 additions and 309 deletions
+11 -4
View File
@@ -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;
}
}
}
+57 -33
View File
@@ -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();
});
}
-8
View File
@@ -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">