5 Commits

3 changed files with 166 additions and 46 deletions
+2
View File
@@ -578,6 +578,7 @@ const translations = {
grp_m4n1fest_hero_btn_stats: "Lihat Statistik Alumni",
grp_m4n1fest_hero_btn_join: "Gabung Grup IKA",
grp_m4n1fest_hero_stat_alumni: "TOTAL ALUMNI",
grp_m4n1fest_hero_stat_alumni_desc: "data diperoleh dari alumni terdata di tracer study",
grp_m4n1fest_hero_stat_batch: "ANGKATAN",
grp_m4n1fest_hero_stat_company: "PERUSAHAAN",
grp_m4n1fest_eyebrow_achievers: "High Achievers",
@@ -1285,6 +1286,7 @@ const translations = {
grp_m4n1fest_hero_btn_stats: "View Alumni Statistics",
grp_m4n1fest_hero_btn_join: "Join Alumni Group",
grp_m4n1fest_hero_stat_alumni: "TOTAL ALUMNI",
grp_m4n1fest_hero_stat_alumni_desc: "data obtained from alumni recorded in tracer study",
grp_m4n1fest_hero_stat_batch: "CLASSES",
grp_m4n1fest_hero_stat_company: "COMPANIES",
grp_m4n1fest_eyebrow_achievers: "High Achievers",
+159 -45
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="id" class="scroll-smooth">
<head>
<meta charset="UTF-8">
@@ -50,6 +50,11 @@
z-index: 40;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
*,
@@ -1183,15 +1188,16 @@
<div class="custom-stats">
<div class="custom-stat-box">
<div class="custom-stat-num custom-serif">850<span class="custom-stat-plus">+</span></div>
<div class="custom-stat-num custom-serif">212<span class="custom-stat-plus">+</span></div>
<div class="custom-stat-label" data-i18n="grp_m4n1fest_hero_stat_alumni">TOTAL ALUMNI</div>
<div class="custom-stat-desc" style="font-size: 0.65rem; color: #a1a1aa; margin-top: 4px; line-height: 1.35; font-weight: normal; font-family: sans-serif;" data-i18n="grp_m4n1fest_hero_stat_alumni_desc">data diperoleh dari alumni terdata di tracer study</div>
</div>
<div class="custom-stat-box">
<div class="custom-stat-num custom-serif">18<span class="custom-stat-plus">+</span></div>
<div class="custom-stat-num custom-serif">22</div>
<div class="custom-stat-label" data-i18n="grp_m4n1fest_hero_stat_batch">ANGKATAN</div>
</div>
<div class="custom-stat-box" style="border-right: none;">
<div class="custom-stat-num custom-serif">150<span class="custom-stat-plus">+</span></div>
<div class="custom-stat-num custom-serif">202<span class="custom-stat-plus">+</span></div>
<div class="custom-stat-label" data-i18n="grp_m4n1fest_hero_stat_company">PERUSAHAAN</div>
</div>
</div>
@@ -1466,50 +1472,66 @@
</div>
<div class="two-col">
<!-- bars -->
<div class="rv">
<!-- bars: skeleton selalu tampil, garis biru terisi otomatis dari data approved -->
<div class="rv" id="skill-bars-container">
<!-- Web Development -->
<div class="skill-row">
<span class="skill-label">Web Development</span>
<div class="skill-track">
<div class="skill-fill" style="width: 78%"></div>
</div>
<span class="skill-pct">78%</span>
<div class="skill-track"><div class="skill-fill" id="bar-web-development" style="width:0%;transition:width 0.8s ease"></div></div>
<span class="skill-pct" id="pct-web-development"></span>
</div>
<!-- Python / Data Science -->
<div class="skill-row">
<span class="skill-label">Python / Data Science</span>
<div class="skill-track">
<div class="skill-fill" style="width: 65%"></div>
</div>
<span class="skill-pct">65%</span>
<div class="skill-track"><div class="skill-fill" id="bar-python-data-science" style="width:0%;transition:width 0.8s ease"></div></div>
<span class="skill-pct" id="pct-python-data-science"></span>
</div>
<!-- Mobile Development -->
<div class="skill-row">
<span class="skill-label">Mobile Development</span>
<div class="skill-track">
<div class="skill-fill" style="width: 54%"></div>
</div>
<span class="skill-pct">54%</span>
<div class="skill-track"><div class="skill-fill" id="bar-mobile-development" style="width:0%;transition:width 0.8s ease"></div></div>
<span class="skill-pct" id="pct-mobile-development"></span>
</div>
<!-- UI/UX Design -->
<div class="skill-row">
<span class="skill-label">UI/UX Design</span>
<div class="skill-track">
<div class="skill-fill" style="width: 48%"></div>
</div>
<span class="skill-pct">48%</span>
<div class="skill-track"><div class="skill-fill" id="bar-uiux-design" style="width:0%;transition:width 0.8s ease"></div></div>
<span class="skill-pct" id="pct-uiux-design"></span>
</div>
<!-- Networking / DevOps -->
<div class="skill-row">
<span class="skill-label">Networking / DevOps</span>
<div class="skill-track">
<div class="skill-fill" style="width: 38%"></div>
</div>
<span class="skill-pct">38%</span>
<div class="skill-track"><div class="skill-fill" id="bar-networking-devops" style="width:0%;transition:width 0.8s ease"></div></div>
<span class="skill-pct" id="pct-networking-devops"></span>
</div>
<!-- Cybersecurity -->
<div class="skill-row">
<span class="skill-label">Cybersecurity</span>
<div class="skill-track">
<div class="skill-fill" style="width: 25%"></div>
</div>
<span class="skill-pct">25%</span>
<div class="skill-track"><div class="skill-fill" id="bar-cybersecurity" style="width:0%;transition:width 0.8s ease"></div></div>
<span class="skill-pct" id="pct-cybersecurity"></span>
</div>
<!-- Machine Learning / AI -->
<div class="skill-row">
<span class="skill-label">Machine Learning / AI</span>
<div class="skill-track"><div class="skill-fill" id="bar-machine-learning-ai" style="width:0%;transition:width 0.8s ease"></div></div>
<span class="skill-pct" id="pct-machine-learning-ai"></span>
</div>
<!-- Database -->
<div class="skill-row">
<span class="skill-label">Database</span>
<div class="skill-track"><div class="skill-fill" id="bar-database" style="width:0%;transition:width 0.8s ease"></div></div>
<span class="skill-pct" id="pct-database"></span>
</div>
<!-- Status note -->
<p id="skill-data-note" style="font-size:0.72rem;color:#9ca3af;margin-top:14px;text-align:right;font-style:italic;">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
style="display:inline-block;vertical-align:middle;margin-right:3px;animation:spin 1s linear infinite" id="skill-note-spinner">
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
</svg>
<span id="skill-note-text">Memuat data skill alumni...</span>
</p>
</div>
<!-- stat box -->
<div class="rv d2" style="height: 100%;">
@@ -1547,12 +1569,24 @@
</p>
</div>
<div class="two-col" style="align-items: flex-start;">
<!-- left: Table -->
<div class="two-col stat-cols" style="align-items: flex-start;">
<!-- left: Chart -->
<div class="chart-box rv d2" style="margin-top: 0;">
<div class="chart-header">
<div class="chart-title" data-i18n="grp_m4n1fest_chart_title">
Grafik Jumlah Lulusan
</div>
</div>
<div style="position: relative; height: 280px; width: 100%;">
<canvas id="kelulusanChart"></canvas>
</div>
</div>
<!-- right: Table -->
<div class="rv d1" style="background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; padding: 0;">
<table style="width: 100%; border-collapse: collapse; text-align: left; font-size: 0.9rem;">
<thead>
<tr style="background: #374151; color: #fff;">
<tr style="background: #1a3a6b; color: #fff;">
<th style="padding: 14px 20px; font-weight: 600;" data-i18n="grp_m4n1fest_table_head_period">Periode</th>
<th style="padding: 14px 20px; font-weight: 600; text-align: right;" data-i18n="grp_m4n1fest_table_head_amount">Jumlah</th>
</tr>
@@ -1584,18 +1618,6 @@
Sumber : BAK UNTAN
</div>
</div>
<!-- right: Chart -->
<div class="chart-box rv d2" style="margin-top: 0;">
<div class="chart-header">
<div class="chart-title" data-i18n="grp_m4n1fest_chart_title">
Grafik Jumlah Lulusan
</div>
</div>
<div style="position: relative; height: 280px; width: 100%;">
<canvas id="kelulusanChart"></canvas>
</div>
</div>
</div>
</div>
</section>
@@ -2705,6 +2727,98 @@
}
}
/* ── SKILL DISTRIBUTION — Live fetch from Google Sheets (approved rows only) ── */
(function fetchSkillData() {
const SHEET_ID = '1mtVB1vLev-OWz6aTuuddGKPS8tCv8MJcGpzws8irpxk';
// Col I=Skill1, J=Skill2, K=Skill3, L=Approved (admin status)
const query = encodeURIComponent("select I,J,K where lower(L)='approved'");
const url = `https://docs.google.com/spreadsheets/d/${SHEET_ID}/gviz/tq?tqx=out:json&tq=${query}`;
// Map: skill label → bar id prefix (must match id="bar-XXX" and id="pct-XXX" in HTML)
const SKILL_MAP = {
'Web Development' : 'web-development',
'Mobile Development' : 'mobile-development',
'Python / Data Science': 'python-data-science',
'UI/UX Design' : 'uiux-design',
'Networking / DevOps' : 'networking-devops',
'Cybersecurity' : 'cybersecurity',
'Machine Learning / AI': 'machine-learning-ai',
'Database' : 'database',
};
const spinner = document.getElementById('skill-note-spinner');
const noteText = document.getElementById('skill-note-text');
function setNote(text, spin, isCta) {
if (spinner) spinner.style.animation = spin ? 'spin 1s linear infinite' : 'none';
if (!noteText) return;
if (isCta) {
noteText.innerHTML = text + ' <a href="#kontribusi" style="color:#2352a0;font-weight:700;text-decoration:underline;text-underline-offset:2px;">Isi form kontribusi</a>';
} else {
noteText.textContent = text;
}
}
function normalizeSkill(val) {
if (!val) return null;
const s = val.toLowerCase().trim();
if (s.includes('web')) return 'Web Development';
if (s.includes('mobile')) return 'Mobile Development';
if (s.includes('python') || s.includes('data science') || s.includes('data science')) return 'Python / Data Science';
if (s.includes('ui') || s.includes('ux')) return 'UI/UX Design';
if (s.includes('network') || s.includes('devops')) return 'Networking / DevOps';
if (s.includes('cyber')) return 'Cybersecurity';
if (s.includes('machine') || s.includes('ai') || s.includes('artificial')) return 'Machine Learning / AI';
if (s.includes('database') || s.includes('db')) return 'Database';
return null;
}
fetch(url)
.then(res => res.text())
.then(raw => {
const json = JSON.parse(raw.replace(/^[^{]+/, '').replace(/[);]+$/, ''));
const rows = (json.table && json.table.rows) ? json.table.rows : [];
if (rows.length === 0) {
setNote('Data skill masih kosong, bantu kami lengkapi data ini!', false, false);
return;
}
// Count normalized skill mentions
const counts = {};
rows.forEach(row => {
[0, 1, 2].forEach(colIdx => {
const cell = row.c && row.c[colIdx];
const val = cell && cell.v ? String(cell.v).trim() : '';
const normalized = normalizeSkill(val);
if (normalized) {
counts[normalized] = (counts[normalized] || 0) + 1;
}
});
});
const totalMentions = Object.values(counts).reduce((s, c) => s + c, 0);
const maxCount = Math.max(...Object.values(counts), 0);
// Update bars for predefined skills
Object.entries(SKILL_MAP).forEach(([label, idKey]) => {
const count = counts[label] || 0;
const pct = totalMentions > 0 ? Math.round((count / totalMentions) * 100) : 0;
const barPct = maxCount > 0 ? Math.round((count / maxCount) * 100) : 0;
const barEl = document.getElementById('bar-' + idKey);
const pctEl = document.getElementById('pct-' + idKey);
if (barEl) barEl.style.width = barPct + '%';
if (pctEl) pctEl.textContent = count > 0 ? pct + '%' : '—';
});
setNote(`Data dari ${rows.length} alumni yang diverifikasi.`, false, false);
})
.catch(() => {
setNote('Data skill masih kosong, bantu kami lengkapi data ini!', false, false);
});
})();
/* ── REVEAL ON SCROLL ── */
const ro = new IntersectionObserver(
(entries) => {
+5 -1
View File
@@ -666,11 +666,15 @@ ul.achiever-achievements li::before {
gap: 52px;
align-items: start;
}
.stat-cols {
grid-template-columns: 1.3fr 0.7fr;
}
.two-col > * {
min-width: 0;
}
@media (max-width: 760px) {
.two-col {
.two-col,
.stat-cols {
grid-template-columns: 1fr;
gap: 32px;
}