diff --git a/assets/global/localization.js b/assets/global/localization.js index 82d1ca7..45130c4 100644 --- a/assets/global/localization.js +++ b/assets/global/localization.js @@ -569,6 +569,51 @@ const translations = { kontrib_guideline_3: "Sertakan fakta dan data yang akurat dan dapat diverifikasi.", kontrib_guideline_4: "Hindari plagiarisme. Karya harus orisinal.", kontrib_guideline_5: "Tim redaksi berhak menyunting konten untuk keperluan editorial.", + + // --- Group 06: ACAAB - Prestasi & Karya Mahasiswa --- + grp_acaab_hero_badge: "MAHASISWA & PRESTASI", + grp_acaab_hero_title: "Sorotan dan Prestasi Karya Mahasiswa", + grp_acaab_hero_desc: "Galeri pencapaian, penghargaan, dan portofolio inovasi teknologi yang dikembangkan oleh mahasiswa Program Studi Informatika Universitas Tanjungpura.", + grp_acaab_stats_heading: "Pencapaian dalam Angka", + grp_acaab_stats_total_ach: "Total Prestasi", + grp_acaab_stats_total_proj: "Total Karya", + grp_acaab_stats_intl: "Internasional", + grp_acaab_stats_national: "Nasional", + grp_acaab_stats_regional: "Regional", + grp_acaab_stats_data_note: "Data dikumpulkan per", + grp_acaab_ach_title: "Prestasi Mahasiswa", + grp_acaab_ach_desc: "Daftar pencapaian dan penghargaan yang diraih oleh mahasiswa Informatika UNTAN di berbagai kompetisi.", + grp_acaab_ach_search: "Cari prestasi...", + grp_acaab_ach_filter_level: "Semua Level", + grp_acaab_ach_filter_cat: "Semua Kategori", + grp_acaab_ach_filter_year: "Semua Tahun", + grp_acaab_ach_showing: "Menampilkan", + grp_acaab_ach_results: "prestasi", + grp_acaab_ach_load_more: "Tampilkan Lebih Banyak", + grp_acaab_ach_show_less: "Tampilkan Lebih Sedikit", + grp_acaab_ach_empty: "Tidak ada prestasi yang cocok dengan filter.", + grp_acaab_ach_loading: "Memuat data prestasi...", + grp_acaab_ach_error: "Gagal memuat data prestasi.", + grp_acaab_proj_title: "Top Projects & Portofolio", + grp_acaab_proj_desc: "Jelajahi inovasi digital dan aplikasi nyata yang dikembangkan oleh mahasiswa Informatika UNTAN.", + grp_acaab_proj_scroll_hint: "← Geser untuk melihat lebih banyak →", + grp_acaab_proj_search: "Cari karya...", + grp_acaab_proj_filter_cat: "Semua Kategori", + grp_acaab_proj_filter_year: "Semua Tahun", + grp_acaab_proj_load_more: "Tampilkan Lebih Banyak", + grp_acaab_proj_empty: "Tidak ada karya yang cocok dengan filter.", + grp_acaab_proj_loading: "Memuat data karya...", + grp_acaab_proj_error: "Gagal memuat data karya.", + grp_acaab_modal_team: "Tim / Peserta", + grp_acaab_modal_generation: "Angkatan", + grp_acaab_modal_members: "Anggota Tim", + grp_acaab_modal_visit: "Kunjungi Proyek", + grp_acaab_modal_read_more: "Selengkapnya", + grp_acaab_modal_no_link: "Link tidak tersedia", + grp_acaab_cta_badge: "HUBUNGI KAMI", + grp_acaab_cta_title: "Ingin Tahu Lebih Lanjut?", + grp_acaab_cta_desc: "Tertarik bergabung dengan Program Studi Informatika UNTAN, mendaftarkan prestasi mahasiswa, atau menjalin kerja sama? Hubungi kami sekarang.", + grp_acaab_cta_btn: "Chat via WhatsApp", }, en: { @@ -1135,6 +1180,51 @@ const translations = { kontrib_guideline_3: "Include accurate and verifiable facts and data.", kontrib_guideline_4: "Avoid plagiarism. Work must be original.", kontrib_guideline_5: "The editorial team reserves the right to edit content for editorial purposes.", + + // --- Group 06: ACAAB - Prestasi & Karya Mahasiswa --- + grp_acaab_hero_badge: "STUDENTS & ACHIEVEMENTS", + grp_acaab_hero_title: "Student Spotlights & Achievements", + grp_acaab_hero_desc: "Gallery of accomplishments, awards, and technology innovation portfolios developed by Informatics students of Tanjungpura University.", + grp_acaab_stats_heading: "Achievements by the Numbers", + grp_acaab_stats_total_ach: "Total Achievements", + grp_acaab_stats_total_proj: "Total Projects", + grp_acaab_stats_intl: "International", + grp_acaab_stats_national: "National", + grp_acaab_stats_regional: "Regional", + grp_acaab_stats_data_note: "Data collected as of", + grp_acaab_ach_title: "Student Achievements", + grp_acaab_ach_desc: "List of awards and accomplishments by Informatics UNTAN students in various competitions.", + grp_acaab_ach_search: "Search achievements...", + grp_acaab_ach_filter_level: "All Levels", + grp_acaab_ach_filter_cat: "All Categories", + grp_acaab_ach_filter_year: "All Years", + grp_acaab_ach_showing: "Showing", + grp_acaab_ach_results: "achievements", + grp_acaab_ach_load_more: "Show More", + grp_acaab_ach_show_less: "Show Less", + grp_acaab_ach_empty: "No achievements match the current filter.", + grp_acaab_ach_loading: "Loading achievements...", + grp_acaab_ach_error: "Failed to load achievements.", + grp_acaab_proj_title: "Top Projects & Portfolios", + grp_acaab_proj_desc: "Explore digital innovations and real-world applications developed by Informatics UNTAN students.", + grp_acaab_proj_scroll_hint: "← Scroll to see more →", + grp_acaab_proj_search: "Search projects...", + grp_acaab_proj_filter_cat: "All Categories", + grp_acaab_proj_filter_year: "All Years", + grp_acaab_proj_load_more: "Show More", + grp_acaab_proj_empty: "No projects match the current filter.", + grp_acaab_proj_loading: "Loading projects...", + grp_acaab_proj_error: "Failed to load projects.", + grp_acaab_modal_team: "Team / Participant", + grp_acaab_modal_generation: "Batch Year", + grp_acaab_modal_members: "Team Members", + grp_acaab_modal_visit: "Visit Project", + grp_acaab_modal_read_more: "Read More", + grp_acaab_modal_no_link: "Link not available", + grp_acaab_cta_badge: "CONTACT US", + grp_acaab_cta_title: "Want to Know More?", + grp_acaab_cta_desc: "Interested in joining the Informatics Study Program at UNTAN, registering a student achievement, or establishing a partnership? Contact us now.", + grp_acaab_cta_btn: "Chat via WhatsApp", } }; diff --git a/groups/ACAAB/hero.png b/groups/ACAAB/hero.png new file mode 100644 index 0000000..33f70e0 Binary files /dev/null and b/groups/ACAAB/hero.png differ diff --git a/groups/ACAAB/index.html b/groups/ACAAB/index.html index cc0e229..4fefabf 100644 --- a/groups/ACAAB/index.html +++ b/groups/ACAAB/index.html @@ -1,5 +1,6 @@  + @@ -7,9 +8,12 @@ - + - + @@ -1139,18 +1144,340 @@ -
+
- Tugas Halaman Statis + Tugas + Halaman Statis

Sorotan dan Prestasi Karya Mahasiswa

-

Kelompok 06 — ACAAB  ·  Informatika UNTAN 2026

+

Kelompok 06 — ACAAB  ·  Informatika UNTAN 2026 +

- -
- + + + + + +
+ +
+
+ +
+
+ +
+ MAHASISWA & PRESTASI +

Sorotan dan Prestasi Karya Mahasiswa

+

Temukan pencapaian luar biasa mahasiswa Informatika + UNTAN.

+
+ +
+
+
+
+ Mahasiswa Informatika UNTAN + +
+
+
+
+
+
+
+
+ + + +
+ + +
+
+ + +
+ +
+ Total Prestasi +
+ +
+ Total Portofolio +
+
+ + +
+ +
+
+ 0
+
+ Prestasi
Mahasiswa
+
+ +
+
+ 0
+
Internasional
+
+ +
+
+ 0
+
Nasional
+
+ +
+
+ 0
+
Regional
+
+ +
+
+ 0
+
+ Proyek
Mahasiswa
+
+
+ +
+ +

+

+
+ + +
+
+

Prestasi Mahasiswa

+

Daftar pencapaian dan penghargaan yang diraih oleh mahasiswa Informatika UNTAN di berbagai kompetisi.

+
+ + +
+ +
+
+ + Filter Prestasi +
+ +
+ +
+ + +
+ +
+ + + +
+
+ + +

+ + +
+
+ + +
+ + +
+
+ + +
+
+ +
+
+

Top Projects & Portofolio

+

Jelajahi inovasi digital dan aplikasi nyata yang dikembangkan oleh mahasiswa Informatika UNTAN.

+
+ +
+ + +
+
+ + +
+
+
+ + Filter Portofolio +
+ +
+
+ + +
+
+ + +
+
+ + +

← Geser untuk melihat lebih banyak →

+ + + + + +
+ +
+
+
+ + +
+
+ +
+
+ +
+ HUBUNGI KAMI +

Ingin Tahu Lebih Lanjut?

+

Tertarik bergabung dengan Program Studi + Informatika UNTAN, mendaftarkan prestasi mahasiswa, atau menjalin kerja sama? + Hubungi kami sekarang.

+
+ + +
+
+
+
+ + +
+
@@ -1215,8 +1542,8 @@
- Footer Illustration + Footer Illustration
@@ -1312,27 +1639,29 @@ })(); - + - + + + \ No newline at end of file diff --git a/groups/ACAAB/main.js b/groups/ACAAB/main.js new file mode 100644 index 0000000..a40ece1 --- /dev/null +++ b/groups/ACAAB/main.js @@ -0,0 +1,822 @@ +/** + * main.js — ACAAB Prestasi & Karya Mahasiswa + * Vanilla JS: fetch data Directus CMS, render sections, handle interactions. + */ + +// ─── Constants ──────────────────────────────────────────────────────────────── + +const API_BASE = 'https://api.ifuntanhub.dev'; +const LANG_KEY = 'if_untan_lang'; + +const BIDANG_MAP = { + 1: 'Kecerdasan Buatan', 2: 'Cyber Security', 3: 'Data', + 4: 'Software Engineering', 5: 'Competitive Programming', 6: 'IoT', + 7: 'Smart City', 8: 'Web3', 9: 'Karya Tulis Ilmiah', 10: 'Robotik', +}; + +// ─── Utilities ──────────────────────────────────────────────────────────────── + +function escHtml(str) { + if (str === null || str === undefined) return ''; + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +function getLang() { + try { + return localStorage.getItem(LANG_KEY) || + (navigator.language && navigator.language.startsWith('en') ? 'en' : 'id'); + } catch (_) { return 'id'; } +} + +// Baca dari translations global yang didefinisikan localization.js +function t(key) { + try { + const lang = getLang(); + if (typeof translations !== 'undefined' && translations[lang] && translations[lang][key]) { + return translations[lang][key]; + } + } catch (_) {} + return key; +} + +// ─── Data Transformation ────────────────────────────────────────────────────── + +function extractRank(title) { + const m = (title || '').match(/(Juara\s+\d+|Juara\s+Harapan\s*\d*|Top\s+\d+|Finalis|Peringkat\s+\d+|Medali\s+\w+)/i); + return m ? m[0] : 'Pemenang'; +} + +function extractLevel(title, category) { + const text = ((title || '') + ' ' + (category || '')).toLowerCase(); + if (/internasional|international|global|world|asean|unesco/.test(text)) return 'Internasional'; + if (/regional|kalbar|kalimantan|provinsi|kota|kabupaten|lokal/.test(text)) return 'Regional'; + return 'Nasional'; +} + +function extractCompetition(title) { + return (title || '') + .replace(/^(Juara\s+\d+|Juara\s+Harapan\s*\d*|Top\s+\d+|Finalis|Peringkat\s+\d+|Medali\s+\w+)\s+(di\s+|pada\s+|dalam\s+)?/i, '') + .trim(); +} + +function getImageUrl(item, seed) { + if (item.galeri && Array.isArray(item.galeri) && item.galeri.length > 0) { + const first = item.galeri[0]; + const imgId = typeof first === 'string' ? first : (first.directus_files_id || first.id); + if (imgId) return `${API_BASE}/assets/${imgId}`; + } + if (item.foto) return `${API_BASE}/assets/${item.foto}`; + return `https://picsum.photos/seed/${seed}/800/600`; +} + +function transformAchievement(item) { + const id = String(item.id); + const title = item.nama_prestasi || 'Untitled Achievement'; + + let category = 'Umum'; + if (item.bidang_id) { + category = (typeof item.bidang_id === 'object' && item.bidang_id.nama_bidang) + ? item.bidang_id.nama_bidang + : (BIDANG_MAP[item.bidang_id] || 'Umum'); + } + + const rank = extractRank(title); + const level = extractLevel(title, category); + const competition = extractCompetition(title); + const year = parseInt(item.tahun) || new Date().getFullYear(); + const anggota = Array.isArray(item.anggota) ? item.anggota : []; + const team = item.nama_tim || (anggota.length > 0 ? anggota[0].nama : 'Mahasiswa'); + const avatar = (item.nama_tim || title || 'IF').substring(0, 2).toUpperCase(); + const description = item.deskripsi || + `${team} berhasil meraih prestasi luar biasa sebagai ${rank} dalam ajang ${competition} tahun ${year} pada bidang ${category.toLowerCase()}.`; + + return { + id, title, category, competition, rank, year, level, description, + team, + nim: item.nim || '', + generation: item.angkatan ? `Angkatan ${item.angkatan}` : 'Informatika', + image: getImageUrl(item, `prestasi-${id}`), + avatar, + members: anggota.map(m => ({ nama: m.nama || '', angkatan: m.angkatan ? parseInt(m.angkatan) : undefined })), + }; +} + +function transformProject(item) { + const id = String(item.id); + const title = item.nama_proyek || 'Untitled Project'; + const anggota = Array.isArray(item.anggota) ? item.anggota : []; + const studentName = item.nama_mahasiswa || (anggota.length > 0 ? anggota[0].nama : 'Mahasiswa'); + const year = parseInt(item.tahun) || new Date().getFullYear(); + const description = item.deskripsi || 'Tidak ada deskripsi.'; + + let categories = []; + if (item.bidang && Array.isArray(item.bidang)) { + categories = item.bidang.map(b => { + if (b.bidang_id && typeof b.bidang_id === 'object' && b.bidang_id.nama_bidang) return b.bidang_id.nama_bidang; + if (typeof b.bidang_id === 'number') return BIDANG_MAP[b.bidang_id] || 'Umum'; + return 'Umum'; + }).filter(Boolean); + } + if (categories.length === 0) categories = ['Umum']; + + let link = item.url || null; + if (link && !/^https?:\/\//i.test(link)) link = `https://${link}`; + + return { + id, title, studentName, year, description, categories, link, + nim: item.nim || '', + image: getImageUrl(item, `project-${id}`), + catatan: item.catatan || '', + members: anggota.map(m => ({ nama: m.nama || '', nim: m.nim || '' })), + }; +} + +// ─── API Fetch ──────────────────────────────────────────────────────────────── + +async function fetchAchievements() { + const res = await fetch(`${API_BASE}/items/prestasi?fields=*.*`); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const { data } = await res.json(); + return (data || []) + .filter(item => item.status === 'published') + .map(transformAchievement); +} + +async function fetchProjects() { + const res = await fetch(`${API_BASE}/items/portfolio?fields=*,bidang.bidang_id.nama_bidang,galeri.*`); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const { data } = await res.json(); + return (data || []) + .filter(item => item.status === 'published') + .map(transformProject); +} + +// ─── App State ──────────────────────────────────────────────────────────────── + +let achievements = []; +let projects = []; +let achVisible = 3; +let projVisible = 6; + +let achFilter = { search: '', level: '', cat: '', year: '' }; +let projFilter = { search: '', cat: '', year: '' }; + +// ─── Init ───────────────────────────────────────────────────────────────────── + +document.addEventListener('DOMContentLoaded', async () => { + initWaButton(); + + // Loading placeholders + showSkeleton('acaab-ach-grid', 3); + showSkeleton('acaab-proj-track', 4); + + const [achResult, projResult] = await Promise.allSettled([ + fetchAchievements(), + fetchProjects(), + ]); + + if (achResult.status === 'fulfilled') { + achievements = achResult.value; + } else { + console.error('[ACAAB] achievements error:', achResult.reason); + showError('acaab-ach-grid', t('grp_acaab_ach_error')); + } + + if (projResult.status === 'fulfilled') { + projects = projResult.value; + } else { + console.error('[ACAAB] projects error:', projResult.reason); + showError('acaab-proj-track', t('grp_acaab_proj_error')); + } + + initStats(); + initAchievements(); + initProjects(); + updateDataNote(); +}); + +// Re-render saat bahasa berubah dari kit navbar +document.addEventListener('languageChanged', () => { + updateStats(); + renderAchievements(); + buildAchFilterOptions(); + renderProjects(); + buildProjFilterOptions(); + updateWaButton(); + updateDataNote(); + updateSearchPlaceholders(); +}); + +// ─── Skeleton / Error ───────────────────────────────────────────────────────── + +function showSkeleton(containerId, count) { + const el = document.getElementById(containerId); + if (!el) return; + el.innerHTML = Array(count).fill(0).map(() => + `
` + ).join(''); +} + +function showError(containerId, msg) { + const el = document.getElementById(containerId); + if (el) el.innerHTML = `

${escHtml(msg)}

`; +} + +// ─── Stats ──────────────────────────────────────────────────────────────────── + +function initStats() { + updateStats(); + + const statsEl = document.getElementById('acaab-stats'); + if (!statsEl) return; + + const intl = achievements.filter(a => a.level === 'Internasional').length; + const national = achievements.filter(a => a.level === 'Nasional').length; + const regional = achievements.filter(a => a.level === 'Regional').length; + + const observer = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (!entry.isIntersecting) return; + animateCount('stat-total-ach', achievements.length, 1600); + animateCount('stat-total-proj', projects.length, 1600); + animateCount('stat-intl', intl, 1200); + animateCount('stat-national', national, 1350); + animateCount('stat-regional', regional, 1500); + observer.disconnect(); + }); + }, { threshold: 0.4 }); + + observer.observe(statsEl); +} + +function updateStats() { + const set = (id, val) => { const el = document.getElementById(id); if (el) el.textContent = val; }; + set('stat-total-ach', achievements.length); + set('stat-total-proj', projects.length); + set('stat-intl', achievements.filter(a => a.level === 'Internasional').length); + set('stat-national', achievements.filter(a => a.level === 'Nasional').length); + set('stat-regional', achievements.filter(a => a.level === 'Regional').length); +} + +function animateCount(elId, target, duration) { + const el = document.getElementById(elId); + if (!el) return; + const start = performance.now(); + (function step(now) { + const p = Math.min((now - start) / duration, 1); + const eased = 1 - Math.pow(1 - p, 4); // easeOutQuart + el.textContent = Math.round(target * eased).toLocaleString(); + if (p < 1) requestAnimationFrame(step); + })(start); +} + +function updateDataNote() { + const el = document.getElementById('acaab-data-note'); + if (!el) return; + const lang = getLang(); + const locale = lang === 'en' ? 'en-US' : 'id-ID'; + const date = new Date().toLocaleDateString(locale, { day: 'numeric', month: 'long', year: 'numeric' }); + el.textContent = `${t('grp_acaab_stats_data_note')} ${date}`; +} + +// ─── Achievements ───────────────────────────────────────────────────────────── + +const ACH_INIT = 3; +const ACH_MORE = 3; + +const LEVEL_CLASS = { + Internasional: 'acaab-badge-intl', + Nasional: 'acaab-badge-nasional', + Regional: 'acaab-badge-regional', +}; + +function initAchievements() { + buildAchFilterOptions(); + renderAchievements(); + bindAchEvents(); +} + +function buildAchFilterOptions() { + const levelSel = document.getElementById('acaab-ach-level'); + const catSel = document.getElementById('acaab-ach-cat'); + const yearSel = document.getElementById('acaab-ach-year'); + if (!levelSel || !catSel || !yearSel) return; + + const levels = ['Internasional', 'Nasional', 'Regional']; + const cats = [...new Set(achievements.map(a => a.category))].sort(); + const years = [...new Set(achievements.map(a => String(a.year)))].filter(Boolean).sort((a, b) => b - a); + + levelSel.innerHTML = `` + + levels.map(l => ``).join(''); + catSel.innerHTML = `` + + cats.map(c => ``).join(''); + yearSel.innerHTML = `` + + years.map(y => ``).join(''); +} + +function filterAchievements() { + const q = achFilter.search.toLowerCase(); + return achievements.filter(a => { + const matchSearch = !q || [a.title, a.team, a.category, a.competition] + .some(s => (s || '').toLowerCase().includes(q)); + const matchLevel = !achFilter.level || a.level === achFilter.level; + const matchCat = !achFilter.cat || a.category === achFilter.cat; + const matchYear = !achFilter.year || String(a.year) === achFilter.year; + return matchSearch && matchLevel && matchCat && matchYear; + }); +} + +function renderAchievements() { + const grid = document.getElementById('acaab-ach-grid'); + const countEl = document.getElementById('acaab-ach-count'); + const moreBtn = document.getElementById('acaab-ach-more'); + const lessBtn = document.getElementById('acaab-ach-less'); + const resetBtn= document.getElementById('acaab-ach-reset'); + if (!grid) return; + + const filtered = filterAchievements(); + const visible = filtered.slice(0, achVisible); + const hasFilter = achFilter.search || achFilter.level || achFilter.cat || achFilter.year; + + if (countEl) countEl.textContent = + `${t('grp_acaab_ach_showing')} ${filtered.length} ${t('grp_acaab_ach_results')}`; + if (resetBtn) resetBtn.style.display = hasFilter ? 'flex' : 'none'; + + if (filtered.length === 0) { + grid.innerHTML = `

${escHtml(t('grp_acaab_ach_empty'))}

`; + if (moreBtn) moreBtn.style.display = 'none'; + if (lessBtn) lessBtn.style.display = 'none'; + return; + } + + grid.innerHTML = visible.map((a, i) => achCardHtml(a, i)).join(''); + + if (moreBtn) moreBtn.style.display = achVisible < filtered.length ? 'inline-block' : 'none'; + if (lessBtn) lessBtn.style.display = achVisible > ACH_INIT ? 'inline-block' : 'none'; + + grid.querySelectorAll('.acaab-ach-readmore').forEach(btn => { + btn.addEventListener('click', () => { + const ach = achievements.find(a => a.id === btn.dataset.id); + if (ach) openAchModal(ach); + }); + }); +} + +function achCardHtml(a, idx) { + return ` +
+
+ +
+ ${escHtml(a.title)} + +
+ + ${escHtml(a.rank)} +
+
+ +
+ +
+ ${escHtml(a.category)} + + ${escHtml(String(a.year))} + +
+ +

${escHtml(a.title)}

+ +

${escHtml(a.description)}

+ +
+
+ +
+
${escHtml(a.avatar)}
+
+ ${escHtml(a.team)} + ${escHtml(a.generation)} +
+
+ + ${escHtml(a.level)} +
+ + +
+
+
`; +} + +function bindAchEvents() { + const search = document.getElementById('acaab-ach-search'); + const level = document.getElementById('acaab-ach-level'); + const cat = document.getElementById('acaab-ach-cat'); + const year = document.getElementById('acaab-ach-year'); + const reset = document.getElementById('acaab-ach-reset'); + const moreBtn = document.getElementById('acaab-ach-more'); + const lessBtn = document.getElementById('acaab-ach-less'); + + const onChange = () => { + achFilter.search = search ? search.value.trim() : ''; + achFilter.level = level ? level.value : ''; + achFilter.cat = cat ? cat.value : ''; + achFilter.year = year ? year.value : ''; + achVisible = ACH_INIT; + renderAchievements(); + }; + + if (search) search.addEventListener('input', onChange); + if (level) level.addEventListener('change', onChange); + if (cat) cat.addEventListener('change', onChange); + if (year) year.addEventListener('change', onChange); + + if (reset) reset.addEventListener('click', () => { + achFilter = { search: '', level: '', cat: '', year: '' }; + if (search) search.value = ''; + if (level) level.value = ''; + if (cat) cat.value = ''; + if (year) year.value = ''; + achVisible = ACH_INIT; + renderAchievements(); + }); + + if (moreBtn) moreBtn.addEventListener('click', () => { achVisible += ACH_MORE; renderAchievements(); }); + if (lessBtn) lessBtn.addEventListener('click', () => { + achVisible = ACH_INIT; + document.getElementById('acaab-ach-section')?.scrollIntoView({ behavior: 'smooth', block: 'start' }); + renderAchievements(); + }); +} + +// ─── Achievement Modal ──────────────────────────────────────────────────────── + +function infoRow(icon, label, value) { + if (!value) return ''; + return `
+ +
+

${escHtml(label)}

+

${escHtml(value)}

+
+
`; +} + +function memberItem(nama, sub) { + const initial = (nama || '?').charAt(0).toUpperCase(); + return `
  • + ${initial} +
    + ${escHtml(nama)} + ${sub ? `${escHtml(sub)}` : ''} +
    +
  • `; +} + +function openAchModal(a) { + const membersHtml = a.members && a.members.length > 0 ? ` +
    +

    Anggota Tim

    +
      + ${a.members.map(m => memberItem(m.nama, m.angkatan ? `Angkatan ${m.angkatan}` : '')).join('')} +
    +
    ` : ''; + + renderModal(` + +
    + ${escHtml(a.title)} +
    + + + +
    + + ${escHtml(a.rank)} + + + ${escHtml(a.level)} + +
    +
    + +
    + ${escHtml(a.category)} +

    ${escHtml(a.title)}

    + +
    + ${infoRow('users', t('grp_acaab_modal_team'), a.team)} + ${a.nim ? infoRow('id-card', 'NIM', a.nim) : ''} + ${infoRow('user-graduate', t('grp_acaab_modal_generation'), a.generation)} + ${infoRow('calendar-alt', 'Tahun', String(a.year))} + ${infoRow('award', 'Kompetisi', a.competition)} + ${infoRow('tag', 'Bidang', a.category)} +
    + ${membersHtml} + +
    +

    Deskripsi

    +

    ${escHtml(a.description)}

    +
    + + +
    + `, true); +} + +// ─── Projects ───────────────────────────────────────────────────────────────── + +const PROJ_INIT = 6; +const PROJ_MORE = 3; + +function initProjects() { + buildProjFilterOptions(); + renderProjects(); + bindProjEvents(); +} + +function buildProjFilterOptions() { + const catSel = document.getElementById('acaab-proj-cat'); + const yearSel = document.getElementById('acaab-proj-year'); + if (!catSel || !yearSel) return; + + const cats = [...new Set(projects.flatMap(p => p.categories))].sort(); + const years = [...new Set(projects.map(p => String(p.year)))].filter(Boolean).sort((a, b) => b - a); + + catSel.innerHTML = `` + + cats.map(c => ``).join(''); + yearSel.innerHTML = `` + + years.map(y => ``).join(''); +} + +function filterProjects() { + const q = projFilter.search.toLowerCase(); + return projects.filter(p => { + const matchSearch = !q || [p.title, p.studentName, p.description, ...p.categories] + .some(s => (s || '').toLowerCase().includes(q)); + const matchCat = !projFilter.cat || p.categories.includes(projFilter.cat); + const matchYear = !projFilter.year || String(p.year) === projFilter.year; + return matchSearch && matchCat && matchYear; + }); +} + +function renderProjects() { + const track = document.getElementById('acaab-proj-track'); + const moreBtn = document.getElementById('acaab-proj-more'); + const resetBtn = document.getElementById('acaab-proj-reset'); + if (!track) return; + + const filtered = filterProjects(); + const visible = filtered.slice(0, projVisible); + const hasFilter = projFilter.search || projFilter.cat || projFilter.year; + + if (resetBtn) resetBtn.style.display = hasFilter ? 'flex' : 'none'; + + if (filtered.length === 0) { + track.innerHTML = `

    ${escHtml(t('grp_acaab_proj_empty'))}

    `; + if (moreBtn) moreBtn.style.display = 'none'; + return; + } + + track.innerHTML = visible.map((p, i) => projCardHtml(p, i)).join(''); + if (moreBtn) moreBtn.style.display = projVisible < filtered.length ? 'inline-block' : 'none'; + + track.querySelectorAll('.acaab-proj-readmore').forEach(btn => { + btn.addEventListener('click', () => { + const proj = projects.find(p => p.id === btn.dataset.id); + if (proj) openProjModal(proj); + }); + }); +} + +function projCardHtml(p, idx) { + const catBadges = p.categories.map(c => ` + + ${escHtml(c)} + `).join(''); + + return ` +
    +
    + +
    + ${escHtml(p.title)} + +
    +

    ${escHtml(p.title)}

    +
    ${catBadges}
    +
    +
    + +
    + +
    + ${p.studentName ? `${escHtml(p.studentName)}` : ''} + ${p.year ? `${escHtml(String(p.year))}` : ''} +
    + +

    ${escHtml(p.description)}

    + +
    + + ${p.link ? ` + + ${escHtml(t('grp_acaab_modal_visit'))} + ` : ''} +
    +
    +
    `; +} + +function bindProjEvents() { + const search = document.getElementById('acaab-proj-search'); + const cat = document.getElementById('acaab-proj-cat'); + const year = document.getElementById('acaab-proj-year'); + const reset = document.getElementById('acaab-proj-reset'); + const moreBtn = document.getElementById('acaab-proj-more'); + const prevBtn = document.getElementById('acaab-proj-prev'); + const nextBtn = document.getElementById('acaab-proj-next'); + + const onChange = () => { + projFilter.search = search ? search.value.trim() : ''; + projFilter.cat = cat ? cat.value : ''; + projFilter.year = year ? year.value : ''; + projVisible = PROJ_INIT; + renderProjects(); + }; + + if (search) search.addEventListener('input', onChange); + if (cat) cat.addEventListener('change', onChange); + if (year) year.addEventListener('change', onChange); + + if (reset) reset.addEventListener('click', () => { + projFilter = { search: '', cat: '', year: '' }; + if (search) search.value = ''; + if (cat) cat.value = ''; + if (year) year.value = ''; + projVisible = PROJ_INIT; + renderProjects(); + }); + + if (moreBtn) moreBtn.addEventListener('click', () => { projVisible += PROJ_MORE; renderProjects(); }); + + const track = document.getElementById('acaab-proj-track'); + if (prevBtn && track) prevBtn.addEventListener('click', () => { + const cardW = (track.firstElementChild ? track.firstElementChild.offsetWidth : 300) + 20; + track.scrollBy({ left: -cardW, behavior: 'smooth' }); + }); + if (nextBtn && track) nextBtn.addEventListener('click', () => { + const cardW = (track.firstElementChild ? track.firstElementChild.offsetWidth : 300) + 20; + track.scrollBy({ left: cardW, behavior: 'smooth' }); + }); +} + +// ─── Project Modal ──────────────────────────────────────────────────────────── + +function openProjModal(p) { + const membersHtml = p.members && p.members.length > 0 ? ` +
    +

    Anggota Tim

    +
      + ${p.members.map(m => memberItem(m.nama, m.nim || '')).join('')} +
    +
    ` : ''; + + const catBadgesImg = p.categories.map(c => ` + + ${escHtml(c)} + `).join(''); + + const linkSection = p.link + ? ` + ${escHtml(t('grp_acaab_modal_visit'))} + ` + : `
    + ${escHtml(t('grp_acaab_modal_no_link'))} +
    `; + + renderModal(` + +
    + ${escHtml(p.title)} +
    + + + +
    ${catBadgesImg}
    +
    + +
    +

    ${escHtml(p.title)}

    + +
    + ${p.studentName ? infoRow('users', t('grp_acaab_modal_team'), p.studentName) : ''} + ${p.nim ? infoRow('id-card', 'NIM', p.nim) : ''} + ${infoRow('calendar-alt', 'Tahun', String(p.year))} + ${infoRow('tag', 'Bidang', p.categories.join(', '))} +
    + ${membersHtml} + +
    +

    Deskripsi

    +

    ${escHtml(p.description)}

    + ${p.catatan ? ` +
    +

    Catatan Tambahan

    +

    ${escHtml(p.catatan)}

    +
    ` : ''} +
    + + ${linkSection} + +
    + `, true); +} + +// ─── Modal shared ───────────────────────────────────────────────────────────── + +function renderModal(innerHtml, hasCloseBtn = false) { + const container = document.getElementById('acaab-modal'); + if (!container) return; + + container.innerHTML = ` +
    +
    + ${innerHtml} +
    +
    `; + + document.body.classList.add('acaab-modal-open'); + + document.getElementById('acaab-modal-close')?.addEventListener('click', closeModal); + if (hasCloseBtn) { + document.getElementById('acaab-modal-close-btn')?.addEventListener('click', closeModal); + } + document.getElementById('acaab-modal-bd').addEventListener('click', e => { + if (e.target === e.currentTarget) closeModal(); + }); + document.addEventListener('keydown', onEsc); +} + +function closeModal() { + const c = document.getElementById('acaab-modal'); + if (c) c.innerHTML = ''; + document.body.classList.remove('acaab-modal-open'); + document.removeEventListener('keydown', onEsc); +} + +function onEsc(e) { if (e.key === 'Escape') closeModal(); } + +// ─── CTA / WhatsApp ─────────────────────────────────────────────────────────── + +const WA_NUMBER = '6285177655161'; + +function initWaButton() { updateWaButton(); } + +function updateWaButton() { + const btn = document.getElementById('acaab-cta-wa'); + if (!btn) return; + const msg = getLang() === 'en' + ? 'Hello! I would like to know more about Informatika UNTAN student achievements and projects.' + : 'Halo! Saya ingin mengetahui lebih lanjut tentang prestasi dan karya mahasiswa Informatika UNTAN.'; + btn.href = `https://wa.me/${WA_NUMBER}?text=${encodeURIComponent(msg)}`; +} + +// ─── Placeholder sync ───────────────────────────────────────────────────────── + +function updateSearchPlaceholders() { + const s1 = document.getElementById('acaab-ach-search'); + const s2 = document.getElementById('acaab-proj-search'); + if (s1) s1.placeholder = t('grp_acaab_ach_search'); + if (s2) s2.placeholder = t('grp_acaab_proj_search'); +} diff --git a/groups/ACAAB/style.css b/groups/ACAAB/style.css new file mode 100644 index 0000000..4aef586 --- /dev/null +++ b/groups/ACAAB/style.css @@ -0,0 +1,206 @@ +/* ===================================================== + ACAAB – Prestasi & Karya Mahasiswa + Custom styles yang tidak dicakup output.css + ===================================================== */ + +/* Cegah horizontal scroll tanpa clip full-bleed */ +body { overflow-x: hidden; } + +/* --- Full-bleed: section keluar dari container prose --- */ +.acaab-full-bleed { + width: 100vw; + margin-left: calc(50% - 50vw); + margin-right: calc(50% - 50vw); +} + +/* --- Sembunyikan scrollbar carousel tapi tetap scrollable --- */ +.acaab-hide-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; +} +.acaab-hide-scrollbar::-webkit-scrollbar { + display: none; +} + +/* --- Card ukuran responsif di carousel --- */ +.acaab-proj-card { + width: 80vw; + flex-shrink: 0; + scroll-snap-align: start; +} +@media (min-width: 768px) { .acaab-proj-card { width: 44vw; } } +@media (min-width: 1024px) { .acaab-proj-card { width: 30vw; } } + +/* --- Snap scroll carousel --- */ +.acaab-carousel-track { + scroll-snap-type: x mandatory; + -webkit-overflow-scrolling: touch; +} + +/* --- Animasi fade-in untuk cards --- */ +@keyframes acaab-fade-in { + from { opacity: 0; transform: translateY(14px); } + to { opacity: 1; transform: translateY(0); } +} +.acaab-fade-in { + animation: acaab-fade-in 0.35s ease both; +} + +/* --- Badge level warna --- */ +.acaab-badge-intl { background-color: #ca8a04; color: #fff; } +.acaab-badge-nasional { background-color: #1d4ed8; color: #fff; } +.acaab-badge-regional { background-color: #64748b; color: #fff; } + +/* --- Skeleton loading --- */ +.acaab-skeleton { + background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%); + background-size: 200% 100%; + animation: acaab-skeleton-pulse 1.5s infinite; + border-radius: 0.5rem; +} +@keyframes acaab-skeleton-pulse { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + +/* --- Modal backdrop --- */ +.acaab-modal-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.65); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + z-index: 200; + display: flex; + align-items: center; + justify-content: center; + padding: 1rem; +} + +/* --- Scroll lock saat modal terbuka --- */ +body.acaab-modal-open { + overflow: hidden; +} + +/* --- Stats grid: divider vertikal di desktop --- */ +@media (min-width: 768px) { + .acaab-stats-grid { + grid-template-columns: 1fr 1px 1fr 1px 1fr; + } +} + +/* --- Line clamp (browser lama) --- */ +.acaab-line-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} +.acaab-line-clamp-3 { + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} + +/* --- Achievement card --- */ +.acaab-ach-card { + position: relative; + background: white; + border-radius: 0.5rem; + overflow: hidden; + box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06); + border: 1px solid #e2e8f0; + display: flex; + flex-direction: column; + height: 100%; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} +.acaab-ach-card:hover { + transform: translateY(-4px); + box-shadow: 0 10px 25px rgba(0,0,0,0.12), 0 4px 10px rgba(0,0,0,0.08); +} +.acaab-ach-top-bar { + position: absolute; + top: 0; left: 0; right: 0; + height: 3px; + background: #003150; + z-index: 10; + transition: background 0.25s ease; +} +.acaab-ach-card:hover .acaab-ach-top-bar { background: #feb401; } +.acaab-ach-card:hover .acaab-card-title { color: #1e40af; } +.acaab-card-title { transition: color 0.2s ease; } +.acaab-ach-read-btn:hover { background: #003150 !important; color: white !important; } + +/* --- Project card wrapper --- */ +.acaab-proj-card-wrap { + position: relative; + background: white; + border-radius: 0.5rem; + overflow: hidden; + box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06); + border: 1px solid #e2e8f0; + display: flex; + flex-direction: column; + flex-shrink: 0; + transition: box-shadow 0.3s ease; +} +.acaab-proj-card-wrap:hover { box-shadow: 0 10px 25px rgba(0,0,0,0.12); } +.acaab-proj-left-bar { + position: absolute; + top: 0; left: 0; + width: 3px; height: 100%; + background: #feb401; + z-index: 10; + transform-origin: bottom; + transition: transform 0.3s ease; +} +.acaab-proj-card-wrap:hover .acaab-proj-left-bar { transform: scaleY(1.08); } +.acaab-proj-card-wrap:hover .acaab-card-img { transform: scale(1.05); } + +/* --- Card image zoom --- */ +.acaab-card-img { + transition: transform 0.5s ease; +} + +/* --- Stats grid dividers responsive --- */ +.acaab-stat-vdivider { display: none; } +@media (min-width: 768px) { + .acaab-stat-vdivider { display: flex; } + .acaab-stat-hdivider { display: none; } +} + +/* --- Filter focus --- */ +#acaab-ach-filters input:focus, +#acaab-ach-filters select:focus, +#acaab-proj-filters input:focus, +#acaab-proj-filters select:focus { + outline: 2px solid #003150; + outline-offset: 1px; + border-color: #003150; +} + +/* --- CTA inner layout: stacked mobile → row desktop --- */ +.acaab-cta-inner { + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + text-align: center; +} +@media (min-width: 768px) { + .acaab-cta-inner { + flex-direction: row; + align-items: center; + justify-content: space-between; + text-align: left; + gap: 3rem; + } +} + +/* --- Reduced motion --- */ +@media (prefers-reduced-motion: reduce) { + .acaab-fade-in, .acaab-skeleton, .acaab-card-img, + .acaab-ach-card, .acaab-proj-card-wrap { animation: none !important; transition: none !important; } +}